From 3b9cead62caca5f56cf9d2b9d9fa3f7dd8da8cec Mon Sep 17 00:00:00 2001 From: Vladislav Kibenko Date: Sat, 30 Sep 2023 18:40:23 +0300 Subject: [PATCH 1/6] fix(resize-event): replace emitter.emit with emit --- packages/bridge/src/events/emitter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bridge/src/events/emitter.ts b/packages/bridge/src/events/emitter.ts index 6187d529e..0311c206a 100644 --- a/packages/bridge/src/events/emitter.ts +++ b/packages/bridge/src/events/emitter.ts @@ -35,7 +35,7 @@ export function createEmitter(): EventEmitter { // add our own listener to make sure, viewport information is always fresh. // Issue: https://github.com/Telegram-Web-Apps/tma.js/issues/10 window.addEventListener('resize', () => { - emitter.emit('viewport_changed', { + emit('viewport_changed', { width: window.innerWidth, height: window.innerHeight, is_state_stable: true, From b227371fac23c7468eee0b7595728cc46bcf218f Mon Sep 17 00:00:00 2001 From: Vladislav Kibenko Date: Sat, 30 Sep 2023 18:41:00 +0300 Subject: [PATCH 2/6] fix(tracking): fix incorrect tracking of components updates --- .../src/useDynamicInitResultValue.ts | 52 ++++++++++++++ packages/sdk-solid/src/useInitResultValue.ts | 14 ++++ packages/sdk-solid/src/useSDK.ts | 67 ++++--------------- 3 files changed, 78 insertions(+), 55 deletions(-) create mode 100644 packages/sdk-solid/src/useDynamicInitResultValue.ts create mode 100644 packages/sdk-solid/src/useInitResultValue.ts diff --git a/packages/sdk-solid/src/useDynamicInitResultValue.ts b/packages/sdk-solid/src/useDynamicInitResultValue.ts new file mode 100644 index 000000000..c6065a0d3 --- /dev/null +++ b/packages/sdk-solid/src/useDynamicInitResultValue.ts @@ -0,0 +1,52 @@ +import { Accessor, createEffect, createSignal, onCleanup } from 'solid-js'; +import { SDKInitResult, SDKInitResultKey, SDKInitResultValue } from './types.js'; +import { useInitResultValue } from './useInitResultValue.js'; + +interface Trackable { + on: (event: any, ...args: any[]) => void; + off: (event: any, ...args: any[]) => void; +} + +type EventName = T extends { + on(event: infer E, ...args: any[]): any +} ? E : never; + +type DynamicComponentKey = { + [K in SDKInitResultKey]: SDKInitResultValue extends Trackable ? K : never; +}[SDKInitResultKey]; + +/** + * Extracts value from the SDK init result by specified key and listens to its all specified + * events, making the returned value to update. + * @param initResult - SDK init result. + * @param key - SDK init result key. + * @param events - tracked events list. + */ +export function useDynamicInitResultValue( + initResult: Accessor, + key: K, + events: EventName>[], +): Accessor> { + // Get original value from init result. + const initResultValue = useInitResultValue(initResult, key); + + // Here we store value, which should always update it in case, some of its props + // were changed/ + const [value, setValue] = createSignal(initResultValue(), { equals: false }); + + createEffect(() => { + const value = initResultValue(); + const listener = () => { + // We use prev => prev on purpose. This will make Solid sure, something inside + // dynamic value changed. + setValue(prev => prev); + }; + + events.forEach(event => value.on(event, listener)); + onCleanup(() => { + events.forEach(event => value.off(event, listener)); + }); + }); + + return value; +} \ No newline at end of file diff --git a/packages/sdk-solid/src/useInitResultValue.ts b/packages/sdk-solid/src/useInitResultValue.ts new file mode 100644 index 000000000..18f2dac34 --- /dev/null +++ b/packages/sdk-solid/src/useInitResultValue.ts @@ -0,0 +1,14 @@ +import { SDKInitResult, SDKInitResultKey, SDKInitResultValue } from './types.js'; +import { Accessor, createMemo } from 'solid-js'; + +/** + * Extracts value from the SDK init result by key. + * @param result - init result accessor. + * @param key - key to extract. + */ +export function useInitResultValue( + result: Accessor, + key: K +): Accessor> { + return createMemo(() => result()[key]); +} \ No newline at end of file diff --git a/packages/sdk-solid/src/useSDK.ts b/packages/sdk-solid/src/useSDK.ts index b1e6a2421..750fc17b4 100644 --- a/packages/sdk-solid/src/useSDK.ts +++ b/packages/sdk-solid/src/useSDK.ts @@ -1,57 +1,14 @@ -import { type Accessor, createEffect, createMemo, createSignal, onCleanup } from 'solid-js'; +import { createMemo, type Accessor } from 'solid-js'; -import type { SDKInitResult, SDKInitResultKey, SDKInitResultValue } from './types.js'; import { useSDKContext } from './hooks.js'; - -interface Trackable { - on: (event: any, ...args: any[]) => void; - off: (event: any, ...args: any[]) => void; -} - -type EventName = T extends { - on(event: infer E, ...args: any[]): any -} ? E : never; - -type DynamicComponentKey = { - [K in SDKInitResultKey]: SDKInitResultValue extends Trackable - ? K - : never; -}[SDKInitResultKey]; +import { useInitResultValue } from './useInitResultValue.js'; +import { useDynamicInitResultValue } from './useDynamicInitResultValue.js'; +import type { SDKInitResultKey, SDKInitResultValue } from './types.js'; export type SDK = { [K in SDKInitResultKey]: Accessor> }; -function useDynamicComponent( - initResult: Accessor, - key: K, - events: EventName>[], -): Accessor> { - const [component, setComponent] = createSignal(initResult()[key], { equals: false }); - - createEffect(() => { - const obj = component(); - - events.forEach(event => { - (obj as any).on(event, () => setComponent(() => obj)); - }); - - onCleanup(() => { - events.forEach(event => { - (obj as any).off(event, () => setComponent(() => obj)); - }); - }); - }); - - return component; -} - -function useInitResultValue(initResult: Accessor, key: K) { - const value = createMemo>(() => initResult()[key]); - - return value; -} - /** * Returns ready to use SDK components. */ @@ -68,13 +25,13 @@ export function useSDK(): SDK { }); return { - backButton: useDynamicComponent(sdk, 'backButton', ['isVisibleChanged']), - closingBehavior: useDynamicComponent(sdk, 'closingBehavior', ['isConfirmationNeededChanged']), + backButton: useDynamicInitResultValue(sdk, 'backButton', ['isVisibleChanged']), + closingBehavior: useDynamicInitResultValue(sdk, 'closingBehavior', ['isConfirmationNeededChanged']), cloudStorage: useInitResultValue(sdk, 'cloudStorage'), haptic: useInitResultValue(sdk, 'haptic'), initData: useInitResultValue(sdk, 'initData'), initDataRaw: useInitResultValue(sdk, 'initDataRaw'), - mainButton: useDynamicComponent(sdk, 'mainButton', [ + mainButton: useDynamicInitResultValue(sdk, 'mainButton', [ 'backgroundColorChanged', 'isVisibleChanged', 'isProgressVisibleChanged', @@ -82,16 +39,16 @@ export function useSDK(): SDK { 'textChanged', 'textColorChanged', ]), - popup: useDynamicComponent(sdk, 'popup', ['isOpenedChanged']), + popup: useDynamicInitResultValue(sdk, 'popup', ['isOpenedChanged']), postEvent: useInitResultValue(sdk, 'postEvent'), - qrScanner: useDynamicComponent(sdk, 'qrScanner', ['isOpenedChanged']), - themeParams: useDynamicComponent(sdk, 'themeParams', ['changed']), - viewport: useDynamicComponent(sdk, 'viewport', [ + qrScanner: useDynamicInitResultValue(sdk, 'qrScanner', ['isOpenedChanged']), + themeParams: useDynamicInitResultValue(sdk, 'themeParams', ['changed']), + viewport: useDynamicInitResultValue(sdk, 'viewport', [ 'heightChanged', 'isExpandedChanged', 'stableHeightChanged', 'widthChanged', ]), - webApp: useDynamicComponent(sdk, 'webApp', ['backgroundColorChanged', 'headerColorChanged']), + webApp: useDynamicInitResultValue(sdk, 'webApp', ['backgroundColorChanged', 'headerColorChanged']), }; } \ No newline at end of file From d1a68bfb3b65b068c26dc7f9c1905dba73c5c07b Mon Sep 17 00:00:00 2001 From: Vladislav Kibenko Date: Sat, 30 Sep 2023 19:00:30 +0300 Subject: [PATCH 3/6] feat(apps): add local playground --- apps/local-playground/README.md | 11 +++++++++++ apps/local-playground/index.html | 14 ++++++++++++++ apps/local-playground/index.ts | 8 ++++++++ apps/local-playground/package.json | 11 +++++++++++ apps/local-playground/tsconfig.json | 26 ++++++++++++++++++++++++++ 5 files changed, 70 insertions(+) create mode 100644 apps/local-playground/README.md create mode 100644 apps/local-playground/index.html create mode 100644 apps/local-playground/index.ts create mode 100644 apps/local-playground/package.json create mode 100644 apps/local-playground/tsconfig.json diff --git a/apps/local-playground/README.md b/apps/local-playground/README.md new file mode 100644 index 000000000..64b94cae8 --- /dev/null +++ b/apps/local-playground/README.md @@ -0,0 +1,11 @@ +# Local Playground + +This application can be used by developers to test the code, written in the current monorepo. +We created this playground to avoid writing the same code from one package to another, adding +`index.html` and `vite.config.ts` files. + +## Usage + +1. First of all just import any code from the other packages. +2. Run `pnpm run dev`. +3. Open URL from the console. \ No newline at end of file diff --git a/apps/local-playground/index.html b/apps/local-playground/index.html new file mode 100644 index 000000000..e691da486 --- /dev/null +++ b/apps/local-playground/index.html @@ -0,0 +1,14 @@ + + + + + + Document + + + + + \ No newline at end of file diff --git a/apps/local-playground/index.ts b/apps/local-playground/index.ts new file mode 100644 index 000000000..92ec70736 --- /dev/null +++ b/apps/local-playground/index.ts @@ -0,0 +1,8 @@ +/* +* You can import any code from the other folders in mono-repo. For example, you could +* use this code: +* +* import { postEvent } from '../../packages/bridge/src/index.ts'; +* +* And test postEvent function here. +* */ \ No newline at end of file diff --git a/apps/local-playground/package.json b/apps/local-playground/package.json new file mode 100644 index 000000000..8466b7104 --- /dev/null +++ b/apps/local-playground/package.json @@ -0,0 +1,11 @@ +{ + "name": "local-playground", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + } +} diff --git a/apps/local-playground/tsconfig.json b/apps/local-playground/tsconfig.json new file mode 100644 index 000000000..91882dbb1 --- /dev/null +++ b/apps/local-playground/tsconfig.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "composite": false, + "declaration": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "inlineSources": true, + "isolatedModules": true, + "lib": [ + "esnext", + "DOM" + ], + "module": "ESNext", + "moduleResolution": "NodeNext", + "noUnusedLocals": true, + "noUnusedParameters": true, + "preserveWatchOutput": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "target": "ESNext" + }, + "exclude": ["node_modules"] +} From 5f417729239b3903625e7c7205ea7953c1e37e0f Mon Sep 17 00:00:00 2001 From: Vladislav Kibenko Date: Sat, 30 Sep 2023 19:00:41 +0300 Subject: [PATCH 4/6] chore(deps): add missing deps --- package.json | 3 +- pnpm-lock.yaml | 84 +++++++++++++++++++++++++++++--------------------- 2 files changed, 51 insertions(+), 36 deletions(-) diff --git a/package.json b/package.json index eb586a025..763a9267b 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,8 @@ "ts-jest": "^29.1.1", "tslib": "^2.6.0", "turbo": "^1.10.14", - "typescript": "^5.1.6" + "typescript": "^5.1.6", + "vite": "^4.4.9" }, "packageManager": "pnpm@7.15.0", "name": "monorepo", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1cecd4a1a..ead5d1dc4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -68,6 +68,9 @@ importers: typescript: specifier: ^5.1.6 version: 5.1.6 + vite: + specifier: ^4.4.9 + version: 4.4.9(@types/node@16.18.38) apps/docs: dependencies: @@ -3488,7 +3491,7 @@ packages: lodash.merge: 4.6.2 lodash.uniq: 4.5.0 resolve-from: 5.0.0 - ts-node: 10.9.1(@types/node@20.4.7)(typescript@5.2.2) + ts-node: 10.9.1(@types/node@16.18.38)(typescript@5.1.6) typescript: 5.2.2 transitivePeerDependencies: - '@swc/core' @@ -7991,7 +7994,7 @@ packages: dependencies: '@types/node': 20.4.7 cosmiconfig: 8.2.0 - ts-node: 10.9.1(@types/node@20.4.7)(typescript@5.2.2) + ts-node: 10.9.1(@types/node@16.18.38)(typescript@5.1.6) typescript: 5.2.2 dev: true optional: true @@ -13365,7 +13368,6 @@ packages: nanoid: 3.3.6 picocolors: 1.0.0 source-map-js: 1.0.2 - dev: false /preferred-pm@3.0.3: resolution: {integrity: sha512-+wZgbxNES/KlJs9q40F/1sfOd/j7f1O9JaHcW5Dsn3aUUOZg3L2bjpVUcKV2jvtElYfoTuQiNeMfQJ4kwUAhCQ==} @@ -14182,6 +14184,14 @@ packages: fsevents: 2.3.3 dev: true + /rollup@3.29.4: + resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.3 + dev: true + /rtl-detect@1.0.4: resolution: {integrity: sha512-EBR4I2VDSSYr7PkBmFy04uhycIpDKp+21p/jARYXlCSjQksTBQcJ0HFUPOO79EPPH5JS6VAhiIQbycf0O3JAxQ==} dev: false @@ -15227,38 +15237,6 @@ packages: yn: 3.1.1 dev: true - /ts-node@10.9.1(@types/node@20.4.7)(typescript@5.2.2): - resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.9 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.3 - '@types/node': 20.4.7 - acorn: 8.8.1 - acorn-walk: 8.2.0 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.2.2 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - dev: true - optional: true - /tsconfig-paths@3.14.2: resolution: {integrity: sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==} dependencies: @@ -15927,6 +15905,42 @@ packages: fsevents: 2.3.3 dev: true + /vite@4.4.9(@types/node@16.18.38): + resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 16.18.38 + esbuild: 0.18.20 + postcss: 8.4.29 + rollup: 3.29.4 + optionalDependencies: + fsevents: 2.3.3 + dev: true + /vitefu@0.2.4(vite@4.3.9): resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==} peerDependencies: From 654891f8e95fe7cd88961e500e07d8ded1fa527a Mon Sep 17 00:00:00 2001 From: Vladislav Kibenko Date: Sat, 30 Sep 2023 19:07:54 +0300 Subject: [PATCH 5/6] docs(changeset): Add logging in case resize event occured --- .changeset/forty-ravens-love.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/forty-ravens-love.md diff --git a/.changeset/forty-ravens-love.md b/.changeset/forty-ravens-love.md new file mode 100644 index 000000000..d1d0cd790 --- /dev/null +++ b/.changeset/forty-ravens-love.md @@ -0,0 +1,5 @@ +--- +"@tma.js/bridge": patch +--- + +Add logging in case resize event occured From 230e77a92b6cf6fd8e21d15fdd5bc16da191d1b2 Mon Sep 17 00:00:00 2001 From: Vladislav Kibenko Date: Sat, 30 Sep 2023 19:08:20 +0300 Subject: [PATCH 6/6] docs(changeset): Fix a bug with incorrect components events tracking --- .changeset/grumpy-tables-camp.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/grumpy-tables-camp.md diff --git a/.changeset/grumpy-tables-camp.md b/.changeset/grumpy-tables-camp.md new file mode 100644 index 000000000..ca7a54a0e --- /dev/null +++ b/.changeset/grumpy-tables-camp.md @@ -0,0 +1,5 @@ +--- +"@tma.js/sdk-solid": patch +--- + +Fix a bug with incorrect components events tracking