@octovise/react-native-sdk 1.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/CHANGELOG.md +43 -0
- package/LICENSE +21 -0
- package/README.md +160 -0
- package/android/src/main/java/com/octovise/sdk/OctoFirebaseMessagingService.kt +72 -0
- package/android/src/main/java/com/octovise/sdk/OctoPushDismissReceiver.kt +54 -0
- package/android/src/main/java/com/octovise/sdk/OctoPushModule.kt +26 -0
- package/android/src/main/java/com/octovise/sdk/OctoPushNotificationBuilder.kt +142 -0
- package/dist/api.d.ts +38 -0
- package/dist/api.js +210 -0
- package/dist/carouselStart.d.ts +1 -0
- package/dist/carouselStart.js +6 -0
- package/dist/components/OctoErrorBoundary.d.ts +15 -0
- package/dist/components/OctoErrorBoundary.js +28 -0
- package/dist/components/OctoImage.d.ts +23 -0
- package/dist/components/OctoImage.js +69 -0
- package/dist/components/OctoMessage.d.ts +9 -0
- package/dist/components/OctoMessage.js +189 -0
- package/dist/components/OctoModal.d.ts +13 -0
- package/dist/components/OctoModal.js +179 -0
- package/dist/components/OctoProvider.d.ts +14 -0
- package/dist/components/OctoProvider.js +92 -0
- package/dist/components/OctoToast.d.ts +13 -0
- package/dist/components/OctoToast.js +392 -0
- package/dist/debug.d.ts +2 -0
- package/dist/debug.js +15 -0
- package/dist/imageFit.d.ts +8 -0
- package/dist/imageFit.js +31 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +195 -0
- package/dist/latency.d.ts +17 -0
- package/dist/latency.js +11 -0
- package/dist/native.d.ts +1 -0
- package/dist/native.js +14 -0
- package/dist/navigation/reactNavigation.d.ts +12 -0
- package/dist/navigation/reactNavigation.js +24 -0
- package/dist/navigation/resolveCTA.d.ts +12 -0
- package/dist/navigation/resolveCTA.js +74 -0
- package/dist/push.d.ts +3 -0
- package/dist/push.js +129 -0
- package/dist/safeNavUrl.d.ts +2 -0
- package/dist/safeNavUrl.js +18 -0
- package/dist/screenStream.d.ts +17 -0
- package/dist/screenStream.js +126 -0
- package/dist/screenTargeting.d.ts +37 -0
- package/dist/screenTargeting.js +154 -0
- package/dist/session.d.ts +2 -0
- package/dist/session.js +22 -0
- package/dist/state.d.ts +8 -0
- package/dist/state.js +53 -0
- package/dist/telemetry.d.ts +6 -0
- package/dist/telemetry.js +65 -0
- package/dist/toastLayout.d.ts +2 -0
- package/dist/toastLayout.js +11 -0
- package/dist/types.d.ts +78 -0
- package/dist/types.js +7 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +4 -0
- package/ios/OctoviseNotificationExtension/NotificationService.swift +128 -0
- package/ios/OctoviseSDK/OctoPushModule.m +6 -0
- package/ios/OctoviseSDK/OctoPushModule.swift +27 -0
- package/package.json +57 -0
- package/react-native.config.js +16 -0
package/dist/api.js
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_BASE_URL = void 0;
|
|
4
|
+
exports.configure = configure;
|
|
5
|
+
exports.buildTelemetryContext = buildTelemetryContext;
|
|
6
|
+
exports.check = check;
|
|
7
|
+
exports.fetchRules = fetchRules;
|
|
8
|
+
exports.trackScreenViews = trackScreenViews;
|
|
9
|
+
exports.setEventObserver = setEventObserver;
|
|
10
|
+
exports.trackEvent = trackEvent;
|
|
11
|
+
exports.sendTelemetry = sendTelemetry;
|
|
12
|
+
exports.testInApp = testInApp;
|
|
13
|
+
exports.registerDevice = registerDevice;
|
|
14
|
+
exports.getUserId = getUserId;
|
|
15
|
+
exports.getBaseUrl = getBaseUrl;
|
|
16
|
+
const react_native_1 = require("react-native");
|
|
17
|
+
const telemetry_1 = require("./telemetry");
|
|
18
|
+
const version_1 = require("./version");
|
|
19
|
+
exports.DEFAULT_BASE_URL = 'https://sdk-api.octovise.com.ar';
|
|
20
|
+
let baseUrl = exports.DEFAULT_BASE_URL;
|
|
21
|
+
let apiKey = '';
|
|
22
|
+
let currentUserId = '';
|
|
23
|
+
let currentUserHash;
|
|
24
|
+
let appVersion;
|
|
25
|
+
function configure(key, userId, userHash, url, appVer) {
|
|
26
|
+
apiKey = key;
|
|
27
|
+
currentUserId = String(userId);
|
|
28
|
+
currentUserHash = userHash;
|
|
29
|
+
if (url)
|
|
30
|
+
baseUrl = url;
|
|
31
|
+
if (appVer)
|
|
32
|
+
appVersion = appVer;
|
|
33
|
+
}
|
|
34
|
+
function errMessage(err) {
|
|
35
|
+
return (err instanceof Error ? err.message : String(err)).slice(0, 200);
|
|
36
|
+
}
|
|
37
|
+
function buildTelemetryContext() {
|
|
38
|
+
const ctx = { sdk_version: version_1.SDK_VERSION };
|
|
39
|
+
if (appVersion)
|
|
40
|
+
ctx.app_version = appVersion;
|
|
41
|
+
if (react_native_1.Platform.Version != null)
|
|
42
|
+
ctx.os_version = String(react_native_1.Platform.Version).slice(0, 64);
|
|
43
|
+
const c = react_native_1.Platform.constants;
|
|
44
|
+
if (c) {
|
|
45
|
+
const model = react_native_1.Platform.OS === 'android'
|
|
46
|
+
? [c.Brand, c.Model].filter(Boolean).join(' ')
|
|
47
|
+
: c.interfaceIdiom;
|
|
48
|
+
if (model)
|
|
49
|
+
ctx.device_model = String(model).slice(0, 128);
|
|
50
|
+
}
|
|
51
|
+
return ctx;
|
|
52
|
+
}
|
|
53
|
+
async function check(sendId) {
|
|
54
|
+
try {
|
|
55
|
+
const parts = [
|
|
56
|
+
`u=${encodeURIComponent(currentUserId)}`,
|
|
57
|
+
'device=mobile',
|
|
58
|
+
'p=app',
|
|
59
|
+
];
|
|
60
|
+
if (currentUserHash)
|
|
61
|
+
parts.push(`h=${encodeURIComponent(currentUserHash)}`);
|
|
62
|
+
if (sendId != null)
|
|
63
|
+
parts.push(`send_id=${encodeURIComponent(String(sendId))}`);
|
|
64
|
+
const res = await fetch(`${baseUrl}/check?${parts.join('&')}`, {
|
|
65
|
+
headers: { 'X-API-Key': apiKey },
|
|
66
|
+
});
|
|
67
|
+
if (res.status === 204)
|
|
68
|
+
return null;
|
|
69
|
+
if (!res.ok) {
|
|
70
|
+
(0, telemetry_1.reportError)(res.status === 401 || res.status === 403 ? 'auth_failed' : 'check_http_error', {
|
|
71
|
+
status: res.status,
|
|
72
|
+
endpoint: 'check',
|
|
73
|
+
});
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
return await res.json();
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
(0, telemetry_1.reportError)('check_network_error', { endpoint: 'check', message: errMessage(err) });
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async function fetchRules() {
|
|
84
|
+
try {
|
|
85
|
+
const parts = [`u=${encodeURIComponent(currentUserId)}`];
|
|
86
|
+
if (currentUserHash)
|
|
87
|
+
parts.push(`h=${encodeURIComponent(currentUserHash)}`);
|
|
88
|
+
const res = await fetch(`${baseUrl}/rules?${parts.join('&')}`, {
|
|
89
|
+
headers: { 'X-API-Key': apiKey },
|
|
90
|
+
});
|
|
91
|
+
if (!res.ok) {
|
|
92
|
+
(0, telemetry_1.reportError)(res.status === 401 || res.status === 403 ? 'auth_failed' : 'rules_http_error', {
|
|
93
|
+
status: res.status,
|
|
94
|
+
endpoint: 'rules',
|
|
95
|
+
});
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
return await res.json();
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
(0, telemetry_1.reportError)('rules_network_error', { endpoint: 'rules', message: errMessage(err) });
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async function trackScreenViews(views) {
|
|
106
|
+
if (views.length === 0)
|
|
107
|
+
return true;
|
|
108
|
+
try {
|
|
109
|
+
const res = await fetch(`${baseUrl}/screen-views`, {
|
|
110
|
+
method: 'POST',
|
|
111
|
+
headers: { 'X-API-Key': apiKey, 'Content-Type': 'application/json' },
|
|
112
|
+
body: JSON.stringify({
|
|
113
|
+
user_id: currentUserId,
|
|
114
|
+
user_hash: currentUserHash,
|
|
115
|
+
device: 'App',
|
|
116
|
+
views,
|
|
117
|
+
}),
|
|
118
|
+
});
|
|
119
|
+
return res.ok;
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
let eventObserver;
|
|
126
|
+
function setEventObserver(cb) {
|
|
127
|
+
eventObserver = cb;
|
|
128
|
+
}
|
|
129
|
+
function trackEvent(sendId, event, meta) {
|
|
130
|
+
try {
|
|
131
|
+
eventObserver?.(event, meta, sendId);
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
}
|
|
135
|
+
const body = JSON.stringify({
|
|
136
|
+
user_id: currentUserId,
|
|
137
|
+
user_hash: currentUserHash,
|
|
138
|
+
send_id: sendId,
|
|
139
|
+
event,
|
|
140
|
+
meta: {
|
|
141
|
+
...meta,
|
|
142
|
+
device: 'mobile',
|
|
143
|
+
platform: react_native_1.Platform.OS,
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
fetch(`${baseUrl}/event`, {
|
|
147
|
+
method: 'POST',
|
|
148
|
+
headers: { 'X-API-Key': apiKey, 'Content-Type': 'application/json' },
|
|
149
|
+
body,
|
|
150
|
+
})
|
|
151
|
+
.then((res) => {
|
|
152
|
+
if (!res.ok)
|
|
153
|
+
(0, telemetry_1.reportError)('event_http_error', { status: res.status, event });
|
|
154
|
+
})
|
|
155
|
+
.catch((err) => (0, telemetry_1.reportError)('event_network_error', { event, message: errMessage(err) }));
|
|
156
|
+
}
|
|
157
|
+
function sendTelemetry(events) {
|
|
158
|
+
if (events.length === 0)
|
|
159
|
+
return;
|
|
160
|
+
const body = JSON.stringify({
|
|
161
|
+
user_id: currentUserId,
|
|
162
|
+
user_hash: currentUserHash,
|
|
163
|
+
platform: react_native_1.Platform.OS,
|
|
164
|
+
context: buildTelemetryContext(),
|
|
165
|
+
events,
|
|
166
|
+
});
|
|
167
|
+
fetch(`${baseUrl}/telemetry`, {
|
|
168
|
+
method: 'POST',
|
|
169
|
+
headers: { 'X-API-Key': apiKey, 'Content-Type': 'application/json' },
|
|
170
|
+
body,
|
|
171
|
+
}).catch(() => { });
|
|
172
|
+
}
|
|
173
|
+
async function testInApp() {
|
|
174
|
+
try {
|
|
175
|
+
const res = await fetch(`${baseUrl}/test/in-app`, {
|
|
176
|
+
method: 'POST',
|
|
177
|
+
headers: { 'X-API-Key': apiKey, 'Content-Type': 'application/json' },
|
|
178
|
+
body: JSON.stringify({
|
|
179
|
+
user_id: currentUserId,
|
|
180
|
+
user_hash: currentUserHash,
|
|
181
|
+
}),
|
|
182
|
+
});
|
|
183
|
+
return res.ok;
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
async function registerDevice(fcmToken) {
|
|
190
|
+
try {
|
|
191
|
+
await fetch(`${baseUrl}/device`, {
|
|
192
|
+
method: 'POST',
|
|
193
|
+
headers: { 'X-API-Key': apiKey, 'Content-Type': 'application/json' },
|
|
194
|
+
body: JSON.stringify({
|
|
195
|
+
user_id: currentUserId,
|
|
196
|
+
user_hash: currentUserHash,
|
|
197
|
+
platform: react_native_1.Platform.OS,
|
|
198
|
+
fcm_token: fcmToken,
|
|
199
|
+
}),
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
function getUserId() {
|
|
206
|
+
return currentUserId;
|
|
207
|
+
}
|
|
208
|
+
function getBaseUrl() {
|
|
209
|
+
return baseUrl;
|
|
210
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function carouselInitialCell(imagePosition?: string): 1 | 2;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface Props {
|
|
3
|
+
children: React.ReactNode;
|
|
4
|
+
onError: (error: Error) => void;
|
|
5
|
+
}
|
|
6
|
+
interface State {
|
|
7
|
+
failed: boolean;
|
|
8
|
+
}
|
|
9
|
+
export default class OctoErrorBoundary extends React.Component<Props, State> {
|
|
10
|
+
state: State;
|
|
11
|
+
static getDerivedStateFromError(): State;
|
|
12
|
+
componentDidCatch(error: Error): void;
|
|
13
|
+
render(): React.ReactNode;
|
|
14
|
+
}
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const react_1 = __importDefault(require("react"));
|
|
7
|
+
class OctoErrorBoundary extends react_1.default.Component {
|
|
8
|
+
constructor() {
|
|
9
|
+
super(...arguments);
|
|
10
|
+
this.state = { failed: false };
|
|
11
|
+
}
|
|
12
|
+
static getDerivedStateFromError() {
|
|
13
|
+
return { failed: true };
|
|
14
|
+
}
|
|
15
|
+
componentDidCatch(error) {
|
|
16
|
+
try {
|
|
17
|
+
this.props.onError(error);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
render() {
|
|
23
|
+
if (this.state.failed)
|
|
24
|
+
return null;
|
|
25
|
+
return this.props.children;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.default = OctoErrorBoundary;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { InAppMessage } from '../types';
|
|
3
|
+
export interface ImageCorners {
|
|
4
|
+
topLeft?: number;
|
|
5
|
+
topRight?: number;
|
|
6
|
+
bottomLeft?: number;
|
|
7
|
+
bottomRight?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface ImageBleed {
|
|
10
|
+
top?: number;
|
|
11
|
+
right?: number;
|
|
12
|
+
bottom?: number;
|
|
13
|
+
left?: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function OctoImage({ message, width, height, corners, bleed, onError, placeholderColor, }: {
|
|
16
|
+
message: Pick<InAppMessage, 'image' | 'image_fit' | 'image_bg'>;
|
|
17
|
+
width: number;
|
|
18
|
+
height: number;
|
|
19
|
+
onError?: () => void;
|
|
20
|
+
corners?: ImageCorners;
|
|
21
|
+
bleed?: ImageBleed;
|
|
22
|
+
placeholderColor?: string;
|
|
23
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.OctoImage = OctoImage;
|
|
37
|
+
const react_1 = __importStar(require("react"));
|
|
38
|
+
const react_native_1 = require("react-native");
|
|
39
|
+
const imageFit_1 = require("../imageFit");
|
|
40
|
+
function OctoImage({ message, width, height, corners, bleed, onError, placeholderColor, }) {
|
|
41
|
+
const { container, image } = (0, imageFit_1.imageRenderStyles)(message, width, height);
|
|
42
|
+
const [loaded, setLoaded] = (0, react_1.useState)(false);
|
|
43
|
+
(0, react_1.useEffect)(() => setLoaded(false), [message.image]);
|
|
44
|
+
const showSkeleton = !loaded && message.image_fit !== 'contain' && !!placeholderColor;
|
|
45
|
+
const radii = corners
|
|
46
|
+
? {
|
|
47
|
+
borderTopLeftRadius: corners.topLeft,
|
|
48
|
+
borderTopRightRadius: corners.topRight,
|
|
49
|
+
borderBottomLeftRadius: corners.bottomLeft,
|
|
50
|
+
borderBottomRightRadius: corners.bottomRight,
|
|
51
|
+
}
|
|
52
|
+
: null;
|
|
53
|
+
const b = bleed || {};
|
|
54
|
+
const extraW = (b.left || 0) + (b.right || 0);
|
|
55
|
+
const extraH = (b.top || 0) + (b.bottom || 0);
|
|
56
|
+
const bleedSize = extraW || extraH ? { width: width + extraW, height: height + extraH } : null;
|
|
57
|
+
const bleedMargin = extraW || extraH
|
|
58
|
+
? {
|
|
59
|
+
marginTop: b.top ? -b.top : undefined,
|
|
60
|
+
marginBottom: b.bottom ? -b.bottom : undefined,
|
|
61
|
+
marginLeft: b.left ? -b.left : undefined,
|
|
62
|
+
marginRight: b.right ? -b.right : undefined,
|
|
63
|
+
}
|
|
64
|
+
: null;
|
|
65
|
+
return (<react_native_1.View style={[container, radii, bleedSize, bleedMargin]}>
|
|
66
|
+
{showSkeleton ? (<react_native_1.View style={[react_native_1.StyleSheet.absoluteFillObject, radii, { backgroundColor: placeholderColor, opacity: 0.08 }]}/>) : null}
|
|
67
|
+
<react_native_1.Image source={{ uri: message.image }} style={[image, radii, bleedSize]} onError={onError} onLoad={() => setLoaded(true)}/>
|
|
68
|
+
</react_native_1.View>);
|
|
69
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { InAppMessage } from '../types';
|
|
3
|
+
interface Props {
|
|
4
|
+
message: InAppMessage | null;
|
|
5
|
+
onCTA?: (url: string) => void;
|
|
6
|
+
onDismissed?: () => void;
|
|
7
|
+
}
|
|
8
|
+
export default function OctoMessage({ message, onCTA, onDismissed }: Props): React.JSX.Element | null;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.default = OctoMessage;
|
|
40
|
+
const react_1 = __importStar(require("react"));
|
|
41
|
+
const react_native_1 = require("react-native");
|
|
42
|
+
const api = __importStar(require("../api"));
|
|
43
|
+
const safeNavUrl_1 = require("../safeNavUrl");
|
|
44
|
+
const debug_1 = require("../debug");
|
|
45
|
+
const latency_1 = require("../latency");
|
|
46
|
+
const telemetry_1 = require("../telemetry");
|
|
47
|
+
const screenTargeting_1 = require("../screenTargeting");
|
|
48
|
+
const OctoModal_1 = __importDefault(require("./OctoModal"));
|
|
49
|
+
const OctoToast_1 = __importDefault(require("./OctoToast"));
|
|
50
|
+
const OctoErrorBoundary_1 = __importDefault(require("./OctoErrorBoundary"));
|
|
51
|
+
const DISMISS_ANIMATION_MS = 320;
|
|
52
|
+
const IMAGE_LOAD_SAFETY_MS = 6000;
|
|
53
|
+
function OctoMessage({ message, onCTA, onDismissed }) {
|
|
54
|
+
const [visible, setVisible] = (0, react_1.useState)(false);
|
|
55
|
+
const [imageReady, setImageReady] = (0, react_1.useState)(false);
|
|
56
|
+
const [imageFailed, setImageFailed] = (0, react_1.useState)(false);
|
|
57
|
+
const shownAt = (0, react_1.useRef)(0);
|
|
58
|
+
const firstInteractionAt = (0, react_1.useRef)(null);
|
|
59
|
+
const effectStartAt = (0, react_1.useRef)(0);
|
|
60
|
+
const revealedRef = (0, react_1.useRef)(false);
|
|
61
|
+
const safetyTimerRef = (0, react_1.useRef)(null);
|
|
62
|
+
const reveal = (loaded) => {
|
|
63
|
+
if (revealedRef.current)
|
|
64
|
+
return;
|
|
65
|
+
revealedRef.current = true;
|
|
66
|
+
if (safetyTimerRef.current) {
|
|
67
|
+
clearTimeout(safetyTimerRef.current);
|
|
68
|
+
safetyTimerRef.current = null;
|
|
69
|
+
}
|
|
70
|
+
shownAt.current = Date.now();
|
|
71
|
+
setImageReady(loaded);
|
|
72
|
+
setVisible(true);
|
|
73
|
+
};
|
|
74
|
+
(0, react_1.useEffect)(() => {
|
|
75
|
+
revealedRef.current = false;
|
|
76
|
+
if (safetyTimerRef.current) {
|
|
77
|
+
clearTimeout(safetyTimerRef.current);
|
|
78
|
+
safetyTimerRef.current = null;
|
|
79
|
+
}
|
|
80
|
+
setVisible(false);
|
|
81
|
+
setImageReady(false);
|
|
82
|
+
setImageFailed(false);
|
|
83
|
+
if (!message)
|
|
84
|
+
return;
|
|
85
|
+
effectStartAt.current = Date.now();
|
|
86
|
+
if (!message.image) {
|
|
87
|
+
reveal(false);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
safetyTimerRef.current = setTimeout(() => reveal(false), IMAGE_LOAD_SAFETY_MS);
|
|
91
|
+
return () => {
|
|
92
|
+
if (safetyTimerRef.current) {
|
|
93
|
+
clearTimeout(safetyTimerRef.current);
|
|
94
|
+
safetyTimerRef.current = null;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}, [message]);
|
|
98
|
+
if (!message)
|
|
99
|
+
return null;
|
|
100
|
+
const msg = message;
|
|
101
|
+
const sendId = msg._send_id ?? 0;
|
|
102
|
+
function buildMeta(extra) {
|
|
103
|
+
return {
|
|
104
|
+
duration_ms: Date.now() - shownAt.current,
|
|
105
|
+
time_to_first_interaction_ms: firstInteractionAt.current
|
|
106
|
+
? firstInteractionAt.current - shownAt.current
|
|
107
|
+
: null,
|
|
108
|
+
device: 'mobile',
|
|
109
|
+
platform: react_native_1.Platform.OS,
|
|
110
|
+
image_loaded: imageReady,
|
|
111
|
+
...extra,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function markInteraction() {
|
|
115
|
+
if (!firstInteractionAt.current) {
|
|
116
|
+
firstInteractionAt.current = Date.now();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function handleImpression() {
|
|
120
|
+
const tImpression = Date.now();
|
|
121
|
+
const latency = msg.__timing
|
|
122
|
+
? (0, latency_1.buildLatencyBreakdown)(msg.__timing, {
|
|
123
|
+
showStart: effectStartAt.current,
|
|
124
|
+
shown: shownAt.current,
|
|
125
|
+
impression: tImpression,
|
|
126
|
+
hasImage: !!msg.image,
|
|
127
|
+
})
|
|
128
|
+
: undefined;
|
|
129
|
+
api.trackEvent(sendId, 'impression', buildMeta({ ...(latency ? { latency } : {}), ...(msg.image ? { image_url: msg.image } : {}) }));
|
|
130
|
+
if (latency) {
|
|
131
|
+
const breakdown = { ...latency, layout: msg.layout, platform: react_native_1.Platform.OS, id: msg.id };
|
|
132
|
+
(0, debug_1.debugLog)('[OctoSDK] latency check→visible', breakdown);
|
|
133
|
+
(0, telemetry_1.reportMetric)('inapp_latency_ms', latency.total_ms, breakdown);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function handleCTAPress() {
|
|
137
|
+
markInteraction();
|
|
138
|
+
api.trackEvent(sendId, 'click', buildMeta({ close_method: 'cta' }));
|
|
139
|
+
setVisible(false);
|
|
140
|
+
const safe = (0, safeNavUrl_1.safeNavUrl)(msg.cta?.url);
|
|
141
|
+
if (safe)
|
|
142
|
+
onCTA?.(safe);
|
|
143
|
+
setTimeout(() => onDismissed?.(), DISMISS_ANIMATION_MS);
|
|
144
|
+
}
|
|
145
|
+
function handleNoneEngagement() {
|
|
146
|
+
markInteraction();
|
|
147
|
+
api.trackEvent(sendId, 'click', buildMeta({ close_method: 'cta' }));
|
|
148
|
+
}
|
|
149
|
+
function handleDismiss(method) {
|
|
150
|
+
api.trackEvent(sendId, 'dismiss', buildMeta({ close_method: method }));
|
|
151
|
+
setVisible(false);
|
|
152
|
+
setTimeout(() => onDismissed?.(), DISMISS_ANIMATION_MS);
|
|
153
|
+
}
|
|
154
|
+
function handleImageError() {
|
|
155
|
+
(0, debug_1.debugLog)('[OctoSDK] image load failed', { url: msg.image, platform: react_native_1.Platform.OS, id: msg.id });
|
|
156
|
+
(0, telemetry_1.reportError)('image_load_failed', { url: msg.image, platform: react_native_1.Platform.OS, layout: msg.layout, id: msg.id });
|
|
157
|
+
setImageFailed(true);
|
|
158
|
+
}
|
|
159
|
+
const ctaDevices = msg.cta?.devices;
|
|
160
|
+
const fieldHidesMobile = Array.isArray(ctaDevices) && !ctaDevices.includes('mobile');
|
|
161
|
+
const ctaNoMobileDest = !!msg.cta?.url && (0, screenTargeting_1.isSynced)() && !(0, screenTargeting_1.ctaHasMobileDestination)(msg.cta.url);
|
|
162
|
+
const dropCta = fieldHidesMobile || ctaNoMobileDest;
|
|
163
|
+
const renderMsg = imageFailed || dropCta
|
|
164
|
+
? {
|
|
165
|
+
...msg,
|
|
166
|
+
...(imageFailed ? { image: undefined } : {}),
|
|
167
|
+
...(dropCta ? { cta: undefined, click_mode: 'cta' } : {}),
|
|
168
|
+
}
|
|
169
|
+
: msg;
|
|
170
|
+
const Component = renderMsg.layout === 'toast' ? OctoToast_1.default : OctoModal_1.default;
|
|
171
|
+
const showPreloader = !!msg.image && !visible && !imageFailed;
|
|
172
|
+
return (<>
|
|
173
|
+
{showPreloader ? (<react_native_1.Image source={{ uri: msg.image }} style={{ position: 'absolute', width: 1, height: 1, opacity: 0 }} fadeDuration={0} onLoad={() => reveal(true)} onError={() => {
|
|
174
|
+
handleImageError();
|
|
175
|
+
reveal(false);
|
|
176
|
+
}}/>) : null}
|
|
177
|
+
<OctoErrorBoundary_1.default onError={(error) => {
|
|
178
|
+
(0, telemetry_1.reportError)('render_crash', {
|
|
179
|
+
layout: renderMsg.layout,
|
|
180
|
+
id: renderMsg.id,
|
|
181
|
+
platform: react_native_1.Platform.OS,
|
|
182
|
+
message: error.message,
|
|
183
|
+
});
|
|
184
|
+
onDismissed?.();
|
|
185
|
+
}}>
|
|
186
|
+
<Component message={renderMsg} visible={visible} onImpression={handleImpression} onCTAPress={handleCTAPress} onNoneEngagement={handleNoneEngagement} onDismiss={handleDismiss} onImageError={handleImageError}/>
|
|
187
|
+
</OctoErrorBoundary_1.default>
|
|
188
|
+
</>);
|
|
189
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { InAppMessage } from '../types';
|
|
3
|
+
interface Props {
|
|
4
|
+
message: InAppMessage;
|
|
5
|
+
visible: boolean;
|
|
6
|
+
onImpression: () => void;
|
|
7
|
+
onCTAPress: () => void;
|
|
8
|
+
onNoneEngagement: () => void;
|
|
9
|
+
onDismiss: (method: string) => void;
|
|
10
|
+
onImageError?: () => void;
|
|
11
|
+
}
|
|
12
|
+
export default function OctoModal({ message, visible, onImpression, onCTAPress, onNoneEngagement, onDismiss, onImageError }: Props): React.JSX.Element;
|
|
13
|
+
export {};
|