@alfalab/bridge-to-native 0.2.2 → 1.0.1

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.
Files changed (58) hide show
  1. package/client/bridge-to-native.d.ts +220 -0
  2. package/client/bridge-to-native.js +267 -0
  3. package/client/constants.d.ts +13 -0
  4. package/client/constants.js +30 -0
  5. package/client/services-and-utils/close-webview-util.d.ts +1 -0
  6. package/client/services-and-utils/close-webview-util.js +11 -0
  7. package/client/services-and-utils/external-links-service.d.ts +15 -0
  8. package/client/services-and-utils/external-links-service.js +71 -0
  9. package/client/services-and-utils/native-navigation-and-title-service.d.ts +70 -0
  10. package/client/services-and-utils/native-navigation-and-title-service.js +236 -0
  11. package/client/services-and-utils/native-params-service.d.ts +24 -0
  12. package/client/services-and-utils/native-params-service.js +79 -0
  13. package/client/types.d.ts +19 -0
  14. package/client/types.js +2 -0
  15. package/package.json +79 -79
  16. package/query-and-headers-keys.d.ts +12 -0
  17. package/query-and-headers-keys.js +49 -0
  18. package/server/extract-native-service-queries.d.ts +9 -0
  19. package/server/extract-native-service-queries.js +41 -0
  20. package/server/index.d.ts +2 -0
  21. package/server/index.js +7 -0
  22. package/server/is-webview-env.d.ts +7 -0
  23. package/server/is-webview-env.js +30 -0
  24. package/server/prepare-native-app-details-for-client.d.ts +14 -0
  25. package/server/prepare-native-app-details-for-client.js +103 -0
  26. package/server/regexp-patterns.d.ts +4 -0
  27. package/server/regexp-patterns.js +10 -0
  28. package/server/types.d.ts +2 -0
  29. package/server/types.js +2 -0
  30. package/server/utils.d.ts +25 -0
  31. package/server/utils.js +53 -0
  32. package/types.d.ts +1 -32
  33. package/bridge-to-native.d.ts +0 -63
  34. package/bridge-to-native.js +0 -147
  35. package/constants.d.ts +0 -15
  36. package/constants.js +0 -32
  37. package/esm/bridge-to-native.d.ts +0 -63
  38. package/esm/bridge-to-native.js +0 -154
  39. package/esm/constants.d.ts +0 -15
  40. package/esm/constants.js +0 -29
  41. package/esm/index.d.ts +0 -2
  42. package/esm/native-fallbacks.d.ts +0 -64
  43. package/esm/native-fallbacks.js +0 -119
  44. package/esm/native-navigation-and-title.d.ts +0 -147
  45. package/esm/native-navigation-and-title.js +0 -326
  46. package/esm/types.d.ts +0 -39
  47. package/esm/types.js +0 -1
  48. package/esm/utils.d.ts +0 -23
  49. package/esm/utils.js +0 -62
  50. package/index.d.ts +0 -2
  51. package/native-fallbacks.d.ts +0 -64
  52. package/native-fallbacks.js +0 -124
  53. package/native-navigation-and-title.d.ts +0 -147
  54. package/native-navigation-and-title.js +0 -329
  55. package/utils.d.ts +0 -23
  56. package/utils.js +0 -70
  57. /package/{esm/index.js → client/index.d.ts} +0 -0
  58. /package/{index.js → client/index.js} +0 -0
@@ -1,326 +0,0 @@
1
- import { DEEP_LINK_PATTERN, PREVIOUS_NATIVE_NAVIGATION_AND_TITLE_STATE_STORAGE_KEY, } from './constants';
2
- import { extractAppNameRouteAndQuery } from './utils';
3
- /**
4
- * Класс, отвечающий за взаимодействие с нативными элементами в приложении – заголовком и нативной кнопкой назад.
5
- */
6
- export class NativeNavigationAndTitle {
7
- b2n;
8
- nativeHistoryStack = [''];
9
- numOfBackSteps = 1;
10
- // Тут сохраняются параметры, которые в последний раз были отправлены в приложение.
11
- // Просто, чтобы не слать одинаковые сигналы в приложение.
12
- lastSetPageSettingsParams = '';
13
- _handleWindowRedirect;
14
- constructor(b2n, pageId, initialNativeTitle = '', handleWindowRedirect) {
15
- this.b2n = b2n;
16
- this.handleBack = this.handleBack.bind(this);
17
- this._handleWindowRedirect = handleWindowRedirect;
18
- const previousState = !!sessionStorage.getItem(PREVIOUS_NATIVE_NAVIGATION_AND_TITLE_STATE_STORAGE_KEY);
19
- if (pageId) {
20
- this.supportSharedSession(pageId, initialNativeTitle);
21
- }
22
- else if (previousState) {
23
- this.restorePreviousState();
24
- }
25
- else {
26
- this.setInitialView(initialNativeTitle);
27
- }
28
- }
29
- /**
30
- * Метод, вызывающий `history.back()` или закрывающий вебвью, если нет записей
31
- * в истории переходов.
32
- */
33
- goBack() {
34
- this.goBackAFewSteps(-1, true);
35
- }
36
- /**
37
- * Метод, вызывающий history.go(-колл. шагов назад) и модифицирует внутреннее
38
- * состояние, чтобы в дальнейшем зарегистрировать этот переход в приложении.
39
- *
40
- * @param stepsNumber Количество шагов назад.
41
- * Возможно передача как положительного, так и отрицательного числа.
42
- * 0 будет проигнорирован.
43
- * @param autoCloseWebview Флаг – закрывать ли вебвью автоматически,
44
- * если переданное кол-во шагов будет больше чем записей в истории.
45
- */
46
- goBackAFewSteps(stepsNumber, autoCloseWebview = false) {
47
- if (!stepsNumber) {
48
- return;
49
- }
50
- const stepsToBack = Math.abs(stepsNumber);
51
- const maxStepsToBack = this.nativeHistoryStack.length - 1;
52
- if (stepsToBack > maxStepsToBack) {
53
- if (autoCloseWebview) {
54
- this.b2n.closeWebview();
55
- return;
56
- }
57
- this.numOfBackSteps = maxStepsToBack;
58
- }
59
- else {
60
- this.numOfBackSteps = stepsToBack;
61
- }
62
- window.history.go(-this.numOfBackSteps);
63
- }
64
- /**
65
- * Метод вызывает `src/shared/utils/handle-redirect` из `newclick-host-ui`
66
- * и регистрирует этот переход в приложении, чтобы кнопка «Назад» в Нативе вызывала
67
- * переход назад в вебе.
68
- */
69
- handleRedirect(pageTitleOrPath, appNameOrHistoryState, path, params, historyState) {
70
- const checkAppNameArgument = (argument) => Boolean(appNameOrHistoryState && typeof appNameOrHistoryState === 'string');
71
- const isAppNameArgument = checkAppNameArgument(appNameOrHistoryState);
72
- if (isAppNameArgument) {
73
- this._handleWindowRedirect(appNameOrHistoryState, path, params, historyState);
74
- }
75
- else {
76
- const { appName: extractedAppName, path: extractedPath, query: extractedQuery, } = extractAppNameRouteAndQuery(pageTitleOrPath);
77
- this._handleWindowRedirect(extractedAppName, extractedPath, extractedQuery, appNameOrHistoryState);
78
- }
79
- const title = isAppNameArgument ? pageTitleOrPath : '';
80
- this.nativeHistoryStack.push(title);
81
- this.syncHistoryWithNative(title, 'navigation');
82
- }
83
- /**
84
- * Информирует натив, что веб находится на первом экране (сбрасывает историю переходов, не влияя на браузерную
85
- * историю), а значит следующее нажатие на кнопку "Назад" в нативе закроет вебвью.
86
- *
87
- * @param pageTitle Заголовок, который нужно отрисовать в нативе.
88
- */
89
- setInitialView(pageTitle = '') {
90
- this.nativeHistoryStack = [pageTitle];
91
- this.syncHistoryWithNative(pageTitle, 'initialization');
92
- this.reassignPopstateListener();
93
- }
94
- /**
95
- * Метод для смены заголовка в нативе без влияния на историю переходов.
96
- *
97
- * @param pageTitle Заголовок, который нужно отрисовать в нативе.
98
- */
99
- setTitle(pageTitle) {
100
- this.nativeHistoryStack[this.nativeHistoryStack.length - 1] = pageTitle;
101
- this.syncHistoryWithNative(pageTitle, 'title-replacing');
102
- }
103
- /**
104
- * Метод для открытия второго web приложения в рамках одной вебвью сессии.
105
- * Сохраняет все текущее состояние текущего экземпляра bridgeToAm и AmNavigationAndTitle в sessionStorage, а
106
- * так же наполняет url необходимыми query параметрами. Работает только в Android окружении.
107
- * В IOS окружении будет открыто новое webview поверх текущего.
108
- *
109
- * @param url адрес второго web приложения, к которому перед переходом на него будут добавлены
110
- * все initial query параметры от натива и параметр nextPageId (Android)
111
- */
112
- navigateInsideASharedSession(url) {
113
- if (this.b2n.environment === 'ios') {
114
- const nativeDeeplink = `/webFeature?type=recommendation&url=${encodeURIComponent(url)}`;
115
- this.handleNativeDeeplink(nativeDeeplink);
116
- return;
117
- }
118
- // В b2n этот метод отмечен модификатором доступа private, но тут его нужно вызвать
119
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
120
- // @ts-ignore
121
- this.b2n.saveCurrentState();
122
- window.location.assign(this.prepareExternalLinkBeforeOpen(url));
123
- }
124
- /**
125
- * Безопасный способ для перезагрузки страницы.
126
- */
127
- pseudoReloadPage() {
128
- // В b2n этот метод отмечен модификатором доступа private, но тут его нужно вызвать
129
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
130
- // @ts-ignore
131
- this.handleRedirect(this.b2n._blankPagePath);
132
- this.goBack();
133
- }
134
- /**
135
- * Вызывает обработчик deeplinks в нативе (АМ) и передает туда переданный deeplink.
136
- * На Android текущее webview будет закрыто из-за технических особенностей.
137
- * На IOS нативная фича открывается в следующем по стеку экране и при выходе из нее пользователь вернется обратно в webview.
138
- * На IOS есть возможность закрыть webview перед открытием нативной фичи, передав второй параметр closeIOSWebviewBeforeCallNativeDeeplinkHandler = true
139
- * @param deeplink диплинк на нативную АМ фичу в AM
140
- * @param [closeIOSWebviewBeforeCallNativeDeeplinkHandler = false] закрыть текущее webview после открытия нативной фичи (применимо только для IOS на Android по техническим причинам webview всегда будет закрываться)
141
- */
142
- handleNativeDeeplink(deeplink, closeIOSWebviewBeforeCallNativeDeeplinkHandler = false) {
143
- const clearedDeeplinkPath = deeplink.replace(DEEP_LINK_PATTERN, '');
144
- if (this.b2n.environment === 'ios') {
145
- if (closeIOSWebviewBeforeCallNativeDeeplinkHandler) {
146
- this.b2n.closeWebview();
147
- setTimeout(() => window.location.replace(`${this.b2n.iosAppId}://${clearedDeeplinkPath}`), 0);
148
- return;
149
- }
150
- window.location.replace(`${this.b2n.iosAppId}://${clearedDeeplinkPath}`);
151
- }
152
- else {
153
- window.location.replace(`alfabank://${clearedDeeplinkPath}`);
154
- }
155
- }
156
- /**
157
- * Метод для сохранения текущего состояния NativeNavigationAndTitle в sessionStorage.
158
- */
159
- saveCurrentState() {
160
- const currentState = {
161
- title: this.nativeHistoryStack[this.nativeHistoryStack.length - 1],
162
- nativeHistoryStack: this.nativeHistoryStack,
163
- };
164
- sessionStorage.setItem(PREVIOUS_NATIVE_NAVIGATION_AND_TITLE_STATE_STORAGE_KEY, JSON.stringify(currentState));
165
- }
166
- /**
167
- * Метод, вычисляющий `pageId`, который нужно послать в приложение
168
- * для правильной синхронизации с нативной-кнопкой "Назад".
169
- *
170
- * @param purpose Цель взаимодействия с приложением.
171
- * @returns Правильный pageId.
172
- */
173
- getNativePageId(purpose) {
174
- function assertUnreachable(val) {
175
- throw new Error(`Unexpected value "${val}"`);
176
- }
177
- let pageId;
178
- switch (purpose) {
179
- case 'initialization':
180
- pageId = this.getNativePageIdForInitialization();
181
- break;
182
- case 'navigation':
183
- pageId = this.getNativePageIdForNavigation();
184
- break;
185
- case 'title-replacing':
186
- pageId = this.getNativePageIdForTitleReplacing();
187
- break;
188
- default:
189
- assertUnreachable(purpose);
190
- }
191
- return pageId;
192
- }
193
- /**
194
- * Вспомогательный метод для `getNativePageId` initialization кейса.
195
- *
196
- * @returns Правильный pageId.
197
- */
198
- getNativePageIdForInitialization() {
199
- // * В iOS для "первой" страницы не нужно слать `pageId`.
200
- // * В Android важно, чтобы `pageId` "первой" страницы
201
- // всегда был одинаковый.
202
- return this.b2n.environment === 'ios' ? null : 1;
203
- }
204
- /**
205
- * Вспомогательный метод для `getNativePageId` navigation кейса.
206
- *
207
- * @returns Правильный pageId.
208
- */
209
- getNativePageIdForNavigation() {
210
- const stackSize = this.nativeHistoryStack.length;
211
- // Нажимая на кнопку назад, можно дойти до "первой" страницы,
212
- // в iOS для "первой" страницы не нужно слать `pageId`.
213
- return this.b2n.environment === 'ios' && stackSize <= 1 ? null : stackSize;
214
- }
215
- /**
216
- * Вспомогательный метод для `getNativePageId` only-title кейса.
217
- *
218
- * @returns Правильный pageId.
219
- */
220
- getNativePageIdForTitleReplacing() {
221
- const stackSize = this.nativeHistoryStack.length;
222
- if (this.b2n.environment === 'android') {
223
- // Для смены заголовка в Андроид просто повторяем текущий `pageId`.
224
- // В отличии от iOS, если не послать `pageId` первой страницы,
225
- // Вебвью не будет закрываться по клику на нативный «Назад».
226
- return stackSize <= 1 ? 1 : stackSize;
227
- }
228
- // Если в iOS не послать `pageId`, следующее нажатие на
229
- // нативную кнопку назад закроет webview.
230
- return stackSize <= 1 ? null : stackSize;
231
- }
232
- /**
233
- * Обработчик для `window.onpopstate` события. Который сработает
234
- * после нажатия на кнопку "Назад" в нативе, вызова `history.back()` и `history.go(-x)`.
235
- */
236
- handleBack() {
237
- const previousState = !!sessionStorage.getItem(PREVIOUS_NATIVE_NAVIGATION_AND_TITLE_STATE_STORAGE_KEY);
238
- if (previousState) {
239
- // В b2n этот метод отмечен модификатором доступа private дабы не торчал наружу, но тут его нужно вызвать
240
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
241
- // @ts-ignore
242
- this.b2n.restorePreviousState();
243
- }
244
- this.nativeHistoryStack = this.nativeHistoryStack.slice(0, -this.numOfBackSteps);
245
- this.numOfBackSteps = 1;
246
- if (this.nativeHistoryStack.length < 1) {
247
- this.b2n.closeWebview();
248
- return;
249
- }
250
- const pageTitle = this.nativeHistoryStack[this.nativeHistoryStack.length - 1];
251
- this.syncHistoryWithNative(pageTitle, 'navigation');
252
- }
253
- /**
254
- * Синхронизирует состояние истории переходов и заголовок с приложением.
255
- *
256
- * @param pageTitle Заголовок, который нужно отрисовать в приложении.
257
- * @param purpose Цель взаимодействия с приложением.
258
- */
259
- syncHistoryWithNative(pageTitle, purpose) {
260
- const pageId = this.getNativePageId(purpose);
261
- if (this.b2n.environment === 'android') {
262
- const pageSettingsObj = { pageTitle };
263
- if (pageId) {
264
- pageSettingsObj.pageId = pageId;
265
- }
266
- const paramsToSend = JSON.stringify(pageSettingsObj);
267
- if (this.lastSetPageSettingsParams !== paramsToSend) {
268
- this.b2n.AndroidBridge?.setPageSettings(paramsToSend);
269
- this.lastSetPageSettingsParams = paramsToSend;
270
- }
271
- }
272
- else {
273
- const pageTitleStr = `?pageTitle=${encodeURIComponent(pageTitle)}`;
274
- const pageIdStr = pageId ? `&pageId=${pageId}` : '';
275
- const paramsToSend = `ios:setPageSettings/${pageTitleStr + pageIdStr}`;
276
- if (this.lastSetPageSettingsParams !== paramsToSend) {
277
- window.location.replace(paramsToSend);
278
- this.lastSetPageSettingsParams = paramsToSend;
279
- }
280
- }
281
- }
282
- /**
283
- * Метод для перехода в веб из другого веб приложения в рамках
284
- * одной вебвью сессии
285
- * @param pageId - Номер текущего page который нужно отправить в приложение
286
- * @param title - Title текущего page который нужно отправить в приложение
287
- */
288
- supportSharedSession(pageId, title) {
289
- this.nativeHistoryStack = new Array(pageId).fill('');
290
- this.syncHistoryWithNative(title, 'title-replacing');
291
- this.reassignPopstateListener();
292
- }
293
- /**
294
- * Восстанавливает свое предыдущее состояние nativeHistoryStack и title из sessionStorage
295
- */
296
- restorePreviousState() {
297
- const previousState = JSON.parse(sessionStorage.getItem(PREVIOUS_NATIVE_NAVIGATION_AND_TITLE_STATE_STORAGE_KEY) || '');
298
- this.nativeHistoryStack = previousState.nativeHistoryStack;
299
- this.syncHistoryWithNative(previousState.title, 'title-replacing');
300
- this.reassignPopstateListener();
301
- sessionStorage.removeItem(PREVIOUS_NATIVE_NAVIGATION_AND_TITLE_STATE_STORAGE_KEY);
302
- }
303
- /**
304
- * Вспомогательный метод для setInitialView, supportSharedSession
305
- * переназначает обработчик @handleBack для `window.onpopstate` события
306
- */
307
- reassignPopstateListener() {
308
- window.removeEventListener('popstate', this.handleBack);
309
- window.addEventListener('popstate', this.handleBack);
310
- }
311
- /**
312
- * Вспомогательный метод для navigateInsideASharedSession.
313
- * Подготавливает внешнюю ссылку в рамках контракта для совместной работы веб-приложений в
314
- * рамках одной вебвью сессии
315
- * @param url - url иного веб приложения
316
- * @return подготовленная согласно контракту ссылка на иное веб приложение с initial query
317
- * параметрами от натива, а так же nextPageId.
318
- */
319
- prepareExternalLinkBeforeOpen(url) {
320
- const currentPageId = this.nativeHistoryStack.length;
321
- const divider = new URL(url).searchParams.toString() ? '&' : '?';
322
- const link = new URL(`${url}${divider}${this.b2n.originalWebviewParams}`);
323
- link.searchParams.set('nextPageId', (currentPageId + 1).toString());
324
- return link.toString();
325
- }
326
- }
package/esm/types.d.ts DELETED
@@ -1,39 +0,0 @@
1
- export declare type NativeParams = {
2
- appVersion: string;
3
- title?: string;
4
- iosAppId?: string;
5
- theme: string;
6
- nextPageId: number | null;
7
- originalWebviewParams: string;
8
- };
9
- export declare type NativeFeatureKey = 'geolocation' | 'linksInBrowser' | 'savedBackStack';
10
- declare type NativeFeaturesParams = Readonly<Record<NativeFeatureKey, {
11
- fromVersion: string;
12
- }>>;
13
- export declare type NativeFeaturesFromVersion = Readonly<{
14
- android: NativeFeaturesParams;
15
- ios: NativeFeaturesParams;
16
- }>;
17
- export declare type Environment = 'android' | 'ios';
18
- export declare type WebViewWindow = Window & {
19
- Android?: {
20
- setPageSettings: (params: string) => void;
21
- };
22
- handleRedirect?: (appName: string, path?: string, params?: Record<string, string>) => VoidFunction;
23
- };
24
- export declare type PdfType = 'pdfFile' | 'base64' | 'binary';
25
- export declare type PreviousBridgeToNativeState = Omit<NativeParams, 'title' | 'theme'> & {
26
- theme: 'dark' | 'light';
27
- };
28
- export declare type PreviousNativeNavigationAndTitleState = {
29
- nativeHistoryStack: string[];
30
- title: string;
31
- };
32
- export declare type SyncPurpose = 'initialization' | 'navigation' | 'title-replacing';
33
- export declare type HandleRedirect = (appName: string, path?: string, params?: Record<string, string>, historyState?: Record<string, unknown>) => void;
34
- export declare type Theme = 'light' | 'dark';
35
- export declare type ExternalNavigationOptions = {
36
- onClick?: () => void;
37
- forceOpenInWebview?: boolean;
38
- };
39
- export {};
package/esm/types.js DELETED
@@ -1 +0,0 @@
1
- export {};
package/esm/utils.d.ts DELETED
@@ -1,23 +0,0 @@
1
- import { Environment } from './types';
2
- /**
3
- * Разделяет веб ссылку на компоненты
4
- * @param route внутренний путь для навигации
5
- * @return объект с appName, route, query
6
- */
7
- export declare const extractAppNameRouteAndQuery: (route: string) => {
8
- appName: string;
9
- path: string;
10
- query: Record<string, string> | undefined;
11
- };
12
- /**
13
- * Возвращает экземпляр `URL` из ссылки, докидывая `https://` при отсутствии.
14
- */
15
- export declare const getUrlInstance: (link: string) => URL;
16
- /**
17
- * Проверяет, что переданная строка содержит версию приложения в правильном формате.
18
- *
19
- * @param version Строка с версией для проверки.
20
- * @returns Правильный формат или нет.
21
- */
22
- export declare const isValidVersionFormat: (version?: string | undefined) => boolean;
23
- export declare const getAppId: (environment: Environment, iosAppId?: string | undefined) => string | null;
package/esm/utils.js DELETED
@@ -1,62 +0,0 @@
1
- import { ANDROID_APP_ID } from './constants';
2
- /**
3
- * Разделяет веб ссылку на компоненты
4
- * @param route внутренний путь для навигации
5
- * @return объект с appName, route, query
6
- */
7
- export const extractAppNameRouteAndQuery = (route) => {
8
- let appName = '';
9
- let path = '';
10
- let query;
11
- const clearedPath = route.replace(/(?:^\/)|(?:\/$)/g, '');
12
- const segments = clearedPath.split('/');
13
- const queryByPath = clearedPath.split('?')[1];
14
- appName = segments.shift()?.split('?')[0] || '';
15
- if (queryByPath) {
16
- query = Array.from(new URLSearchParams(queryByPath).entries()).reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {});
17
- }
18
- path = segments.join('/').replace(`?${queryByPath}`, '');
19
- return { appName, path, query };
20
- };
21
- /**
22
- * Возвращает экземпляр `URL` из ссылки, докидывая `https://` при отсутствии.
23
- */
24
- export const getUrlInstance = (link) => {
25
- const protocolRequiredPattern = /^https?:\/\//;
26
- let url;
27
- if (protocolRequiredPattern.test(link)) {
28
- url = new URL(link);
29
- }
30
- else {
31
- try {
32
- // Пробуем докинуть `https://`, как правило, это помогает.
33
- url = new URL(`https://${link}`);
34
- }
35
- catch (e) {
36
- // Кажется, добавив протокол, сюда мы больше не сможем вывалиться, но на всякий случай...
37
- url = new URL('about:blank');
38
- }
39
- }
40
- return url;
41
- };
42
- /**
43
- * Проверяет, что переданная строка содержит версию приложения в правильном формате.
44
- *
45
- * @param version Строка с версией для проверки.
46
- * @returns Правильный формат или нет.
47
- */
48
- export const isValidVersionFormat = (version) => {
49
- if (!version)
50
- return false;
51
- const versionPattern = /^\d+\.\d+\.\d+$/;
52
- return versionPattern.test(version);
53
- };
54
- export const getAppId = (environment, iosAppId) => {
55
- if (environment === 'android') {
56
- return atob(ANDROID_APP_ID);
57
- }
58
- if (environment === 'ios' && iosAppId && typeof iosAppId === 'string') {
59
- return iosAppId;
60
- }
61
- return null;
62
- };
package/index.d.ts DELETED
@@ -1,2 +0,0 @@
1
- export { BridgeToNative } from './bridge-to-native';
2
- export { NativeParams, Theme, Environment, NativeFeatureKey, PdfType } from './types';
@@ -1,64 +0,0 @@
1
- import { ExternalNavigationOptions, PdfType } from './types';
2
- import type { BridgeToNative } from './bridge-to-native';
3
- /**
4
- * Класс содержит реализацию обходных путей для веб-фич, которые не работают в нативном-вебвью.
5
- */
6
- export declare class NativeFallbacks {
7
- private b2n;
8
- constructor(b2n: BridgeToNative);
9
- /**
10
- * Метод, возвращающий пропсы для ссылок, ведущих на ВНЕШНИЙ ресурс. Которые просто
11
- * нужно «подмешать» к ссылке в JSX:
12
- *
13
- * ```
14
- * <a {...bridgeToNative.nativeFallbacks.getExternalLinkProps('https://ya.ru')}>Link to external feature</a>
15
- * ```
16
- * Либо просто достать интересующие поля - onClick или href
17
- * ```
18
- * const {onClick, href} = bridgeToNative.nativeFallbacks.getExternalLinkProps(url, clickHandler)
19
- * document.querySelector('.myLink').onclick = onClick;
20
- * <a {...bridgeToNative.nativeFallbacks.getExternalLinkProps('https://ya.ru')}>Link to external feature</a>
21
- * ```
22
- * В разных OS и разных версиях приложения, открытие ресурса будет работать по-разному:
23
- *
24
- * - Если текущая версия приложения может открыть ссылку в браузере и не задан параметр `forceOpenInWebview`,
25
- * обогащаем URL специальным query-параметром (`target=_blank` в приложении не работает).
26
- * - Если это iOS, меняем URL на диплинк, который откроет ссылку в новом вебвью, поверх текущего.
27
- * К первому-вебвью, пользователь вернётся, когда закроет второе вебвью с внешним ресурсом.
28
- * - В старых приложениях на Андроид – URL не меняем, но добавляем `onClick` для сбрасывания синхронизации
29
- * навигации с приложением. Это «фолбэк-сценарий» с плохим UX (сайт полностью выпадает из истории), но другого способа нет.
30
- *
31
- * @param link Строка - валидный урл.
32
- * @param options - опции
33
- * @param options.forceOpenInWebview Boolean - по умолчанию = false, если передать true,
34
- * все ссылки будут открываться в рамках webview, иначе открытие по возможности будет происходить в браузере.
35
- * @param options.onClick Дополнительный обработчик на клик, например, для отправки метрики.
36
- * Внимание! Не факт, что в «фолбэк-сценарии» асинхронная операция будет выполнена (метрика отправлена)!
37
- * @returns Пропсы для ссылки в вебвью окружении.
38
- */
39
- getExternalLinkProps(link: string, options?: ExternalNavigationOptions): {
40
- href: string;
41
- onClick: (() => void) | undefined;
42
- };
43
- /**
44
- * Метод для открытия PDF в нативном вьювере.
45
- *
46
- * Есть нюансы с версиями приложения, OS устройства.
47
- * Надо тестировать по моде статистики.
48
- *
49
- * @param url ссылка на pdf
50
- * @param type тип pdf ссылки
51
- * @param title название pdf файла
52
- */
53
- openPdf(url: string, type?: PdfType, title?: string): void;
54
- /**
55
- * Метод, для перехода на ВНЕШНИЙ ресурс.
56
- *
57
- * См. описание в `getExternalLinkProps`, чтобы узнать, как выбирается способ для перехода.
58
- *
59
- * @param link Строка - валидный урл.
60
- * @param forceOpenInWebview Boolean - по умолчанию = false, если передать true,
61
- * все ссылки будут открываться в рамках webview, иначе открытие по возможности будет происходить в браузере.
62
- */
63
- visitExternalResource(link: string, forceOpenInWebview?: boolean): void;
64
- }
@@ -1,124 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NativeFallbacks = void 0;
4
- const utils_1 = require("./utils");
5
- /**
6
- * Класс содержит реализацию обходных путей для веб-фич, которые не работают в нативном-вебвью.
7
- */
8
- class NativeFallbacks {
9
- constructor(b2n) {
10
- this.b2n = b2n;
11
- }
12
- /**
13
- * Метод, возвращающий пропсы для ссылок, ведущих на ВНЕШНИЙ ресурс. Которые просто
14
- * нужно «подмешать» к ссылке в JSX:
15
- *
16
- * ```
17
- * <a {...bridgeToNative.nativeFallbacks.getExternalLinkProps('https://ya.ru')}>Link to external feature</a>
18
- * ```
19
- * Либо просто достать интересующие поля - onClick или href
20
- * ```
21
- * const {onClick, href} = bridgeToNative.nativeFallbacks.getExternalLinkProps(url, clickHandler)
22
- * document.querySelector('.myLink').onclick = onClick;
23
- * <a {...bridgeToNative.nativeFallbacks.getExternalLinkProps('https://ya.ru')}>Link to external feature</a>
24
- * ```
25
- * В разных OS и разных версиях приложения, открытие ресурса будет работать по-разному:
26
- *
27
- * - Если текущая версия приложения может открыть ссылку в браузере и не задан параметр `forceOpenInWebview`,
28
- * обогащаем URL специальным query-параметром (`target=_blank` в приложении не работает).
29
- * - Если это iOS, меняем URL на диплинк, который откроет ссылку в новом вебвью, поверх текущего.
30
- * К первому-вебвью, пользователь вернётся, когда закроет второе вебвью с внешним ресурсом.
31
- * - В старых приложениях на Андроид – URL не меняем, но добавляем `onClick` для сбрасывания синхронизации
32
- * навигации с приложением. Это «фолбэк-сценарий» с плохим UX (сайт полностью выпадает из истории), но другого способа нет.
33
- *
34
- * @param link Строка - валидный урл.
35
- * @param options - опции
36
- * @param options.forceOpenInWebview Boolean - по умолчанию = false, если передать true,
37
- * все ссылки будут открываться в рамках webview, иначе открытие по возможности будет происходить в браузере.
38
- * @param options.onClick Дополнительный обработчик на клик, например, для отправки метрики.
39
- * Внимание! Не факт, что в «фолбэк-сценарии» асинхронная операция будет выполнена (метрика отправлена)!
40
- * @returns Пропсы для ссылки в вебвью окружении.
41
- */
42
- getExternalLinkProps(link, options = {}) {
43
- const { onClick, forceOpenInWebview } = options;
44
- const url = (0, utils_1.getUrlInstance)(link);
45
- const appId = (0, utils_1.getAppId)(this.b2n.environment, this.b2n.iosAppId);
46
- if (!forceOpenInWebview && this.b2n.canUseNativeFeature('linksInBrowser')) {
47
- url.searchParams.append('openInBrowser', 'true');
48
- return { href: url.href, onClick };
49
- }
50
- if (this.b2n.iosAppId || this.b2n.checkAndroidAllowOpenInNewWebview()) {
51
- return {
52
- href: `${appId}://webFeature?type=recommendation&url=${encodeURIComponent(url.href)}`,
53
- onClick: options === null || options === void 0 ? void 0 : options.onClick,
54
- };
55
- }
56
- return {
57
- href: url.href,
58
- onClick: () => {
59
- var _a;
60
- onClick === null || onClick === void 0 ? void 0 : onClick();
61
- (_a = this.b2n.nativeNavigationAndTitle) === null || _a === void 0 ? void 0 : _a.setInitialView('');
62
- },
63
- };
64
- }
65
- /**
66
- * Метод для открытия PDF в нативном вьювере.
67
- *
68
- * Есть нюансы с версиями приложения, OS устройства.
69
- * Надо тестировать по моде статистики.
70
- *
71
- * @param url ссылка на pdf
72
- * @param type тип pdf ссылки
73
- * @param title название pdf файла
74
- */
75
- openPdf(url, type = 'pdfFile', title) {
76
- const params = new URLSearchParams();
77
- params.append('type', type);
78
- params.append('url', decodeURIComponent(url));
79
- if (title) {
80
- params.append('title', title.replace(/\s/g, '_'));
81
- }
82
- let replaceUrl = url;
83
- const paramsStr = params.toString();
84
- if (this.b2n.environment === 'ios') {
85
- replaceUrl = `${this.b2n.iosAppId}:///dashboard/pdf_viewer?${paramsStr}`;
86
- }
87
- // У андройда через диплинк открывается, но предыдущий экран затирается.
88
- // Поэтому мы открываем base64 через конвертирование в бинарный pdf (через ручки сервиса)
89
- // Это позволяет перейти назад к вебвью
90
- if (this.b2n.environment === 'android' && type === 'base64') {
91
- replaceUrl = `/services/base64-to-pdf?${paramsStr}`;
92
- }
93
- const windowObjectReference = window.open(replaceUrl);
94
- if (windowObjectReference === null) {
95
- window.location.replace(replaceUrl);
96
- }
97
- }
98
- /**
99
- * Метод, для перехода на ВНЕШНИЙ ресурс.
100
- *
101
- * См. описание в `getExternalLinkProps`, чтобы узнать, как выбирается способ для перехода.
102
- *
103
- * @param link Строка - валидный урл.
104
- * @param forceOpenInWebview Boolean - по умолчанию = false, если передать true,
105
- * все ссылки будут открываться в рамках webview, иначе открытие по возможности будет происходить в браузере.
106
- */
107
- visitExternalResource(link, forceOpenInWebview = false) {
108
- var _a;
109
- const url = (0, utils_1.getUrlInstance)(link);
110
- const appId = (0, utils_1.getAppId)(this.b2n.environment, this.b2n.iosAppId);
111
- if (!forceOpenInWebview && this.b2n.canUseNativeFeature('linksInBrowser')) {
112
- url.searchParams.append('openInBrowser', 'true');
113
- window.location.replace(url.href);
114
- }
115
- else if (this.b2n.iosAppId || this.b2n.checkAndroidAllowOpenInNewWebview()) {
116
- window.location.replace(`${appId}://webFeature?type=recommendation&url=${encodeURIComponent(url.href)}`);
117
- }
118
- else {
119
- (_a = this.b2n.nativeNavigationAndTitle) === null || _a === void 0 ? void 0 : _a.setInitialView('');
120
- window.location.replace(url.href);
121
- }
122
- }
123
- }
124
- exports.NativeFallbacks = NativeFallbacks;