diff --git a/.eslintrc.js b/.eslintrc.js index 8387fcc7..d1c2ed6d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -49,4 +49,12 @@ module.exports = { }, ], }, + overrides: [ + { + files: ['packages/zombie/**/*'], + rules: { + 'no-console': 'off', + }, + }, + ], }; diff --git a/packages/toolbox/package.json b/packages/toolbox/package.json index 4cb3a94c..7bd33afa 100644 --- a/packages/toolbox/package.json +++ b/packages/toolbox/package.json @@ -12,7 +12,7 @@ "dependencies": { "@mantou/ecs": "^0.0.1", "@mantou/gem": "^1.7.5", - "duoyun-ui": "^1.1.5", + "duoyun-ui": "^1.1.9", "jszip": "^3.10.0", "qoijs": "^1.0.0" }, diff --git a/packages/toolbox/src/pages/audio.ts b/packages/toolbox/src/pages/audio.ts index ce5bc509..df0f6d2f 100644 --- a/packages/toolbox/src/pages/audio.ts +++ b/packages/toolbox/src/pages/audio.ts @@ -1,7 +1,7 @@ import { GemElement, html, adoptedStyle, customElement, createCSSSheet, css } from '@mantou/gem'; import { theme } from 'duoyun-ui/lib/theme'; import { utf8ToB64 } from 'duoyun-ui/lib/encode'; -import { throttle } from 'duoyun-ui/lib/utils'; +import { throttle } from 'duoyun-ui/lib/timer'; import { getInputItemType, getInputItemValue, normalizeFilename, sampleToChart, saveFile } from 'src/utils'; import QOI from 'qoijs'; import JSZip from 'jszip'; @@ -223,7 +223,7 @@ export class PAudioElement extends GemElement { `, )} - + ${Object.entries(args).map(([k, v]) => diff --git a/packages/toolbox/src/pages/font.ts b/packages/toolbox/src/pages/font.ts index 954a9830..9afde1ec 100644 --- a/packages/toolbox/src/pages/font.ts +++ b/packages/toolbox/src/pages/font.ts @@ -3,7 +3,7 @@ import { theme } from 'duoyun-ui/lib/theme'; import { getCorSrc, getInputItemType, getInputItemValue, normalizeFilename, saveFile } from 'src/utils'; import QOI from 'qoijs'; import { Font, encodeFont } from '@mantou/ecs'; -import { sleep } from 'duoyun-ui/lib/utils'; +import { sleep } from 'duoyun-ui/lib/timer'; import type { DuoyunFormItemElement } from 'duoyun-ui/elements/form'; import type { DuoyunSelectElement } from 'duoyun-ui/elements/select'; diff --git a/packages/webapp/package.json b/packages/webapp/package.json index a25d4e78..7b630871 100644 --- a/packages/webapp/package.json +++ b/packages/webapp/package.json @@ -19,7 +19,7 @@ "@nesbox/fceux": "^0.0.1", "@nesbox/mtapp": "^0.0.1", "@nesbox/wasm4": "^0.0.1", - "duoyun-ui": "^1.1.8", + "duoyun-ui": "^1.1.9", "front-matter": "^4.0.2", "graphql": "^16.2.0", "jszip": "^3.10.0", diff --git a/packages/webapp/src/app.ts b/packages/webapp/src/app.ts index 35528a71..d9e20657 100644 --- a/packages/webapp/src/app.ts +++ b/packages/webapp/src/app.ts @@ -16,9 +16,9 @@ import { import { hotkeys } from 'duoyun-ui/lib/hotkeys'; import { Loadbar } from 'duoyun-ui/elements/page-loadbar'; import { createPath } from '@mantou/gem/elements/route'; -import { routes, locationStore } from 'src/routes'; import { DuoyunRouteElement } from 'duoyun-ui/elements/route'; import { mediaQuery } from '@mantou/gem/helper/mediaquery'; +import { routes, locationStore } from 'src/routes'; import { preventDefault } from 'src/utils/common'; import { paramKeys, queryKeys, viewTransitionName } from 'src/constants'; @@ -239,7 +239,7 @@ export class AppRootElement extends GemElement { .open=${!!configure.settingsState} @close=${toggleSettingsState} > - + { - updateStore(configure, { windowHasFocus: true }); + updateConfigureStore({ windowHasFocus: true }); }); addEventListener('blur', () => { - updateStore(configure, { windowHasFocus: false }); + updateConfigureStore({ windowHasFocus: false }); }); -export const [configure] = createCacheStore( +export const [configure, updateConfigureStore] = useCacheStore( localStorageKeys.CONFIGURE_LOCAL_STORAGE_KEY, { windowHasFocus: document.hasFocus(), @@ -201,41 +201,41 @@ export function getShortcut(command: keyof typeof defaultShortcuts, isDisplay = } export const deleteUser = () => { - updateStore(configure, { user: undefined, profile: undefined }); + updateConfigureStore({ user: undefined, profile: undefined }); }; export const toggleScreencastMode = () => { - updateStore(configure, { screencastMode: !configure.screencastMode }); + updateConfigureStore({ screencastMode: !configure.screencastMode }); }; export const toggleFriendListState = () => { - updateStore(configure, { friendListState: !configure.friendListState }); + updateConfigureStore({ friendListState: !configure.friendListState }); }; export const toggleSettingsState = () => { - updateStore(configure, { settingsState: !configure.settingsState }); + updateConfigureStore({ settingsState: !configure.settingsState }); if (!configure.settingsState) dispatchGlobalEvent(globalEvents.CLOSE_SETTINGS, null); }; export const toggleSideNavState = (sideNavState = !configure.sideNavState) => { - updateStore(configure, { sideNavState }); + updateConfigureStore({ sideNavState }); }; export const toggleSearchState = () => { - updateStore(configure, { + updateConfigureStore({ searchState: !configure.searchState, searchCommand: configure.searchState ? undefined : configure.searchCommand, }); }; export const setSearchCommand = (command: SearchCommand | null) => { - updateStore(configure, { searchCommand: command || undefined, searchState: true }); + updateConfigureStore({ searchCommand: command || undefined, searchState: true }); }; export const setNesFile = (file?: File) => { - updateStore(configure, { openNesFile: file }); + updateConfigureStore({ openNesFile: file }); }; -export const navStore = createStore({ +export const [navStore, updateNavStore] = useStore({ room: false, }); diff --git a/packages/webapp/src/elements/canvas.ts b/packages/webapp/src/elements/canvas.ts index 99a00f2e..94066f2f 100644 --- a/packages/webapp/src/elements/canvas.ts +++ b/packages/webapp/src/elements/canvas.ts @@ -13,9 +13,9 @@ import { import { BaseDirectory } from '@tauri-apps/api/fs'; import { Time } from 'duoyun-ui/lib/time'; +import { logger } from 'src/logger'; import { saveFile } from 'src/utils/common'; import { VideoFilter } from 'src/constants'; -import { logger } from 'src/logger'; import normalVert from 'src/shaders/normal.vert?raw'; import 'duoyun-ui/elements/reflect'; diff --git a/packages/webapp/src/elements/fps.ts b/packages/webapp/src/elements/fps.ts index 4a7d3bac..f74391f2 100644 --- a/packages/webapp/src/elements/fps.ts +++ b/packages/webapp/src/elements/fps.ts @@ -5,9 +5,8 @@ import { customElement, createCSSSheet, css, - createStore, connectStore, - updateStore, + useStore, } from '@mantou/gem'; import { theme } from 'src/theme'; @@ -20,7 +19,7 @@ export const fpsStyle = createCSSSheet(css` } `); -const store = createStore({ +const [store, update] = useStore({ min: 0, max: 0, fps: 0, @@ -53,12 +52,7 @@ const tick = () => { }); const avgFps = Math.round(sum / frames.length); - updateStore(store, { - fps, - avgFps, - min, - max, - }); + update({ fps, avgFps, min, max }); timer = requestAnimationFrame(tick); }; diff --git a/packages/webapp/src/elements/license.ts b/packages/webapp/src/elements/license.ts index 85d22a16..fe0bc451 100644 --- a/packages/webapp/src/elements/license.ts +++ b/packages/webapp/src/elements/license.ts @@ -5,16 +5,15 @@ import { customElement, createCSSSheet, css, - createStore, connectStore, - updateStore, + useStore, } from '@mantou/gem'; import { getCorSrc } from 'src/utils/common'; import 'duoyun-ui/elements/loading'; -const store = createStore({ license: '' }); +const [store, update] = useStore({ license: '' }); const style = createCSSSheet(css` :host { @@ -32,7 +31,7 @@ export class NesboxLicenseElement extends GemElement { fetch(getCorSrc('https://raw.githubusercontent.com/mantou132/nesbox/dev/LICENSE')) .then((res) => res.text()) .then((license) => { - updateStore(store, { + update({ license: license .split(/\n{2,}/) .map((line) => line.replaceAll('\n', ' ')) diff --git a/packages/webapp/src/gamepad.ts b/packages/webapp/src/gamepad.ts index 68cb34b1..90ed5624 100644 --- a/packages/webapp/src/gamepad.ts +++ b/packages/webapp/src/gamepad.ts @@ -99,7 +99,7 @@ function readGamepad() { } } -export const listener = () => { +export const listenerGamepad = () => { if ([...navigator.getGamepads()].find((e) => e?.connected)) { readGamepad(); } else { @@ -111,46 +111,43 @@ export const listener = () => { }; export const startKeyboardSimulation = () => { + const map = Object.entries({ + // scroll + w: GamepadBtnIndex.Up, + a: GamepadBtnIndex.Left, + s: GamepadBtnIndex.Down, + d: GamepadBtnIndex.Right, + // select/start + f: GamepadBtnIndex.Select, + h: GamepadBtnIndex.Start, + // detail/switch + j: GamepadBtnIndex.B, + k: GamepadBtnIndex.A, + space: GamepadBtnIndex.A, + // exit + 4: GamepadBtnIndex.FrontLeftTop, + // settings + 5: GamepadBtnIndex.FrontRightTop, + // page/tab navigation + 6: GamepadBtnIndex.FrontLeftBottom, + 7: GamepadBtnIndex.FrontRightBottom, + }); addEventListener( 'keydown', - hotkeys({ - // scroll - w: () => dispatchPressEvent(GamepadBtnIndex.Up), - a: () => dispatchPressEvent(GamepadBtnIndex.Left), - s: () => dispatchPressEvent(GamepadBtnIndex.Down), - d: () => dispatchPressEvent(GamepadBtnIndex.Right), - // select/start - f: () => dispatchPressEvent(GamepadBtnIndex.Select), - h: () => dispatchPressEvent(GamepadBtnIndex.Start), - // detail/switch - j: () => dispatchPressEvent(GamepadBtnIndex.B), - k: () => dispatchPressEvent(GamepadBtnIndex.A), - space: () => dispatchPressEvent(GamepadBtnIndex.A), - // exit - 4: () => dispatchPressEvent(GamepadBtnIndex.FrontLeftTop), - // settings - 5: () => dispatchPressEvent(GamepadBtnIndex.FrontRightTop), - // page/tab navigation - 6: () => dispatchPressEvent(GamepadBtnIndex.FrontLeftBottom), - 7: () => dispatchPressEvent(GamepadBtnIndex.FrontRightBottom), - }), + hotkeys( + map.reduce( + (p, [key, btn]) => Object.assign(p, { [key]: () => dispatchPressEvent(btn) }), + {} as Record void>, + ), + ), ); addEventListener( 'keyup', - hotkeys({ - w: () => dispatchReleaseEvent(GamepadBtnIndex.Up), - a: () => dispatchReleaseEvent(GamepadBtnIndex.Left), - s: () => dispatchReleaseEvent(GamepadBtnIndex.Down), - d: () => dispatchReleaseEvent(GamepadBtnIndex.Right), - f: () => dispatchReleaseEvent(GamepadBtnIndex.Select), - h: () => dispatchReleaseEvent(GamepadBtnIndex.Start), - j: () => dispatchReleaseEvent(GamepadBtnIndex.B), - k: () => dispatchReleaseEvent(GamepadBtnIndex.A), - space: () => dispatchReleaseEvent(GamepadBtnIndex.A), - 4: () => dispatchReleaseEvent(GamepadBtnIndex.FrontLeftTop), - 5: () => dispatchReleaseEvent(GamepadBtnIndex.FrontRightTop), - 6: () => dispatchReleaseEvent(GamepadBtnIndex.FrontLeftBottom), - 7: () => dispatchReleaseEvent(GamepadBtnIndex.FrontRightBottom), - }), + hotkeys( + map.reduce( + (p, [key, btn]) => Object.assign(p, { [key]: () => dispatchReleaseEvent(btn) }), + {} as Record void>, + ), + ), ); }; diff --git a/packages/webapp/src/index.ts b/packages/webapp/src/index.ts index d1706fae..f017f281 100644 --- a/packages/webapp/src/index.ts +++ b/packages/webapp/src/index.ts @@ -1,23 +1,25 @@ import { history, html, render, styleMap } from '@mantou/gem'; import { mediaQuery } from '@mantou/gem/helper/mediaquery'; -import { Toast } from 'duoyun-ui/elements/toast'; import { DuoyunDropAreaElement } from 'duoyun-ui/elements/drop-area'; import { createPath } from 'duoyun-ui/elements/route'; +import { isInputElement } from 'duoyun-ui/lib/element'; import { isMtApp, mtApp } from '@nesbox/mtapp'; +import { initApp } from 'duoyun-ui/helper/webapp'; import { routes } from 'src/routes'; +import { logger } from 'src/logger'; import { matchRoute } from 'src/utils/common'; import { COMMAND, globalEvents, isApp, isTauriMacApp, isTauriWinApp, RELEASE } from 'src/constants'; import { theme } from 'src/theme'; import { configure } from 'src/configure'; -import { logger } from 'src/logger'; import { gotoRedirectUri, isExpiredProfile, logout } from 'src/auth'; -import { GamepadBtnIndex, listener, startKeyboardSimulation } from 'src/gamepad'; +import { GamepadBtnIndex, listenerGamepad, startKeyboardSimulation } from 'src/gamepad'; import { dropHandler } from 'src/drop'; +import 'duoyun-ui/helper/error'; import 'src/modules/meta'; -listener(); +listenerGamepad(); logger.info('MODE\t', import.meta.env.MODE); logger.info('RELEASE\t', RELEASE); @@ -156,46 +158,13 @@ addEventListener(globalEvents.PRESS_HOST_BUTTON_INDEX, ({ timeStamp, detail }) = } }); -let unloading = false; -addEventListener('beforeunload', () => { - unloading = true; - setTimeout(() => (unloading = false), 1000); -}); -function printError(err: Error | ErrorEvent | DOMException) { - if (err instanceof DOMException && err.name === 'AbortError') { - return; - } - const ignoreError = [ - // chrome - 'ResizeObserver', - 'Script error.', - ]; - if (unloading || ignoreError.some((msg) => err.message?.startsWith(msg))) return; - Toast.open('error', err.message || String(err)); -} - -function handleRejection({ reason }: PromiseRejectionEvent) { - if (reason) { - const errors = reason.errors || reason; - if (Array.isArray(errors)) { - errors.forEach((err) => printError(err)); - } else { - printError(reason.reason || reason); - } - } -} - -addEventListener('error', printError); -addEventListener('unhandledrejection', handleRejection); - // https://github.com/tauri-apps/tauri/issues/2626#issuecomment-1151090395 addEventListener( 'keydown', (event) => { if (isTauriMacApp && event.key !== 'Tab') { const ele = event.composedPath()[0]; - const isInput = ele instanceof HTMLInputElement || ele instanceof HTMLTextAreaElement; - if (!ele || !isInput || event.key === 'Escape') { + if (!ele || !isInputElement(ele as HTMLElement) || event.key === 'Escape') { // not system shortcut if (!(event.key === 'w' && (event.ctrlKey || event.metaKey))) { event.preventDefault(); @@ -208,28 +177,16 @@ addEventListener( addEventListener('contextmenu', (evt) => { const ele = evt.composedPath()[0]; - const isInput = ele instanceof HTMLInputElement || ele instanceof HTMLTextAreaElement; - if (isApp && !isInput) { + if (isApp && !isInputElement(ele as HTMLElement)) { evt.preventDefault(); } }); -if (COMMAND === 'build') { - navigator.serviceWorker?.register('/sw.js', { type: 'module' }); -} else { - navigator.serviceWorker?.getRegistration().then((reg) => reg?.unregister()); -} - addEventListener('load', () => { logger.info('Loaded!'); }); -// Installed -matchMedia(mediaQuery.PWA).addEventListener('change', ({ matches }) => { - if (matches) { - const w = 1024; - const h = 640; - resizeTo(w, h); - moveTo((screen.width - w) / 2, (screen.height - h) / 2); - } +initApp({ + serviceWorkerScript: COMMAND === 'build' ? '/sw.js' : '', + initWindowSize: [1024, 640], }); diff --git a/packages/webapp/src/logger.ts b/packages/webapp/src/logger.ts index b1995117..efd9fb36 100644 --- a/packages/webapp/src/logger.ts +++ b/packages/webapp/src/logger.ts @@ -1,25 +1,3 @@ -// 参考 https://www.npmjs.com/package/log4js -export class Logger { - _type: string; - - constructor(type: string) { - this._type = type; - } - - info = (...args: any[]) => { - // eslint-disable-next-line no-console - console.log(`[${this._type}]:`, ...args); - }; - - warn = (...args: any[]) => { - // eslint-disable-next-line no-console - console.warn(`[${this._type}]:`, ...args); - }; - - error = (...args: any[]) => { - // eslint-disable-next-line no-console - console.error(`[${this._type}]:`, ...args); - }; -} +import { Logger } from '@mantou/gem/helper/logger'; export const logger = new Logger('DEBUG'); diff --git a/packages/webapp/src/modules/account-settings.ts b/packages/webapp/src/modules/account-settings.ts index 4d7fd2a2..2c9287a4 100644 --- a/packages/webapp/src/modules/account-settings.ts +++ b/packages/webapp/src/modules/account-settings.ts @@ -9,7 +9,7 @@ import { refobject, RefObject, } from '@mantou/gem'; -import { throttle } from 'duoyun-ui/lib/utils'; +import { throttle } from 'duoyun-ui/lib/timer'; import { locale } from 'duoyun-ui/lib/locale'; import { Toast } from 'duoyun-ui/elements/toast'; diff --git a/packages/webapp/src/modules/game-item.ts b/packages/webapp/src/modules/game-item.ts index e52b7575..af8625de 100644 --- a/packages/webapp/src/modules/game-item.ts +++ b/packages/webapp/src/modules/game-item.ts @@ -11,8 +11,8 @@ import { connectStore, } from '@mantou/gem'; import { createPath } from 'duoyun-ui/elements/route'; -import { routes } from 'src/routes'; import { mediaQuery } from '@mantou/gem/helper/mediaquery'; +import { routes } from 'src/routes'; import { getCDNSrc, setViewTransitionName } from 'src/utils/common'; import { paramKeys, viewTransitionName } from 'src/constants'; diff --git a/packages/webapp/src/modules/game-list.ts b/packages/webapp/src/modules/game-list.ts index 3ad683b7..c15d53e9 100644 --- a/packages/webapp/src/modules/game-list.ts +++ b/packages/webapp/src/modules/game-list.ts @@ -15,8 +15,8 @@ import { } from '@mantou/gem'; import { isNotNullish } from 'duoyun-ui/lib/types'; import { mediaQuery } from '@mantou/gem/helper/mediaquery'; -import { locationStore } from 'src/routes'; import { isMtApp } from '@nesbox/mtapp'; +import { locationStore } from 'src/routes'; import { changeQuery } from 'src/utils/common'; import { queryKeys } from 'src/constants'; diff --git a/packages/webapp/src/modules/lobby-chat.ts b/packages/webapp/src/modules/lobby-chat.ts index bd880be7..cdd11f19 100644 --- a/packages/webapp/src/modules/lobby-chat.ts +++ b/packages/webapp/src/modules/lobby-chat.ts @@ -9,7 +9,7 @@ import { RefObject, refobject, } from '@mantou/gem'; -import { polling } from 'duoyun-ui/lib/utils'; +import { polling } from 'duoyun-ui/lib/timer'; import { hotkeys } from 'duoyun-ui/lib/hotkeys'; import { i18n } from 'src/i18n/basic'; diff --git a/packages/webapp/src/modules/meta.ts b/packages/webapp/src/modules/meta.ts index 110fb788..e4349153 100644 --- a/packages/webapp/src/modules/meta.ts +++ b/packages/webapp/src/modules/meta.ts @@ -1,5 +1,6 @@ import { GemElement, html, adoptedStyle, createCSSSheet, css, customElement, connectStore, history } from '@mantou/gem'; import { mediaQuery } from '@mantou/gem/helper/mediaquery'; +import { getWebManifestURL } from 'duoyun-ui/helper/webapp'; import { i18n } from 'src/i18n/basic'; import { themeStore } from 'src/theme'; @@ -32,10 +33,10 @@ export class ModuleMetaElement extends GemElement { state: State = {}; mounted = () => { addEventListener('load', async () => { - const { getWebManifestURL } = await import('src/webmanifest'); + const { genWebManifest } = await import('src/webmanifest'); this.effect( () => { - this.setState({ manifest: getWebManifestURL() }); + this.setState({ manifest: getWebManifestURL(genWebManifest()) }); }, () => [i18n.currentLanguage, configure.theme], ); diff --git a/packages/webapp/src/modules/nav.ts b/packages/webapp/src/modules/nav.ts index 3f53dba1..113d7cbc 100644 --- a/packages/webapp/src/modules/nav.ts +++ b/packages/webapp/src/modules/nav.ts @@ -1,20 +1,10 @@ -import { - GemElement, - html, - adoptedStyle, - customElement, - createCSSSheet, - css, - connectStore, - history, - updateStore, -} from '@mantou/gem'; +import { GemElement, html, adoptedStyle, customElement, createCSSSheet, css, connectStore, history } from '@mantou/gem'; import { mediaQuery } from '@mantou/gem/helper/mediaquery'; import { commonHandle } from 'duoyun-ui/lib/hotkeys'; import { waitLoading } from 'duoyun-ui/elements/wait'; import { focusStyle } from 'duoyun-ui/lib/styles'; -import { locationStore, routes } from 'src/routes'; import { createPath, RouteItem } from 'duoyun-ui/elements/route'; +import { locationStore, routes } from 'src/routes'; import { paramKeys, viewTransitionName } from 'src/constants'; import { i18n } from 'src/i18n/basic'; @@ -26,6 +16,7 @@ import { toggleSearchState, toggleSideNavState, navStore, + updateNavStore, } from 'src/configure'; import { theme } from 'src/theme'; import { createRoom, favoriteGame, leaveRoom } from 'src/services/api'; @@ -128,8 +119,8 @@ const style = createCSSSheet(css` } `); -export const mountedRoom = () => updateStore(navStore, { room: true }); -export const unmountedRoom = () => updateStore(navStore, { room: false }); +export const mountedRoom = () => updateNavStore({ room: true }); +export const unmountedRoom = () => updateNavStore({ room: false }); /** * @customElement m-nav diff --git a/packages/webapp/src/modules/room-chat.ts b/packages/webapp/src/modules/room-chat.ts index 96a09b12..071b5deb 100644 --- a/packages/webapp/src/modules/room-chat.ts +++ b/packages/webapp/src/modules/room-chat.ts @@ -15,7 +15,7 @@ import { classMap, } from '@mantou/gem'; import { hotkeys } from 'duoyun-ui/lib/hotkeys'; -import { sleep } from 'duoyun-ui/lib/utils'; +import { sleep } from 'duoyun-ui/lib/timer'; import { Time } from 'duoyun-ui/lib/time'; import { SysMsg, TextMsg } from 'src/netplay/common'; diff --git a/packages/webapp/src/modules/room-voice.ts b/packages/webapp/src/modules/room-voice.ts index c4673497..050e90c0 100644 --- a/packages/webapp/src/modules/room-voice.ts +++ b/packages/webapp/src/modules/room-voice.ts @@ -1,17 +1,17 @@ -import { GemElement, html, customElement, createStore, connectStore, updateStore } from '@mantou/gem'; +import { GemElement, html, customElement, connectStore, useStore } from '@mantou/gem'; +import { logger } from 'src/logger'; import { globalEvents, VoiceSignalDetail } from 'src/constants'; import { configure } from 'src/configure'; import { icons } from 'src/icons'; import { sendVoiceMsg } from 'src/services/api'; -import { logger } from 'src/logger'; import { ScVoiceMsgKind } from 'src/generated/graphql'; import { i18n } from 'src/i18n/basic'; import 'duoyun-ui/elements/use'; import 'src/elements/tooltip'; -export const voiceStore = createStore<{ audioLevel: Record }>({ +export const [voiceStore, updateVoiceStore] = useStore<{ audioLevel: Record }>({ audioLevel: {}, }); @@ -85,7 +85,7 @@ export class MVoiceRoomElement extends GemElement { }); streams[0].onremovetrack = ({ track }) => { const userId = this.state.receiverTracks.find((e) => e.trackId === track.id)?.userId; - updateStore(voiceStore, { audioLevel: { ...voiceStore.audioLevel, [String(userId)]: undefined } }); + updateVoiceStore({ audioLevel: { ...voiceStore.audioLevel, [String(userId)]: undefined } }); }; }); @@ -130,7 +130,7 @@ export class MVoiceRoomElement extends GemElement { const inboundRtp = stats.find(([_, stat]) => stat.type === 'inbound-rtp')?.[1]; const userId = this.state.receiverTracks[i]?.userId; if (inboundRtp) { - updateStore(voiceStore, { + updateVoiceStore({ audioLevel: { ...voiceStore.audioLevel, [userId]: inboundRtp.audioLevel || 0 }, }); } @@ -140,7 +140,7 @@ export class MVoiceRoomElement extends GemElement { const mediaSource = stats.find(([_, stat]) => stat.type === 'media-source')?.[1]; if (mediaSource) { // Safari not support - updateStore(voiceStore, { + updateVoiceStore({ audioLevel: { ...voiceStore.audioLevel, [configure.user!.id]: mediaSource.audioLevel || 0 }, }); } @@ -148,7 +148,7 @@ export class MVoiceRoomElement extends GemElement { }, 60); return () => { - updateStore(voiceStore, { audioLevel: {} }); + updateVoiceStore({ audioLevel: {} }); clearInterval(intervalTimer); this.#audioEle.pause(); userMediaStream?.getTracks().forEach((track) => track.stop()); diff --git a/packages/webapp/src/modules/search.ts b/packages/webapp/src/modules/search.ts index dec20414..721736c1 100644 --- a/packages/webapp/src/modules/search.ts +++ b/packages/webapp/src/modules/search.ts @@ -16,9 +16,9 @@ import { locale } from 'duoyun-ui/lib/locale'; import { isIncludesString } from 'duoyun-ui/lib/utils'; import { isNotNullish } from 'duoyun-ui/lib/types'; import { getDisplayKey, hotkeys, isMac } from 'duoyun-ui/lib/hotkeys'; -import { routes } from 'src/routes'; import { mediaQuery } from '@mantou/gem/helper/mediaquery'; import { createPath } from 'duoyun-ui/elements/route'; +import { routes } from 'src/routes'; import { getCDNSrc, getTempText, matchRoute } from 'src/utils/common'; import { friendStore, store, toggleFriendChatState } from 'src/store'; diff --git a/packages/webapp/src/modules/side-nav.ts b/packages/webapp/src/modules/side-nav.ts index 884e0e9f..74f0b9f8 100644 --- a/packages/webapp/src/modules/side-nav.ts +++ b/packages/webapp/src/modules/side-nav.ts @@ -9,8 +9,8 @@ import { boolattribute, history, } from '@mantou/gem'; -import { routes } from 'src/routes'; import { SwipeEventDetail } from 'duoyun-ui/elements/gesture'; +import { routes } from 'src/routes'; import { configure, toggleSideNavState } from 'src/configure'; import { theme } from 'src/theme'; diff --git a/packages/webapp/src/modules/stage.ts b/packages/webapp/src/modules/stage.ts index b7fe2496..39a37ef0 100644 --- a/packages/webapp/src/modules/stage.ts +++ b/packages/webapp/src/modules/stage.ts @@ -19,6 +19,7 @@ import { Nes, Button, Player } from '@mantou/nes'; import { isNotNullish } from 'duoyun-ui/lib/types'; import { clamp } from 'duoyun-ui/lib/number'; +import { logger } from 'src/logger'; import { ChannelMessage, ChannelMessageType, @@ -41,7 +42,6 @@ import { VideoFilter, VideoRenderMethod, } from 'src/constants'; -import { logger } from 'src/logger'; import { configure } from 'src/configure'; import { store } from 'src/store'; import { diff --git a/packages/webapp/src/mt-app.ts b/packages/webapp/src/mt-app.ts index c93976a6..1874f5ce 100644 --- a/packages/webapp/src/mt-app.ts +++ b/packages/webapp/src/mt-app.ts @@ -5,14 +5,13 @@ import { customElement, createCSSSheet, css, - createStore, connectStore, history, - updateStore, QueryString, + useStore, } from '@mantou/gem'; import { createPath } from 'duoyun-ui/elements/route'; -import { forever } from 'duoyun-ui/lib/utils'; +import { forever } from 'duoyun-ui/lib/timer'; import { Loadbar } from 'duoyun-ui/elements/page-loadbar'; import { Toast } from 'duoyun-ui/elements/toast'; import { isNotNullish } from 'duoyun-ui/lib/types'; @@ -27,11 +26,7 @@ import { i18n } from 'src/i18n/basic'; import 'src/modules/mt-nav'; type MtStore = { imgUrl: string; inertNav: boolean }; -const mtAppStore = createStore({ imgUrl: '', inertNav: false }); - -export const updateMtApp = (data: Partial) => { - updateStore(mtAppStore, data); -}; +export const [mtAppStore, updateMtApp] = useStore({ imgUrl: '', inertNav: false }); const style = createCSSSheet(css` :host { diff --git a/packages/webapp/src/netplay/client.ts b/packages/webapp/src/netplay/client.ts index c4120bd0..06d4e800 100644 --- a/packages/webapp/src/netplay/client.ts +++ b/packages/webapp/src/netplay/client.ts @@ -1,13 +1,13 @@ -import { createStore, updateStore } from '@mantou/gem'; +import { useStore } from '@mantou/gem'; import { Player } from '@mantou/nes'; +import { logger } from 'src/logger'; import { globalEvents, SignalDetail, SignalType } from 'src/constants'; import { configure } from 'src/configure'; -import { logger } from 'src/logger'; import { sendSignal } from 'src/services/api'; import { ChannelMessage, ChannelMessageType, Ping, RoleAnswer, RoleOffer, RTCBasic, TextMsg } from 'src/netplay/common'; -export const pingStore = createStore<{ ping?: number }>({}); +export const [pingStore, updatePingStore] = useStore<{ ping?: number }>({}); export class RTCClient extends RTCBasic { #host = 0; @@ -65,7 +65,7 @@ export class RTCClient extends RTCBasic { const msg = JSON.parse(data) as ChannelMessage; switch (msg.type) { case ChannelMessageType.PING: - updateStore(pingStore, { ping: Date.now() - msg.timestamp }); + updatePingStore({ ping: Date.now() - msg.timestamp }); break; case ChannelMessageType.ROLE_ANSWER: this.roles = (msg as RoleAnswer).roles; @@ -143,6 +143,6 @@ export class RTCClient extends RTCBasic { clearTimeout(this.#restartTimer); clearTimeout(this.#pingTimer); - updateStore(pingStore, { ping: undefined }); + updatePingStore({ ping: undefined }); }; } diff --git a/packages/webapp/src/netplay/host.ts b/packages/webapp/src/netplay/host.ts index 62088abd..28e82d35 100644 --- a/packages/webapp/src/netplay/host.ts +++ b/packages/webapp/src/netplay/host.ts @@ -4,9 +4,9 @@ import { Player } from '@mantou/nes'; +import { logger } from 'src/logger'; import { globalEvents, RTCTransportType, SignalDetail, SignalType } from 'src/constants'; import { configure } from 'src/configure'; -import { logger } from 'src/logger'; import { sendSignal } from 'src/services/api'; import { ChannelMessage, diff --git a/packages/webapp/src/pages/homepage.ts b/packages/webapp/src/pages/homepage.ts index ffb48198..8c6fd1ae 100644 --- a/packages/webapp/src/pages/homepage.ts +++ b/packages/webapp/src/pages/homepage.ts @@ -1,18 +1,8 @@ -import { - GemElement, - html, - adoptedStyle, - customElement, - createCSSSheet, - css, - raw, - history, - connectStore, -} from '@mantou/gem'; +import { GemElement, html, adoptedStyle, customElement, createCSSSheet, css, raw, connectStore } from '@mantou/gem'; import { isMac } from 'duoyun-ui/lib/hotkeys'; import { mediaQuery } from '@mantou/gem/helper/mediaquery'; import { Toast } from 'duoyun-ui/elements/toast'; -import { createPath, RouteItem } from 'duoyun-ui/elements/route'; +import { RouteItem } from 'duoyun-ui/elements/route'; import { waitLoading } from 'duoyun-ui/elements/wait'; import { routes } from 'src/routes'; diff --git a/packages/webapp/src/pages/mt-games.ts b/packages/webapp/src/pages/mt-games.ts index 8c120716..a56fe239 100644 --- a/packages/webapp/src/pages/mt-games.ts +++ b/packages/webapp/src/pages/mt-games.ts @@ -6,10 +6,9 @@ import { createCSSSheet, css, connectStore, - createStore, - updateStore, state, repeat, + useStore, } from '@mantou/gem'; import { formatDuration, Time } from 'duoyun-ui/lib/time'; import { locationStore } from 'src/routes'; @@ -32,11 +31,9 @@ import 'src/modules/game-detail'; import 'src/elements/rotor'; import 'src/elements/scroll'; -const mtGamesStore = createStore({ currentIndex: 0, focusId: 0 }); +const [mtGamesStore, updateMtGamesStore] = useStore({ currentIndex: 0, focusId: 0 }); const setMtGamesStore = (obj: Partial) => { - queueMicrotask(() => { - updateStore(mtGamesStore, obj); - }); + queueMicrotask(() => updateMtGamesStore(obj)); }; const style = createCSSSheet(css` diff --git a/packages/webapp/src/pages/mt-room.ts b/packages/webapp/src/pages/mt-room.ts index 2ba5313f..dc7b0ffb 100644 --- a/packages/webapp/src/pages/mt-room.ts +++ b/packages/webapp/src/pages/mt-room.ts @@ -11,8 +11,8 @@ import { } from '@mantou/gem'; import { createPath, matchPath } from 'duoyun-ui/elements/route'; import { waitLoading } from 'duoyun-ui/elements/wait'; -import { routes } from 'src/routes'; import { DuoyunWakeLockBaseElement } from 'duoyun-ui/elements/base/wake-lock'; +import { routes } from 'src/routes'; import { playHintSound } from 'src/utils/common'; import { globalEvents, queryKeys } from 'src/constants'; diff --git a/packages/webapp/src/pages/mt-rooms.ts b/packages/webapp/src/pages/mt-rooms.ts index 9192031c..edbb4466 100644 --- a/packages/webapp/src/pages/mt-rooms.ts +++ b/packages/webapp/src/pages/mt-rooms.ts @@ -6,10 +6,9 @@ import { createCSSSheet, css, connectStore, - createStore, - updateStore, + useStore, } from '@mantou/gem'; -import { polling } from 'duoyun-ui/lib/utils'; +import { polling } from 'duoyun-ui/lib/timer'; import { getCDNSrc } from 'src/utils/common'; import { store } from 'src/store'; @@ -22,7 +21,7 @@ import 'duoyun-ui/elements/empty'; import 'duoyun-ui/elements/heading'; import 'src/elements/rotor'; -const mtRoomsStore = createStore({ currentId: store.roomIds?.[0] || 0 }); +const [mtRoomsStore, updateMtRoomsStore] = useStore({ currentId: store.roomIds?.[0] || 0 }); const style = createCSSSheet(css` :host { @@ -57,8 +56,7 @@ export class PMtRoomsElement extends GemElement { ${store.roomIds?.length ? html` ) => - updateStore(mtRoomsStore, { currentId: store.roomIds![detail] })} + @change=${({ detail }: CustomEvent) => updateMtRoomsStore({ currentId: store.roomIds![detail] })} .index=${index >= 0 ? index : 0} .finite=${true} .data=${store.roomIds.map((id) => ({ diff --git a/packages/webapp/src/pages/room.ts b/packages/webapp/src/pages/room.ts index 849fcb85..19527353 100644 --- a/packages/webapp/src/pages/room.ts +++ b/packages/webapp/src/pages/room.ts @@ -19,11 +19,12 @@ import { isNotBoolean } from 'duoyun-ui/lib/types'; import { Toast } from 'duoyun-ui/elements/toast'; import { hash } from 'duoyun-ui/lib/encode'; import { Time } from 'duoyun-ui/lib/time'; -import { getStringFromTemplate, once } from 'duoyun-ui/lib/utils'; +import { getStringFromTemplate } from 'duoyun-ui/lib/utils'; +import { once } from 'duoyun-ui/lib/timer'; import { mediaQuery } from '@mantou/gem/helper/mediaquery'; -import { routes } from 'src/routes'; import { locale } from 'duoyun-ui/lib/locale'; import { DuoyunWakeLockBaseElement } from 'duoyun-ui/elements/base/wake-lock'; +import { routes } from 'src/routes'; import { preventDefault } from 'src/utils/common'; import { BcMsgEvent, BcMsgType, queryKeys } from 'src/constants'; diff --git a/packages/webapp/src/pages/rooms.ts b/packages/webapp/src/pages/rooms.ts index 3b52dc3e..d69b27fc 100644 --- a/packages/webapp/src/pages/rooms.ts +++ b/packages/webapp/src/pages/rooms.ts @@ -1,5 +1,5 @@ import { html, adoptedStyle, customElement, createCSSSheet, css, connectStore, GemElement } from '@mantou/gem'; -import { polling } from 'duoyun-ui/lib/utils'; +import { polling } from 'duoyun-ui/lib/timer'; import { mediaQuery } from '@mantou/gem/helper/mediaquery'; import { getRooms } from 'src/services/guest-api'; diff --git a/packages/webapp/src/routes.ts b/packages/webapp/src/routes.ts index 8dabb064..48e8d149 100644 --- a/packages/webapp/src/routes.ts +++ b/packages/webapp/src/routes.ts @@ -1,4 +1,4 @@ -import { html, createStore, updateStore, connect } from '@mantou/gem'; +import { html, connect, useStore } from '@mantou/gem'; import { GemRouteElement, RouteItem } from '@mantou/gem/elements/route'; import { ValueOf } from 'duoyun-ui/lib/types'; import { isMtApp } from '@nesbox/mtapp'; @@ -130,11 +130,11 @@ const getInitRoutes = () => { type Routes = ReturnType; export type Route = ValueOf; -export const routes = createStore(getInitRoutes() as Routes); +export const [routes, updateRoutes] = useStore(getInitRoutes() as Routes); connect(i18n.store, () => { Object.entries(getInitRoutes()).forEach(([routeName, route]) => { routes[routeName as keyof Routes].title = route.title; }); - updateStore(routes); + updateRoutes(); }); diff --git a/packages/webapp/src/services/api.ts b/packages/webapp/src/services/api.ts index b49722c5..decc26d2 100644 --- a/packages/webapp/src/services/api.ts +++ b/packages/webapp/src/services/api.ts @@ -1,6 +1,5 @@ -import { updateStore } from '@mantou/gem'; import { Toast } from 'duoyun-ui/elements/toast'; -import { debounce } from 'duoyun-ui/lib/utils'; +import { debounce } from 'duoyun-ui/lib/timer'; import { mediaQuery } from '@mantou/gem/helper/mediaquery'; import { documentVisible, playHintSound, playSound } from 'src/utils/common'; @@ -92,9 +91,9 @@ import { UpdateRoomScreenshotMutation, UpdateRoomScreenshotMutationVariables, } from 'src/generated/graphql'; -import { store, friendStore, convertGame } from 'src/store'; +import { store, updateStore, friendStore, updateFriendStore, convertGame } from 'src/store'; import { request, subscribe } from 'src/services'; -import { configure, parseAccount, Settings } from 'src/configure'; +import { configure, updateConfigureStore, parseAccount, Settings } from 'src/configure'; import { i18n, isCurrentLang } from 'src/i18n/basic'; import { gotoLogin, logout } from 'src/auth'; import { getGames } from 'src/services/guest-api'; @@ -103,7 +102,7 @@ export const enterLobby = async () => { const { enterLobby } = await request(EnterLobby, { input: { area: i18n.currentLanguage.split('-')[0] }, }); - updateStore(store, { lobbyInfo: enterLobby }); + updateStore({ lobbyInfo: enterLobby }); }; export const leaveLobby = async () => { @@ -112,7 +111,7 @@ export const leaveLobby = async () => { export const sendLobbyMsg = async (text: string) => { await request(SendLobbyMsg, { input: { text } }); - updateStore(store, { + updateStore({ lobbyMessage: [ ...store.lobbyMessage, { @@ -136,7 +135,7 @@ export const getGameIds = async () => { if (!store.gameIds?.length) await getGames(); const { topGames, favorites, recentGames } = await request(GetGameIds, {}); - updateStore(store, { + updateStore({ favoriteIds: favorites.filter((id) => isCurrentLang(store.games[id]!)), topGameIds: [...new Set([...topGames, ...store.gameIds!])] .filter((id) => isCurrentLang(store.games[id]!)) @@ -150,8 +149,8 @@ export const createRoom = async (input: ScNewRoom) => { if (COMMAND === 'serve') input.private = true; const { createRoom } = await request(CreateRoom, { input }); configure.user!.playing = createRoom; - updateStore(configure); - updateStore(store, { + updateConfigureStore(); + updateStore({ recentGameIds: [input.gameId, ...(store.recentGameIds || []).filter((id) => id !== input.gameId)], }); }; @@ -159,7 +158,7 @@ export const createRoom = async (input: ScNewRoom) => { export const updateRoom = async (input: ScUpdateRoom) => { const { updateRoom } = await request(UpdateRoom, { input }); configure.user!.playing = updateRoom; - updateStore(configure); + updateConfigureStore(); }; export const updateRoomScreenshot = async (input: ScUpdateRoomScreenshot) => { @@ -180,8 +179,8 @@ export const enterPubRoom = async (roomId: number) => { input: { roomId }, }); configure.user!.playing = enterPubRoom; - updateStore(configure); - updateStore(store, { + updateConfigureStore(); + updateStore({ recentGameIds: [enterPubRoom.gameId, ...(store.recentGameIds || []).filter((id) => id !== enterPubRoom.gameId)], }); }; @@ -189,12 +188,12 @@ export const enterPubRoom = async (roomId: number) => { export const leaveRoom = async () => { await request(LeaveRoom, {}); delete configure.user!.playing; - updateStore(configure); + updateConfigureStore(); }; export const getAccount = async () => { const { account } = await request(GetAccount, {}); - updateStore(configure, { + updateConfigureStore({ user: parseAccount(account), }); }; @@ -209,7 +208,7 @@ export const updateAccount = async ({ const { updateAccount } = await request(UpdateAccount, { input: { nickname, settings: JSON.stringify(settings) }, }); - updateStore(configure, { + updateConfigureStore({ user: parseAccount(updateAccount), }); }; @@ -222,7 +221,7 @@ export const updatePassword = async (input: ScUpdatePassword) => { export const getFriends = async () => { const { friends, invites } = await request(GetFriends, {}); - updateStore(friendStore, { + updateFriendStore({ friendIds: friends.map((e) => { friendStore.friends[e.user.id] = e; return e.user.id; @@ -247,13 +246,13 @@ export const acceptFriend = async (targetId: number, accept: boolean) => { friendStore.friendIds = friendStore.friendIds?.filter((id) => id !== targetId); delete friendStore.friends[targetId]; } - updateStore(friendStore); + updateFriendStore(); }; export const deleteFriend = async (targetId: number) => { await acceptFriend(targetId, false); friendStore.messageIds[targetId]?.forEach((id) => delete friendStore.messages[id]); - updateStore(friendStore, { + updateFriendStore({ draft: { ...friendStore.draft, [targetId]: undefined }, messageIds: { ...friendStore.messageIds, [targetId]: undefined }, }); @@ -268,17 +267,17 @@ export const acceptInvite = async (inviteId: number, accept: boolean) => { await request(AcceptInvite, { input: { inviteId, accept } }); if (accept) { configure.user!.playing = friendStore.invites[inviteId]?.room; - updateStore(configure); + updateConfigureStore(); } friendStore.inviteIds = friendStore.inviteIds?.filter((id) => id !== inviteId); delete friendStore.invites[inviteId]; - updateStore(friendStore); + updateFriendStore(); }; export const getRecord = async (gameId: number) => { const { record } = await request(GetRecord, { gameId }); store.record[gameId] = record; - updateStore(store); + updateStore(); }; export const createComment = async (input: ScNewComment) => { @@ -295,7 +294,7 @@ export const createComment = async (input: ScNewComment) => { [configure.user!.id]: createComment, }, }; - updateStore(store); + updateStore(); }; export const getMessages = async (targetId: number) => { @@ -304,7 +303,7 @@ export const getMessages = async (targetId: number) => { friendStore.messages[e.id] = e; return e.id; }); - updateStore(friendStore); + updateFriendStore(); }; export const readMessage = debounce(async (targetId: number) => { @@ -312,7 +311,7 @@ export const readMessage = debounce(async (targetId: number) => { input: { targetId }, }); friendStore.friends[targetId] = readMessage; - updateStore(friendStore); + updateFriendStore(); }); export const createMessage = async (targetId: number, body: string) => { @@ -321,7 +320,7 @@ export const createMessage = async (targetId: number, body: string) => { }); friendStore.messageIds[targetId] = [...(friendStore.messageIds[targetId] || []), createMessage.id]; friendStore.messages[createMessage.id] = createMessage; - updateStore(friendStore); + updateFriendStore(); playHintSound('sended'); }; @@ -338,7 +337,7 @@ export const favoriteGame = async (gameId: number, favorite: boolean) => { } else { store.favoriteIds = store.favoriteIds?.filter((id) => id !== gameId); } - updateStore(store); + updateStore(); }; export const sendSignal = async (targetId: number, signal: Signal) => { @@ -371,7 +370,7 @@ export const subscribeEvent = () => { } = event; if (lobbyMessage) { - updateStore(store, { + updateStore({ lobbyMessage: [...store.lobbyMessage, lobbyMessage], }); } @@ -393,13 +392,13 @@ export const subscribeEvent = () => { documentVisible().then(() => readMessage(newMessage.userId)); playHintSound('received'); } - updateStore(friendStore); + updateFriendStore(); } if (newGame) { store.games[newGame.id] = convertGame(newGame); if (isCurrentLang(newGame)) { - updateStore(store, { + updateStore({ gameIds: [...(store.gameIds || []), newGame.id], }); } @@ -409,29 +408,29 @@ export const subscribeEvent = () => { const originRoom = store.rooms[updateRoom.id]; if (originRoom) { Object.assign(originRoom, updateRoom); - updateStore(store); + updateStore(); } if (configure.user?.playing?.id === updateRoom.id) { configure.user.playing = updateRoom; - updateStore(configure); + updateConfigureStore(); } } if (deleteRoom) { delete store.rooms[deleteRoom]; - updateStore(store, { roomIds: store.roomIds?.filter((id) => id !== deleteRoom) }); + updateStore({ roomIds: store.roomIds?.filter((id) => id !== deleteRoom) }); if (configure.user && configure.user.playing?.id === deleteRoom) { if (configure.user.playing.host !== configure.user.id) { Toast.open('warning', i18n.get('tip.room.deleted')); } delete configure.user.playing; - updateStore(configure); + updateConfigureStore(); } } if (newInvite) { friendStore.invites[newInvite.id] = newInvite; - updateStore(friendStore, { + updateFriendStore({ inviteIds: [...(friendStore.inviteIds || []), newInvite.id], }); playSound('new_invite'); @@ -439,12 +438,12 @@ export const subscribeEvent = () => { if (deleteInvite) { delete friendStore.invites[deleteInvite]; - updateStore(friendStore, { inviteIds: friendStore.inviteIds?.filter((id) => id !== deleteInvite) }); + updateFriendStore({ inviteIds: friendStore.inviteIds?.filter((id) => id !== deleteInvite) }); } if (applyFriend) { friendStore.friends[applyFriend.user.id] = applyFriend; - updateStore(friendStore, { + updateFriendStore({ friendIds: [...new Set([...(friendStore.friendIds || []), applyFriend.user.id])], }); playSound('apply_friend'); @@ -452,7 +451,7 @@ export const subscribeEvent = () => { if (acceptFriend) { friendStore.friends[acceptFriend.user.id] = acceptFriend; - updateStore(friendStore, { + updateFriendStore({ friendIds: [...new Set([...(friendStore.friendIds || []), acceptFriend.user.id])], }); } @@ -460,7 +459,7 @@ export const subscribeEvent = () => { if (deleteFriend) { delete friendStore.friends[deleteFriend]; friendStore.messageIds[deleteFriend]?.forEach((id) => delete friendStore.messages[id]); - updateStore(friendStore, { + updateFriendStore({ friendIds: friendStore.friendIds?.filter((id) => id !== deleteFriend), messageIds: { ...friendStore.messageIds, [deleteFriend]: undefined }, draft: { ...friendStore.draft, [deleteFriend]: undefined }, @@ -471,7 +470,7 @@ export const subscribeEvent = () => { const friend = friendStore.friends[updateUser.id]; if (friend) { friendStore.friends[updateUser.id] = { ...friend, user: updateUser }; - updateStore(friendStore); + updateFriendStore(); } } @@ -496,12 +495,12 @@ export const subscribeEvent = () => { // 同步 favorite 列表,例如手机同步到电视 if (favorite && !store.favoriteIds?.includes(favorite)) { store.favoriteIds = [favorite, ...(store.favoriteIds || [])]; - updateStore(store); + updateStore(); } if (deleteFavorite && store.favoriteIds?.includes(deleteFavorite)) { store.favoriteIds = store.favoriteIds?.filter((id) => id !== deleteFavorite); - updateStore(store); + updateStore(); } } })(); diff --git a/packages/webapp/src/services/error.ts b/packages/webapp/src/services/error.ts index 8665068b..a12fe8df 100644 --- a/packages/webapp/src/services/error.ts +++ b/packages/webapp/src/services/error.ts @@ -13,7 +13,7 @@ export const errorCodeMap: Record = { }; // https://github.com/grpc/grpc/blob/master/doc/statuscodes.md -export const grapCommonErrorMap: Record = { +export const grpcCommonErrorMap: Record = { 2: errorCodeMap[500], 3: errorCodeMap[400], 4: errorCodeMap[504], diff --git a/packages/webapp/src/services/guest-api.ts b/packages/webapp/src/services/guest-api.ts index bf56e3d9..006ebf6f 100644 --- a/packages/webapp/src/services/guest-api.ts +++ b/packages/webapp/src/services/guest-api.ts @@ -1,6 +1,5 @@ -import { updateStore } from '@mantou/gem'; import { b64ToUtf8 } from 'duoyun-ui/lib/encode'; -import { debounce } from 'duoyun-ui/lib/utils'; +import { debounce } from 'duoyun-ui/lib/timer'; import { isNotNullish } from 'duoyun-ui/lib/types'; import { request } from 'src/services'; @@ -23,15 +22,15 @@ import { GetRoomsQueryVariables, ScRegisterReq, } from 'src/generated/guestgraphql'; -import { configure, parseAccount } from 'src/configure'; -import { store, convertGame } from 'src/store'; +import { updateConfigureStore, parseAccount } from 'src/configure'; +import { store, updateStore, convertGame } from 'src/store'; import { isCurrentLang } from 'src/i18n/basic'; export const GUEST_ENDPOINT = '/guestgraphql'; function setUser(resp: LoginMutation['login']) { const tokenParsed = JSON.parse(b64ToUtf8(resp.token.split('.')[1])); - updateStore(configure, { + updateConfigureStore({ user: parseAccount(resp.user), profile: { token: resp.token, @@ -65,7 +64,7 @@ export const getGames = debounce(async () => { if (isCurrentLang(e)) return e.id; }) .filter(isNotNullish); - updateStore(store, { + updateStore({ gameIds, topGameIds: [...new Set([...topGames, ...gameIds])].filter((id) => isCurrentLang(store.games[id]!)).splice(0, 5), }); @@ -74,7 +73,7 @@ export const getGames = debounce(async () => { export const getRooms = async () => { const { rooms } = await request(GetRooms, {}, options); if (!store.gameIds?.length) await getGames(); - updateStore(store, { + updateStore({ roomIds: rooms .filter(({ gameId }) => gameId in store.games && isCurrentLang(store.games[gameId]!)) .map((e) => { @@ -90,5 +89,5 @@ export const getComments = async (gameId: number) => { userIds: comments.map((e) => e.user.id), comments: Object.fromEntries(comments.map((e) => [e.user.id, e])), }; - updateStore(store); + updateStore(); }; diff --git a/packages/webapp/src/services/index.ts b/packages/webapp/src/services/index.ts index 1689f933..7866b8f4 100644 --- a/packages/webapp/src/services/index.ts +++ b/packages/webapp/src/services/index.ts @@ -1,7 +1,6 @@ -import { randomStr } from '@mantou/gem'; import { SubscriptionClient } from 'subscriptions-transport-ws'; -import { errorCodeMap, grapCommonErrorMap, graphqlErrorMap } from 'src/services/error'; +import { errorCodeMap, grpcCommonErrorMap, graphqlErrorMap } from 'src/services/error'; import { configure } from 'src/configure'; import { logout, isExpiredProfile } from 'src/auth'; import { i18n } from 'src/i18n/basic'; @@ -77,7 +76,7 @@ export async function request( throw new AggregateError( errors.map((err) => { const { message, extensions } = err as Error & { extensions?: { code: number } }; - const i18nMsg = extensions && (graphqlErrorMap[extensions.code] || grapCommonErrorMap[extensions.code]); + const i18nMsg = extensions && (graphqlErrorMap[extensions.code] || grpcCommonErrorMap[extensions.code]); return new Error(i18nMsg ? i18n.get(i18nMsg) : message); }), ); diff --git a/packages/webapp/src/store.ts b/packages/webapp/src/store.ts index edb071fb..73b88b74 100644 --- a/packages/webapp/src/store.ts +++ b/packages/webapp/src/store.ts @@ -1,8 +1,8 @@ import frontmatter from 'front-matter'; import { ElementOf } from 'duoyun-ui/lib/types'; -import { createCacheStore, sleep } from 'duoyun-ui/lib/utils'; +import { useCacheStore } from 'duoyun-ui/lib/utils'; +import { sleep } from 'duoyun-ui/lib/timer'; import { commonAnimationOptions } from 'duoyun-ui/lib/animations'; -import { updateStore } from '@mantou/gem'; import { localStorageKeys } from 'src/constants'; import { @@ -56,7 +56,7 @@ interface Store { lobbyMessage: LobbyMessage[]; } -export const [store] = createCacheStore( +export const [store, updateStore] = useCacheStore( localStorageKeys.STORE_LOCAL_STORAGE_KEY, { games: {}, @@ -73,7 +73,7 @@ export const [store] = createCacheStore( ); export function clearLobbyMessage() { - updateStore(store, { lobbyMessage: [] }); + updateStore({ lobbyMessage: [] }); } interface FriendStore { @@ -88,7 +88,7 @@ interface FriendStore { friendChatState?: number; } -export const [friendStore] = createCacheStore( +export const [friendStore, updateFriendStore] = useCacheStore( localStorageKeys.FRIEND_CHAT_STORAGE_KEY, { draft: {}, @@ -104,17 +104,17 @@ export const [friendStore] = createCacheStore( ); export function changeFriendChatDraft(friendId: number, body?: string) { - updateStore(friendStore, { draft: { ...friendStore.draft, [friendId]: body } }); + updateFriendStore({ draft: { ...friendStore.draft, [friendId]: body } }); } export const toggleFriendChatState = async (id?: number) => { if (id && id === friendStore.friendChatState) { // re-focus on friend chat - updateStore(friendStore, { friendChatState: undefined }); + updateFriendStore({ friendChatState: undefined }); } // wait close animation await sleep(Number(commonAnimationOptions.duration)); - updateStore(friendStore, { + updateFriendStore({ recentFriendChat: id || friendStore.friendChatState || friendStore.recentFriendChat, friendChatState: id, }); diff --git a/packages/webapp/src/theme.ts b/packages/webapp/src/theme.ts index 523e083a..107c8854 100644 --- a/packages/webapp/src/theme.ts +++ b/packages/webapp/src/theme.ts @@ -1,11 +1,10 @@ import { updateTheme as updateDuoyunTheme, darkTheme } from 'duoyun-ui/lib/theme'; -import { updateStore } from '@mantou/gem'; import { createTheme, getThemeStore, updateTheme } from '@mantou/gem/helper/theme'; import { mediaQuery } from '@mantou/gem/helper/mediaquery'; import { isMtApp } from '@nesbox/mtapp'; import { i18n } from 'src/i18n/basic'; -import { configure } from 'src/configure'; +import { configure, updateConfigureStore } from 'src/configure'; export const themeNames = { get default() { @@ -101,7 +100,7 @@ export const theme = createTheme({ ...defaultTheme }); export const themeStore = getThemeStore(theme); export function changeTheme(name: ThemeName) { - updateStore(configure, { theme: name }); + updateConfigureStore({ theme: name }); switch (name) { case 'punk': updateTheme(theme, punkTheme); diff --git a/packages/webapp/src/utils/game.ts b/packages/webapp/src/utils/game.ts index da3f5a6c..ddfb1f88 100644 --- a/packages/webapp/src/utils/game.ts +++ b/packages/webapp/src/utils/game.ts @@ -1,5 +1,6 @@ -import { debounce, once } from 'duoyun-ui/lib/utils'; +import { once } from 'duoyun-ui/lib/timer'; import { clamp } from 'duoyun-ui/lib/number'; +import { Cache } from 'duoyun-ui/lib/cache'; import { default as initNes, Nes, Button } from '@mantou/nes'; import { VideoRefreshRate } from 'src/constants'; @@ -50,13 +51,9 @@ export function requestFrame(render: () => void, generator = VideoRefreshRate.AU }; } -const getRectCache = new WeakMap DOMRect>(); +const rectCache = new Cache({ maxAge: 500, renewal: true }); export const positionMapping = (event: PointerEvent, canvas: NesboxCanvasElement) => { - if (!getRectCache.has(canvas)) { - const fn = debounce(() => canvas.canvasRef.element!.getBoundingClientRect()); - getRectCache.set(canvas, fn); - } - const stage = getRectCache.get(canvas)!(); + const stage = rectCache.get('', () => canvas.canvasRef.element!.getBoundingClientRect()); const aspectRadio = canvas.width / canvas.height; const width = aspectRadio > stage.width / stage.height ? stage.width : aspectRadio * stage.height; const halfWidth = width / 2; diff --git a/packages/webapp/src/webmanifest.ts b/packages/webapp/src/webmanifest.ts index bc002d2c..d5e4c8c2 100644 --- a/packages/webapp/src/webmanifest.ts +++ b/packages/webapp/src/webmanifest.ts @@ -1,17 +1,8 @@ -import { utf8ToB64 } from 'duoyun-ui/lib/encode'; import { routes } from 'src/routes'; import { COMMAND } from 'src/constants'; import { i18n } from 'src/i18n/basic'; -export function getWebManifestURL() { - return `data:application/json;base64,${utf8ToB64( - JSON.stringify(genWebManifest(), (_, value) => - typeof value === 'string' && value.startsWith('/') ? new URL(value, location.origin).href : value, - ), - )}`; -} - export function genWebManifest() { return { id: 'com.nesbox' + (COMMAND === 'serve' ? '.dev' : ''), diff --git a/packages/zombie/index.ts b/packages/zombie/index.ts index 029cc7f4..d090ab17 100644 --- a/packages/zombie/index.ts +++ b/packages/zombie/index.ts @@ -5,7 +5,7 @@ import { fetchList, Item } from 'list'; import { Game, get8BBitGames } from '8bbit'; import { fetchImage } from 'image'; import { fetchEnDes, fetchJaDes, fetchZhDes } from 'description'; -import { existGames, incudesString, normalzile, removePunctuation } from 'utils'; +import { existGames, incudesString, normalize, removePunctuation } from 'utils'; import metadata2 from './metadata2.json'; import metadata1 from './metadata1.json'; @@ -26,7 +26,7 @@ const metadata: Data[] = [...metadata2]; const write = () => writeFile(resolve(__dirname, 'metadata2.json'), JSON.stringify(metadata, null, 2)); const appendData = async (lang: keyof Item, item: Item, game: Game) => { - const t = normalzile(item[lang]); + const t = normalize(item[lang]); if (!existGames.includes(t) && !metadataMap[t] && !metadata2Map[t]) { try { const description = ( diff --git a/packages/zombie/rom.ts b/packages/zombie/rom.ts index dec56fa3..ef1934ac 100644 --- a/packages/zombie/rom.ts +++ b/packages/zombie/rom.ts @@ -7,7 +7,7 @@ import { Data } from 'index'; import { get8BBitGames } from '8bbit'; import { fetchList, Item } from 'list'; -import { existGames, incudesString, normalzile, removePunctuation } from 'utils'; +import { existGames, incudesString, normalize, removePunctuation } from 'utils'; import metadata2 from './metadata2.json'; import metadata1 from './metadata1.json'; @@ -19,11 +19,11 @@ const metadata2Map = Object.fromEntries(metadata2.map((e: Data) => [e.title, e]) const write = async () => writeFile(resolve(__dirname, 'roms.json'), JSON.stringify(roms, null, 2)); -const getData = (title: string) => metadataMap[normalzile(title)] || metadata2Map[normalzile(title)]; +const getData = (title: string) => metadataMap[normalize(title)] || metadata2Map[normalize(title)]; const setRom = (item: Item, rom: string) => { if (item.ja) Object.assign(roms, { [getData(item.ja).title]: rom }); - if (item.zh && !existGames.includes(normalzile(item.zh))) Object.assign(roms, { [getData(item.zh).title]: rom }); + if (item.zh && !existGames.includes(normalize(item.zh))) Object.assign(roms, { [getData(item.zh).title]: rom }); if (item.en) Object.assign(roms, { [getData(item.en).title]: rom }); write(); }; diff --git a/packages/zombie/utils.ts b/packages/zombie/utils.ts index 5e3c5c62..9204f2eb 100644 --- a/packages/zombie/utils.ts +++ b/packages/zombie/utils.ts @@ -6,7 +6,7 @@ export const removePunctuation = (str: string) => .replace(/\s+/g, '') .replace('brothers', 'bros'); export const incudesString = (str1: string, str2: string) => str1.includes(str2) || str2.includes(str1); -export const normalzile = (str: string) => str.replaceAll('–', '-').replaceAll('’', "'").replaceAll('‘', "'"); +export const normalize = (str: string) => str.replaceAll('–', '-').replaceAll('’', "'").replaceAll('‘', "'"); export const existGames = [ '热血格斗传说', diff --git a/yarn.lock b/yarn.lock index 10b5dc3f..0a2f8079 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1259,10 +1259,10 @@ dependencies: lit-html "^1.4.1" -"@mantou/gem@^1.7.5": - version "1.7.5" - resolved "https://registry.yarnpkg.com/@mantou/gem/-/gem-1.7.5.tgz#789f3d7101f11be6c1c341a6e1ac29e07aa9347d" - integrity sha512-mlvLo4/7Q3ehSS1KwNrkB1cb1dsnKLLp0td6IpEJEzHehPe8Kzp4oRBTABQpDJN0+zDj62zdDwDPNQnVEGSJzQ== +"@mantou/gem@^1.7.9": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@mantou/gem/-/gem-1.7.11.tgz#4d16c91b387a52c558ea3f014e28f774e81a7537" + integrity sha512-rsfBELVKCmNfbl6O9GPRefn/Os65Il9ehRWMpuM23ufzRix8Gj3qa5UMD5FS77z6LFzXRe+JoHRb0HEtrZNjJA== dependencies: lit-html "^1.4.1" @@ -3890,19 +3890,10 @@ dset@^3.1.0: resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.2.tgz#89c436ca6450398396dc6538ea00abc0c54cd45a" integrity sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q== -duoyun-ui@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/duoyun-ui/-/duoyun-ui-1.1.5.tgz#a573b4e5346e006f8580763c6d2c7259c2bbf628" - integrity sha512-TjcMzdk4GsB1WnRl0z6kCPEXKWp09ccJrr/6z/xY6jeznuD8tQ3Cs5NmcYiOnOSgq+pdZ9329YrRPrCxsmM67Q== - dependencies: - d3-geo "^3.0.1" - deep-query-selector "^1.0.1" - elkjs "^0.7.1" - -duoyun-ui@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/duoyun-ui/-/duoyun-ui-1.1.5.tgz#a573b4e5346e006f8580763c6d2c7259c2bbf628" - integrity sha512-TjcMzdk4GsB1WnRl0z6kCPEXKWp09ccJrr/6z/xY6jeznuD8tQ3Cs5NmcYiOnOSgq+pdZ9329YrRPrCxsmM67Q== +duoyun-ui@^1.1.9: + version "1.1.18" + resolved "https://registry.yarnpkg.com/duoyun-ui/-/duoyun-ui-1.1.18.tgz#a780659ba2f3abf3656e92537bf542eff5892918" + integrity sha512-Nb6FdKkmPpY/xDUBv+pe4FLM29ZdYmvqOW74ed1Q5orp1fRfVR1mjksQNiQfaFdPwRaL2FPo/jIa+M9aC3Tk+g== dependencies: d3-geo "^3.0.1" deep-query-selector "^1.0.1"