react-native-insider 8.1.0 → 9.0.0-nh

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 (48) hide show
  1. package/README.md +89 -660
  2. package/RNInsider.podspec +33 -12
  3. package/android/build.gradle +16 -7
  4. package/android/src/main/java/com/useinsider/react/RNInsiderAppFrameViewManager.java +253 -0
  5. package/android/src/main/java/com/useinsider/react/RNInsiderModule.java +238 -178
  6. package/android/src/main/java/com/useinsider/react/RNInsiderPackage.java +5 -1
  7. package/android/src/main/java/com/useinsider/react/RNUtils.java +111 -0
  8. package/babel.config.js +4 -1
  9. package/index.d.ts +196 -161
  10. package/index.js +60 -11
  11. package/ios/RNInsider/RNInsider.m +245 -341
  12. package/ios/RNInsider/RNInsiderAppFrameView.h +33 -0
  13. package/ios/RNInsider/RNInsiderAppFrameView.mm +315 -0
  14. package/ios/RNInsider/RNNotificationHandler.h +4 -1
  15. package/ios/RNInsider/RNNotificationHandler.m +16 -18
  16. package/ios/RNInsider/RNUtils.h +2 -0
  17. package/ios/RNInsider/RNUtils.m +79 -0
  18. package/package.json +43 -12
  19. package/src/EventHandler.js +17 -0
  20. package/src/InsiderAppFrame.d.ts +45 -0
  21. package/src/InsiderAppFrame.js +265 -0
  22. package/src/InsiderAppFrameLogic.d.ts +35 -0
  23. package/src/InsiderAppFrameLogic.js +192 -0
  24. package/src/InsiderAppFrameStatus.d.ts +22 -0
  25. package/src/InsiderAppFrameStatus.js +35 -0
  26. package/src/InsiderAppFramesError.d.ts +25 -0
  27. package/src/InsiderAppFramesError.js +77 -0
  28. package/src/InsiderEvent.js +1 -0
  29. package/src/InsiderEvents.d.ts +93 -0
  30. package/src/InsiderEvents.js +124 -0
  31. package/src/InsiderIdentifier.js +1 -1
  32. package/src/InsiderUser.d.ts +3 -2
  33. package/src/InsiderUser.js +27 -0
  34. package/src/Util.js +2 -1
  35. package/src/specs/InsiderAppFrameNativeComponent.ts +54 -0
  36. package/.orbit-output/config/MASTER.md +0 -129
  37. package/.orbit-output/config/architecture/API_DOCS.md +0 -342
  38. package/.orbit-output/config/architecture/ARCHITECTURE.md +0 -209
  39. package/.orbit-output/config/architecture/CODE-CATALOG.md +0 -172
  40. package/.orbit-output/config/architecture/project.yaml +0 -138
  41. package/.orbit-output/config/automation/AUTOMATION-SETUP.md +0 -50
  42. package/.orbit-output/config/enforcement/ENFORCEMENT-RULES.md +0 -41
  43. package/.orbit-output/config/enforcement/validate.sh +0 -49
  44. package/.orbit-output/skills/custom/academy-doc-generator.md +0 -598
  45. package/android/.settings/org.eclipse.buildship.core.prefs +0 -13
  46. package/android/src/test/java/com/useinsider/react/RNInsiderModuleTest.java +0 -169
  47. package/docs/app-cards/app-cards_new.md +0 -569
  48. package/ios/RNInsiderTests/RNInsiderTriggerPushProcessTests.m +0 -91
@@ -0,0 +1,265 @@
1
+ import React, {
2
+ forwardRef,
3
+ useCallback,
4
+ useEffect,
5
+ useImperativeHandle,
6
+ useRef,
7
+ useState,
8
+ } from 'react';
9
+ import {Platform} from 'react-native';
10
+
11
+ import InsiderAppFrameNativeComponent from './specs/InsiderAppFrameNativeComponent';
12
+ import {InsiderAppFramesError} from './InsiderAppFramesError';
13
+ import {InsiderAppFrameStatus} from './InsiderAppFrameStatus';
14
+ import {
15
+ resolveFrameStyle,
16
+ parseActionData,
17
+ normalizeStatus,
18
+ collapsesHeight,
19
+ isErrorStatus,
20
+ shouldWarnAboutUnsupportedReactNative,
21
+ APP_FRAMES_MIN_IOS_REACT_NATIVE_MINOR,
22
+ } from './InsiderAppFrameLogic';
23
+
24
+ let hasWarnedAboutUnsupportedReactNative = false;
25
+
26
+ /**
27
+ * Whether the native component can exist in this app. Read on every render rather than cached at
28
+ * module scope: it is a few property reads, and a cached answer would outlive the host it was
29
+ * read from in tests.
30
+ */
31
+ function readHostSupport() {
32
+ // Read from Platform: a deep import of `ReactNativeVersion` triggers React Native's
33
+ // deep-import deprecation warning in the consumer's logs. Optional-chained because some
34
+ // Platform stubs have no `constants`.
35
+ const reactNativeVersion = Platform.constants?.reactNativeVersion ?? null;
36
+
37
+ // Absent under the legacy architecture, where the iOS component is not compiled at all.
38
+ const isFabricEnabled = typeof global !== 'undefined' && global.nativeFabricUIManager != null;
39
+
40
+ return {
41
+ reactNativeVersion,
42
+ isFabricEnabled,
43
+ isUnsupported: shouldWarnAboutUnsupportedReactNative(
44
+ Platform.OS,
45
+ reactNativeVersion,
46
+ isFabricEnabled,
47
+ ),
48
+ // Narrower than isUnsupported: only the legacy architecture makes rendering throw. The
49
+ // version arm stays a warning, since Fabric below 0.77 fails without throwing.
50
+ cannotRender: Platform.OS === 'ios' && !isFabricEnabled,
51
+ };
52
+ }
53
+
54
+ function warnOnceIfComponentCannotRegister({
55
+ reactNativeVersion,
56
+ isFabricEnabled,
57
+ isUnsupported,
58
+ cannotRender,
59
+ }) {
60
+ if (!__DEV__ || hasWarnedAboutUnsupportedReactNative || !isUnsupported) {
61
+ return;
62
+ }
63
+
64
+ hasWarnedAboutUnsupportedReactNative = true;
65
+ const versionLabel = reactNativeVersion
66
+ ? `${reactNativeVersion.major}.${reactNativeVersion.minor}.${reactNativeVersion.patch}`
67
+ : 'an unknown version';
68
+ const minimumLabel = `0.${APP_FRAMES_MIN_IOS_REACT_NATIVE_MINOR}`;
69
+ const isBelowMinimum =
70
+ reactNativeVersion?.major === 0 &&
71
+ reactNativeVersion.minor < APP_FRAMES_MIN_IOS_REACT_NATIVE_MINOR;
72
+
73
+ console.warn(
74
+ `[Insider] InsiderAppFrame requires React Native >= 0.${APP_FRAMES_MIN_IOS_REACT_NATIVE_MINOR} ` +
75
+ 'with the New Architecture enabled on iOS. This app is on ' +
76
+ `${versionLabel}${isFabricEnabled ? '' : ' with the New Architecture off'}. ` +
77
+ (cannotRender
78
+ ? 'Rendering the native component would throw there, so the frame renders nothing and ' +
79
+ 'emits no events. Enable the New Architecture' +
80
+ (isBelowMinimum ? ` and upgrade to React Native >= ${minimumLabel}` : '') +
81
+ ' to use InsiderAppFrame on iOS. '
82
+ : `Below 0.${APP_FRAMES_MIN_IOS_REACT_NATIVE_MINOR} the native component is not ` +
83
+ 'guaranteed to register; if it does not, the frame shows no content and emits no ' +
84
+ 'events. Upgrade React Native to use InsiderAppFrame on iOS. ') +
85
+ 'The rest of the Insider SDK is unaffected.',
86
+ );
87
+ }
88
+
89
+ /**
90
+ * An empty or non-string `placementId` reaches native as no placement, which lands on the
91
+ * collapsing `NO_PLACEMENT` status with no error — indistinguishable from an empty placement.
92
+ * Warned per offending value, since two different broken placements are two different bugs.
93
+ */
94
+ function warnIfPlacementIdUnusable(placementId) {
95
+ if (!__DEV__ || (typeof placementId === 'string' && placementId)) {
96
+ return;
97
+ }
98
+
99
+ console.warn(
100
+ `[Insider] InsiderAppFrame was given a placementId of ${JSON.stringify(placementId)}, which ` +
101
+ 'is not a non-empty string. Native has no placement to resolve, so the frame collapses to ' +
102
+ 'nothing without reporting an error. Check the value reaching the prop.',
103
+ );
104
+ }
105
+
106
+ let hasWarnedAboutDisabledAppFrames = false;
107
+
108
+ /**
109
+ * App Frames is off by default in the native SDK. DISABLED has exactly one cause — the app-wide
110
+ * opt-in flag has not reached the SDK — and React Native developers rarely see the native log
111
+ * line that names it, so it is surfaced here.
112
+ */
113
+ function warnOnceAboutDisabledAppFrames(placementId) {
114
+ if (!__DEV__ || hasWarnedAboutDisabledAppFrames) {
115
+ return;
116
+ }
117
+
118
+ hasWarnedAboutDisabledAppFrames = true;
119
+ console.warn(
120
+ `[Insider] InsiderAppFrame got status DISABLED for placement "${placementId}", so it will ` +
121
+ 'render nothing.\n\nMost often this means the app has not opted in to App Frames, which is ' +
122
+ 'off by default in the native SDK:\n\n' +
123
+ ' Android — AndroidManifest.xml, inside <application>:\n' +
124
+ ' <meta-data android:name="com.useinsider.insider.APP_FRAMES_ENABLED" android:value="true" />\n\n' +
125
+ ' iOS — Info.plist:\n' +
126
+ ' <key>Insider</key>\n' +
127
+ ' <dict><key>App Frames Enabled</key><true/></dict>\n\n' +
128
+ 'This status has no other cause, so if the app does opt in, the flag is not reaching the ' +
129
+ 'SDK — check for a nested plist key, a manifest-merger drop, or a misspelled meta-data ' +
130
+ 'name. Shown once, in development only.',
131
+ );
132
+ }
133
+
134
+ /**
135
+ * Renders an inline App Frames placement.
136
+ *
137
+ * The height the native view reports is applied here because Yoga assigns an
138
+ * exact frame and leaves the native self-sizing paths inert. An explicit
139
+ * `height` in `style` still wins, matching the precedence rule both native
140
+ * SDKs use.
141
+ */
142
+ function InsiderAppFrame(
143
+ {
144
+ placementId,
145
+ style,
146
+ onStatusChange,
147
+ onLoadError,
148
+ onHeightChange,
149
+ onDismissRequest,
150
+ onAction,
151
+ ...rest
152
+ },
153
+ ref,
154
+ ) {
155
+ // `undefined` means no height has been reported yet, so the caller's own `flex`/`aspectRatio`
156
+ // applies while loading instead of a forced zero height.
157
+ //
158
+ // Intentionally not reset on a `placementId` change: native keeps its own last reported height
159
+ // across a swap and suppresses a report equal to it, so zeroing here would leave a new placement
160
+ // of the same height in a zero-height box.
161
+ const [reportedHeight, setReportedHeight] = useState(undefined);
162
+
163
+ // Refs rather than state so `getStatus()`/`getError()` already return the new value inside the
164
+ // corresponding callback, as reading the native view does.
165
+ const statusRef = useRef(InsiderAppFrameStatus.DETACHED);
166
+ const errorRef = useRef(null);
167
+
168
+ useImperativeHandle(
169
+ ref,
170
+ () => ({
171
+ getStatus: () => statusRef.current,
172
+ getError: () => errorRef.current,
173
+ }),
174
+ [],
175
+ );
176
+
177
+ const hostSupport = readHostSupport();
178
+
179
+ useEffect(() => {
180
+ warnOnceIfComponentCannotRegister(hostSupport);
181
+ // Mount-only: the host cannot change while the app runs.
182
+ }, []);
183
+
184
+ useEffect(() => {
185
+ // Block body: a returned value would be treated by React as a cleanup function.
186
+ warnIfPlacementIdUnusable(placementId);
187
+ }, [placementId]);
188
+
189
+ const handleHeightChange = useCallback(
190
+ event => {
191
+ const {height} = event.nativeEvent;
192
+ setReportedHeight(height);
193
+ onHeightChange?.(height);
194
+ },
195
+ [onHeightChange],
196
+ );
197
+
198
+ const handleLoadError = useCallback(
199
+ event => {
200
+ const nextError = InsiderAppFramesError.from(event.nativeEvent);
201
+ errorRef.current = nextError;
202
+ onLoadError?.(nextError);
203
+ },
204
+ [onLoadError],
205
+ );
206
+
207
+ const handleAction = useCallback(
208
+ event => onAction?.(parseActionData(event.nativeEvent.data)),
209
+ [onAction],
210
+ );
211
+
212
+ const handleStatusChange = useCallback(
213
+ event => {
214
+ const {status: rawStatus, previousStatus} = event.nativeEvent;
215
+ const nextStatus = normalizeStatus(rawStatus);
216
+
217
+ statusRef.current = nextStatus;
218
+
219
+ // Native fires the error event before the status event, so an ERROR_* status must keep
220
+ // the error it just received; any other status clears it.
221
+ if (!isErrorStatus(nextStatus)) {
222
+ errorRef.current = null;
223
+ }
224
+
225
+ // Native zeroes its own size on collapse without firing a height event.
226
+ if (collapsesHeight(nextStatus)) {
227
+ setReportedHeight(0);
228
+ }
229
+
230
+ if (nextStatus === InsiderAppFrameStatus.DISABLED) {
231
+ warnOnceAboutDisabledAppFrames(placementId);
232
+ }
233
+
234
+ onStatusChange?.(nextStatus, normalizeStatus(previousStatus));
235
+ },
236
+ [onStatusChange, placementId],
237
+ );
238
+
239
+ const handleDismissRequest = useCallback(
240
+ () => onDismissRequest?.(),
241
+ [onDismissRequest],
242
+ );
243
+
244
+ // Rendering the native component here is not inert: under the legacy architecture React Native
245
+ // throws resolving its view config ("not found in the UIManager"), which unmounts the host's
246
+ // whole tree. After every hook, so the ref handle still answers DETACHED.
247
+ if (hostSupport.cannotRender) {
248
+ return null;
249
+ }
250
+
251
+ return (
252
+ <InsiderAppFrameNativeComponent
253
+ {...rest}
254
+ placementId={placementId}
255
+ style={resolveFrameStyle(style, reportedHeight)}
256
+ onStatusChange={handleStatusChange}
257
+ onLoadError={handleLoadError}
258
+ onHeightChange={handleHeightChange}
259
+ onDismissRequest={handleDismissRequest}
260
+ onAction={handleAction}
261
+ />
262
+ );
263
+ }
264
+
265
+ export default forwardRef(InsiderAppFrame);
@@ -0,0 +1,35 @@
1
+ import type {InsiderAppFrameStatusType} from './InsiderAppFrameStatus';
2
+ import type {StyleProp, ViewStyle} from 'react-native';
3
+
4
+ export declare function flattenStyle(style: StyleProp<ViewStyle>): ViewStyle;
5
+ export declare function hasExplicitHeight(style: StyleProp<ViewStyle>): boolean;
6
+ /** Applies the reported height to `style` unless `style` already sets `height`. */
7
+ export declare function resolveFrameStyle(
8
+ style: StyleProp<ViewStyle>,
9
+ reportedHeight: number | undefined,
10
+ ): StyleProp<ViewStyle>;
11
+ /** Parses an action payload; returns `{}` for malformed input instead of throwing. */
12
+ export declare function parseActionData(raw: string | undefined): Record<string, unknown>;
13
+ /** Maps a status string onto `InsiderAppFrameStatus`, falling back to `UNRECOGNIZED`. */
14
+ export declare function normalizeStatus(status: unknown): InsiderAppFrameStatusType;
15
+
16
+ /** Statuses in which the frame collapses to zero height. */
17
+ export declare const COLLAPSING_STATUSES: readonly InsiderAppFrameStatusType[];
18
+ export declare function collapsesHeight(status: InsiderAppFrameStatusType): boolean;
19
+
20
+ /** The three `ERROR_*` statuses. `UNAVAILABLE` and `DISABLED` are resting states, not errors. */
21
+ export declare const ERROR_STATUSES: readonly InsiderAppFrameStatusType[];
22
+ export declare function isErrorStatus(status: InsiderAppFrameStatusType): boolean;
23
+
24
+ /** The in-flight loading statuses: `RESOLVING`, `DOWNLOADING` and `RENDERING`. */
25
+ export declare const LOADING_STATUSES: readonly InsiderAppFrameStatusType[];
26
+ export declare function isLoadingStatus(status: InsiderAppFrameStatusType): boolean;
27
+
28
+ /** Minimum React Native minor version (0.x) that supports App Frames on iOS. */
29
+ export declare const APP_FRAMES_MIN_IOS_REACT_NATIVE_MINOR: number;
30
+ /** True when App Frames cannot render on this platform and React Native setup. */
31
+ export declare function shouldWarnAboutUnsupportedReactNative(
32
+ platformOS: string,
33
+ version: {major: number; minor: number; patch: number} | null | undefined,
34
+ isFabricEnabled?: boolean,
35
+ ): boolean;
@@ -0,0 +1,192 @@
1
+ import {InsiderAppFrameStatus} from './InsiderAppFrameStatus';
2
+
3
+ // Kept free of React and react-native imports so these helpers test under plain Jest.
4
+
5
+ function flattenStyle(style) {
6
+ if (!style) {
7
+ return {};
8
+ }
9
+
10
+ if (Array.isArray(style)) {
11
+ return style.reduce(
12
+ (accumulator, entry) => Object.assign(accumulator, flattenStyle(entry)),
13
+ {},
14
+ );
15
+ }
16
+
17
+ // Unlike React Native's `flattenStyle`, non-objects (numeric style refs, strings) yield `{}`
18
+ // rather than `undefined`, so `hasExplicitHeight` can dereference the result and a string is
19
+ // not spread into indexed keys.
20
+ if (typeof style !== 'object') {
21
+ return {};
22
+ }
23
+
24
+ return Object.assign({}, style);
25
+ }
26
+
27
+ function hasExplicitHeight(style) {
28
+ return flattenStyle(style).height !== undefined;
29
+ }
30
+
31
+ /**
32
+ * Applies the native-reported height unless the caller declared one. Yoga assigns every native
33
+ * view an exact frame, which makes the native self-sizing paths inert.
34
+ *
35
+ * With no height reported yet (`undefined`, unlike a legitimate `0`) the style is returned
36
+ * untouched, so the caller's own `flex`/`aspectRatio` takes effect.
37
+ */
38
+ function resolveFrameStyle(style, reportedHeight) {
39
+ if (reportedHeight === undefined || hasExplicitHeight(style)) {
40
+ return style;
41
+ }
42
+
43
+ return [style, {height: reportedHeight}];
44
+ }
45
+
46
+ /**
47
+ * Maps a bridge status string onto `InsiderAppFrameStatus`, falling back to `UNRECOGNIZED`.
48
+ * Android forwards native enum names verbatim, so a newer SDK can send a member this package
49
+ * does not know; normalising keeps the value inside the typed union.
50
+ */
51
+ function normalizeStatus(status) {
52
+ if (Object.prototype.hasOwnProperty.call(InsiderAppFrameStatus, status)) {
53
+ return InsiderAppFrameStatus[status];
54
+ }
55
+
56
+ // The Android bridge does not log the raw value, so this is the only place it is visible.
57
+ if (__DEV__) {
58
+ console.warn(
59
+ `[Insider] InsiderAppFrame received an unrecognised status ${JSON.stringify(status)} and ` +
60
+ 'mapped it to UNRECOGNIZED. This usually means the native SDK is newer than this ' +
61
+ 'package. Everything still works; the status is simply one this version cannot name.',
62
+ );
63
+ }
64
+
65
+ return InsiderAppFrameStatus.UNRECOGNIZED;
66
+ }
67
+
68
+ /**
69
+ * Statuses on which the frame occupies no space. Native zeroes its own size on these without
70
+ * firing a height event, so the height applied from JS must be zeroed too. Matches both native
71
+ * implementations, which also collapse on DISABLED.
72
+ *
73
+ * DETACHED is absent so a frame scrolled off-window keeps its height and does not jump when it
74
+ * returns.
75
+ */
76
+ const COLLAPSING_STATUSES = Object.freeze([
77
+ InsiderAppFrameStatus.NO_PLACEMENT,
78
+ InsiderAppFrameStatus.DISABLED,
79
+ InsiderAppFrameStatus.UNAVAILABLE,
80
+ InsiderAppFrameStatus.DISMISSED,
81
+ InsiderAppFrameStatus.ERROR_RESOLVING,
82
+ InsiderAppFrameStatus.ERROR_DOWNLOADING,
83
+ InsiderAppFrameStatus.ERROR_RENDERING,
84
+ ]);
85
+
86
+ function collapsesHeight(status) {
87
+ return COLLAPSING_STATUSES.indexOf(status) !== -1;
88
+ }
89
+
90
+ /**
91
+ * The statuses that mean a step failed. `UNAVAILABLE` and `DISABLED` are resting states, not
92
+ * failures.
93
+ */
94
+ const ERROR_STATUSES = Object.freeze([
95
+ InsiderAppFrameStatus.ERROR_RESOLVING,
96
+ InsiderAppFrameStatus.ERROR_DOWNLOADING,
97
+ InsiderAppFrameStatus.ERROR_RENDERING,
98
+ ]);
99
+
100
+ function isErrorStatus(status) {
101
+ return ERROR_STATUSES.indexOf(status) !== -1;
102
+ }
103
+
104
+ /**
105
+ * The in-flight loading phases. A free function rather than a member of
106
+ * `InsiderAppFrameStatus`, which `normalizeStatus` looks up by key.
107
+ */
108
+ const LOADING_STATUSES = Object.freeze([
109
+ InsiderAppFrameStatus.RESOLVING,
110
+ InsiderAppFrameStatus.DOWNLOADING,
111
+ InsiderAppFrameStatus.RENDERING,
112
+ ]);
113
+
114
+ function isLoadingStatus(status) {
115
+ return LOADING_STATUSES.indexOf(status) !== -1;
116
+ }
117
+
118
+ /**
119
+ * The first React Native release whose codegen reads `codegenConfig.ios.componentProvider`.
120
+ */
121
+ const APP_FRAMES_MIN_IOS_REACT_NATIVE_MINOR = 77;
122
+
123
+ /**
124
+ * True when App Frames is unsupported on the running iOS host: without the New Architecture the
125
+ * frame renders nothing, and below 0.77 it may not register. Either way it warrants a warning.
126
+ *
127
+ * Returns false on an unreadable version rather than guessing.
128
+ */
129
+ function shouldWarnAboutUnsupportedReactNative(platformOS, version, isFabricEnabled) {
130
+ if (platformOS !== 'ios') {
131
+ return false;
132
+ }
133
+
134
+ // The iOS component exists only under the New Architecture, whatever the React Native version.
135
+ // Explicitly `false` only: an undetermined value must not produce a warning.
136
+ if (isFabricEnabled === false) {
137
+ return true;
138
+ }
139
+
140
+ if (
141
+ !version ||
142
+ typeof version.major !== 'number' ||
143
+ typeof version.minor !== 'number'
144
+ ) {
145
+ return false;
146
+ }
147
+
148
+ if (version.major > 0) {
149
+ return false;
150
+ }
151
+
152
+ return version.minor < APP_FRAMES_MIN_IOS_REACT_NATIVE_MINOR;
153
+ }
154
+
155
+ function warnAboutUnusableActionData(raw) {
156
+ if (__DEV__) {
157
+ console.warn(
158
+ '[Insider] InsiderAppFrame received action data that is not a JSON object ' +
159
+ `(${JSON.stringify(raw)}), so onAction is given {} instead.`,
160
+ );
161
+ }
162
+ }
163
+
164
+ function parseActionData(raw) {
165
+ try {
166
+ const parsed = JSON.parse(raw);
167
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
168
+ return parsed;
169
+ }
170
+ } catch (error) {
171
+ // Reported below with the other unusable shapes.
172
+ }
173
+
174
+ warnAboutUnusableActionData(raw);
175
+ return {};
176
+ }
177
+
178
+ export {
179
+ flattenStyle,
180
+ hasExplicitHeight,
181
+ resolveFrameStyle,
182
+ normalizeStatus,
183
+ collapsesHeight,
184
+ COLLAPSING_STATUSES,
185
+ isErrorStatus,
186
+ ERROR_STATUSES,
187
+ isLoadingStatus,
188
+ LOADING_STATUSES,
189
+ shouldWarnAboutUnsupportedReactNative,
190
+ APP_FRAMES_MIN_IOS_REACT_NATIVE_MINOR,
191
+ parseActionData,
192
+ };
@@ -0,0 +1,22 @@
1
+ export declare const InsiderAppFrameStatus: {
2
+ readonly DETACHED: 'DETACHED';
3
+ readonly NO_PLACEMENT: 'NO_PLACEMENT';
4
+ readonly RESOLVING: 'RESOLVING';
5
+ readonly DOWNLOADING: 'DOWNLOADING';
6
+ readonly RENDERING: 'RENDERING';
7
+ readonly UNAVAILABLE: 'UNAVAILABLE';
8
+ readonly READY: 'READY';
9
+ readonly DISMISSED: 'DISMISSED';
10
+ readonly ERROR_RESOLVING: 'ERROR_RESOLVING';
11
+ readonly ERROR_DOWNLOADING: 'ERROR_DOWNLOADING';
12
+ readonly ERROR_RENDERING: 'ERROR_RENDERING';
13
+ /** App Frames is not enabled for the app. A terminal resting state, not an error. */
14
+ readonly DISABLED: 'DISABLED';
15
+ /** Reported when the native SDK sends a status this version does not recognize. */
16
+ readonly UNRECOGNIZED: 'UNRECOGNIZED';
17
+ };
18
+
19
+ export type InsiderAppFrameStatusType =
20
+ (typeof InsiderAppFrameStatus)[keyof typeof InsiderAppFrameStatus];
21
+
22
+ export default InsiderAppFrameStatus;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The content-lifecycle phase an InsiderAppFrame is in, mirroring the native
3
+ * `InsiderAppFramesViewStatus` enum. Observe transitions via `onStatusChange`.
4
+ *
5
+ * Only `DISABLED` is terminal. `UNAVAILABLE` (no campaign targets the placement)
6
+ * and `DISABLED` are not errors; only the `ERROR_*` values are. App Frames fail
7
+ * silently by design, so prefer collapsing the slot over surfacing any of these
8
+ * to the end user.
9
+ *
10
+ * @readonly
11
+ * @enum {string}
12
+ */
13
+ const InsiderAppFrameStatus = Object.freeze({
14
+ DETACHED: 'DETACHED',
15
+ NO_PLACEMENT: 'NO_PLACEMENT',
16
+ RESOLVING: 'RESOLVING',
17
+ DOWNLOADING: 'DOWNLOADING',
18
+ RENDERING: 'RENDERING',
19
+ UNAVAILABLE: 'UNAVAILABLE',
20
+ READY: 'READY',
21
+ DISMISSED: 'DISMISSED',
22
+ ERROR_RESOLVING: 'ERROR_RESOLVING',
23
+ ERROR_DOWNLOADING: 'ERROR_DOWNLOADING',
24
+ ERROR_RENDERING: 'ERROR_RENDERING',
25
+
26
+ // Its only cause is that the app has not opted in to App Frames (Android's
27
+ // APP_FRAMES_ENABLED meta-data / iOS's `App Frames Enabled` Info.plist key).
28
+ DISABLED: 'DISABLED',
29
+
30
+ // JS-only: a status reported by a native SDK newer than this package.
31
+ UNRECOGNIZED: 'UNRECOGNIZED',
32
+ });
33
+
34
+ export { InsiderAppFrameStatus };
35
+ export default InsiderAppFrameStatus;
@@ -0,0 +1,25 @@
1
+ export declare const InsiderAppFramesErrorCode: {
2
+ readonly UNKNOWN: 'UNKNOWN';
3
+ readonly RESOLUTION_FAILED: 'RESOLUTION_FAILED';
4
+ readonly RESPONSE_MALFORMED: 'RESPONSE_MALFORMED';
5
+ readonly DOWNLOADING_FAILED: 'DOWNLOADING_FAILED';
6
+ readonly PLACEMENT_UNTRUSTED: 'PLACEMENT_UNTRUSTED';
7
+ readonly CONTENT_DISPLAY_FAILED: 'CONTENT_DISPLAY_FAILED';
8
+ readonly RENDERING_FAILED: 'RENDERING_FAILED';
9
+ };
10
+
11
+ export type InsiderAppFramesErrorCodeType =
12
+ (typeof InsiderAppFramesErrorCode)[keyof typeof InsiderAppFramesErrorCode];
13
+
14
+ /** The `dismissCode` value when the content supplied none. */
15
+ export declare const NO_DISMISS_CODE: -1;
16
+
17
+ export declare class InsiderAppFramesError extends Error {
18
+ readonly code: InsiderAppFramesErrorCodeType;
19
+ /** The code the content dismissed with, or `NO_DISMISS_CODE`. */
20
+ readonly dismissCode: number;
21
+ /** The underlying platform error's message, or `null`. `message` always describes `code`. */
22
+ readonly cause: string | null;
23
+ constructor(code: string, message: string, dismissCode?: number, cause?: string | null);
24
+ static from(error: unknown): InsiderAppFramesError;
25
+ }
@@ -0,0 +1,77 @@
1
+ const InsiderAppFramesErrorCode = Object.freeze({
2
+ UNKNOWN: 'UNKNOWN',
3
+ RESOLUTION_FAILED: 'RESOLUTION_FAILED',
4
+ RESPONSE_MALFORMED: 'RESPONSE_MALFORMED',
5
+ DOWNLOADING_FAILED: 'DOWNLOADING_FAILED',
6
+ PLACEMENT_UNTRUSTED: 'PLACEMENT_UNTRUSTED',
7
+ CONTENT_DISPLAY_FAILED: 'CONTENT_DISPLAY_FAILED',
8
+ RENDERING_FAILED: 'RENDERING_FAILED',
9
+ });
10
+
11
+ const NO_DISMISS_CODE = -1;
12
+
13
+ class InsiderAppFramesError extends Error {
14
+ #code;
15
+ #dismissCode;
16
+ #cause;
17
+
18
+ constructor(code, message, dismissCode, cause) {
19
+ super(message);
20
+
21
+ this.name = 'InsiderAppFramesError';
22
+ const isKnownCode = Object.prototype.hasOwnProperty.call(InsiderAppFramesErrorCode, code);
23
+ this.#code = isKnownCode ? InsiderAppFramesErrorCode[code] : InsiderAppFramesErrorCode.UNKNOWN;
24
+
25
+ // The Android bridge does not log the raw code, so this is the only place it is visible.
26
+ if (__DEV__ && !isKnownCode && code !== undefined && code !== null) {
27
+ console.warn(
28
+ `[Insider] InsiderAppFramesError received an unrecognised code ${JSON.stringify(code)} ` +
29
+ 'and mapped it to UNKNOWN. This usually means the native SDK is newer than this package.',
30
+ );
31
+ }
32
+ this.#dismissCode = typeof dismissCode === 'number' ? dismissCode : NO_DISMISS_CODE;
33
+ // Codegen event fields are non-nullable, so an absent cause arrives as an empty string.
34
+ this.#cause = typeof cause === 'string' && cause ? cause : null;
35
+ }
36
+
37
+ get code() {
38
+ return this.#code;
39
+ }
40
+
41
+ get dismissCode() {
42
+ return this.#dismissCode;
43
+ }
44
+
45
+ /**
46
+ * The originating platform error's message (Android `getCause()`, iOS
47
+ * `NSUnderlyingErrorKey`), or null when there is none.
48
+ */
49
+ get cause() {
50
+ return this.#cause;
51
+ }
52
+
53
+ static from(error) {
54
+ if (error instanceof InsiderAppFramesError) {
55
+ return error;
56
+ }
57
+
58
+ if (typeof error === 'string') {
59
+ return new InsiderAppFramesError(InsiderAppFramesErrorCode.UNKNOWN, error);
60
+ }
61
+
62
+ if (error && typeof error === 'object') {
63
+ const message =
64
+ typeof error.message === 'string' && error.message
65
+ ? error.message
66
+ : 'An unknown App Frames error occurred.';
67
+ return new InsiderAppFramesError(error.code, message, error.dismissCode, error.cause);
68
+ }
69
+
70
+ return new InsiderAppFramesError(
71
+ InsiderAppFramesErrorCode.UNKNOWN,
72
+ 'An unknown App Frames error occurred.',
73
+ );
74
+ }
75
+ }
76
+
77
+ export {InsiderAppFramesError, InsiderAppFramesErrorCode, NO_DISMISS_CODE};
@@ -5,6 +5,7 @@ import { shouldNotProceed, generateJSONErrorString, checkParameters, showParamet
5
5
  const Insider = NativeModules.RNInsider;
6
6
 
7
7
  export default class RNInsiderEvent {
8
+ // Stryker disable next-line StringLiteral: the constructor always overwrites name
8
9
  name = '';
9
10
  parameters = [];
10
11