@alfalab/bridge-to-native 1.5.1-beta.82667a7 → 1.5.2-beta.b951ff8

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.
@@ -1,5 +1,5 @@
1
1
 
2
- import { type BrowserHistoryApiWrappers, type HapticsOptions, type HistoryPushStateParams, type HistoryReplaceStateParams, type LocationAssignParam, type LogError, type NativeFeatureKey, type PdfType, type SheetDragArea } from './types';
2
+ import { type BrowserHistoryApiWrappers, type HistoryPushStateParams, type HistoryReplaceStateParams, type LocationAssignParam, type LogError, type NativeFeatureKey, type PdfType, type WebFeaturesGeoConfiguration, type WebFeaturesHapticsOptions, type WebFeaturesSheetDragArea } from './types';
3
3
  /**
4
4
  * Сервис, предоставляет методы для WA, работающего внутри NA.
5
5
  */
@@ -18,10 +18,11 @@ export declare class BridgeToNative {
18
18
  browserHistoryApiWrappers?: BrowserHistoryApiWrappers;
19
19
  logError?: LogError;
20
20
  } | undefined);
21
- private nativeMessagesService;
22
21
  private nativeParamsService;
23
22
  private externalLinksService;
24
23
  private nativeNavigationAndTitleService;
24
+ private nativeBridgeService;
25
+ private webFeaturesService;
25
26
  /**
26
27
  * Схема NA.
27
28
  */
@@ -79,6 +80,16 @@ export declare class BridgeToNative {
79
80
  * ресурс в новом WV.
80
81
  */
81
82
  getHrefToOpenInBrowser(link: string): string;
83
+ /**
84
+ * Запрашивает текущую конфигурацию геолокации WebFeatures.
85
+ *
86
+ * ВАЖНО!
87
+ * Доступно только в iOS начиная с версии АМ `17.0.0`.
88
+ * В несовместимом окружении возвращённый промис будет отклонён.
89
+ *
90
+ * @returns Промис с `permission` и `userCoordinate` (`null`, если доступ не разрешён).
91
+ */
92
+ getGeoConfiguration(): Promise<WebFeaturesGeoConfiguration>;
82
93
  /**
83
94
  * Делает один шаг назад по браузерной истории и модифицирует внутреннее состояние B2N,
84
95
  * чтобы в дальнейшем зарегистрировать этот переход в NA.
@@ -243,34 +254,46 @@ export declare class BridgeToNative {
243
254
  */
244
255
  setTitle(nativeTitle: string): void;
245
256
  /**
246
- * Возвращает статус доступа к геолокации и, при наличии разрешения, координаты пользователя
257
+ * Воспроизводит тактильный отклик WebFeatures.
247
258
  *
248
- * @param data пустой объект или null
249
- */
250
- getGeoConfiguration(data?: Record<string, never> | null): Promise<import("./types").GeoConfiguration>;
251
- /**
252
- * Воспроизводит тактильный отклик
259
+ * ВАЖНО!
260
+ * Доступно только в iOS начиная с версии АМ `17.0.0`.
261
+ * В несовместимом окружении вызов игнорируется.
262
+ * Управляется нативным фича-тоглом `webViewHapticsIOS`.
253
263
  *
254
- * @param type Тип тактильного отклика
255
- * @param intensity Интенсивность в диапазоне 0...1. Применяется только для impact-типов.Если не передана — используется значение по умолчанию
264
+ * @param options.type Тип тактильного отклика.
265
+ * @param options.intensity Интенсивность `0...1` (только для impact-типов).
266
+ * B2N пробрасывает значение в натив без валидации диапазона.
256
267
  */
257
- vibrate(options: HapticsOptions): void;
268
+ vibrate(options: WebFeaturesHapticsOptions): void;
258
269
  /**
259
- * Задаёт область шторы, за которую можно её перетаскивать
270
+ * Задаёт область нативной шторы, за которую можно её перетаскивать.
271
+ *
272
+ * ВАЖНО!
273
+ * Доступно только в iOS начиная с версии АМ `17.0.0`.
274
+ * В несовместимом окружении вызов игнорируется.
260
275
  *
261
- * @param area Область шторы, за которую можно её перетаскивать
262
- * - `wholeSheet` — вся штора (значение по умолчанию в нативе);
263
- * - `navigationBar` — только навигационная панель шторы.
276
+ * @param area Область перетаскивания шторы.
264
277
  */
265
- setSheetDragArea(area: SheetDragArea): void;
278
+ setSheetDragArea(area: WebFeaturesSheetDragArea): void;
266
279
  /**
267
- * Скрывает нативный таббар
280
+ * Скрывает нативный таббар.
281
+ *
282
+ * ВАЖНО!
283
+ * Доступно только в iOS начиная с версии АМ `17.0.0`.
284
+ * В несовместимом окружении вызов игнорируется.
285
+ * Управляется нативным фича-тоглом `mainSuperAppIOS`.
268
286
  */
269
287
  hideTabbar(): void;
270
288
  /**
271
- * Показывает нативный таббар и, при переданном selectedId, выбирает вкладку
289
+ * Показывает нативный таббар и, при переданном `selectedId`, выбирает вкладку.
290
+ *
291
+ * ВАЖНО!
292
+ * Доступно только в iOS начиная с версии АМ `17.0.0`.
293
+ * В несовместимом окружении вызов игнорируется.
294
+ * Управляется нативным фича-тоглом `mainSuperAppIOS`.
272
295
  *
273
- * @param selectedId id вкладки, которую выбрать при показе таббара. Если null или не передан — текущая вкладка не меняется
296
+ * @param selectedId id вкладки, которую выбрать при показе.
274
297
  */
275
- showTabbar(selectedId?: string | null): void;
298
+ showTabbar(selectedId?: string): void;
276
299
  }
@@ -3,9 +3,10 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.BridgeToNative = void 0;
5
5
  const external_links_service_1 = require("./services-and-utils/external-links-service");
6
- const native_messages_service_1 = require("./services-and-utils/native-messages-service");
6
+ const native_bridge_service_1 = require("./services-and-utils/native-bridge-service");
7
7
  const native_navigation_and_title_service_1 = require("./services-and-utils/native-navigation-and-title-service");
8
8
  const native_params_service_1 = require("./services-and-utils/native-params-service");
9
+ const web_features_service_1 = require("./services-and-utils/web-features-service");
9
10
  /**
10
11
  * Сервис, предоставляет методы для WA, работающего внутри NA.
11
12
  */
@@ -20,12 +21,13 @@ class BridgeToNative {
20
21
  * если не передать, B2N не будет логировать ошибки.
21
22
  */
22
23
  constructor(options) {
23
- var _a, _b, _c, _d;
24
+ var _a, _b, _c, _d, _e;
24
25
  this.options = options;
25
- this.nativeMessagesService = new native_messages_service_1.NativeMessagesService();
26
26
  this.nativeParamsService = new native_params_service_1.NativeParamsService((_a = this.options) === null || _a === void 0 ? void 0 : _a.logError);
27
27
  this.externalLinksService = new external_links_service_1.ExternalLinksService(this.nativeParamsService, (_b = this.options) === null || _b === void 0 ? void 0 : _b.logError);
28
28
  this.nativeNavigationAndTitleService = new native_navigation_and_title_service_1.NativeNavigationAndTitleService(this.nativeParamsService, (_c = this.options) === null || _c === void 0 ? void 0 : _c.browserHistoryApiWrappers, (_d = this.options) === null || _d === void 0 ? void 0 : _d.logError);
29
+ this.nativeBridgeService = new native_bridge_service_1.NativeBridgeService((_e = this.options) === null || _e === void 0 ? void 0 : _e.logError);
30
+ this.webFeaturesService = new web_features_service_1.WebFeaturesService(this.nativeParamsService, this.nativeBridgeService);
29
31
  }
30
32
  /**
31
33
  * Схема NA.
@@ -104,6 +106,18 @@ class BridgeToNative {
104
106
  getHrefToOpenInBrowser(link) {
105
107
  return this.externalLinksService.getHrefToOpenInBrowser(link);
106
108
  }
109
+ /**
110
+ * Запрашивает текущую конфигурацию геолокации WebFeatures.
111
+ *
112
+ * ВАЖНО!
113
+ * Доступно только в iOS начиная с версии АМ `17.0.0`.
114
+ * В несовместимом окружении возвращённый промис будет отклонён.
115
+ *
116
+ * @returns Промис с `permission` и `userCoordinate` (`null`, если доступ не разрешён).
117
+ */
118
+ getGeoConfiguration() {
119
+ return this.webFeaturesService.getGeoConfiguration();
120
+ }
107
121
  /**
108
122
  * Делает один шаг назад по браузерной истории и модифицирует внутреннее состояние B2N,
109
123
  * чтобы в дальнейшем зарегистрировать этот переход в NA.
@@ -299,45 +313,55 @@ class BridgeToNative {
299
313
  this.nativeNavigationAndTitleService.setTitle(nativeTitle);
300
314
  }
301
315
  /**
302
- * Возвращает статус доступа к геолокации и, при наличии разрешения, координаты пользователя
316
+ * Воспроизводит тактильный отклик WebFeatures.
303
317
  *
304
- * @param data пустой объект или null
305
- */
306
- getGeoConfiguration(data = {}) {
307
- return this.nativeMessagesService.getGeoConfiguration(data);
308
- }
309
- /**
310
- * Воспроизводит тактильный отклик
318
+ * ВАЖНО!
319
+ * Доступно только в iOS начиная с версии АМ `17.0.0`.
320
+ * В несовместимом окружении вызов игнорируется.
321
+ * Управляется нативным фича-тоглом `webViewHapticsIOS`.
311
322
  *
312
- * @param type Тип тактильного отклика
313
- * @param intensity Интенсивность в диапазоне 0...1. Применяется только для impact-типов.Если не передана — используется значение по умолчанию
323
+ * @param options.type Тип тактильного отклика.
324
+ * @param options.intensity Интенсивность `0...1` (только для impact-типов).
325
+ * B2N пробрасывает значение в натив без валидации диапазона.
314
326
  */
315
327
  vibrate(options) {
316
- this.nativeMessagesService.vibrate(options);
328
+ this.webFeaturesService.vibrate(options);
317
329
  }
318
330
  /**
319
- * Задаёт область шторы, за которую можно её перетаскивать
331
+ * Задаёт область нативной шторы, за которую можно её перетаскивать.
332
+ *
333
+ * ВАЖНО!
334
+ * Доступно только в iOS начиная с версии АМ `17.0.0`.
335
+ * В несовместимом окружении вызов игнорируется.
320
336
  *
321
- * @param area Область шторы, за которую можно её перетаскивать
322
- * - `wholeSheet` — вся штора (значение по умолчанию в нативе);
323
- * - `navigationBar` — только навигационная панель шторы.
337
+ * @param area Область перетаскивания шторы.
324
338
  */
325
339
  setSheetDragArea(area) {
326
- this.nativeMessagesService.setSheetDragArea(area);
340
+ this.webFeaturesService.setSheetDragArea(area);
327
341
  }
328
342
  /**
329
- * Скрывает нативный таббар
343
+ * Скрывает нативный таббар.
344
+ *
345
+ * ВАЖНО!
346
+ * Доступно только в iOS начиная с версии АМ `17.0.0`.
347
+ * В несовместимом окружении вызов игнорируется.
348
+ * Управляется нативным фича-тоглом `mainSuperAppIOS`.
330
349
  */
331
350
  hideTabbar() {
332
- this.nativeMessagesService.hideTabbar();
351
+ this.webFeaturesService.hideTabbar();
333
352
  }
334
353
  /**
335
- * Показывает нативный таббар и, при переданном selectedId, выбирает вкладку
354
+ * Показывает нативный таббар и, при переданном `selectedId`, выбирает вкладку.
355
+ *
356
+ * ВАЖНО!
357
+ * Доступно только в iOS начиная с версии АМ `17.0.0`.
358
+ * В несовместимом окружении вызов игнорируется.
359
+ * Управляется нативным фича-тоглом `mainSuperAppIOS`.
336
360
  *
337
- * @param selectedId id вкладки, которую выбрать при показе таббара. Если null или не передан — текущая вкладка не меняется
361
+ * @param selectedId id вкладки, которую выбрать при показе.
338
362
  */
339
363
  showTabbar(selectedId) {
340
- this.nativeMessagesService.showTabbar(selectedId);
364
+ this.webFeaturesService.showTabbar(selectedId);
341
365
  }
342
366
  }
343
367
  exports.BridgeToNative = BridgeToNative;
@@ -1,4 +1,4 @@
1
- import { type NativeFeaturesFromVersion } from './types';
1
+ import { type NativeFeaturesFromVersion, type WebFeatureAction } from './types';
2
2
  export declare const ANDROID_APP_ID = "alfabank";
3
3
  export declare const DEEP_LINK_PATTERN: RegExp;
4
4
  export declare const NATIVE_FEATURES_FROM_VERSION: NativeFeaturesFromVersion;
@@ -11,3 +11,6 @@ export declare const VERSION_TO_IOS_APP_ID: {
11
11
  readonly '13.5.0': "assistmekz";
12
12
  readonly '14.5.0': "smartfinancementor";
13
13
  };
14
+ export declare const WEB_FEATURES_FROM_VERSION: Readonly<Record<WebFeatureAction, {
15
+ fromVersion: string;
16
+ }>>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.VERSION_TO_IOS_APP_ID = exports.NATIVE_FEATURES_FROM_VERSION = exports.DEEP_LINK_PATTERN = exports.ANDROID_APP_ID = void 0;
3
+ exports.WEB_FEATURES_FROM_VERSION = exports.VERSION_TO_IOS_APP_ID = exports.NATIVE_FEATURES_FROM_VERSION = exports.DEEP_LINK_PATTERN = exports.ANDROID_APP_ID = void 0;
4
4
  exports.ANDROID_APP_ID = 'alfabank';
5
5
  exports.DEEP_LINK_PATTERN = /^(\/|alfabank:\/{3}dashboard\/|alfabank:\/{3}|alfabank:\/{2}|https:\/{2}online.alfabank.ru\/)/;
6
6
  exports.NATIVE_FEATURES_FROM_VERSION = {
@@ -28,3 +28,10 @@ exports.VERSION_TO_IOS_APP_ID = {
28
28
  '13.5.0': 'assistmekz',
29
29
  '14.5.0': 'smartfinancementor',
30
30
  };
31
+ exports.WEB_FEATURES_FROM_VERSION = {
32
+ 'geo.configuration': { fromVersion: '17.0.0' },
33
+ 'haptics.vibrate': { fromVersion: '17.0.0' },
34
+ 'sheet.setDragArea': { fromVersion: '17.0.0' },
35
+ 'tabbar.hide': { fromVersion: '17.0.0' },
36
+ 'tabbar.show': { fromVersion: '17.0.0' },
37
+ };
@@ -0,0 +1,38 @@
1
+ import { type LogError, type WebFeatureAction } from '../types';
2
+ export declare const CALL_TIMEOUT_MS = 10000;
3
+ /**
4
+ * Сервис-транспорт для взаимодействия WA с NA через WebFeatures-мост.
5
+ *
6
+ * Отправляет сообщения (`send` / `call`) и обрабатывает асинхронные ответы на `call`
7
+ * через `window.webBridge.callback`.
8
+ *
9
+ * Отправляет сообщения напрямую через мост соответствующей платформы.
10
+ */
11
+ export declare class NativeBridgeService {
12
+ private logError?;
13
+ private pendingCalls;
14
+ private savedCallback?;
15
+ constructor(logError?: LogError | undefined);
16
+ /**
17
+ * Отправляет fire-and-forget сообщение в NA (без ожидания ответа).
18
+ */
19
+ send(action: WebFeatureAction, data: unknown): void;
20
+ /**
21
+ * Отправляет сообщение типа `call` и возвращает промис с ответом NA.
22
+ *
23
+ * @throws Если транспорт недоступен или ответ не пришёл за {@link CALL_TIMEOUT_MS}.
24
+ */
25
+ call<Data>(action: WebFeatureAction, data: unknown): Promise<Data>;
26
+ private handleCallback;
27
+ /**
28
+ * Проверяет доступность транспорта для указанного типа сообщения.
29
+ */
30
+ private hasTransport;
31
+ /**
32
+ * Отправляет тело сообщения доступным транспортом.
33
+ *
34
+ * @returns `true`, если сообщение удалось передать.
35
+ */
36
+ private dispatch;
37
+ private reportNoTransport;
38
+ }
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NativeBridgeService = exports.CALL_TIMEOUT_MS = void 0;
4
+ exports.CALL_TIMEOUT_MS = 10000;
5
+ const BRIDGE_KIND_SEND = 'send';
6
+ const BRIDGE_KIND_CALL = 'call';
7
+ let requestIdCounter = 0;
8
+ const generateRequestId = () => {
9
+ if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
10
+ return crypto.randomUUID();
11
+ }
12
+ requestIdCounter += 1;
13
+ return String(requestIdCounter);
14
+ };
15
+ /**
16
+ * Сервис-транспорт для взаимодействия WA с NA через WebFeatures-мост.
17
+ *
18
+ * Отправляет сообщения (`send` / `call`) и обрабатывает асинхронные ответы на `call`
19
+ * через `window.webBridge.callback`.
20
+ *
21
+ * Отправляет сообщения напрямую через мост соответствующей платформы.
22
+ */
23
+ class NativeBridgeService {
24
+ constructor(logError) {
25
+ var _a;
26
+ this.logError = logError;
27
+ this.pendingCalls = new Map();
28
+ this.handleCallback = this.handleCallback.bind(this);
29
+ this.savedCallback = (_a = window.webBridge) === null || _a === void 0 ? void 0 : _a.callback;
30
+ window.webBridge = Object.assign(Object.assign({}, window.webBridge), { callback: this.handleCallback });
31
+ }
32
+ /**
33
+ * Отправляет fire-and-forget сообщение в NA (без ожидания ответа).
34
+ */
35
+ send(action, data) {
36
+ const body = {
37
+ kind: BRIDGE_KIND_SEND,
38
+ action,
39
+ requestId: generateRequestId(),
40
+ data,
41
+ };
42
+ if (!this.dispatch(body)) {
43
+ this.reportNoTransport('send', action);
44
+ }
45
+ }
46
+ /**
47
+ * Отправляет сообщение типа `call` и возвращает промис с ответом NA.
48
+ *
49
+ * @throws Если транспорт недоступен или ответ не пришёл за {@link CALL_TIMEOUT_MS}.
50
+ */
51
+ call(action, data) {
52
+ const requestId = generateRequestId();
53
+ const body = {
54
+ kind: BRIDGE_KIND_CALL,
55
+ action,
56
+ requestId,
57
+ data,
58
+ };
59
+ if (!this.hasTransport(BRIDGE_KIND_CALL)) {
60
+ this.reportNoTransport('call', action);
61
+ return Promise.reject(new Error(`NativeBridgeService: transport is not available for "${action}"`));
62
+ }
63
+ return new Promise((resolve, reject) => {
64
+ const timeoutId = setTimeout(() => {
65
+ this.pendingCalls.delete(requestId);
66
+ reject(new Error(`NativeBridgeService: request "${action}" timed out`));
67
+ }, exports.CALL_TIMEOUT_MS);
68
+ this.pendingCalls.set(requestId, {
69
+ resolve: resolve,
70
+ reject,
71
+ timeoutId,
72
+ });
73
+ this.dispatch(body);
74
+ });
75
+ }
76
+ handleCallback(requestId, payload) {
77
+ var _a;
78
+ const pending = this.pendingCalls.get(requestId);
79
+ if (!pending) {
80
+ (_a = this.savedCallback) === null || _a === void 0 ? void 0 : _a.call(this, requestId, payload);
81
+ return;
82
+ }
83
+ clearTimeout(pending.timeoutId);
84
+ this.pendingCalls.delete(requestId);
85
+ if (payload.error) {
86
+ const error = new Error(payload.error.message);
87
+ error.code = payload.error.code;
88
+ pending.reject(error);
89
+ return;
90
+ }
91
+ pending.resolve(payload.data);
92
+ }
93
+ /**
94
+ * Проверяет доступность транспорта для указанного типа сообщения.
95
+ */
96
+ // eslint-disable-next-line class-methods-use-this -- единая точка доступа к глобалам window.
97
+ hasTransport(kind) {
98
+ var _a, _b, _c, _d, _e;
99
+ return Boolean(((_c = (_b = (_a = window.webkit) === null || _a === void 0 ? void 0 : _a.messageHandlers) === null || _b === void 0 ? void 0 : _b.bridge) === null || _c === void 0 ? void 0 : _c.postMessage) ||
100
+ (kind === BRIDGE_KIND_CALL
101
+ ? typeof ((_d = window.Android) === null || _d === void 0 ? void 0 : _d.call) === 'function'
102
+ : typeof ((_e = window.Android) === null || _e === void 0 ? void 0 : _e.send) === 'function'));
103
+ }
104
+ /**
105
+ * Отправляет тело сообщения доступным транспортом.
106
+ *
107
+ * @returns `true`, если сообщение удалось передать.
108
+ */
109
+ // eslint-disable-next-line class-methods-use-this -- единая точка доступа к глобалам window.
110
+ dispatch(body) {
111
+ var _a, _b, _c, _d, _e;
112
+ if ((_c = (_b = (_a = window.webkit) === null || _a === void 0 ? void 0 : _a.messageHandlers) === null || _b === void 0 ? void 0 : _b.bridge) === null || _c === void 0 ? void 0 : _c.postMessage) {
113
+ window.webkit.messageHandlers.bridge.postMessage(body);
114
+ return true;
115
+ }
116
+ if (body.kind === BRIDGE_KIND_CALL && typeof ((_d = window.Android) === null || _d === void 0 ? void 0 : _d.call) === 'function') {
117
+ window.Android.call(JSON.stringify(body));
118
+ return true;
119
+ }
120
+ if (body.kind === BRIDGE_KIND_SEND && typeof ((_e = window.Android) === null || _e === void 0 ? void 0 : _e.send) === 'function') {
121
+ window.Android.send(JSON.stringify(body));
122
+ return true;
123
+ }
124
+ return false;
125
+ }
126
+ reportNoTransport(kind, action) {
127
+ var _a;
128
+ const message = `NativeBridgeService: native bridge is not available, "${action}" (${kind}) was not sent`;
129
+ (_a = this.logError) === null || _a === void 0 ? void 0 : _a.call(this, message, { action, kind });
130
+ // eslint-disable-next-line no-console -- сообщаем потребителю о недоступности моста.
131
+ console.error(message);
132
+ }
133
+ }
134
+ exports.NativeBridgeService = NativeBridgeService;
@@ -1,4 +1,4 @@
1
- import { type Environment, type NativeFeatureKey } from '../types';
1
+ import { type Environment, type NativeFeatureKey, type WebFeatureAction } from '../types';
2
2
  export declare const isValidVersionFormat: (version?: string) => version is string;
3
3
  /**
4
4
  * Сравнивает версии формата `x.y.z` покомпонентно, как числа.
@@ -10,3 +10,8 @@ export declare const isVersionHigherOrEqual: (version: string, versionToCompare:
10
10
  * Версия в неизвестном формате считается `0.0.0`.
11
11
  */
12
12
  export declare const canUseNativeFeature: (platform: Environment, appVersion: string, feature: NativeFeatureKey) => boolean;
13
+ /**
14
+ * Проверяет, поддерживает ли NA указанной платформы и версии фичу WebFeatures.
15
+ * Фичи WebFeatures доступны только на iOS. Версия в неизвестном формате считается `0.0.0`.
16
+ */
17
+ export declare const canUseWebFeature: (platform: Environment, appVersion: string, action: WebFeatureAction) => boolean;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.canUseNativeFeature = exports.isVersionHigherOrEqual = exports.isValidVersionFormat = void 0;
3
+ exports.canUseWebFeature = exports.canUseNativeFeature = exports.isVersionHigherOrEqual = exports.isValidVersionFormat = void 0;
4
4
  const constants_1 = require("../constants");
5
5
  const VERSION_FORMAT_PATTERN = /^\d+\.\d+\.\d+$/;
6
6
  const isValidVersionFormat = (version) => Boolean(version) && VERSION_FORMAT_PATTERN.test(version);
@@ -32,3 +32,15 @@ const canUseNativeFeature = (platform, appVersion, feature) => {
32
32
  return (0, exports.isVersionHigherOrEqual)((0, exports.isValidVersionFormat)(appVersion) ? appVersion : '0.0.0', fromVersion);
33
33
  };
34
34
  exports.canUseNativeFeature = canUseNativeFeature;
35
+ /**
36
+ * Проверяет, поддерживает ли NA указанной платформы и версии фичу WebFeatures.
37
+ * Фичи WebFeatures доступны только на iOS. Версия в неизвестном формате считается `0.0.0`.
38
+ */
39
+ const canUseWebFeature = (platform, appVersion, action) => {
40
+ if (platform !== 'ios') {
41
+ return false;
42
+ }
43
+ const { fromVersion } = constants_1.WEB_FEATURES_FROM_VERSION[action];
44
+ return (0, exports.isVersionHigherOrEqual)((0, exports.isValidVersionFormat)(appVersion) ? appVersion : '0.0.0', fromVersion);
45
+ };
46
+ exports.canUseWebFeature = canUseWebFeature;
@@ -5,9 +5,9 @@ import { type Environment, type LogError, type NativeFeatureKey, type Theme } fr
5
5
  export declare class NativeParamsService {
6
6
  private logError?;
7
7
  AndroidBridge: {
8
- call?: (message: string) => void;
9
- send?: (message: string) => void;
10
8
  setPageSettings: (params: string) => void;
9
+ call?: (body: string) => void;
10
+ send?: (body: string) => void;
11
11
  } | undefined;
12
12
  appId: string;
13
13
  appVersion: string;
@@ -1,22 +1,4 @@
1
- import { type GeoConfiguration, type LogError, type NativeBridgeMessage } from '../types';
1
+ import { type LogError } from '../types';
2
2
  export declare function appendFromCurrentQueryParamForIos(nativeUrl: string): string;
3
3
  export declare const closeWebviewUtil: () => void;
4
4
  export declare const validateUrl: (link: URL | string, logError?: LogError) => URL | null;
5
- /**
6
- * Map для сопоставления запросов посланных в натив и ответов от него.
7
- * Сопоставление происходит по requestId.
8
- * Сейчас запрос с типом call только один - это geo.configuration.
9
- */
10
- export declare const pendingRequests: Map<string, {
11
- resolve: (data: GeoConfiguration) => void;
12
- reject: (error: unknown) => void;
13
- }>;
14
- /**
15
- * Универсальная функция для отправки сообщений в нативное приложение
16
- * (Сигнатуры функций натива в iOS и Android отличаются)
17
- */
18
- export declare const sendMessage: (message: NativeBridgeMessage) => void;
19
- /**
20
- * Добавляет обработчик webBridge.callback который будет вызываться нативом
21
- */
22
- export declare const installCallback: () => void;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.installCallback = exports.sendMessage = exports.pendingRequests = exports.validateUrl = exports.closeWebviewUtil = void 0;
3
+ exports.validateUrl = exports.closeWebviewUtil = void 0;
4
4
  exports.appendFromCurrentQueryParamForIos = appendFromCurrentQueryParamForIos;
5
5
  const QUERY_CLOSE_WEBVIEW_KEY = 'closeWebView';
6
6
  const QUERY_CLOSE_WEBVIEW_VALUE = 'true';
@@ -48,64 +48,3 @@ const validateUrl = (link, logError) => {
48
48
  return url;
49
49
  };
50
50
  exports.validateUrl = validateUrl;
51
- /**
52
- * Map для сопоставления запросов посланных в натив и ответов от него.
53
- * Сопоставление происходит по requestId.
54
- * Сейчас запрос с типом call только один - это geo.configuration.
55
- */
56
- exports.pendingRequests = new Map();
57
- /**
58
- * Универсальная функция для отправки сообщений в нативное приложение
59
- * (Сигнатуры функций натива в iOS и Android отличаются)
60
- */
61
- const sendMessage = (message) => {
62
- var _a, _b, _c;
63
- const android = window.Android;
64
- if (message.kind === 'call' && typeof (android === null || android === void 0 ? void 0 : android.call) === 'function') {
65
- android.call(JSON.stringify(message));
66
- }
67
- else if (message.kind === 'send' && typeof (android === null || android === void 0 ? void 0 : android.send) === 'function') {
68
- android.send(JSON.stringify(message));
69
- }
70
- else if ((_c = (_b = (_a = window.webkit) === null || _a === void 0 ? void 0 : _a.messageHandlers) === null || _b === void 0 ? void 0 : _b.bridge) === null || _c === void 0 ? void 0 : _c.postMessage) {
71
- window.webkit.messageHandlers.bridge.postMessage(message);
72
- }
73
- else {
74
- throw new Error('Native bridge is not available');
75
- }
76
- };
77
- exports.sendMessage = sendMessage;
78
- /**
79
- * Находит ожидающий запрос по requestId, удаляет его из pendingRequests и завершает Promise:
80
- * успешно с данными или с ошибкой. Если в ответе отсутствуют и данные, и ошибка, отклоняет Promise.
81
- *
82
- * Ответы с неизвестным requestId, включая повторные ответы и ответы после тайм-аута, игнорируются.
83
- *
84
- * @param requestId Уникальный uuid исходного call-запроса, возвращённый нативом.
85
- * @param response Ответ натива: data содержит конфигурацию геолокации
86
- * либо error содержит ошибку. По контракту одно из этих полей равно null.
87
- */
88
- const callback = (requestId, response) => {
89
- var _a;
90
- const pending = exports.pendingRequests.get(requestId);
91
- if (!pending)
92
- return;
93
- exports.pendingRequests.delete(requestId);
94
- if (response.error) {
95
- pending.reject(Object.assign(new Error((_a = response.error.message) !== null && _a !== void 0 ? _a : 'Native bridge Geolocation error'), response.error));
96
- }
97
- else if (response.data) {
98
- pending.resolve(response.data);
99
- }
100
- else {
101
- pending.reject(new Error('Native bridge returned no Geolocation data'));
102
- }
103
- };
104
- /**
105
- * Добавляет обработчик webBridge.callback который будет вызываться нативом
106
- */
107
- const installCallback = () => {
108
- window.webBridge = window.webBridge || {};
109
- window.webBridge.callback = callback;
110
- };
111
- exports.installCallback = installCallback;
@@ -0,0 +1,37 @@
1
+ import { type WebFeaturesGeoConfiguration, type WebFeaturesHapticsOptions, type WebFeaturesSheetDragArea } from '../types';
2
+ import { type NativeBridgeService } from './native-bridge-service';
3
+ import { type NativeParamsService } from './native-params-service';
4
+ /**
5
+ * Доменный сервис фич WebFeatures (iOS, АМ ≥ 17.0.0).
6
+ *
7
+ * Инкапсулирует гейт доступности по платформе и версии NA и формирует
8
+ * данные для вызовов через {@link NativeBridgeService}.
9
+ */
10
+ export declare class WebFeaturesService {
11
+ private nativeParamsService;
12
+ private nativeBridgeService;
13
+ constructor(nativeParamsService: NativeParamsService, nativeBridgeService: NativeBridgeService);
14
+ /**
15
+ * Возвращает статус доступа к геолокации и, при наличии разрешения, координаты пользователя
16
+ *
17
+ * @throws Если фича недоступна в текущем окружении или NA вернул ошибку.
18
+ */
19
+ getGeoConfiguration(): Promise<WebFeaturesGeoConfiguration>;
20
+ /**
21
+ * Воспроизводит тактильный отклик.
22
+ */
23
+ vibrate({ type, intensity }: WebFeaturesHapticsOptions): void;
24
+ /**
25
+ * Задаёт область шторы, за которую можно её перетаскивать.
26
+ */
27
+ setSheetDragArea(area: WebFeaturesSheetDragArea): void;
28
+ /**
29
+ * Скрывает нативный таббар.
30
+ */
31
+ hideTabbar(): void;
32
+ /**
33
+ * Показывает нативный таббар и, при переданном `selectedId`, выбирает вкладку.
34
+ */
35
+ showTabbar(selectedId?: string): void;
36
+ private isAvailable;
37
+ }
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WebFeaturesService = void 0;
4
+ const native_features_1 = require("./native-features");
5
+ /**
6
+ * Доменный сервис фич WebFeatures (iOS, АМ ≥ 17.0.0).
7
+ *
8
+ * Инкапсулирует гейт доступности по платформе и версии NA и формирует
9
+ * данные для вызовов через {@link NativeBridgeService}.
10
+ */
11
+ class WebFeaturesService {
12
+ constructor(nativeParamsService, nativeBridgeService) {
13
+ this.nativeParamsService = nativeParamsService;
14
+ this.nativeBridgeService = nativeBridgeService;
15
+ }
16
+ /**
17
+ * Возвращает статус доступа к геолокации и, при наличии разрешения, координаты пользователя
18
+ *
19
+ * @throws Если фича недоступна в текущем окружении или NA вернул ошибку.
20
+ */
21
+ getGeoConfiguration() {
22
+ if (!this.isAvailable('geo.configuration')) {
23
+ return Promise.reject(new Error('WebFeatures: geo.configuration is unavailable in this environment'));
24
+ }
25
+ return this.nativeBridgeService.call('geo.configuration', {});
26
+ }
27
+ /**
28
+ * Воспроизводит тактильный отклик.
29
+ */
30
+ vibrate({ type, intensity }) {
31
+ if (!this.isAvailable('haptics.vibrate')) {
32
+ return;
33
+ }
34
+ this.nativeBridgeService.send('haptics.vibrate', Object.assign({ type }, (intensity === undefined ? {} : { intensity })));
35
+ }
36
+ /**
37
+ * Задаёт область шторы, за которую можно её перетаскивать.
38
+ */
39
+ setSheetDragArea(area) {
40
+ if (!this.isAvailable('sheet.setDragArea')) {
41
+ return;
42
+ }
43
+ this.nativeBridgeService.send('sheet.setDragArea', { area });
44
+ }
45
+ /**
46
+ * Скрывает нативный таббар.
47
+ */
48
+ hideTabbar() {
49
+ if (!this.isAvailable('tabbar.hide')) {
50
+ return;
51
+ }
52
+ this.nativeBridgeService.send('tabbar.hide', {});
53
+ }
54
+ /**
55
+ * Показывает нативный таббар и, при переданном `selectedId`, выбирает вкладку.
56
+ */
57
+ showTabbar(selectedId) {
58
+ if (!this.isAvailable('tabbar.show')) {
59
+ return;
60
+ }
61
+ this.nativeBridgeService.send('tabbar.show', selectedId === undefined ? {} : { selectedId });
62
+ }
63
+ isAvailable(action) {
64
+ return (0, native_features_1.canUseWebFeature)(this.nativeParamsService.environment, this.nativeParamsService.appVersion, action);
65
+ }
66
+ }
67
+ exports.WebFeaturesService = WebFeaturesService;
package/client/types.d.ts CHANGED
@@ -22,36 +22,27 @@ export type NativeFeaturesFromVersion = Readonly<{
22
22
  }>;
23
23
  export type PdfType = 'pdfFile' | 'base64' | 'binary';
24
24
  export type Theme = 'light' | 'dark';
25
- export type NativeBridgeMessage = {
26
- kind: 'call' | 'send';
27
- action: string;
28
- requestId: string;
29
- data: object | null;
25
+ export type WebFeatureAction = 'geo.configuration' | 'haptics.vibrate' | 'sheet.setDragArea' | 'tabbar.hide' | 'tabbar.show';
26
+ export type WebFeaturesGeoPermission = 'allowed' | 'denied' | 'notDetermined' | 'restricted';
27
+ export type WebFeaturesUserCoordinate = {
28
+ latitude: number;
29
+ longitude: number;
30
30
  };
31
- export type NativeBridgeResponse = {
32
- data: GeoConfiguration | null;
33
- error: {
34
- code: number;
35
- message?: string;
36
- details?: unknown;
37
- } | null;
31
+ export type WebFeaturesGeoConfiguration = {
32
+ permission: WebFeaturesGeoPermission;
33
+ userCoordinate: WebFeaturesUserCoordinate | null;
38
34
  };
39
- export type HapticsOptions = {
40
- type: 'light' | 'medium' | 'heavy' | 'soft' | 'rigid' | 'success' | 'warning' | 'error' | 'selection';
35
+ export type WebFeaturesHapticsType = 'light' | 'medium' | 'heavy' | 'soft' | 'rigid' | 'success' | 'warning' | 'error' | 'selection';
36
+ export type WebFeaturesHapticsOptions = {
37
+ type: WebFeaturesHapticsType;
41
38
  intensity?: number;
42
39
  };
43
- export type GeoConfiguration = {
44
- /**
45
- * allowed — доступ разрешён;
46
- * denied — доступ запрещён;
47
- * notDetermined — пользователь ещё не принял решение;
48
- * restricted — доступ ограничен
49
- */
50
- permission: 'allowed' | 'denied' | 'notDetermined' | 'restricted';
51
- userCoordinate: {
52
- latitude: number;
53
- longitude: number;
40
+ export type WebFeaturesSheetDragArea = 'wholeSheet' | 'navigationBar';
41
+ export type WebFeaturesCallbackPayload = {
42
+ data: unknown;
43
+ error: {
44
+ code: number;
45
+ message: string;
54
46
  } | null;
55
47
  };
56
- export type SheetDragArea = 'wholeSheet' | 'navigationBar';
57
48
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfalab/bridge-to-native",
3
- "version": "1.5.1-beta.82667a7",
3
+ "version": "1.5.2-beta.b951ff8",
4
4
  "license": "MIT",
5
5
  "description": "Утилита для удобной работы веб приложения внутри нативного приложения и коммуникации с ним.",
6
6
  "engines": {
@@ -64,6 +64,11 @@
64
64
  },
65
65
  "eslintConfig": {
66
66
  "extends": "./node_modules/arui-presets-lint/eslint",
67
+ "settings": {
68
+ "react": {
69
+ "version": "18.0.0"
70
+ }
71
+ },
67
72
  "parserOptions": {
68
73
  "project": [
69
74
  "./__tests__/tsconfig.json",
@@ -1,9 +0,0 @@
1
- import { type GeoConfiguration, type HapticsOptions, type SheetDragArea } from '../types';
2
- export declare class NativeMessagesService {
3
- getGeoConfiguration(data?: Record<string, never> | null): Promise<GeoConfiguration>;
4
- vibrate(options: HapticsOptions): void;
5
- setSheetDragArea(area: SheetDragArea): void;
6
- hideTabbar(): void;
7
- showTabbar(selectedId?: string | null): void;
8
- private send;
9
- }
@@ -1,63 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NativeMessagesService = void 0;
4
- const utils_1 = require("./utils");
5
- class NativeMessagesService {
6
- // eslint-disable-next-line class-methods-use-this
7
- getGeoConfiguration(data = {}) {
8
- return new Promise((resolve, reject) => {
9
- const requestId = crypto.randomUUID();
10
- const timeout = setTimeout(() => {
11
- utils_1.pendingRequests.delete(requestId);
12
- reject(new Error('Native bridge geolocation request timed out'));
13
- }, 10000);
14
- const cleanup = () => {
15
- clearTimeout(timeout);
16
- utils_1.pendingRequests.delete(requestId);
17
- };
18
- utils_1.pendingRequests.set(requestId, {
19
- resolve: (result) => {
20
- cleanup();
21
- resolve(result);
22
- },
23
- reject: (error) => {
24
- cleanup();
25
- reject(error);
26
- },
27
- });
28
- try {
29
- (0, utils_1.installCallback)();
30
- (0, utils_1.sendMessage)({
31
- kind: 'call',
32
- action: 'geo.configuration',
33
- requestId,
34
- data,
35
- });
36
- }
37
- catch (error) {
38
- cleanup();
39
- reject(error);
40
- }
41
- });
42
- }
43
- vibrate(options) {
44
- if (options.intensity !== undefined && (options.intensity < 0 || options.intensity > 1)) {
45
- throw new RangeError('Haptic intensity must be between 0 and 1');
46
- }
47
- this.send('haptics.vibrate', options);
48
- }
49
- setSheetDragArea(area) {
50
- this.send('sheet.setDragArea', { area });
51
- }
52
- hideTabbar() {
53
- this.send('tabbar.hide', {});
54
- }
55
- showTabbar(selectedId) {
56
- this.send('tabbar.show', selectedId === undefined ? {} : { selectedId });
57
- }
58
- // eslint-disable-next-line class-methods-use-this
59
- send(action, data) {
60
- (0, utils_1.sendMessage)({ kind: 'send', action, requestId: crypto.randomUUID(), data });
61
- }
62
- }
63
- exports.NativeMessagesService = NativeMessagesService;