@adapty/capacitor 3.17.1 → 4.0.0-beta.2
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.swift +1 -1
- package/README.md +8 -0
- package/android/build.gradle +2 -2
- package/dist/esm/adapty-instance.d.ts +7 -0
- package/dist/esm/adapty-instance.js +8 -0
- package/dist/esm/adapty-instance.js.map +1 -0
- package/dist/esm/adapty.d.ts +112 -59
- package/dist/esm/adapty.js +132 -69
- package/dist/esm/adapty.js.map +1 -1
- package/dist/esm/coders/parse-flow.d.ts +3 -0
- package/dist/esm/coders/parse-flow.js +4 -0
- package/dist/esm/coders/parse-flow.js.map +1 -0
- package/dist/esm/default-configs.js +2 -2
- package/dist/esm/default-configs.js.map +1 -1
- package/dist/esm/index.d.ts +4 -4
- package/dist/esm/index.js +3 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/adapty-plugin.d.ts +31 -17
- package/dist/esm/types/adapty-plugin.js.map +1 -1
- package/dist/esm/types/configs.d.ts +12 -14
- package/dist/esm/types/configs.js.map +1 -1
- package/dist/esm/types/flow-events.d.ts +1 -0
- package/dist/esm/types/flow-events.js +2 -0
- package/dist/esm/types/flow-events.js.map +1 -0
- package/dist/esm/types/index.d.ts +2 -2
- package/dist/esm/types/index.js +1 -1
- package/dist/esm/types/index.js.map +1 -1
- package/dist/esm/types/inputs.d.ts +1 -1
- package/dist/esm/types/inputs.js.map +1 -1
- package/dist/esm/ui-builder/base-view-emitter.d.ts +6 -0
- package/dist/esm/ui-builder/base-view-emitter.js +12 -0
- package/dist/esm/ui-builder/base-view-emitter.js.map +1 -1
- package/dist/esm/ui-builder/create-flow-view.d.ts +14 -0
- package/dist/esm/ui-builder/create-flow-view.js +16 -0
- package/dist/esm/ui-builder/create-flow-view.js.map +1 -0
- package/dist/esm/ui-builder/create-onboarding-view.d.ts +2 -0
- package/dist/esm/ui-builder/create-onboarding-view.js +2 -0
- package/dist/esm/ui-builder/create-onboarding-view.js.map +1 -1
- package/dist/esm/ui-builder/{paywall-view-controller.d.ts → flow-view-controller.d.ts} +37 -37
- package/dist/esm/ui-builder/{paywall-view-controller.js → flow-view-controller.js} +54 -51
- package/dist/esm/ui-builder/flow-view-controller.js.map +1 -0
- package/dist/esm/ui-builder/flow-view-emitter.d.ts +38 -0
- package/dist/esm/ui-builder/flow-view-emitter.js +151 -0
- package/dist/esm/ui-builder/flow-view-emitter.js.map +1 -0
- package/dist/esm/ui-builder/index.d.ts +5 -5
- package/dist/esm/ui-builder/index.js +3 -2
- package/dist/esm/ui-builder/index.js.map +1 -1
- package/dist/esm/ui-builder/onboarding-view-controller.d.ts +7 -5
- package/dist/esm/ui-builder/onboarding-view-controller.js +7 -5
- package/dist/esm/ui-builder/onboarding-view-controller.js.map +1 -1
- package/dist/esm/ui-builder/types.d.ts +11 -358
- package/dist/esm/ui-builder/types.js +52 -22
- package/dist/esm/ui-builder/types.js.map +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esm/version.js.map +1 -1
- package/dist/plugin.cjs.js +903 -679
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +903 -679
- package/dist/plugin.js.map +1 -1
- package/package.json +3 -5
- package/AdaptyCapacitor.podspec +0 -20
- package/dist/esm/coders/parse-paywall.d.ts +0 -3
- package/dist/esm/coders/parse-paywall.js +0 -4
- package/dist/esm/coders/parse-paywall.js.map +0 -1
- package/dist/esm/types/paywall-events.d.ts +0 -1
- package/dist/esm/types/paywall-events.js +0 -2
- package/dist/esm/types/paywall-events.js.map +0 -1
- package/dist/esm/ui-builder/create-paywall-view.d.ts +0 -25
- package/dist/esm/ui-builder/create-paywall-view.js +0 -29
- package/dist/esm/ui-builder/create-paywall-view.js.map +0 -1
- package/dist/esm/ui-builder/paywall-view-controller.js.map +0 -1
- package/dist/esm/ui-builder/paywall-view-emitter.d.ts +0 -16
- package/dist/esm/ui-builder/paywall-view-emitter.js +0 -114
- package/dist/esm/ui-builder/paywall-view-emitter.js.map +0 -1
|
@@ -2,49 +2,52 @@ import { AdaptyError } from '@adapty/core';
|
|
|
2
2
|
import { coderFactory } from '../coders/factory';
|
|
3
3
|
import { LogContext, Log } from '../logger';
|
|
4
4
|
import { mapValues, withErrorContext } from '../utils';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { FlowViewEmitter } from './flow-view-emitter';
|
|
6
|
+
import { DEFAULT_FLOW_EVENT_HANDLERS } from './types';
|
|
7
7
|
const DEFAULT_PARAMS = {
|
|
8
8
|
prefetchProducts: true,
|
|
9
9
|
loadTimeoutMs: 5000,
|
|
10
|
+
android: {
|
|
11
|
+
enableSafeArea: true,
|
|
12
|
+
},
|
|
10
13
|
};
|
|
11
14
|
/**
|
|
12
|
-
* Controller for managing
|
|
15
|
+
* Controller for managing flow views.
|
|
13
16
|
*
|
|
14
17
|
* @remarks
|
|
15
|
-
* This class provides methods to present, dismiss, and handle events for
|
|
16
|
-
* created with the
|
|
18
|
+
* This class provides methods to present, dismiss, and handle events for flow views
|
|
19
|
+
* created with the Flow Builder. Create instances using the {@link createFlowView} function
|
|
17
20
|
* rather than directly constructing this class.
|
|
18
21
|
*
|
|
19
22
|
* @public
|
|
20
23
|
*/
|
|
21
|
-
export class
|
|
24
|
+
export class FlowViewController {
|
|
22
25
|
/**
|
|
23
26
|
* Intended way to create a ViewController instance.
|
|
24
27
|
* It prepares a native controller to be presented
|
|
25
28
|
* and creates reference between native controller and JS instance
|
|
26
29
|
* @internal
|
|
27
30
|
*/
|
|
28
|
-
static async create(
|
|
29
|
-
const controller = new
|
|
31
|
+
static async create(flow, params, adaptyPlugin) {
|
|
32
|
+
const controller = new FlowViewController(adaptyPlugin);
|
|
30
33
|
const ctx = new LogContext();
|
|
31
|
-
const methodKey = '
|
|
34
|
+
const methodKey = 'adapty_ui_create_flow_view';
|
|
32
35
|
const log = ctx.call({ methodName: methodKey });
|
|
33
|
-
log.start(() => ({
|
|
34
|
-
const
|
|
35
|
-
const paramsCoder = coderFactory.
|
|
36
|
+
log.start(() => ({ flow, params }));
|
|
37
|
+
const flowCoder = coderFactory.createFlowCoder();
|
|
38
|
+
const paramsCoder = coderFactory.createUiCreateFlowViewParamsCoder();
|
|
36
39
|
const paramsWithDefaults = Object.assign(Object.assign({}, DEFAULT_PARAMS), params);
|
|
37
|
-
const data = Object.assign({ method: methodKey,
|
|
40
|
+
const data = Object.assign({ method: methodKey, flow: flowCoder.encode(flow) }, paramsCoder.encode(paramsWithDefaults));
|
|
38
41
|
const result = (await controller.adaptyPlugin.handleMethodCall(methodKey, JSON.stringify(data), ctx, log));
|
|
39
42
|
controller.id = result.id;
|
|
40
|
-
controller.viewEmitter = new
|
|
41
|
-
await controller.setEventHandlers(
|
|
43
|
+
controller.viewEmitter = new FlowViewEmitter(controller.id, controller.adaptyPlugin);
|
|
44
|
+
await controller.setEventHandlers(DEFAULT_FLOW_EVENT_HANDLERS);
|
|
42
45
|
return controller;
|
|
43
46
|
}
|
|
44
47
|
/**
|
|
45
48
|
* Since constructors in JS cannot be async, it is not
|
|
46
49
|
* preferred to create ViewControllers in direct way.
|
|
47
|
-
* Consider using @link
|
|
50
|
+
* Consider using {@link FlowViewController.create} instead
|
|
48
51
|
*
|
|
49
52
|
* @internal
|
|
50
53
|
*/
|
|
@@ -56,31 +59,31 @@ export class PaywallViewController {
|
|
|
56
59
|
await this.dismiss();
|
|
57
60
|
}
|
|
58
61
|
catch (error) {
|
|
59
|
-
Log.warn('setEventHandlers', () => 'Failed to dismiss
|
|
62
|
+
Log.warn('setEventHandlers', () => 'Failed to dismiss flow', () => ({ error }));
|
|
60
63
|
}
|
|
61
64
|
};
|
|
62
65
|
this.adaptyPlugin = adaptyPlugin;
|
|
63
66
|
}
|
|
64
67
|
/**
|
|
65
|
-
* Presents the
|
|
68
|
+
* Presents the flow view as a modal screen.
|
|
66
69
|
*
|
|
67
70
|
* @remarks
|
|
68
|
-
* Calling `present` on an already visible
|
|
69
|
-
* The
|
|
70
|
-
* On Android, the
|
|
71
|
+
* Calling `present` on an already visible flow view will result in an error.
|
|
72
|
+
* The flow will be displayed with the configured presentation style on iOS.
|
|
73
|
+
* On Android, the flow is always presented as a full-screen activity.
|
|
71
74
|
*
|
|
72
75
|
* @param options - Optional presentation options
|
|
73
76
|
* @param options.iosPresentationStyle - iOS presentation style. Available options: `'full_screen'` (default) or `'page_sheet'`. Only affects iOS platform.
|
|
74
|
-
* @returns A promise that resolves when the
|
|
77
|
+
* @returns A promise that resolves when the flow is presented.
|
|
75
78
|
* @throws {@link AdaptyError} if the view reference is invalid or the view is already presented.
|
|
76
79
|
*
|
|
77
80
|
* @example
|
|
78
81
|
* Present with default full-screen style
|
|
79
82
|
* ```typescript
|
|
80
|
-
* import { adapty,
|
|
83
|
+
* import { adapty, createFlowView } from '@adapty/capacitor';
|
|
81
84
|
*
|
|
82
|
-
* const
|
|
83
|
-
* const view = await
|
|
85
|
+
* const flow = await adapty.getFlow({ placementId: 'YOUR_PLACEMENT_ID' });
|
|
86
|
+
* const view = await createFlowView(flow);
|
|
84
87
|
* await view.present();
|
|
85
88
|
* ```
|
|
86
89
|
*
|
|
@@ -93,7 +96,7 @@ export class PaywallViewController {
|
|
|
93
96
|
async present(options = {}) {
|
|
94
97
|
var _a;
|
|
95
98
|
const ctx = new LogContext();
|
|
96
|
-
const methodKey = '
|
|
99
|
+
const methodKey = 'adapty_ui_present_flow_view';
|
|
97
100
|
const log = ctx.call({ methodName: methodKey });
|
|
98
101
|
log.start(() => ({ _id: this.id, iosPresentationStyle: options.iosPresentationStyle }));
|
|
99
102
|
if (this.id === null) {
|
|
@@ -110,20 +113,20 @@ export class PaywallViewController {
|
|
|
110
113
|
await this.adaptyPlugin.handleMethodCall(methodKey, JSON.stringify(data), ctx, log);
|
|
111
114
|
}
|
|
112
115
|
/**
|
|
113
|
-
* Dismisses the
|
|
116
|
+
* Dismisses the flow view.
|
|
114
117
|
*
|
|
115
118
|
* @remarks
|
|
116
|
-
* This method closes the
|
|
119
|
+
* This method closes the flow and cleans up associated resources.
|
|
117
120
|
* After dismissing, the view controller instance cannot be reused.
|
|
118
121
|
*
|
|
119
|
-
* @returns A promise that resolves when the
|
|
122
|
+
* @returns A promise that resolves when the flow is dismissed.
|
|
120
123
|
* @throws {@link AdaptyError} if the view reference is invalid.
|
|
121
124
|
*
|
|
122
125
|
* @example
|
|
123
126
|
* ```typescript
|
|
124
|
-
* import {
|
|
127
|
+
* import { createFlowView } from '@adapty/capacitor';
|
|
125
128
|
*
|
|
126
|
-
* const view = await
|
|
129
|
+
* const view = await createFlowView(flow);
|
|
127
130
|
* await view.present();
|
|
128
131
|
* // ... later
|
|
129
132
|
* await view.dismiss();
|
|
@@ -131,7 +134,7 @@ export class PaywallViewController {
|
|
|
131
134
|
*/
|
|
132
135
|
async dismiss() {
|
|
133
136
|
const ctx = new LogContext();
|
|
134
|
-
const methodKey = '
|
|
137
|
+
const methodKey = 'adapty_ui_dismiss_flow_view';
|
|
135
138
|
const log = ctx.call({ methodName: methodKey });
|
|
136
139
|
log.start(() => ({ _id: this.id }));
|
|
137
140
|
if (this.id === null) {
|
|
@@ -152,7 +155,7 @@ export class PaywallViewController {
|
|
|
152
155
|
* Displays a dialog to the user.
|
|
153
156
|
*
|
|
154
157
|
* @remarks
|
|
155
|
-
* Use this method to show custom dialogs within the
|
|
158
|
+
* Use this method to show custom dialogs within the flow.
|
|
156
159
|
* If you provide two actions in the config, the primary action should cancel the operation
|
|
157
160
|
* and leave things unchanged, while the secondary action should confirm the operation.
|
|
158
161
|
*
|
|
@@ -204,20 +207,20 @@ export class PaywallViewController {
|
|
|
204
207
|
return await this.adaptyPlugin.handleMethodCall(methodKey, JSON.stringify(data), ctx, log);
|
|
205
208
|
}
|
|
206
209
|
/**
|
|
207
|
-
* Registers event handlers for
|
|
210
|
+
* Registers event handlers for flow UI events.
|
|
208
211
|
*
|
|
209
212
|
* @remarks
|
|
210
213
|
* Each event type can have only one handler — new handlers replace existing ones.
|
|
211
|
-
* Default handlers are registered automatically in {@link
|
|
212
|
-
*
|
|
213
|
-
* - `
|
|
214
|
-
* - `
|
|
215
|
-
* -
|
|
216
|
-
*
|
|
214
|
+
* Default handlers are registered automatically in {@link createFlowView} (see `DEFAULT_FLOW_EVENT_HANDLERS`).
|
|
215
|
+
* Only two defaults close the view; all others keep it open:
|
|
216
|
+
* - `onCloseButtonPress` - closes the view (returns `true`)
|
|
217
|
+
* - `onError` - closes the view (returns `true`)
|
|
218
|
+
* - all other handlers keep the view open by default (return `false`),
|
|
219
|
+
* including `onAndroidSystemBack`, `onRestoreCompleted`, and `onPurchaseCompleted`
|
|
217
220
|
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
+
* Returning `true` from a handler closes the view; returning `false` keeps it open.
|
|
222
|
+
* To retain default behavior in a custom listener, return the same value as the default
|
|
223
|
+
* implementation (only `onCloseButtonPress` and `onError` close the view by default).
|
|
221
224
|
*
|
|
222
225
|
* Calling this method multiple times will replace previously registered handlers for provided events.
|
|
223
226
|
*
|
|
@@ -229,9 +232,9 @@ export class PaywallViewController {
|
|
|
229
232
|
* @example
|
|
230
233
|
* Register custom event handlers
|
|
231
234
|
* ```typescript
|
|
232
|
-
* import {
|
|
235
|
+
* import { createFlowView } from '@adapty/capacitor';
|
|
233
236
|
*
|
|
234
|
-
* const view = await
|
|
237
|
+
* const view = await createFlowView(flow);
|
|
235
238
|
*
|
|
236
239
|
* const unsubscribe = await view.setEventHandlers({
|
|
237
240
|
* onPurchaseStarted: (product) => {
|
|
@@ -239,7 +242,7 @@ export class PaywallViewController {
|
|
|
239
242
|
* },
|
|
240
243
|
* onPurchaseCompleted: (result) => {
|
|
241
244
|
* console.log('Purchase completed:', result.type);
|
|
242
|
-
* // Return true to
|
|
245
|
+
* // Return true to close the view after purchase (default keeps it open)
|
|
243
246
|
* return result.type !== 'user_cancelled';
|
|
244
247
|
* },
|
|
245
248
|
* onPurchaseFailed: (error) => {
|
|
@@ -265,13 +268,13 @@ export class PaywallViewController {
|
|
|
265
268
|
});
|
|
266
269
|
}
|
|
267
270
|
Log.verbose('setEventHandlers', () => 'Registering event handlers for view', () => ({ id: this.id }));
|
|
268
|
-
const viewEmitter = (_a = this.viewEmitter) !== null && _a !== void 0 ? _a : new
|
|
271
|
+
const viewEmitter = (_a = this.viewEmitter) !== null && _a !== void 0 ? _a : new FlowViewEmitter(this.id, this.adaptyPlugin);
|
|
269
272
|
this.viewEmitter = viewEmitter;
|
|
270
273
|
const wrappedErrorLogEventHandlers = mapValues(eventHandlers, (handler, eventName) => handler && typeof handler === 'function'
|
|
271
|
-
? withErrorContext(handler, eventName, '
|
|
274
|
+
? withErrorContext(handler, eventName, 'FlowViewController')
|
|
272
275
|
: undefined);
|
|
273
276
|
// Merge with defaults to ensure default behavior is preserved after unsubscribe/resubscribe cycles
|
|
274
|
-
const finalEventHandlers = Object.assign(Object.assign({},
|
|
277
|
+
const finalEventHandlers = Object.assign(Object.assign({}, DEFAULT_FLOW_EVENT_HANDLERS), wrappedErrorLogEventHandlers);
|
|
275
278
|
for (const [eventName, handler] of Object.entries(finalEventHandlers)) {
|
|
276
279
|
if (handler && typeof handler === 'function') {
|
|
277
280
|
try {
|
|
@@ -305,7 +308,7 @@ export class PaywallViewController {
|
|
|
305
308
|
*
|
|
306
309
|
* @example
|
|
307
310
|
* ```typescript
|
|
308
|
-
* const view = await
|
|
311
|
+
* const view = await createFlowView(flow);
|
|
309
312
|
* await view.setEventHandlers({ onPurchaseCompleted: handlePurchase });
|
|
310
313
|
*
|
|
311
314
|
* // Later, clear all handlers
|
|
@@ -320,4 +323,4 @@ export class PaywallViewController {
|
|
|
320
323
|
}
|
|
321
324
|
}
|
|
322
325
|
}
|
|
323
|
-
//# sourceMappingURL=
|
|
326
|
+
//# sourceMappingURL=flow-view-controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow-view-controller.js","sourceRoot":"","sources":["../../../src/ui-builder/flow-view-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG3C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAG5C,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEvD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAStD,OAAO,EAAE,2BAA2B,EAAE,MAAM,SAAS,CAAC;AAItD,MAAM,cAAc,GAA8B;IAChD,gBAAgB,EAAE,IAAI;IACtB,aAAa,EAAE,IAAI;IACnB,OAAO,EAAE;QACP,cAAc,EAAE,IAAI;KACrB;CACF,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,OAAO,kBAAkB;IAK7B;;;;;OAKG;IACH,MAAM,CAAC,KAAK,CAAC,MAAM,CACjB,IAAgB,EAChB,MAAiC,EACjC,YAAoB;QAEpB,MAAM,UAAU,GAAG,IAAI,kBAAkB,CAAC,YAAY,CAAC,CAAC;QAExD,MAAM,GAAG,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,MAAM,SAAS,GAAG,4BAA4B,CAAC;QAC/C,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC;QAChD,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;QAEpC,MAAM,SAAS,GAAG,YAAY,CAAC,eAAe,EAAE,CAAC;QACjD,MAAM,WAAW,GAAG,YAAY,CAAC,iCAAiC,EAAE,CAAC;QACrE,MAAM,kBAAkB,mCACnB,cAAc,GACd,MAAM,CACV,CAAC;QAEF,MAAM,IAAI,mBACR,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,IACzB,WAAW,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAC1C,CAAC;QAEF,MAAM,MAAM,GAAG,CAAC,MAAM,UAAU,CAAC,YAAY,CAAC,gBAAgB,CAC5D,SAAS,EACT,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EACpB,GAAG,EACH,GAAG,CACJ,CAAiB,CAAC;QACnB,UAAU,CAAC,EAAE,GAAG,MAAM,CAAC,EAAE,CAAC;QAC1B,UAAU,CAAC,WAAW,GAAG,IAAI,eAAe,CAAC,UAAU,CAAC,EAAE,EAAE,UAAU,CAAC,YAAY,CAAC,CAAC;QAErF,MAAM,UAAU,CAAC,gBAAgB,CAAC,2BAA2B,CAAC,CAAC;QAE/D,OAAO,UAAU,CAAC;IACpB,CAAC;IAED;;;;;;OAMG;IACH,YAAoB,YAAoB;QAxDhC,OAAE,GAAkB,IAAI,CAAC;QAEzB,gBAAW,GAA2B,IAAI,CAAC;QAoN3C,mBAAc,GAAG,KAAK,IAAI,EAAE;YAClC,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YACvB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,GAAG,CAAC,IAAI,CACN,kBAAkB,EAClB,GAAG,EAAE,CAAC,wBAAwB,EAC9B,GAAG,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAClB,CAAC;YACJ,CAAC;QACH,CAAC,CAAC;QAvKA,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACnC,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACI,KAAK,CAAC,OAAO,CAAC,UAAiE,EAAE;;QACtF,MAAM,GAAG,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,MAAM,SAAS,GAAG,6BAA6B,CAAC;QAChD,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC;QAChD,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,oBAAoB,EAAE,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC,CAAC;QAExF,IAAI,IAAI,CAAC,EAAE,KAAK,IAAI,EAAE,CAAC;YACrB,MAAM,IAAI,WAAW,CAAC;gBACpB,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,mBAAmB;aAC7B,CAAC,CAAC;QACL,CAAC;QAED,MAAM,IAAI,GAA2C;YACnD,MAAM,EAAE,SAAS;YACjB,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,sBAAsB,EAAE,MAAA,OAAO,CAAC,oBAAoB,mCAAI,aAAa;SACtE,CAAC;QAEF,MAAM,IAAI,CAAC,YAAY,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IACtF,CAAC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACI,KAAK,CAAC,OAAO;QAClB,MAAM,GAAG,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,MAAM,SAAS,GAAG,6BAA6B,CAAC;QAChD,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC;QAChD,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAEpC,IAAI,IAAI,CAAC,EAAE,KAAK,IAAI,EAAE,CAAC;YACrB,MAAM,IAAI,WAAW,CAAC;gBACpB,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,mBAAmB;aAC7B,CAAC,CAAC;QACL,CAAC;QAED,MAAM,IAAI,GAA2C;YACnD,MAAM,EAAE,SAAS;YACjB,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,OAAO,EAAE,IAAI;SACd,CAAC;QAEF,MAAM,IAAI,CAAC,YAAY,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;QACpF,IAAI,CAAC,kBAAkB,EAAE,CAAC;IAC5B,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACI,KAAK,CAAC,UAAU,CAAC,MAA4B;QAClD,MAAM,GAAG,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,MAAM,SAAS,GAAG,uBAAuB,CAAC;QAC1C,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC;QAChD,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAEpC,IAAI,IAAI,CAAC,EAAE,KAAK,IAAI,EAAE,CAAC;YACrB,MAAM,IAAI,WAAW,CAAC;gBACpB,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,mBAAmB;aAC7B,CAAC,CAAC;QACL,CAAC;QAED,MAAM,YAAY,GAAG;YACnB,oBAAoB,EAAE,MAAM,CAAC,kBAAkB;YAC/C,sBAAsB,EAAE,MAAM,CAAC,oBAAoB;YACnD,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,OAAO,EAAE,MAAM,CAAC,OAAO;SACxB,CAAC;QAEF,MAAM,IAAI,GAAsC;YAC9C,MAAM,EAAE,SAAS;YACjB,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,aAAa,EAAE,YAAY;SAC5B,CAAC;QAEF,OAAO,MAAM,IAAI,CAAC,YAAY,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAC7F,CAAC;IAcD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiDG;IACI,KAAK,CAAC,gBAAgB,CAAC,gBAA4C,EAAE;;QAC1E,MAAM,GAAG,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,kBAAkB,EAAE,CAAC,CAAC;QACzD,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAEpC,IAAI,IAAI,CAAC,EAAE,KAAK,IAAI,EAAE,CAAC;YACrB,MAAM,IAAI,WAAW,CAAC;gBACpB,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,mBAAmB;aAC7B,CAAC,CAAC;QACL,CAAC;QAED,GAAG,CAAC,OAAO,CACT,kBAAkB,EAClB,GAAG,EAAE,CAAC,qCAAqC,EAC3C,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CACxB,CAAC;QAEF,MAAM,WAAW,GAAG,MAAA,IAAI,CAAC,WAAW,mCAAI,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QACxF,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAE/B,MAAM,4BAA4B,GAAG,SAAS,CAAC,aAAa,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,EAAE,CACnF,OAAO,IAAI,OAAO,OAAO,KAAK,UAAU;YACtC,CAAC,CAAC,gBAAgB,CAAC,OAAO,EAAE,SAAmB,EAAE,oBAAoB,CAAC;YACtE,CAAC,CAAC,SAAS,CACd,CAAC;QAEF,mGAAmG;QACnG,MAAM,kBAAkB,mCACnB,2BAA2B,GAC3B,4BAA4B,CAChC,CAAC;QAEF,KAAK,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,EAAE,CAAC;YACtE,IAAI,OAAO,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;gBAC7C,IAAI,CAAC;oBACH,MAAM,WAAW,CAAC,WAAW,CAAC,SAAoC,EAAE,OAAO,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;oBAClG,GAAG,CAAC,OAAO,CACT,kBAAkB,EAClB,GAAG,EAAE,CAAC,wBAAwB,EAC9B,GAAG,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CACtB,CAAC;gBACJ,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,GAAG,CAAC,KAAK,CACP,kBAAkB,EAClB,GAAG,EAAE,CAAC,kCAAkC,SAAS,EAAE,EACnD,GAAG,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAClB,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;QAED,8BAA8B;QAC9B,MAAM,WAAW,GAAG,GAAG,EAAE;YACvB,GAAG,CAAC,IAAI,CACN,kBAAkB,EAClB,GAAG,EAAE,CAAC,uCAAuC,EAC7C,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CACxB,CAAC;YACF,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;gBACrB,IAAI,CAAC,WAAW,CAAC,kBAAkB,EAAE,CAAC;gBACtC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;YAC1B,CAAC;QACH,CAAC,CAAC;QAEF,OAAO,WAAW,CAAC;IACrB,CAAC;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACI,kBAAkB;QACvB,GAAG,CAAC,IAAI,CACN,oBAAoB,EACpB,GAAG,EAAE,CAAC,sCAAsC,EAC5C,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CACxB,CAAC;QAEF,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,IAAI,CAAC,WAAW,CAAC,kBAAkB,EAAE,CAAC;YACtC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAC1B,CAAC;IACH,CAAC;CACF","sourcesContent":["import { AdaptyError } from '@adapty/core';\n\nimport type { Adapty } from '../adapty';\nimport { coderFactory } from '../coders/factory';\nimport { LogContext, Log } from '../logger';\nimport type { AdaptyFlow } from '../types';\nimport type { components } from '../types/api';\nimport { mapValues, withErrorContext } from '../utils';\n\nimport { FlowViewEmitter } from './flow-view-emitter';\nimport type {\n AdaptyUiView,\n CreateFlowViewParamsInput,\n AdaptyUiDialogConfig,\n AdaptyUiDialogActionType,\n FlowEventHandlers,\n AdaptyIOSPresentationStyle,\n} from './types';\nimport { DEFAULT_FLOW_EVENT_HANDLERS } from './types';\n\ntype Req = components['requests'];\n\nconst DEFAULT_PARAMS: CreateFlowViewParamsInput = {\n prefetchProducts: true,\n loadTimeoutMs: 5000,\n android: {\n enableSafeArea: true,\n },\n};\n\n/**\n * Controller for managing flow views.\n *\n * @remarks\n * This class provides methods to present, dismiss, and handle events for flow views\n * created with the Flow Builder. Create instances using the {@link createFlowView} function\n * rather than directly constructing this class.\n *\n * @public\n */\nexport class FlowViewController {\n private id: string | null = null;\n private adaptyPlugin: Adapty;\n private viewEmitter: FlowViewEmitter | null = null;\n\n /**\n * Intended way to create a ViewController instance.\n * It prepares a native controller to be presented\n * and creates reference between native controller and JS instance\n * @internal\n */\n static async create(\n flow: AdaptyFlow,\n params: CreateFlowViewParamsInput,\n adaptyPlugin: Adapty,\n ): Promise<FlowViewController> {\n const controller = new FlowViewController(adaptyPlugin);\n\n const ctx = new LogContext();\n const methodKey = 'adapty_ui_create_flow_view';\n const log = ctx.call({ methodName: methodKey });\n log.start(() => ({ flow, params }));\n\n const flowCoder = coderFactory.createFlowCoder();\n const paramsCoder = coderFactory.createUiCreateFlowViewParamsCoder();\n const paramsWithDefaults: CreateFlowViewParamsInput = {\n ...DEFAULT_PARAMS,\n ...params,\n };\n\n const data: Req['AdaptyUICreateFlowView.Request'] = {\n method: methodKey,\n flow: flowCoder.encode(flow),\n ...paramsCoder.encode(paramsWithDefaults),\n };\n\n const result = (await controller.adaptyPlugin.handleMethodCall(\n methodKey,\n JSON.stringify(data),\n ctx,\n log,\n )) as AdaptyUiView;\n controller.id = result.id;\n controller.viewEmitter = new FlowViewEmitter(controller.id, controller.adaptyPlugin);\n\n await controller.setEventHandlers(DEFAULT_FLOW_EVENT_HANDLERS);\n\n return controller;\n }\n\n /**\n * Since constructors in JS cannot be async, it is not\n * preferred to create ViewControllers in direct way.\n * Consider using {@link FlowViewController.create} instead\n *\n * @internal\n */\n private constructor(adaptyPlugin: Adapty) {\n this.adaptyPlugin = adaptyPlugin;\n }\n\n /**\n * Presents the flow view as a modal screen.\n *\n * @remarks\n * Calling `present` on an already visible flow view will result in an error.\n * The flow will be displayed with the configured presentation style on iOS.\n * On Android, the flow is always presented as a full-screen activity.\n *\n * @param options - Optional presentation options\n * @param options.iosPresentationStyle - iOS presentation style. Available options: `'full_screen'` (default) or `'page_sheet'`. Only affects iOS platform.\n * @returns A promise that resolves when the flow is presented.\n * @throws {@link AdaptyError} if the view reference is invalid or the view is already presented.\n *\n * @example\n * Present with default full-screen style\n * ```typescript\n * import { adapty, createFlowView } from '@adapty/capacitor';\n *\n * const flow = await adapty.getFlow({ placementId: 'YOUR_PLACEMENT_ID' });\n * const view = await createFlowView(flow);\n * await view.present();\n * ```\n *\n * @example\n * Present with page sheet style on iOS\n * ```typescript\n * await view.present({ iosPresentationStyle: 'page_sheet' });\n * ```\n */\n public async present(options: { iosPresentationStyle?: AdaptyIOSPresentationStyle } = {}): Promise<void> {\n const ctx = new LogContext();\n const methodKey = 'adapty_ui_present_flow_view';\n const log = ctx.call({ methodName: methodKey });\n log.start(() => ({ _id: this.id, iosPresentationStyle: options.iosPresentationStyle }));\n\n if (this.id === null) {\n throw new AdaptyError({\n adaptyCode: 2002,\n message: 'No view reference',\n });\n }\n\n const data: Req['AdaptyUIPresentFlowView.Request'] = {\n method: methodKey,\n id: this.id,\n ios_presentation_style: options.iosPresentationStyle ?? 'full_screen',\n };\n\n await this.adaptyPlugin.handleMethodCall(methodKey, JSON.stringify(data), ctx, log);\n }\n\n /**\n * Dismisses the flow view.\n *\n * @remarks\n * This method closes the flow and cleans up associated resources.\n * After dismissing, the view controller instance cannot be reused.\n *\n * @returns A promise that resolves when the flow is dismissed.\n * @throws {@link AdaptyError} if the view reference is invalid.\n *\n * @example\n * ```typescript\n * import { createFlowView } from '@adapty/capacitor';\n *\n * const view = await createFlowView(flow);\n * await view.present();\n * // ... later\n * await view.dismiss();\n * ```\n */\n public async dismiss(): Promise<void> {\n const ctx = new LogContext();\n const methodKey = 'adapty_ui_dismiss_flow_view';\n const log = ctx.call({ methodName: methodKey });\n log.start(() => ({ _id: this.id }));\n\n if (this.id === null) {\n throw new AdaptyError({\n adaptyCode: 2002,\n message: 'No view reference',\n });\n }\n\n const data: Req['AdaptyUIDismissFlowView.Request'] = {\n method: methodKey,\n id: this.id,\n destroy: true,\n };\n\n await this.adaptyPlugin.handleMethodCall(methodKey, JSON.stringify(data), ctx, log);\n this.clearEventHandlers();\n }\n\n /**\n * Displays a dialog to the user.\n *\n * @remarks\n * Use this method to show custom dialogs within the flow.\n * If you provide two actions in the config, the primary action should cancel the operation\n * and leave things unchanged, while the secondary action should confirm the operation.\n *\n * @param config - Configuration for the dialog.\n * @param config.title - The dialog title.\n * @param config.content - The dialog message content.\n * @param config.primaryActionTitle - The title for the primary (default) action button.\n * @param config.secondaryActionTitle - Optional. The title for the secondary action button.\n * @returns A promise that resolves to the action type that the user selected: `'primary'` or `'secondary'`.\n * @throws {@link AdaptyError} if the view reference is invalid.\n *\n * @example\n * Show confirmation dialog\n * ```typescript\n * const action = await view.showDialog({\n * title: 'Confirm Purchase',\n * content: 'Are you sure you want to proceed with this purchase?',\n * primaryActionTitle: 'Cancel',\n * secondaryActionTitle: 'Continue'\n * });\n *\n * if (action === 'secondary') {\n * console.log('User confirmed');\n * }\n * ```\n */\n public async showDialog(config: AdaptyUiDialogConfig): Promise<AdaptyUiDialogActionType> {\n const ctx = new LogContext();\n const methodKey = 'adapty_ui_show_dialog';\n const log = ctx.call({ methodName: methodKey });\n log.start(() => ({ _id: this.id }));\n\n if (this.id === null) {\n throw new AdaptyError({\n adaptyCode: 2002,\n message: 'No view reference',\n });\n }\n\n const dialogConfig = {\n default_action_title: config.primaryActionTitle,\n secondary_action_title: config.secondaryActionTitle,\n title: config.title,\n content: config.content,\n };\n\n const data: Req['AdaptyUIShowDialog.Request'] = {\n method: methodKey,\n id: this.id,\n configuration: dialogConfig,\n };\n\n return await this.adaptyPlugin.handleMethodCall(methodKey, JSON.stringify(data), ctx, log);\n }\n\n private onRequestClose = async () => {\n try {\n await this.dismiss();\n } catch (error) {\n Log.warn(\n 'setEventHandlers',\n () => 'Failed to dismiss flow',\n () => ({ error }),\n );\n }\n };\n\n /**\n * Registers event handlers for flow UI events.\n *\n * @remarks\n * Each event type can have only one handler — new handlers replace existing ones.\n * Default handlers are registered automatically in {@link createFlowView} (see `DEFAULT_FLOW_EVENT_HANDLERS`).\n * Only two defaults close the view; all others keep it open:\n * - `onCloseButtonPress` - closes the view (returns `true`)\n * - `onError` - closes the view (returns `true`)\n * - all other handlers keep the view open by default (return `false`),\n * including `onAndroidSystemBack`, `onRestoreCompleted`, and `onPurchaseCompleted`\n *\n * Returning `true` from a handler closes the view; returning `false` keeps it open.\n * To retain default behavior in a custom listener, return the same value as the default\n * implementation (only `onCloseButtonPress` and `onError` close the view by default).\n *\n * Calling this method multiple times will replace previously registered handlers for provided events.\n *\n * @see {@link https://adapty.io/docs/capacitor-handling-events | Handling View Events}\n *\n * @param eventHandlers - Set of event handling callbacks. Only provided handlers will be registered or updated.\n * @returns A promise that resolves to an unsubscribe function that removes all registered listeners.\n *\n * @example\n * Register custom event handlers\n * ```typescript\n * import { createFlowView } from '@adapty/capacitor';\n *\n * const view = await createFlowView(flow);\n *\n * const unsubscribe = await view.setEventHandlers({\n * onPurchaseStarted: (product) => {\n * console.log('Purchase started:', product.vendorProductId);\n * },\n * onPurchaseCompleted: (result) => {\n * console.log('Purchase completed:', result.type);\n * // Return true to close the view after purchase (default keeps it open)\n * return result.type !== 'user_cancelled';\n * },\n * onPurchaseFailed: (error) => {\n * console.error('Purchase failed:', error);\n * }\n * });\n *\n * await view.present();\n *\n * // Later, unsubscribe all handlers\n * unsubscribe();\n * ```\n */\n public async setEventHandlers(eventHandlers: Partial<FlowEventHandlers> = {}): Promise<() => void> {\n const ctx = new LogContext();\n const log = ctx.call({ methodName: 'setEventHandlers' });\n log.start(() => ({ _id: this.id }));\n\n if (this.id === null) {\n throw new AdaptyError({\n adaptyCode: 2002,\n message: 'No view reference',\n });\n }\n\n Log.verbose(\n 'setEventHandlers',\n () => 'Registering event handlers for view',\n () => ({ id: this.id }),\n );\n\n const viewEmitter = this.viewEmitter ?? new FlowViewEmitter(this.id, this.adaptyPlugin);\n this.viewEmitter = viewEmitter;\n\n const wrappedErrorLogEventHandlers = mapValues(eventHandlers, (handler, eventName) =>\n handler && typeof handler === 'function'\n ? withErrorContext(handler, eventName as string, 'FlowViewController')\n : undefined,\n );\n\n // Merge with defaults to ensure default behavior is preserved after unsubscribe/resubscribe cycles\n const finalEventHandlers: FlowEventHandlers = {\n ...DEFAULT_FLOW_EVENT_HANDLERS,\n ...wrappedErrorLogEventHandlers,\n };\n\n for (const [eventName, handler] of Object.entries(finalEventHandlers)) {\n if (handler && typeof handler === 'function') {\n try {\n await viewEmitter.addListener(eventName as keyof FlowEventHandlers, handler, this.onRequestClose);\n Log.verbose(\n 'setEventHandlers',\n () => 'Registered handler for',\n () => ({ eventName }),\n );\n } catch (error) {\n Log.error(\n 'setEventHandlers',\n () => `Failed to register handler for ${eventName}`,\n () => ({ error }),\n );\n }\n }\n }\n\n // Return unsubscribe function\n const unsubscribe = () => {\n Log.info(\n 'setEventHandlers',\n () => 'Unsubscribing event handlers for view',\n () => ({ id: this.id }),\n );\n if (this.viewEmitter) {\n this.viewEmitter.removeAllListeners();\n this.viewEmitter = null;\n }\n };\n\n return unsubscribe;\n }\n\n /**\n * Clears all registered event handlers.\n *\n * @remarks\n * This method removes all previously registered event handlers.\n * After calling this method, no event handlers will be active\n * until you call {@link setEventHandlers} again.\n *\n * Use this after dismiss to remove all event handlers\n *\n * @example\n * ```typescript\n * const view = await createFlowView(flow);\n * await view.setEventHandlers({ onPurchaseCompleted: handlePurchase });\n *\n * // Later, clear all handlers\n * view.clearEventHandlers();\n * ```\n */\n public clearEventHandlers(): void {\n Log.info(\n 'clearEventHandlers',\n () => 'Clearing all event handlers for view',\n () => ({ id: this.id }),\n );\n\n if (this.viewEmitter) {\n this.viewEmitter.removeAllListeners();\n this.viewEmitter = null;\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { FlowEventIdType, ParsedFlowEvent } from '@adapty/core';
|
|
2
|
+
import type { Adapty } from '../adapty';
|
|
3
|
+
import { LogContext } from '../logger';
|
|
4
|
+
import { BaseViewEmitter } from './base-view-emitter';
|
|
5
|
+
import type { FlowEventHandlers } from './types';
|
|
6
|
+
/**
|
|
7
|
+
* FlowViewEmitter manages event handlers for flow view events.
|
|
8
|
+
* Each event type can have only one handler - new handlers replace existing ones.
|
|
9
|
+
*
|
|
10
|
+
* In addition to the standard synchronous dispatch inherited from
|
|
11
|
+
* {@link BaseViewEmitter}, this emitter handles three asynchronous round-trip
|
|
12
|
+
* events that must reply back to native:
|
|
13
|
+
* - `onRequestPermission` — awaits the async handler, then replies with the status.
|
|
14
|
+
* - `onObserverPurchaseInitiated` — injects start/finish callbacks (observer mode).
|
|
15
|
+
* - `onObserverRestoreInitiated` — injects start/finish callbacks (observer mode).
|
|
16
|
+
*/
|
|
17
|
+
export declare class FlowViewEmitter extends BaseViewEmitter<FlowEventHandlers, ParsedFlowEvent, FlowEventIdType> {
|
|
18
|
+
private adapty;
|
|
19
|
+
constructor(viewId: string, adapty: Adapty);
|
|
20
|
+
protected parseEventData(rawEventData: string, ctx: LogContext): ParsedFlowEvent;
|
|
21
|
+
protected getNativeEventForHandler(event: keyof FlowEventHandlers): FlowEventIdType | null;
|
|
22
|
+
protected getHandlerForNativeEvent(nativeEvent: FlowEventIdType, eventData: ParsedFlowEvent): keyof FlowEventHandlers | null;
|
|
23
|
+
protected extractCallbackArgs(handlerName: keyof FlowEventHandlers, eventData: ParsedFlowEvent): unknown[];
|
|
24
|
+
protected getEventViewId(eventData: ParsedFlowEvent): string | null;
|
|
25
|
+
protected getEmitterName(): string;
|
|
26
|
+
/**
|
|
27
|
+
* Routes the three asynchronous round-trip events to their dedicated
|
|
28
|
+
* handlers, bypassing the standard synchronous boolean/close dispatch.
|
|
29
|
+
*/
|
|
30
|
+
protected handleSpecialEvent(handlerName: keyof FlowEventHandlers, eventData: ParsedFlowEvent): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Permission is an async round-trip: await the client handler, then reply to
|
|
33
|
+
* native with the resulting status (defaults to `denied` on error/no handler).
|
|
34
|
+
*/
|
|
35
|
+
private handlePermissionRequest;
|
|
36
|
+
private handleObserverPurchase;
|
|
37
|
+
private handleObserverRestore;
|
|
38
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { FlowEventId, HANDLER_TO_NATIVE_EVENT, NATIVE_EVENT_RESOLVER, extractFlowCallbackArgs, filterUndefined, } from '@adapty/core';
|
|
2
|
+
import { parseFlowEvent } from '../coders/parse-flow';
|
|
3
|
+
import { Log, LogContext } from '../logger';
|
|
4
|
+
import { BaseViewEmitter } from './base-view-emitter';
|
|
5
|
+
/**
|
|
6
|
+
* FlowViewEmitter manages event handlers for flow view events.
|
|
7
|
+
* Each event type can have only one handler - new handlers replace existing ones.
|
|
8
|
+
*
|
|
9
|
+
* In addition to the standard synchronous dispatch inherited from
|
|
10
|
+
* {@link BaseViewEmitter}, this emitter handles three asynchronous round-trip
|
|
11
|
+
* events that must reply back to native:
|
|
12
|
+
* - `onRequestPermission` — awaits the async handler, then replies with the status.
|
|
13
|
+
* - `onObserverPurchaseInitiated` — injects start/finish callbacks (observer mode).
|
|
14
|
+
* - `onObserverRestoreInitiated` — injects start/finish callbacks (observer mode).
|
|
15
|
+
*/
|
|
16
|
+
export class FlowViewEmitter extends BaseViewEmitter {
|
|
17
|
+
constructor(viewId, adapty) {
|
|
18
|
+
super(viewId);
|
|
19
|
+
this.adapty = adapty;
|
|
20
|
+
}
|
|
21
|
+
parseEventData(rawEventData, ctx) {
|
|
22
|
+
const result = parseFlowEvent(rawEventData, ctx);
|
|
23
|
+
if (!result) {
|
|
24
|
+
throw new Error('Failed to parse flow event');
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
getNativeEventForHandler(event) {
|
|
29
|
+
return HANDLER_TO_NATIVE_EVENT[event];
|
|
30
|
+
}
|
|
31
|
+
getHandlerForNativeEvent(nativeEvent, eventData) {
|
|
32
|
+
var _a, _b;
|
|
33
|
+
return (_b = (_a = NATIVE_EVENT_RESOLVER[nativeEvent]) === null || _a === void 0 ? void 0 : _a.call(NATIVE_EVENT_RESOLVER, eventData)) !== null && _b !== void 0 ? _b : null;
|
|
34
|
+
}
|
|
35
|
+
extractCallbackArgs(handlerName, eventData) {
|
|
36
|
+
return extractFlowCallbackArgs(handlerName, eventData);
|
|
37
|
+
}
|
|
38
|
+
getEventViewId(eventData) {
|
|
39
|
+
var _a, _b;
|
|
40
|
+
return (_b = (_a = eventData === null || eventData === void 0 ? void 0 : eventData.view) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : null;
|
|
41
|
+
}
|
|
42
|
+
getEmitterName() {
|
|
43
|
+
return 'FlowViewEmitter';
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Routes the three asynchronous round-trip events to their dedicated
|
|
47
|
+
* handlers, bypassing the standard synchronous boolean/close dispatch.
|
|
48
|
+
*/
|
|
49
|
+
handleSpecialEvent(handlerName, eventData) {
|
|
50
|
+
if (handlerName === 'onRequestPermission' && eventData.id === FlowEventId.DidAskPermission) {
|
|
51
|
+
void this.handlePermissionRequest(eventData);
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
if (handlerName === 'onObserverPurchaseInitiated' && eventData.id === FlowEventId.ObserverDidInitiatePurchase) {
|
|
55
|
+
this.handleObserverPurchase(eventData);
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
if (handlerName === 'onObserverRestoreInitiated' && eventData.id === FlowEventId.ObserverDidInitiateRestore) {
|
|
59
|
+
this.handleObserverRestore(eventData);
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Permission is an async round-trip: await the client handler, then reply to
|
|
66
|
+
* native with the resulting status (defaults to `denied` on error/no handler).
|
|
67
|
+
*/
|
|
68
|
+
async handlePermissionRequest(event) {
|
|
69
|
+
const handlerData = this.handlers.get('onRequestPermission');
|
|
70
|
+
let status = 'denied';
|
|
71
|
+
let detail;
|
|
72
|
+
if (handlerData) {
|
|
73
|
+
try {
|
|
74
|
+
const handler = handlerData.handler;
|
|
75
|
+
const response = await handler(event.permission, event.customArgs);
|
|
76
|
+
status = response.status;
|
|
77
|
+
detail = response.detail;
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
status = 'denied';
|
|
81
|
+
Log.warn('flow_view_did_answer_permission', () => `onRequestPermission handler threw; replying denied: ${error}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const method = 'flow_view_did_answer_permission';
|
|
85
|
+
const ctx = new LogContext();
|
|
86
|
+
const log = ctx.call({ methodName: method });
|
|
87
|
+
const body = JSON.stringify(filterUndefined({
|
|
88
|
+
method,
|
|
89
|
+
event_id: event.eventId,
|
|
90
|
+
status,
|
|
91
|
+
detail,
|
|
92
|
+
}));
|
|
93
|
+
await this.adapty
|
|
94
|
+
.handleMethodCall(method, body, ctx, log)
|
|
95
|
+
.catch((error) => Log.warn(method, () => `Failed to reply to permission request: ${error}`));
|
|
96
|
+
}
|
|
97
|
+
// Pass a fully-typed request object into `send` so each literal is checked
|
|
98
|
+
// against its own `Req[...]` arm (a `satisfies` on a method-union body cannot
|
|
99
|
+
// narrow, so we type per-call instead).
|
|
100
|
+
handleObserverPurchase(event) {
|
|
101
|
+
const handlerData = this.handlers.get('onObserverPurchaseInitiated');
|
|
102
|
+
if (!handlerData)
|
|
103
|
+
return;
|
|
104
|
+
const { handler, onRequestClose } = handlerData;
|
|
105
|
+
const send = (body) => {
|
|
106
|
+
const ctx = new LogContext();
|
|
107
|
+
const log = ctx.call({ methodName: body.method });
|
|
108
|
+
void this.adapty
|
|
109
|
+
.handleMethodCall(body.method, JSON.stringify(body), ctx, log)
|
|
110
|
+
.catch((error) => Log.warn(body.method, () => `Failed observer purchase signal: ${error}`));
|
|
111
|
+
};
|
|
112
|
+
const purchaseHandler = handler;
|
|
113
|
+
try {
|
|
114
|
+
// The observer's documented way to close the view after driving the
|
|
115
|
+
// purchase: returning `true` requests dismissal (close-on-`true`).
|
|
116
|
+
const shouldClose = purchaseHandler(event.product, () => send({ method: 'observer_purchase_did_start', event_id: event.eventId }), () => send({ method: 'observer_purchase_did_finish', event_id: event.eventId }));
|
|
117
|
+
if (shouldClose) {
|
|
118
|
+
onRequestClose().catch((error) => Log.warn('observer_purchase_did_initiate', () => `Failed to close view after observer purchase: ${error}`, () => ({ error })));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
Log.warn('observer_purchase_handler_failed', () => `onObserverPurchaseInitiated handler threw: ${error}`, () => ({ error }));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
handleObserverRestore(event) {
|
|
126
|
+
const handlerData = this.handlers.get('onObserverRestoreInitiated');
|
|
127
|
+
if (!handlerData)
|
|
128
|
+
return;
|
|
129
|
+
const { handler, onRequestClose } = handlerData;
|
|
130
|
+
const send = (body) => {
|
|
131
|
+
const ctx = new LogContext();
|
|
132
|
+
const log = ctx.call({ methodName: body.method });
|
|
133
|
+
void this.adapty
|
|
134
|
+
.handleMethodCall(body.method, JSON.stringify(body), ctx, log)
|
|
135
|
+
.catch((error) => Log.warn(body.method, () => `Failed observer restore signal: ${error}`));
|
|
136
|
+
};
|
|
137
|
+
const restoreHandler = handler;
|
|
138
|
+
try {
|
|
139
|
+
// The observer's documented way to close the view after driving the
|
|
140
|
+
// restore: returning `true` requests dismissal (close-on-`true`).
|
|
141
|
+
const shouldClose = restoreHandler(() => send({ method: 'observer_restore_did_start', event_id: event.eventId }), () => send({ method: 'observer_restore_did_finish', event_id: event.eventId }));
|
|
142
|
+
if (shouldClose) {
|
|
143
|
+
onRequestClose().catch((error) => Log.warn('observer_restore_did_initiate', () => `Failed to close view after observer restore: ${error}`, () => ({ error })));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
Log.warn('observer_restore_handler_failed', () => `onObserverRestoreInitiated handler threw: ${error}`, () => ({ error }));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=flow-view-emitter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow-view-emitter.js","sourceRoot":"","sources":["../../../src/ui-builder/flow-view-emitter.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n FlowEventId,\n HANDLER_TO_NATIVE_EVENT,\n NATIVE_EVENT_RESOLVER,\n extractFlowCallbackArgs,\n filterUndefined,\n} from '@adapty/core';\nimport type { FlowEventIdType, ParsedFlowEvent, FlowPermissionStatus } from '@adapty/core';\n\nimport type { Adapty } from '../adapty';\nimport { parseFlowEvent } from '../coders/parse-flow';\nimport { Log, LogContext } from '../logger';\nimport type { components } from '../types/api';\n\nimport { BaseViewEmitter } from './base-view-emitter';\nimport type { FlowEventHandlers } from './types';\n\ntype Req = components['requests'];\n\n/**\n * FlowViewEmitter manages event handlers for flow view events.\n * Each event type can have only one handler - new handlers replace existing ones.\n *\n * In addition to the standard synchronous dispatch inherited from\n * {@link BaseViewEmitter}, this emitter handles three asynchronous round-trip\n * events that must reply back to native:\n * - `onRequestPermission` — awaits the async handler, then replies with the status.\n * - `onObserverPurchaseInitiated` — injects start/finish callbacks (observer mode).\n * - `onObserverRestoreInitiated` — injects start/finish callbacks (observer mode).\n */\nexport class FlowViewEmitter extends BaseViewEmitter<FlowEventHandlers, ParsedFlowEvent, FlowEventIdType> {\n private adapty: Adapty;\n\n constructor(viewId: string, adapty: Adapty) {\n super(viewId);\n this.adapty = adapty;\n }\n\n protected parseEventData(rawEventData: string, ctx: LogContext): ParsedFlowEvent {\n const result = parseFlowEvent(rawEventData, ctx);\n if (!result) {\n throw new Error('Failed to parse flow event');\n }\n return result;\n }\n\n protected getNativeEventForHandler(event: keyof FlowEventHandlers): FlowEventIdType | null {\n return HANDLER_TO_NATIVE_EVENT[event];\n }\n\n protected getHandlerForNativeEvent(\n nativeEvent: FlowEventIdType,\n eventData: ParsedFlowEvent,\n ): keyof FlowEventHandlers | null {\n return NATIVE_EVENT_RESOLVER[nativeEvent]?.(eventData) ?? null;\n }\n\n protected extractCallbackArgs(handlerName: keyof FlowEventHandlers, eventData: ParsedFlowEvent): unknown[] {\n return extractFlowCallbackArgs(handlerName, eventData);\n }\n\n protected getEventViewId(eventData: ParsedFlowEvent): string | null {\n return eventData?.view?.id ?? null;\n }\n\n protected getEmitterName(): string {\n return 'FlowViewEmitter';\n }\n\n /**\n * Routes the three asynchronous round-trip events to their dedicated\n * handlers, bypassing the standard synchronous boolean/close dispatch.\n */\n protected override handleSpecialEvent(handlerName: keyof FlowEventHandlers, eventData: ParsedFlowEvent): boolean {\n if (handlerName === 'onRequestPermission' && eventData.id === FlowEventId.DidAskPermission) {\n void this.handlePermissionRequest(eventData);\n return true;\n }\n if (handlerName === 'onObserverPurchaseInitiated' && eventData.id === FlowEventId.ObserverDidInitiatePurchase) {\n this.handleObserverPurchase(eventData);\n return true;\n }\n if (handlerName === 'onObserverRestoreInitiated' && eventData.id === FlowEventId.ObserverDidInitiateRestore) {\n this.handleObserverRestore(eventData);\n return true;\n }\n return false;\n }\n\n /**\n * Permission is an async round-trip: await the client handler, then reply to\n * native with the resulting status (defaults to `denied` on error/no handler).\n */\n private async handlePermissionRequest(\n event: Extract<ParsedFlowEvent, { id: typeof FlowEventId.DidAskPermission }>,\n ): Promise<void> {\n const handlerData = this.handlers.get('onRequestPermission');\n let status: FlowPermissionStatus = 'denied';\n let detail: string | undefined;\n if (handlerData) {\n try {\n const handler = handlerData.handler as FlowEventHandlers['onRequestPermission'];\n const response = await handler(event.permission, event.customArgs);\n status = response.status;\n detail = response.detail;\n } catch (error) {\n status = 'denied';\n Log.warn(\n 'flow_view_did_answer_permission',\n () => `onRequestPermission handler threw; replying denied: ${error}`,\n );\n }\n }\n const method = 'flow_view_did_answer_permission';\n const ctx = new LogContext();\n const log = ctx.call({ methodName: method });\n const body = JSON.stringify(\n filterUndefined({\n method,\n event_id: event.eventId,\n status,\n detail,\n } satisfies Req['FlowViewDidAnswerPermission.Request']),\n );\n await this.adapty\n .handleMethodCall(method, body, ctx, log)\n .catch((error) => Log.warn(method, () => `Failed to reply to permission request: ${error}`));\n }\n\n // Pass a fully-typed request object into `send` so each literal is checked\n // against its own `Req[...]` arm (a `satisfies` on a method-union body cannot\n // narrow, so we type per-call instead).\n private handleObserverPurchase(\n event: Extract<ParsedFlowEvent, { id: typeof FlowEventId.ObserverDidInitiatePurchase }>,\n ): void {\n const handlerData = this.handlers.get('onObserverPurchaseInitiated');\n if (!handlerData) return;\n const { handler, onRequestClose } = handlerData;\n const send = (body: Req['ObserverPurchaseDidStart.Request'] | Req['ObserverPurchaseDidFinish.Request']) => {\n const ctx = new LogContext();\n const log = ctx.call({ methodName: body.method });\n void this.adapty\n .handleMethodCall(body.method, JSON.stringify(body), ctx, log)\n .catch((error) => Log.warn(body.method, () => `Failed observer purchase signal: ${error}`));\n };\n const purchaseHandler = handler as FlowEventHandlers['onObserverPurchaseInitiated'];\n try {\n // The observer's documented way to close the view after driving the\n // purchase: returning `true` requests dismissal (close-on-`true`).\n const shouldClose = purchaseHandler(\n event.product,\n () => send({ method: 'observer_purchase_did_start', event_id: event.eventId }),\n () => send({ method: 'observer_purchase_did_finish', event_id: event.eventId }),\n );\n if (shouldClose) {\n onRequestClose().catch((error) =>\n Log.warn(\n 'observer_purchase_did_initiate',\n () => `Failed to close view after observer purchase: ${error}`,\n () => ({ error }),\n ),\n );\n }\n } catch (error) {\n Log.warn(\n 'observer_purchase_handler_failed',\n () => `onObserverPurchaseInitiated handler threw: ${error}`,\n () => ({ error }),\n );\n }\n }\n\n private handleObserverRestore(\n event: Extract<ParsedFlowEvent, { id: typeof FlowEventId.ObserverDidInitiateRestore }>,\n ): void {\n const handlerData = this.handlers.get('onObserverRestoreInitiated');\n if (!handlerData) return;\n const { handler, onRequestClose } = handlerData;\n const send = (body: Req['ObserverRestoreDidStart.Request'] | Req['ObserverRestoreDidFinish.Request']) => {\n const ctx = new LogContext();\n const log = ctx.call({ methodName: body.method });\n void this.adapty\n .handleMethodCall(body.method, JSON.stringify(body), ctx, log)\n .catch((error) => Log.warn(body.method, () => `Failed observer restore signal: ${error}`));\n };\n const restoreHandler = handler as FlowEventHandlers['onObserverRestoreInitiated'];\n try {\n // The observer's documented way to close the view after driving the\n // restore: returning `true` requests dismissal (close-on-`true`).\n const shouldClose = restoreHandler(\n () => send({ method: 'observer_restore_did_start', event_id: event.eventId }),\n () => send({ method: 'observer_restore_did_finish', event_id: event.eventId }),\n );\n if (shouldClose) {\n onRequestClose().catch((error) =>\n Log.warn(\n 'observer_restore_did_initiate',\n () => `Failed to close view after observer restore: ${error}`,\n () => ({ error }),\n ),\n );\n }\n } catch (error) {\n Log.warn(\n 'observer_restore_handler_failed',\n () => `onObserverRestoreInitiated handler threw: ${error}`,\n () => ({ error }),\n );\n }\n }\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { FlowViewController } from './flow-view-controller';
|
|
2
2
|
export { OnboardingViewController } from './onboarding-view-controller';
|
|
3
|
-
export type {
|
|
4
|
-
export type { AdaptyCustomAsset, AdaptyUiView, AdaptyUiDialogConfig,
|
|
5
|
-
export
|
|
6
|
-
export {
|
|
3
|
+
export type { CreateFlowViewParamsInput, CreateOnboardingViewParamsInput } from './types';
|
|
4
|
+
export type { AdaptyCustomAsset, AdaptyUiView, AdaptyUiDialogConfig, AdaptyIOSPresentationStyle, FlowEventHandlers, OnboardingEventHandlers, AdaptyPermission, FlowPermissionResponse, FlowPermissionStatus, } from './types';
|
|
5
|
+
export { AdaptyUiDialogActionType } from './types';
|
|
6
|
+
export { createFlowView } from './create-flow-view';
|
|
7
7
|
export { createOnboardingView } from './create-onboarding-view';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Export UI types and classes for convenience
|
|
2
|
-
export {
|
|
2
|
+
export { FlowViewController } from './flow-view-controller';
|
|
3
3
|
export { OnboardingViewController } from './onboarding-view-controller';
|
|
4
|
-
export {
|
|
4
|
+
export { AdaptyUiDialogActionType } from './types';
|
|
5
|
+
export { createFlowView } from './create-flow-view';
|
|
5
6
|
export { createOnboardingView } from './create-onboarding-view';
|
|
6
7
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ui-builder/index.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ui-builder/index.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAaxE,OAAO,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAC;AAEnD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC","sourcesContent":["// Export UI types and classes for convenience\nexport { FlowViewController } from './flow-view-controller';\nexport { OnboardingViewController } from './onboarding-view-controller';\nexport type { CreateFlowViewParamsInput, CreateOnboardingViewParamsInput } from './types';\nexport type {\n AdaptyCustomAsset,\n AdaptyUiView,\n AdaptyUiDialogConfig,\n AdaptyIOSPresentationStyle,\n FlowEventHandlers,\n OnboardingEventHandlers,\n AdaptyPermission,\n FlowPermissionResponse,\n FlowPermissionStatus,\n} from './types';\nexport { AdaptyUiDialogActionType } from './types';\n\nexport { createFlowView } from './create-flow-view';\nexport { createOnboardingView } from './create-onboarding-view';\n"]}
|
|
@@ -5,6 +5,8 @@ export declare const DEFAULT_ONBOARDING_PARAMS: CreateOnboardingViewParamsInput;
|
|
|
5
5
|
/**
|
|
6
6
|
* Controller for managing onboarding views.
|
|
7
7
|
*
|
|
8
|
+
* @deprecated Since 4.0.0. Migrate onboardings to the Flow Builder API.
|
|
9
|
+
*
|
|
8
10
|
* @remarks
|
|
9
11
|
* This class provides methods to present, dismiss, and handle events for onboarding views
|
|
10
12
|
* created with the Onboarding Builder. Create instances using the {@link createOnboardingView} function
|
|
@@ -26,7 +28,7 @@ export declare class OnboardingViewController {
|
|
|
26
28
|
/**
|
|
27
29
|
* Since constructors in JS cannot be async, it is not
|
|
28
30
|
* preferred to create ViewControllers in direct way.
|
|
29
|
-
* Consider using @link
|
|
31
|
+
* Consider using {@link OnboardingViewController.create} instead
|
|
30
32
|
*
|
|
31
33
|
* @internal
|
|
32
34
|
*/
|
|
@@ -116,11 +118,11 @@ export declare class OnboardingViewController {
|
|
|
116
118
|
* // Return true to keep default closing behavior
|
|
117
119
|
* return true;
|
|
118
120
|
* },
|
|
119
|
-
*
|
|
120
|
-
* console.log('
|
|
121
|
+
* onCustom: (actionId, meta) => {
|
|
122
|
+
* console.log('Custom action:', actionId, meta);
|
|
121
123
|
* },
|
|
122
|
-
*
|
|
123
|
-
* console.log('
|
|
124
|
+
* onStateUpdated: (action, meta) => {
|
|
125
|
+
* console.log('State updated:', action, meta);
|
|
124
126
|
* }
|
|
125
127
|
* });
|
|
126
128
|
*
|
|
@@ -11,6 +11,8 @@ export const DEFAULT_ONBOARDING_PARAMS = {
|
|
|
11
11
|
/**
|
|
12
12
|
* Controller for managing onboarding views.
|
|
13
13
|
*
|
|
14
|
+
* @deprecated Since 4.0.0. Migrate onboardings to the Flow Builder API.
|
|
15
|
+
*
|
|
14
16
|
* @remarks
|
|
15
17
|
* This class provides methods to present, dismiss, and handle events for onboarding views
|
|
16
18
|
* created with the Onboarding Builder. Create instances using the {@link createOnboardingView} function
|
|
@@ -43,7 +45,7 @@ export class OnboardingViewController {
|
|
|
43
45
|
/**
|
|
44
46
|
* Since constructors in JS cannot be async, it is not
|
|
45
47
|
* preferred to create ViewControllers in direct way.
|
|
46
|
-
* Consider using @link
|
|
48
|
+
* Consider using {@link OnboardingViewController.create} instead
|
|
47
49
|
*
|
|
48
50
|
* @internal
|
|
49
51
|
*/
|
|
@@ -172,11 +174,11 @@ export class OnboardingViewController {
|
|
|
172
174
|
* // Return true to keep default closing behavior
|
|
173
175
|
* return true;
|
|
174
176
|
* },
|
|
175
|
-
*
|
|
176
|
-
* console.log('
|
|
177
|
+
* onCustom: (actionId, meta) => {
|
|
178
|
+
* console.log('Custom action:', actionId, meta);
|
|
177
179
|
* },
|
|
178
|
-
*
|
|
179
|
-
* console.log('
|
|
180
|
+
* onStateUpdated: (action, meta) => {
|
|
181
|
+
* console.log('State updated:', action, meta);
|
|
180
182
|
* }
|
|
181
183
|
* });
|
|
182
184
|
*
|