react-native-insider 8.1.0 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -660
- package/RNInsider.podspec +33 -12
- package/android/build.gradle +16 -2
- package/android/src/main/java/com/useinsider/react/RNInsiderAppFrameViewManager.java +253 -0
- package/android/src/main/java/com/useinsider/react/RNInsiderModule.java +238 -174
- package/android/src/main/java/com/useinsider/react/RNInsiderPackage.java +5 -1
- package/android/src/main/java/com/useinsider/react/RNUtils.java +111 -0
- package/babel.config.js +4 -1
- package/index.d.ts +196 -161
- package/index.js +60 -11
- package/ios/RNInsider/RNInsider.m +245 -341
- package/ios/RNInsider/RNInsiderAppFrameView.h +33 -0
- package/ios/RNInsider/RNInsiderAppFrameView.mm +315 -0
- package/ios/RNInsider/RNNotificationHandler.h +4 -1
- package/ios/RNInsider/RNNotificationHandler.m +16 -18
- package/ios/RNInsider/RNUtils.h +2 -0
- package/ios/RNInsider/RNUtils.m +79 -0
- package/package.json +43 -12
- package/src/EventHandler.js +17 -0
- package/src/InsiderAppFrame.d.ts +45 -0
- package/src/InsiderAppFrame.js +265 -0
- package/src/InsiderAppFrameLogic.d.ts +35 -0
- package/src/InsiderAppFrameLogic.js +192 -0
- package/src/InsiderAppFrameStatus.d.ts +22 -0
- package/src/InsiderAppFrameStatus.js +35 -0
- package/src/InsiderAppFramesError.d.ts +25 -0
- package/src/InsiderAppFramesError.js +77 -0
- package/src/InsiderEvent.js +1 -0
- package/src/InsiderEvents.d.ts +93 -0
- package/src/InsiderEvents.js +124 -0
- package/src/InsiderIdentifier.js +1 -1
- package/src/InsiderUser.d.ts +3 -2
- package/src/InsiderUser.js +27 -0
- package/src/Util.js +2 -1
- package/src/specs/InsiderAppFrameNativeComponent.ts +54 -0
- package/.orbit-output/config/MASTER.md +0 -129
- package/.orbit-output/config/architecture/API_DOCS.md +0 -342
- package/.orbit-output/config/architecture/ARCHITECTURE.md +0 -209
- package/.orbit-output/config/architecture/CODE-CATALOG.md +0 -172
- package/.orbit-output/config/architecture/project.yaml +0 -138
- package/.orbit-output/config/automation/AUTOMATION-SETUP.md +0 -50
- package/.orbit-output/config/enforcement/ENFORCEMENT-RULES.md +0 -41
- package/.orbit-output/config/enforcement/validate.sh +0 -49
- package/.orbit-output/skills/custom/academy-doc-generator.md +0 -598
- package/android/.settings/org.eclipse.buildship.core.prefs +0 -13
- package/android/src/test/java/com/useinsider/react/RNInsiderModuleTest.java +0 -169
- package/docs/app-cards/app-cards_new.md +0 -569
- package/ios/RNInsiderTests/RNInsiderTriggerPushProcessTests.m +0 -91
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parameters attached to a recorded Insider event.
|
|
3
|
+
*
|
|
4
|
+
* Sourced from the native SDK — the map is unmodifiable on Android and a defensive copy
|
|
5
|
+
* on iOS. Treat it as READ-ONLY; do not mutate it.
|
|
6
|
+
*
|
|
7
|
+
* Values are normalized for the bridge on both platforms before delivery. In particular a
|
|
8
|
+
* `Date` nested inside the parameters arrives as epoch **MILLISECONDS** (a number),
|
|
9
|
+
* following this SDK's epoch-millis convention for dates crossing the bridge. Note the
|
|
10
|
+
* deliberate contrast with the callback's top-level `timestamp`, which is in SECONDS.
|
|
11
|
+
* Values of a type the bridge cannot represent degrade to their string description rather
|
|
12
|
+
* than being dropped.
|
|
13
|
+
*/
|
|
14
|
+
export type InsiderRecordedEventParameters = {
|
|
15
|
+
readonly [key: string]: any;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Callback invoked for every event recorded by the Insider SDK.
|
|
20
|
+
*
|
|
21
|
+
* ⚠️ `timestamp` IS IN **SECONDS**, NOT MILLISECONDS. ⚠️
|
|
22
|
+
*
|
|
23
|
+
* @param name - The recorded event name.
|
|
24
|
+
* @param parameters - Read-only event parameters.
|
|
25
|
+
* @param timestamp - Whole seconds since the Unix epoch. Convert with
|
|
26
|
+
* `new Date(timestamp * 1000)`.
|
|
27
|
+
*/
|
|
28
|
+
export type InsiderEventRecordedCallback = (
|
|
29
|
+
name: string,
|
|
30
|
+
parameters: InsiderRecordedEventParameters,
|
|
31
|
+
timestamp: number
|
|
32
|
+
) => void;
|
|
33
|
+
|
|
34
|
+
/** Handle returned by `events.addObserver`; call `remove()` to unsubscribe. */
|
|
35
|
+
export interface InsiderEventSubscription {
|
|
36
|
+
/**
|
|
37
|
+
* Unsubscribes this listener. When the last subscriber is removed the underlying
|
|
38
|
+
* native observer is unregistered as well.
|
|
39
|
+
*/
|
|
40
|
+
remove(): void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Main API for subscribing to the Insider SDK's recorded-event stream.
|
|
45
|
+
*
|
|
46
|
+
* Mirrors the native SDK namespaces — `[[Insider events] addObserver:]` on iOS,
|
|
47
|
+
* `Insider.Instance.events().addObserver(...)` on Android — so the wrapper reads
|
|
48
|
+
* the way the SDK it wraps does.
|
|
49
|
+
*/
|
|
50
|
+
interface InsiderEvents {
|
|
51
|
+
/**
|
|
52
|
+
* Subscribes to every event recorded by the Insider SDK.
|
|
53
|
+
*
|
|
54
|
+
* ## ⚠️ `timestamp` is in SECONDS, not milliseconds
|
|
55
|
+
*
|
|
56
|
+
* The `timestamp` argument is passed through from the native SDKs **unchanged**, in
|
|
57
|
+
* **whole seconds** since the Unix epoch (this is deliberate — it keeps parity with the
|
|
58
|
+
* native Android/iOS SDKs). JavaScript's `Date` constructor and `Date.now()` both use
|
|
59
|
+
* **milliseconds**, so you MUST multiply by 1000 before building a `Date`:
|
|
60
|
+
*
|
|
61
|
+
* ```typescript
|
|
62
|
+
* Insider.events.addObserver((name, parameters, timestamp) => {
|
|
63
|
+
* const date = new Date(timestamp * 1000); // ✅ correct — timestamp is in SECONDS
|
|
64
|
+
* // const wrong = new Date(timestamp); // ❌ WRONG — yields a date in 1970
|
|
65
|
+
* });
|
|
66
|
+
* ```
|
|
67
|
+
*
|
|
68
|
+
* ## Threading
|
|
69
|
+
*
|
|
70
|
+
* The native SDK delivers on a dedicated background thread, so the callback may be
|
|
71
|
+
* invoked off the JS main work loop. Keep the callback cheap and non-blocking.
|
|
72
|
+
*
|
|
73
|
+
* ## Parameters are read-only
|
|
74
|
+
*
|
|
75
|
+
* `parameters` is unmodifiable on Android / a defensive copy on iOS. Do not mutate it.
|
|
76
|
+
*
|
|
77
|
+
* ## Unsubscribing
|
|
78
|
+
*
|
|
79
|
+
* Unlike `insiderIDListener`, this method returns a subscription. Call `remove()` on it
|
|
80
|
+
* to unsubscribe — important because this fires on EVERY event. Multiple subscribers are
|
|
81
|
+
* supported; the native observer is registered once and torn down only when the last
|
|
82
|
+
* subscriber is removed.
|
|
83
|
+
*
|
|
84
|
+
* @param callback - Invoked as `(name, parameters, timestamp)` for each recorded event.
|
|
85
|
+
* @returns A subscription handle, or `undefined` if the SDK is unavailable or the
|
|
86
|
+
* callback failed validation.
|
|
87
|
+
*/
|
|
88
|
+
addObserver(callback: InsiderEventRecordedCallback): InsiderEventSubscription | undefined;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
declare const InsiderEvents: InsiderEvents;
|
|
92
|
+
|
|
93
|
+
export default InsiderEvents;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { NativeModules } from 'react-native';
|
|
2
|
+
|
|
3
|
+
import { shouldNotProceed, generateJSONErrorString, checkParameters, showParameterWarningLog } from './Util';
|
|
4
|
+
import eventHandler from './EventHandler';
|
|
5
|
+
|
|
6
|
+
const Insider = NativeModules.RNInsider;
|
|
7
|
+
|
|
8
|
+
const INSIDER_EVENT_RECORDED = 'INSIDER_EVENT_RECORDED';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* JS-side fan-out registry for the native event-recorded stream.
|
|
12
|
+
*
|
|
13
|
+
* NOTE — deliberate deviation from the older `insiderIDListener` API:
|
|
14
|
+
* `insiderIDListener` returns nothing, so a caller can never unsubscribe and every
|
|
15
|
+
* extra call stacks another duplicate native + JS listener. That is tolerable for a
|
|
16
|
+
* rarely-firing ID change, but `events.addObserver` fires on EVERY tagged event,
|
|
17
|
+
* so leaked/duplicated subscriptions are a real correctness and performance problem.
|
|
18
|
+
*
|
|
19
|
+
* Therefore this API registers with the native SDK exactly ONCE (guarded by
|
|
20
|
+
* `eventRecordedNativeSubscription`) and fans out to N JS subscribers itself. Each
|
|
21
|
+
* subscriber gets back a `{ remove() }` handle; when the LAST subscriber is removed we
|
|
22
|
+
* tear the native observer down again. This matters on Android in particular, where the
|
|
23
|
+
* native SDK holds observers STRONGLY and would otherwise keep the listener (and the
|
|
24
|
+
* emitter it captures) alive for the lifetime of the process.
|
|
25
|
+
*/
|
|
26
|
+
// Holds one record per SUBSCRIPTION rather than per callback, so that registering the
|
|
27
|
+
// same function twice yields two independent subscriptions (a Set keyed on the function
|
|
28
|
+
// itself would dedupe them, and removing one would silently kill the other).
|
|
29
|
+
const eventRecordedSubscribers = new Set();
|
|
30
|
+
let eventRecordedNativeSubscription = null;
|
|
31
|
+
|
|
32
|
+
function dispatchEventRecorded(payload) {
|
|
33
|
+
if (payload === null || payload === undefined) return;
|
|
34
|
+
|
|
35
|
+
// Copy first: a subscriber may call remove() from inside its own callback, and
|
|
36
|
+
// mutating the Set while iterating it would skip the following subscribers.
|
|
37
|
+
const subscribers = Array.from(eventRecordedSubscribers);
|
|
38
|
+
subscribers.forEach(subscriber => {
|
|
39
|
+
try {
|
|
40
|
+
subscriber.callback(payload.name, payload.parameters || {}, payload.timestamp);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
Insider.putErrorLog(generateJSONErrorString(error));
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function removeEventRecordedSubscriber(subscriber) {
|
|
48
|
+
try {
|
|
49
|
+
if (!eventRecordedSubscribers.delete(subscriber)) return;
|
|
50
|
+
if (eventRecordedSubscribers.size > 0) return;
|
|
51
|
+
|
|
52
|
+
if (eventRecordedNativeSubscription !== null) {
|
|
53
|
+
eventRecordedNativeSubscription.remove();
|
|
54
|
+
eventRecordedNativeSubscription = null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
Insider.unregisterInsiderEventListener();
|
|
58
|
+
} catch (error) {
|
|
59
|
+
Insider.putErrorLog(generateJSONErrorString(error));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const InsiderEvents = Object.freeze({
|
|
64
|
+
/**
|
|
65
|
+
* Subscribes to every event recorded by the Insider SDK.
|
|
66
|
+
*
|
|
67
|
+
* IMPORTANT: `timestamp` is delivered in WHOLE SECONDS since the Unix epoch —
|
|
68
|
+
* NOT milliseconds — matching the native SDKs exactly. Use
|
|
69
|
+
* `new Date(timestamp * 1000)` to build a JS Date.
|
|
70
|
+
*
|
|
71
|
+
* Unlike `insiderIDListener`, this returns a subscription; call `remove()` on it to
|
|
72
|
+
* unsubscribe. The native observer is registered once and torn down when the last
|
|
73
|
+
* subscriber leaves.
|
|
74
|
+
*
|
|
75
|
+
* @param {(name: string, parameters: object, timestamp: number) => void} callback
|
|
76
|
+
* @returns {{ remove: () => void } | undefined} subscription, or undefined if setup failed
|
|
77
|
+
*/
|
|
78
|
+
addObserver(callback) {
|
|
79
|
+
if (shouldNotProceed()) return;
|
|
80
|
+
if (checkParameters([{ type: 'function', value: callback }])) {
|
|
81
|
+
showParameterWarningLog("events.addObserver", [{ type: 'function', value: callback }]);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const subscriber = { callback: callback };
|
|
85
|
+
// Tracks whether THIS call is the one performing native registration. Only a failure
|
|
86
|
+
// during that phase may be rolled back — see the catch below.
|
|
87
|
+
let isRegisteringNatively = false;
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
eventRecordedSubscribers.add(subscriber);
|
|
91
|
+
|
|
92
|
+
if (eventRecordedNativeSubscription === null) {
|
|
93
|
+
isRegisteringNatively = true;
|
|
94
|
+
|
|
95
|
+
eventRecordedNativeSubscription = eventHandler.addListener(INSIDER_EVENT_RECORDED, dispatchEventRecorded);
|
|
96
|
+
|
|
97
|
+
Insider.registerInsiderEventListener();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
remove: () => removeEventRecordedSubscriber(subscriber)
|
|
102
|
+
};
|
|
103
|
+
} catch (error) {
|
|
104
|
+
Insider.putErrorLog(generateJSONErrorString(error));
|
|
105
|
+
|
|
106
|
+
// Roll the failed subscribe back so a later call can retry. Without this the
|
|
107
|
+
// half-built state (non-null `eventRecordedNativeSubscription` + a registered
|
|
108
|
+
// subscriber) would permanently satisfy the guard above, and events would never
|
|
109
|
+
// arrive with nothing left to re-trigger registration.
|
|
110
|
+
eventRecordedSubscribers.delete(subscriber);
|
|
111
|
+
|
|
112
|
+
// ONLY undo the native side when this call is the one that was registering it.
|
|
113
|
+
// If registration already succeeded earlier, a throw here came from somewhere else
|
|
114
|
+
// on a live stream — tearing down and letting a later subscribe re-register would
|
|
115
|
+
// stack a SECOND native observer and double-deliver every event.
|
|
116
|
+
if (isRegisteringNatively && eventRecordedNativeSubscription !== null) {
|
|
117
|
+
eventRecordedNativeSubscription.remove();
|
|
118
|
+
eventRecordedNativeSubscription = null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
export default InsiderEvents;
|
package/src/InsiderIdentifier.js
CHANGED
|
@@ -7,10 +7,10 @@ const Insider = NativeModules.RNInsider;
|
|
|
7
7
|
const ADD_EMAIL = 'addEmail';
|
|
8
8
|
const ADD_PHONE_NUMBER = 'addPhoneNumber';
|
|
9
9
|
const ADD_USER_ID = 'addUserID';
|
|
10
|
-
const ADD_CUSTOM_IDENTIFIER = 'addCustomIdentifier';
|
|
11
10
|
|
|
12
11
|
export default class RNInsiderIdentifier {
|
|
13
12
|
identifiers = {};
|
|
13
|
+
// Stryker disable next-line BlockStatement: the field initializer above already sets identifiers to {}
|
|
14
14
|
constructor() {
|
|
15
15
|
this.identifiers = {};
|
|
16
16
|
}
|
package/src/InsiderUser.d.ts
CHANGED
|
@@ -17,10 +17,11 @@ declare class RNInsiderUser {
|
|
|
17
17
|
setPushOptin(value: boolean): this;
|
|
18
18
|
setLocationOptin(value: boolean): this;
|
|
19
19
|
setWhatsappOptin(value: boolean): this;
|
|
20
|
+
setPromotionalOptin(value: boolean): this;
|
|
20
21
|
|
|
21
|
-
login(identifiers: RNInsiderIdentifier, insiderIDResult?: (insiderID: string) => void): this;
|
|
22
|
+
login(identifiers: RNInsiderIdentifier, insiderIDResult?: (insiderID: string | null) => void): this;
|
|
22
23
|
logout(): this;
|
|
23
|
-
logoutResettingInsiderID(identifiers?: RNInsiderIdentifier[] | null, insiderIDResult?: (insiderID: string) => void): this;
|
|
24
|
+
logoutResettingInsiderID(identifiers?: RNInsiderIdentifier[] | null, insiderIDResult?: (insiderID: string | null) => void): this;
|
|
24
25
|
|
|
25
26
|
setCustomAttributeWithString(key: string, value: string): this;
|
|
26
27
|
setCustomAttributeWithInt(key: string, value: number): this;
|
package/src/InsiderUser.js
CHANGED
|
@@ -229,10 +229,30 @@ export default class RNInsiderUser {
|
|
|
229
229
|
return this;
|
|
230
230
|
}
|
|
231
231
|
|
|
232
|
+
setPromotionalOptin(value) {
|
|
233
|
+
if (shouldNotProceed()) return this;
|
|
234
|
+
if (checkParameters([{ type: 'boolean', value }])) {
|
|
235
|
+
showParameterWarningLog("setPromotionalOptin", [{ type: 'boolean', value }]);
|
|
236
|
+
return this;
|
|
237
|
+
}
|
|
238
|
+
try {
|
|
239
|
+
Insider.setPromotionalOptin(value);
|
|
240
|
+
} catch (error) {
|
|
241
|
+
Insider.putErrorLog(generateJSONErrorString(error));
|
|
242
|
+
}
|
|
243
|
+
return this;
|
|
244
|
+
}
|
|
245
|
+
|
|
232
246
|
login(identifiers, insiderIDResult) {
|
|
233
247
|
if (shouldNotProceed()) { return this; }
|
|
234
248
|
try {
|
|
235
249
|
if (insiderIDResult !== null && insiderIDResult !== undefined) {
|
|
250
|
+
if (checkParameters([{ type: 'function', value: insiderIDResult }])) {
|
|
251
|
+
showParameterWarningLog("login", [{ type: 'function', value: insiderIDResult }]);
|
|
252
|
+
|
|
253
|
+
return this;
|
|
254
|
+
}
|
|
255
|
+
|
|
236
256
|
Insider.loginWithReturningID(identifiers.identifiers, insiderIDResult);
|
|
237
257
|
|
|
238
258
|
return;
|
|
@@ -258,6 +278,13 @@ export default class RNInsiderUser {
|
|
|
258
278
|
logoutResettingInsiderID(identifiers, insiderIDResult) {
|
|
259
279
|
if (shouldNotProceed()) { return this; }
|
|
260
280
|
try {
|
|
281
|
+
if (insiderIDResult !== null && insiderIDResult !== undefined
|
|
282
|
+
&& checkParameters([{ type: 'function', value: insiderIDResult }])) {
|
|
283
|
+
showParameterWarningLog("logoutResettingInsiderID", [{ type: 'function', value: insiderIDResult }]);
|
|
284
|
+
|
|
285
|
+
return this;
|
|
286
|
+
}
|
|
287
|
+
|
|
261
288
|
const identifiersArray = (identifiers && Array.isArray(identifiers))
|
|
262
289
|
? identifiers.map(id => id.identifiers)
|
|
263
290
|
: null;
|
package/src/Util.js
CHANGED
|
@@ -13,6 +13,7 @@ export function generateJSONErrorString(error) {
|
|
|
13
13
|
export function checkParameters(parameters) {
|
|
14
14
|
return parameters.some(param =>
|
|
15
15
|
param.value === null ||
|
|
16
|
+
// Stryker disable next-line ConditionalExpression: typeof undefined never equals a declared type, so the type check below already rejects it
|
|
16
17
|
param.value === undefined ||
|
|
17
18
|
param.type !== typeof param.value
|
|
18
19
|
);
|
|
@@ -25,7 +26,7 @@ export function showParameterWarningLog(functionName, parameters = []) {
|
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
const invalidParams = parameters
|
|
28
|
-
.filter(param => param
|
|
29
|
+
.filter(param => checkParameters([param]))
|
|
29
30
|
.map(param => `Expected ${param.type} but got ${typeof param.value} (${param.value})`);
|
|
30
31
|
|
|
31
32
|
console.warn(
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type {HostComponent, ViewProps} from 'react-native';
|
|
2
|
+
import type {
|
|
3
|
+
DirectEventHandler,
|
|
4
|
+
Double,
|
|
5
|
+
Int32,
|
|
6
|
+
} from 'react-native/Libraries/Types/CodegenTypes';
|
|
7
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
8
|
+
|
|
9
|
+
export type InsiderAppFrameDismissEvent = Readonly<{
|
|
10
|
+
placementId: string;
|
|
11
|
+
}>;
|
|
12
|
+
|
|
13
|
+
export type InsiderAppFrameStatusEvent = Readonly<{
|
|
14
|
+
placementId: string;
|
|
15
|
+
status: string;
|
|
16
|
+
previousStatus: string;
|
|
17
|
+
}>;
|
|
18
|
+
|
|
19
|
+
export type InsiderAppFrameErrorEvent = Readonly<{
|
|
20
|
+
placementId: string;
|
|
21
|
+
code: string;
|
|
22
|
+
/** Always the canonical text for `code`; the originating error travels in `cause`. */
|
|
23
|
+
message: string;
|
|
24
|
+
/**
|
|
25
|
+
* The originating platform error's message, or an empty string — codegen event payloads
|
|
26
|
+
* are non-nullable.
|
|
27
|
+
*/
|
|
28
|
+
cause: string;
|
|
29
|
+
dismissCode: Int32;
|
|
30
|
+
}>;
|
|
31
|
+
|
|
32
|
+
export type InsiderAppFrameHeightEvent = Readonly<{
|
|
33
|
+
placementId: string;
|
|
34
|
+
height: Double;
|
|
35
|
+
}>;
|
|
36
|
+
|
|
37
|
+
export type InsiderAppFrameActionEvent = Readonly<{
|
|
38
|
+
placementId: string;
|
|
39
|
+
data: string;
|
|
40
|
+
}>;
|
|
41
|
+
|
|
42
|
+
export interface NativeProps extends ViewProps {
|
|
43
|
+
placementId?: string;
|
|
44
|
+
|
|
45
|
+
onStatusChange?: DirectEventHandler<InsiderAppFrameStatusEvent>;
|
|
46
|
+
onLoadError?: DirectEventHandler<InsiderAppFrameErrorEvent>;
|
|
47
|
+
onHeightChange?: DirectEventHandler<InsiderAppFrameHeightEvent>;
|
|
48
|
+
onDismissRequest?: DirectEventHandler<InsiderAppFrameDismissEvent>;
|
|
49
|
+
onAction?: DirectEventHandler<InsiderAppFrameActionEvent>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export default codegenNativeComponent<NativeProps>(
|
|
53
|
+
'InsiderAppFrameView',
|
|
54
|
+
) as HostComponent<NativeProps>;
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
# MASTER.md — Single Source of Truth
|
|
2
|
-
|
|
3
|
-
> react-native-insider v8.0.3 | Generated by Orbit Init | 2026-04-16 (synced 2026-06-29)
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Project Overview
|
|
8
|
-
|
|
9
|
-
React Native bridge SDK (`react-native-insider`) wrapping native Insider iOS/Android SDKs via NativeModules. Published to npm as a library — not an application. v8.0.0 introduces the **App Cards** module and **identifier-scoped Message Center**.
|
|
10
|
-
|
|
11
|
-
## Technology Stack
|
|
12
|
-
|
|
13
|
-
| Layer | Technology | Version |
|
|
14
|
-
|-------|-----------|---------|
|
|
15
|
-
| JS Bridge | JavaScript (ES6+, ES private fields) | — |
|
|
16
|
-
| Type Declarations | TypeScript (.d.ts) | — |
|
|
17
|
-
| iOS Native | Objective-C | — |
|
|
18
|
-
| Android Native | Java | — |
|
|
19
|
-
| Framework | React Native | 0.83.0 |
|
|
20
|
-
| React | React | 19.2.0 |
|
|
21
|
-
| iOS SDK | InsiderMobile | 15.1.0 |
|
|
22
|
-
| iOS Geofence | InsiderGeofence | 1.2.4 |
|
|
23
|
-
| iOS Hybrid | InsiderHybrid | 1.7.6 |
|
|
24
|
-
| Android SDK | com.useinsider:insider | 16.0.7 |
|
|
25
|
-
| Android Hybrid | com.useinsider:insiderhybrid | 1.3.4 |
|
|
26
|
-
| Testing | Jest | 29.7.0 |
|
|
27
|
-
| Build | Babel + @react-native/babel-preset | 0.83.0 |
|
|
28
|
-
| Android Build | Android Gradle Plugin | 8.4.2 |
|
|
29
|
-
| CI/CD | GitHub Actions | 3 workflows |
|
|
30
|
-
|
|
31
|
-
## Project Structure
|
|
32
|
-
|
|
33
|
-
```
|
|
34
|
-
./
|
|
35
|
-
├── index.js / index.d.ts # Main entry point (re-exports AppCardsError)
|
|
36
|
-
├── src/ # JS modules
|
|
37
|
-
│ ├── InsiderUser/Product/Event/Identifier (fluent builders)
|
|
38
|
-
│ ├── InsiderAppCards / InsiderAppCard / InsiderAppCardsError (App Cards)
|
|
39
|
-
│ ├── Util.js # validation + resolveWithCallback
|
|
40
|
-
│ ├── *Enum* (Gender, CallbackType, ContentOptimizerDataType, CloseButtonPosition)
|
|
41
|
-
│ └── __tests__/ # per-module tests
|
|
42
|
-
├── ios/RNInsider/ # Objective-C bridge
|
|
43
|
-
├── android/src/main/java/ # Java bridge
|
|
44
|
-
├── __tests__/ + __mocks__/ # Jest tests
|
|
45
|
-
├── docs/ # internal feature specs
|
|
46
|
-
├── example/ # Demo React Native app
|
|
47
|
-
├── .github/workflows/ # CI/CD
|
|
48
|
-
├── .orbit-output/ # Orbit architecture docs (below)
|
|
49
|
-
└── devops/ # Release scripts
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
## .orbit Structure
|
|
53
|
-
|
|
54
|
-
```
|
|
55
|
-
.orbit-output/
|
|
56
|
-
├── config/
|
|
57
|
-
│ ├── MASTER.md # THIS FILE — single source of truth
|
|
58
|
-
│ └── architecture/
|
|
59
|
-
│ ├── ARCHITECTURE.md # Architecture overview + diagrams
|
|
60
|
-
│ ├── CODE-CATALOG.md # All classes, functions, modules
|
|
61
|
-
│ ├── API_DOCS.md # Bridge API reference
|
|
62
|
-
│ └── project.yaml # Machine-readable config
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
**When to read:**
|
|
66
|
-
- New to project → ARCHITECTURE.md
|
|
67
|
-
- Looking for a class/function → CODE-CATALOG.md
|
|
68
|
-
- Understanding bridge API → API_DOCS.md
|
|
69
|
-
- Quick reference → this file (MASTER.md)
|
|
70
|
-
|
|
71
|
-
## Code Patterns
|
|
72
|
-
|
|
73
|
-
### Naming
|
|
74
|
-
- Classes: `PascalCase` (RNInsider, RNInsiderProduct, InsiderAppCard)
|
|
75
|
-
- Methods: `camelCase` (tagEvent, getCurrentUser, markAsRead)
|
|
76
|
-
- Constants: `UPPER_SNAKE_CASE` (NOTIFICATION_OPEN, INVALID_PARAMETER)
|
|
77
|
-
- Product field keys: `snake_case` strings (product_id, sale_price)
|
|
78
|
-
- Private fields: `#field` (App Card domain models)
|
|
79
|
-
|
|
80
|
-
### Architecture Pattern
|
|
81
|
-
- **Static facade** (RNInsider) + **fluent builders** (Product, Event, User, Identifier) + **frozen singleton** (AppCards)
|
|
82
|
-
- Every public method: `shouldNotProceed()` → `checkParameters()` → try/catch → `putErrorLog()`
|
|
83
|
-
- Products serialized as 3 bridge args: requiredFields, optionalFields, customParameters
|
|
84
|
-
- Dates as epoch-millis strings (bridge int64 limitation)
|
|
85
|
-
- Callback events indexed by array position matching InsiderCallbackType enum
|
|
86
|
-
- App Cards: Promise + completion-callback duality via `Util.resolveWithCallback`
|
|
87
|
-
- Polymorphic action types via `InsiderAppCardAction.fromData()` factory
|
|
88
|
-
- Polymorphic 2nd arg in `itemRemovedFromCart` and `visitCartPage` (saleID OR customParameters)
|
|
89
|
-
|
|
90
|
-
### Testing
|
|
91
|
-
- Jest with react-native preset
|
|
92
|
-
- Global mock in `__mocks__/react-native.js`
|
|
93
|
-
- Test pattern: `describe('Class.method') → it('should ...')`
|
|
94
|
-
- Per-module suites under `src/__tests__/` plus top-level `__tests__/`
|
|
95
|
-
|
|
96
|
-
## Common Commands
|
|
97
|
-
|
|
98
|
-
| Command | Purpose |
|
|
99
|
-
|---------|---------|
|
|
100
|
-
| `npm test` | Run all Jest tests |
|
|
101
|
-
| `npx jest __tests__/Util.test.js` | Run single test file |
|
|
102
|
-
| `npm install` | Install dependencies |
|
|
103
|
-
| `npm pack` | Pack for local testing |
|
|
104
|
-
| `cd example && npm install` | Install example app deps |
|
|
105
|
-
| `cd example/ios && pod install` | Install iOS pods for example |
|
|
106
|
-
|
|
107
|
-
## Key Entry Points
|
|
108
|
-
|
|
109
|
-
| File | Role |
|
|
110
|
-
|------|------|
|
|
111
|
-
| `index.js` | Main SDK entry — RNInsider static class |
|
|
112
|
-
| `src/InsiderAppCards.js` | App Cards module (Promise + callback API) |
|
|
113
|
-
| `ios/RNInsider/RNInsider.m` | iOS bridge module |
|
|
114
|
-
| `android/.../RNInsiderModule.java` | Android bridge module |
|
|
115
|
-
| `example/App.tsx` | Example app entry |
|
|
116
|
-
|
|
117
|
-
## Version Management
|
|
118
|
-
|
|
119
|
-
- Source of truth: `package.json` → version (currently 8.0.3)
|
|
120
|
-
- iOS: `RNInsider.podspec` reads from package.json
|
|
121
|
-
- Android: `build.gradle` has pinned native SDK versions (insider:16.0.7)
|
|
122
|
-
- SDK string: `'RN-' + version` (e.g., RN-8.0.3)
|
|
123
|
-
- Release: `release/X.Y.Z` branch → master merge → npm publish
|
|
124
|
-
- Dual-track: main + `-nh` (non-Huawei) variant
|
|
125
|
-
- PR titles must contain `MOB-[0-9]+`
|
|
126
|
-
|
|
127
|
-
---
|
|
128
|
-
|
|
129
|
-
*Auto-generated by Orbit Init v3.11 | 2026-04-16 (synced 2026-06-12)*
|