@tma.js/sdk 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tma.js/sdk",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "TypeScript Source Development Kit for Telegram Mini Apps client application.",
5
5
  "author": "Vladislav Kibenko <wolfram.deus@gmail.com>",
6
6
  "homepage": "https://github.com/Telegram-Mini-Apps/tma.js#readme",
package/src/index.ts CHANGED
@@ -154,6 +154,7 @@ export type { RequestId, CreateRequestIdFunc } from './types/index.js';
154
154
  export { Utils } from './utils/index.js';
155
155
  export { compareVersions, type Version } from './version/index.js';
156
156
  export {
157
+ isStableViewportPlatform,
157
158
  requestViewport,
158
159
  Viewport,
159
160
  type RequestViewportResult,
@@ -1,53 +1,19 @@
1
1
  import { getStorageValue, saveStorageValue } from '~/storage.js';
2
- import { requestViewport, Viewport } from '~/viewport/index.js';
2
+ import {
3
+ isStableViewportPlatform,
4
+ requestViewport,
5
+ Viewport,
6
+ type ViewportProps,
7
+ } from '~/viewport/index.js';
3
8
  import type { PostEvent } from '~/bridge/index.js';
4
9
  import type { Platform } from '~/types/index.js';
5
10
 
6
11
  /**
7
- * Returns true in case, specified platform supports calling Mini Apps
8
- * "web_app_request_viewport" method.
9
- * @param platform - platform identifier.
10
- */
11
- function isRequestSupportedPlatform(platform: Platform): boolean {
12
- return !['macos', 'web', 'weba'].includes(platform);
13
- }
14
-
15
- /**
16
- * Attempts to create Viewport instance using known parameters and local storage.
17
- * @param isPageReload - was page reloaded.
18
- * @param platform - platform identifier.
19
- * @param postEvent - Bridge postEvent function.
20
- */
21
- function tryCreate(
22
- isPageReload: boolean,
23
- platform: Platform,
24
- postEvent: PostEvent,
25
- ): Viewport | null {
26
- if (isPageReload || !isRequestSupportedPlatform(platform)) {
27
- return new Viewport({
28
- height: window.innerHeight,
29
- isExpanded: true,
30
- postEvent,
31
- stableHeight: window.innerHeight,
32
- width: window.innerWidth,
33
- });
34
- }
35
-
36
- const state = getStorageValue('viewport');
37
- if (state) {
38
- return new Viewport({ ...state, postEvent });
39
- }
40
-
41
- return null;
42
- }
43
-
44
- /**
45
- * Synchronizes specified Viewport instance with Telegram application and always saves its state
46
- * in local storage.
47
- * @param viewport - Viewport instance.
12
+ * Creates new bound instance of the Viewport component.
13
+ * @param props - properties to create new instance.
48
14
  */
49
- function bind(viewport: Viewport): Viewport {
50
- viewport.listen();
15
+ function instantiate(props: ViewportProps): Viewport {
16
+ const viewport = new Viewport(props);
51
17
 
52
18
  // TODO: Should probably use throttle for height.
53
19
  viewport.on('change', () => saveStorageValue('viewport', {
@@ -57,59 +23,72 @@ function bind(viewport: Viewport): Viewport {
57
23
  width: viewport.width,
58
24
  }));
59
25
 
26
+ viewport.listen();
27
+
60
28
  return viewport;
61
29
  }
62
30
 
63
31
  /**
64
- * Creates Viewport instance using its actual state from the storage. Otherwise, creates it
65
- * with default parameters.
32
+ * Creates Viewport instance using its actual state from the Telegram application.
66
33
  * @param isPageReload - was page reloaded.
67
34
  * @param platform - platform identifier.
68
35
  * @param postEvent - Bridge postEvent function.
36
+ * @param complete - is initialization complete.
69
37
  */
70
- export function createViewportSync(
38
+ export function createViewport(
71
39
  isPageReload: boolean,
72
40
  platform: Platform,
73
41
  postEvent: PostEvent,
74
- ): Viewport {
75
- const viewport = bind(
76
- tryCreate(isPageReload, platform, postEvent) || new Viewport({
77
- width: 0,
78
- height: 0,
79
- isExpanded: false,
80
- postEvent,
81
- stableHeight: 0,
82
- }),
83
- );
42
+ complete: boolean,
43
+ ): Viewport | Promise<Viewport> {
44
+ // If page was reloaded, we expect viewport to be restored from the storage.
45
+ const state = isPageReload ? getStorageValue('viewport') : null;
46
+ if (state) {
47
+ return instantiate({ ...state, postEvent });
48
+ }
84
49
 
85
- if (isRequestSupportedPlatform(platform)) {
86
- viewport.sync({ postEvent, timeout: 100 }).catch((e) => {
87
- // eslint-disable-next-line no-console
88
- console.error('Unable to actualize viewport state', e);
50
+ // If platform has a stable viewport, it means we could instantiate Viewport using
51
+ // the window global object properties.
52
+ if (isStableViewportPlatform(platform)) {
53
+ return instantiate({
54
+ height: window.innerHeight,
55
+ isExpanded: true,
56
+ postEvent,
57
+ stableHeight: window.innerHeight,
58
+ width: window.innerWidth,
89
59
  });
90
60
  }
91
61
 
92
- return viewport;
93
- }
94
-
95
- /**
96
- * Creates Viewport instance using its actual state from the Telegram application.
97
- * @param isPageReload - was page reloaded.
98
- * @param platform - platform identifier.
99
- * @param postEvent - Bridge postEvent function.
100
- */
101
- export async function createViewportAsync(
102
- isPageReload: boolean,
103
- platform: Platform,
104
- postEvent: PostEvent,
105
- ): Promise<Viewport> {
106
- return bind(
107
- tryCreate(isPageReload, platform, postEvent)
108
- || await requestViewport({ postEvent, timeout: 100 })
109
- .then(({ height, isStateStable, ...rest }) => new Viewport({
62
+ // If initialization is complete, we have to create Viewport instance using its actual
63
+ // state from the Telegram application.
64
+ if (complete) {
65
+ return requestViewport({
66
+ postEvent,
67
+ timeout: 5000,
68
+ })
69
+ .then(({ height, isStateStable, ...rest }) => instantiate({
110
70
  ...rest,
111
71
  height,
112
72
  stableHeight: isStateStable ? height : 0,
113
- })),
114
- );
73
+ }));
74
+ }
75
+
76
+ // Otherwise we have no sources to get viewport properties from. In this case we just
77
+ // return some "empty" Viewport instance and synchronize it in the background.
78
+ const viewport = instantiate({
79
+ width: 0,
80
+ height: 0,
81
+ isExpanded: false,
82
+ postEvent,
83
+ stableHeight: 0,
84
+ });
85
+
86
+ /* c8 ignore start */
87
+ viewport.sync({ postEvent, timeout: 5000 }).catch((e) => {
88
+ // eslint-disable-next-line no-console
89
+ console.error('Unable to actualize viewport state', e);
90
+ });
91
+ /* c8 ignore stop */
92
+
93
+ return viewport;
115
94
  }
package/src/init/init.ts CHANGED
@@ -7,9 +7,10 @@ import {
7
7
  createClosingBehavior,
8
8
  createMainButton,
9
9
  createMiniApp,
10
- createRequestIdGenerator, createSettingsButton,
11
- createThemeParams, createViewportAsync,
12
- createViewportSync,
10
+ createRequestIdGenerator,
11
+ createSettingsButton,
12
+ createThemeParams,
13
+ createViewport,
13
14
  } from '~/init/creators/index.js';
14
15
  import { processCSSVars } from '~/init/css/index.js';
15
16
  import { InitData } from '~/init-data/index.js';
@@ -21,13 +22,16 @@ import { Utils } from '~/utils/index.js';
21
22
 
22
23
  import type { InitOptions, InitResult } from './types.js';
23
24
 
24
- type ComputedInitResult<O> = O extends { async: true } ? Promise<InitResult> : InitResult;
25
+ type ComputedInitResult<O> = O extends { async: true } | { complete: true }
26
+ ? Promise<InitResult>
27
+ : InitResult;
25
28
 
26
29
  export function init(): InitResult;
27
30
  export function init<O extends InitOptions>(options: O): ComputedInitResult<O>;
28
31
  export function init(options: InitOptions = {}): InitResult | Promise<InitResult> {
29
32
  const {
30
33
  async = false,
34
+ complete = async,
31
35
  cssVars = false,
32
36
  acceptCustomStyles = false,
33
37
  } = options;
@@ -99,12 +103,9 @@ export function init(options: InitOptions = {}): InitResult | Promise<InitResult
99
103
  : {}),
100
104
  };
101
105
 
102
- const viewport = async
103
- ? createViewportAsync(isPageReload, platform, postEvent)
104
- : createViewportSync(isPageReload, platform, postEvent);
105
-
106
- if (viewport instanceof Promise) {
107
- return viewport.then((vp) => {
106
+ const viewport = createViewport(isPageReload, platform, postEvent, complete);
107
+ if (viewport instanceof Promise || complete) {
108
+ return Promise.resolve(viewport).then((vp) => {
108
109
  processCSSVars(
109
110
  cssVars,
110
111
  result.miniApp,
@@ -112,10 +113,7 @@ export function init(options: InitOptions = {}): InitResult | Promise<InitResult
112
113
  vp,
113
114
  );
114
115
 
115
- return {
116
- ...result,
117
- viewport: vp,
118
- };
116
+ return { ...result, viewport: vp };
119
117
  });
120
118
  }
121
119
 
@@ -128,7 +126,7 @@ export function init(options: InitOptions = {}): InitResult | Promise<InitResult
128
126
 
129
127
  return { ...result, viewport };
130
128
  } catch (e) {
131
- if (async) {
129
+ if (complete) {
132
130
  return Promise.reject(e);
133
131
  }
134
132
  throw e;
package/src/init/types.ts CHANGED
@@ -62,10 +62,7 @@ export type InitCSSVarsOption = boolean | InitCSSVarsSpecificOption;
62
62
 
63
63
  export interface InitOptions {
64
64
  /**
65
- * True if synchronization must be performed asynchronously. This allows init function to
66
- * perform async operations. One of them is the actual viewport state retrieving from the
67
- * Telegram application. Otherwise, viewport state will be retrieved later.
68
- * @default false
65
+ * @deprecated This option name was considered inappropriate. Use `complete` instead.
69
66
  */
70
67
  async?: boolean;
71
68
 
@@ -87,4 +84,11 @@ export interface InitOptions {
87
84
  * @default false
88
85
  */
89
86
  cssVars?: InitCSSVarsOption;
87
+
88
+ /**
89
+ * True if initialization must be performed completely. This includes retrieving some components
90
+ * state from the Telegram application, and as a result, this makes initialization asynchronous.
91
+ * @default false
92
+ */
93
+ complete?: boolean;
90
94
  }
@@ -17,7 +17,7 @@ export const contactParser = searchParams<RequestedContact>({
17
17
  from: 'first_name',
18
18
  },
19
19
  lastName: {
20
- type: string(),
20
+ type: string().optional(),
21
21
  from: 'last_name',
22
22
  },
23
23
  }),
@@ -31,7 +31,7 @@ export interface RequestedContact {
31
31
  userId: number;
32
32
  phoneNumber: string;
33
33
  firstName: string;
34
- lastName: string;
34
+ lastName?: string;
35
35
  };
36
36
  authDate: Date;
37
37
  hash: string;
@@ -7,8 +7,8 @@ export type Platform =
7
7
  | 'ios'
8
8
  | 'macos'
9
9
  | 'tdesktop'
10
- | 'web'
11
- | 'weba'
12
10
  | 'unigram'
13
11
  | 'unknown'
12
+ | 'web'
13
+ | 'weba'
14
14
  | string;
@@ -1,3 +1,4 @@
1
+ export * from './isStableViewportPlatform.js';
1
2
  export * from './requestViewport.js';
2
3
  export * from './types.js';
3
4
  export * from './Viewport.js';
@@ -0,0 +1,10 @@
1
+ import type { Platform } from '~/types/index.js';
2
+
3
+ /**
4
+ * Returns true if specified platform has stable viewport. Stable means not changing from time to
5
+ * time.
6
+ * @param platform - platform identifier.
7
+ */
8
+ export function isStableViewportPlatform(platform: Platform): boolean {
9
+ return ['macos', 'tdesktop', 'unigram', 'web', 'weba'].includes(platform);
10
+ }