init template

main
Khanh 2025-05-10 19:23:08 +07:00
commit c62d531e8d
19 changed files with 4004 additions and 0 deletions

3
.env.example Normal file
View File

@ -0,0 +1,3 @@
APP_ID=2984220425889259895
ZMP_TOKEN=
VITE_GAME_LINK=

40
.gitignore vendored Normal file
View File

@ -0,0 +1,40 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Dependency directories
node_modules/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# Misc
.DS_Store
Thumbs.db
# Production build
www/
.idea/

126
README.md Normal file
View File

@ -0,0 +1,126 @@
# Zalo Mini App
## Development
### Using Zalo Mini App Extension
1. Install [Visual Studio Code](https://code.visualstudio.com/download) and [Zalo Mini App Extension](https://mini.zalo.me/docs/dev-tools).
1. In the **Home** tab, process **Config App ID** and **Install Dependencies**.
1. Navigate to the **Run** tab, select the suitable launcher, and click **Start**.
### Using Zalo Mini App CLI
1. [Install Node JS](https://nodejs.org/en/download/).
1. [Install Zalo Mini App CLI](https://mini.zalo.me/docs/dev-tools/cli/intro/).
1. **Install dependencies**:
```bash
npm install
```
1. **Start** the dev server:
```bash
zmp start
```
1. **Open** `localhost:3000` in your browser.
## Deployment
1. **Create** a mini program. For instructions on how to create a mini program, please refer to the [Coffee Shop Tutorial](https://mini.zalo.me/tutorial/coffee-shop/step-1/)
1. **Deploy** your mini program to Zalo using the mini app ID created.
- **Using Zalo Mini App Extension**: navigate to the **Deploy** panel > **Login** > **Deploy**.
- **Using Zalo Mini App CLI**:
```bash
zmp login
zmp deploy
```
1. Open the mini app in Zalo by scanning the QR code.
## Resources
- [Zalo Mini App Official Website](https://mini.zalo.me/)
- [ZaUI Documentation](https://mini.zalo.me/documents/zaui/)
- [ZMP SDK Documentation](https://mini.zalo.me/documents/api/)
- [DevTools Documentation](https://mini.zalo.me/docs/dev-tools/)
- [Ready-made Mini App Templates](https://mini.zalo.me/zaui-templates)
- [Community Support](https://mini.zalo.me/community)
# Unity Game
## Getting started
1. **Install dependencies**:
```bash
npm install react-unity-webgl
```
or
```bash
pnpm install react-unity-webgl
```
or
```bash
yarn install react-unity-webgl
```
1. **Create** the game component:
```tsx
import clsx from 'clsx';
import { useEffect } from 'react';
import { Unity, useUnityContext } from 'react-unity-webgl';
import zmpSdk from 'zmp-sdk';
export const UnityGame = () => {
const gameLink = import.meta.env.VITE_GAME_LINK;
const { unityProvider, isLoaded, loadingProgression, UNSAFE__unityInstance } = useUnityContext({
loaderUrl: `${gameLink}/Build/Build.loader.js`,
dataUrl: `${gameLink}/Build/Build.data.unityweb`,
frameworkUrl: `${gameLink}/Build/Build.framework.js.unityweb`,
codeUrl: `${gameLink}/Build/Build.wasm.unityweb`,
});
useEffect(() => {
window.ZaloMiniAppSDK = zmpSdk;
window.unityInstance = UNSAFE__unityInstance;
}, [UNSAFE__unityInstance]);
return (
<>
{!isLoaded && (
<div className={'h-full w-full items-center justify-center text-center'}>
Loading {loadingProgression * 100 + 10}%
</div>
)}
<Unity
id='unity-canvas'
unityProvider={unityProvider}
className={clsx('h-full w-full', !isLoaded && 'hidden')}
devicePixelRatio={window.devicePixelRatio}
/>
</>
);
};
```
1. **Add** game link to env:
```
APP_ID=
ZMP_TOKEN=
VITE_GAME_LINK=[your game link]
```
1. **Import** and use it anywhere:
```tsx
export default function MainApp() {
return (
<div className={'flex h-dvh w-screen flex-col items-center justify-center'}>
<UnityGame />
</div>
);
}
```

16
app-config.json Normal file
View File

@ -0,0 +1,16 @@
{
"app": {
"title": "promogame-react-unity-template",
"textColor": {
"light": "black",
"dark": "white"
},
"statusBar": "transparent",
"actionBarHidden": true,
"hideIOSSafeAreaBottom": true,
"hideAndroidBottomNavigationBar": true
},
"listCSS": [],
"listSyncJS": [],
"listAsyncJS": []
}

67
biome.json Normal file
View File

@ -0,0 +1,67 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.3/schema.json",
"organizeImports": {
"enabled": true
},
"files": {
"ignoreUnknown": false,
"ignore": ["src/main.tsx", "src/generated/*.ts"]
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"complexity": {
"useSimplifiedLogicExpression": "warn"
},
"nursery": {
"useSortedClasses": "warn"
},
"correctness": {
"noUnusedImports": "error",
"noUnusedPrivateClassMembers": "error",
"noUnusedVariables": "error"
},
"suspicious": {
"noConsoleLog": "off",
"noArrayIndexKey": "off"
},
"style": {
"noNonNullAssertion": "off"
}
}
},
"formatter": {
"enabled": true,
"formatWithErrors": true,
"attributePosition": "auto",
"indentStyle": "space",
"indentWidth": 2,
"lineEnding": "lf",
"lineWidth": 120
},
"vcs": {
"enabled": true,
"clientKind": "git"
},
"javascript": {
"formatter": {
"arrowParentheses": "always",
"bracketSameLine": false,
"bracketSpacing": true,
"quoteStyle": "single",
"jsxQuoteStyle": "single",
"quoteProperties": "asNeeded",
"semicolons": "always",
"trailingCommas": "all",
"indentStyle": "space",
"indentWidth": 2
}
},
"json": {
"formatter": {
"enabled": true,
"trailingCommas": "none"
}
}
}

22
index.html Normal file
View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, viewport-fit=cover"
/>
<meta name="theme-color" content="#007aff" />
<meta name="format-detection" content="telephone=no" />
<meta name="msapplication-tap-highlight" content="no" />
<title>Zalo Mini App</title>
</head>
<body>
<div id="app"></div>
<!-- built script files will be auto injected -->
<script type="module" src="/src/app.ts"></script>
</body>
</html>

51
package.json Normal file
View File

@ -0,0 +1,51 @@
{
"name": "promogame-unity-template",
"private": true,
"version": "1.0.0",
"description": "zmp-blank-templates",
"repository": "",
"license": "UNLICENSED",
"browserslist": [
"Android >= 5",
"IOS >= 9.3",
"Edge >= 15",
"Safari >= 9.1",
"Chrome >= 49",
"Firefox >= 31",
"Samsung >= 5"
],
"scripts": {
"login": "zmp login",
"start": "zmp start",
"start-device": "zmp start -D",
"deploy": "zmp deploy"
},
"dependencies": {
"clsx": "^2.1.1",
"parse-path": "^7.0.0",
"prop-types": "^15.8.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-unity-webgl": "^9.6.0",
"usehooks-ts": "^3.1.0",
"zmp-sdk": "latest",
"zmp-ui": "latest",
"zustand": "^5.0.1"
},
"devDependencies": {
"@biomejs/biome": "^1.9.4",
"@types/react": "^18.3.1",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.1",
"autoprefixer": "^10.4.19",
"cross-env": "^7.0.3",
"postcss": "^8.4.38",
"postcss-cli": "^8.3.1",
"postcss-preset-env": "^6.7.0",
"sass": "^1.76.0",
"tailwindcss": "^3.4.3",
"vite": "^5.2.13",
"vite-tsconfig-paths": "^4.3.2",
"zmp-vite-plugin": "latest"
}
}

3474
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

5
postcss.config.js Normal file
View File

@ -0,0 +1,5 @@
const tailwindcss = require("tailwindcss");
module.exports = {
plugins: [tailwindcss("./tailwind.config.js"), require("autoprefixer")],
};

9
src/MainApp.tsx Normal file
View File

@ -0,0 +1,9 @@
import { UnityGame } from './components/unity-game';
export default function MainApp() {
return (
<div className={'flex h-dvh w-screen flex-col items-center justify-center'}>
<UnityGame />
</div>
);
}

21
src/app.ts Normal file
View File

@ -0,0 +1,21 @@
// ZaUI stylesheet
import 'zmp-ui/zaui.css';
// Tailwind stylesheet
import 'css/tailwind.scss';
// Your stylesheet
import 'css/app.scss';
// React core
import React from 'react';
import { createRoot } from 'react-dom/client';
// Expose app configuration
import appConfig from '../app-config.json';
if (!window.APP_CONFIG) {
window.APP_CONFIG = appConfig;
}
import App from './MainApp';
const root = createRoot(document.getElementById('app')!);
root.render(React.createElement(App));

View File

@ -0,0 +1,35 @@
import clsx from 'clsx';
import { useEffect } from 'react';
import { Unity, useUnityContext } from 'react-unity-webgl';
import zmpSdk from 'zmp-sdk';
export const UnityGame = () => {
const gameLink = import.meta.env.VITE_GAME_LINK;
const { unityProvider, isLoaded, loadingProgression, UNSAFE__unityInstance } = useUnityContext({
loaderUrl: `${gameLink}/Build/Build.loader.js`,
dataUrl: `${gameLink}/Build/Build.data.unityweb`,
frameworkUrl: `${gameLink}/Build/Build.framework.js.unityweb`,
codeUrl: `${gameLink}/Build/Build.wasm.unityweb`,
});
useEffect(() => {
window.ZaloMiniAppSDK = zmpSdk;
window.unityInstance = UNSAFE__unityInstance;
}, [UNSAFE__unityInstance]);
return (
<>
{!isLoaded && (
<div className={'h-full w-full items-center justify-center text-center'}>
Loading {loadingProgression * 100 + 10}%
</div>
)}
<Unity
id='unity-canvas'
unityProvider={unityProvider}
className={clsx('h-full w-full', !isLoaded && 'hidden')}
devicePixelRatio={window.devicePixelRatio}
/>
</>
);
};

38
src/css/app.scss Normal file
View File

@ -0,0 +1,38 @@
.page {
padding: 16px 16px 96px 16px;
}
.section-container {
padding: 16px;
background: #ffffff;
border-radius: 8px;
margin-bottom: 24px;
}
.progress {
display: inline-block;
height: 50px;
width: 80%;
margin-bottom: 200px;
border-radius: 99px;
background: #f9f9f9;
}
.bar {
border-radius: 99px;
width: 0%;
height: 100%;
transition: width;
transition-duration: 1s;
transition-timing-function: cubic-bezier(.36,.55,.63,.48);
box-shadow: 0px 45px 50px rgba(0, 0, 0, 0.25);
}
.pattern {
background-color: #DFDBE5;
}
.zaui-list-item {
cursor: pointer;
}

3
src/css/tailwind.scss Normal file
View File

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

10
src/lib/types.ts Normal file
View File

@ -0,0 +1,10 @@
export {};
declare global {
interface Window {
APP_ID: string;
APP_CONFIG: unknown;
ZaloMiniAppSDK: unknown;
unityInstance: unknown;
}
}

14
tailwind.config.js Normal file
View File

@ -0,0 +1,14 @@
module.exports = {
darkMode: ["selector", '[zaui-theme="dark"]'],
purge: {
enabled: true,
content: ["./src/**/*.{js,jsx,ts,tsx,vue}"],
},
theme: {
extend: {
fontFamily: {
mono: ["Roboto Mono", "monospace"],
},
},
},
};

35
tsconfig.json Normal file
View File

@ -0,0 +1,35 @@
{
"compilerOptions": {
"noEmit": true,
"target": "es6",
"module": "esnext",
"noImplicitAny": false,
"preserveConstEnums": true,
"jsx": "react-jsx",
"lib": ["dom", "dom.iterable", "es5", "es6", "es7", "es2017", "es2020"],
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"allowJs": true,
"skipLibCheck": true,
"declaration": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"noEmitOnError": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedLocals": false,
"strict": true,
"strictFunctionTypes": false,
"pretty": true,
"removeComments": true,
"sourceMap": true,
"resolveJsonModule": true,
"types": ["vite/client"],
"baseUrl": "src"
},
"exclude": ["node_modules"],
"include": ["src"]
}

16
vite.config.mts Normal file
View File

@ -0,0 +1,16 @@
import { defineConfig } from 'vite';
import zaloMiniApp from 'zmp-vite-plugin';
import react from '@vitejs/plugin-react';
import tsconfigPaths from 'vite-tsconfig-paths';
// https://vitejs.dev/config/
export default () => {
return defineConfig({
root: './src',
base: '',
plugins: [zaloMiniApp(), react(), tsconfigPaths()],
build: {
assetsInlineLimit: 0,
},
});
};

19
zmp-cli.json Normal file
View File

@ -0,0 +1,19 @@
{
"cwd": "/Users/lap15182-local/Documents/zmp-blank-templates",
"name": "zmp-blank-templates",
"framework": "react-typescript",
"cssPreProcessor": "scss",
"includeTailwind": true,
"package": "zmp-ui",
"stateManagement": "jotai",
"newProject": true,
"template": "single-view",
"theming": {
"customColor": false,
"color": "#007aff",
"darkTheme": false,
"iconFonts": true,
"fillBars": false,
"useUiKits": true
}
}