@kontextso/sdk-react-native 0.0.4
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/dist/index.d.mts +147 -0
- package/dist/index.d.ts +147 -0
- package/dist/index.js +1229 -0
- package/dist/index.mjs +1205 -0
- package/package.json +57 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1229 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
AdsContext: () => AdsContext,
|
|
34
|
+
AdsProvider: () => AdsProvider,
|
|
35
|
+
InlineAd: () => InlineAd_default,
|
|
36
|
+
VISITOR_ID_KEY: () => VISITOR_ID_KEY,
|
|
37
|
+
useAd: () => useAd
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(src_exports);
|
|
40
|
+
|
|
41
|
+
// src/formats/InlineAd.tsx
|
|
42
|
+
var import_react11 = require("react");
|
|
43
|
+
var import_react_native5 = require("react-native");
|
|
44
|
+
|
|
45
|
+
// src/hooks/useAdViewed.tsx
|
|
46
|
+
var import_react5 = require("react");
|
|
47
|
+
|
|
48
|
+
// src/context/AdsProvider.tsx
|
|
49
|
+
var import_react4 = __toESM(require("react"));
|
|
50
|
+
|
|
51
|
+
// src/hooks/useInitializeAds.tsx
|
|
52
|
+
var import_react = require("react");
|
|
53
|
+
|
|
54
|
+
// src/utils.ts
|
|
55
|
+
var UNRETRIABLE_ERRORS = [403, 429];
|
|
56
|
+
var fetchWithTimeout = async (input, init) => {
|
|
57
|
+
const { timeout = 16e3 } = init || {};
|
|
58
|
+
const controller = new AbortController();
|
|
59
|
+
const id = setTimeout(() => controller.abort(), timeout);
|
|
60
|
+
try {
|
|
61
|
+
const response = await fetch(input, {
|
|
62
|
+
...init,
|
|
63
|
+
signal: controller.signal
|
|
64
|
+
});
|
|
65
|
+
clearTimeout(id);
|
|
66
|
+
return response;
|
|
67
|
+
} catch (e) {
|
|
68
|
+
clearTimeout(id);
|
|
69
|
+
throw e;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
var fetchRetry = async (input, init, maxRetries = 3, retryPeriod = 500) => {
|
|
73
|
+
let retries = 0;
|
|
74
|
+
let response;
|
|
75
|
+
let unretriableError;
|
|
76
|
+
while (retries < maxRetries) {
|
|
77
|
+
try {
|
|
78
|
+
response = await fetchWithTimeout(input, init);
|
|
79
|
+
if (UNRETRIABLE_ERRORS.includes(response.status)) {
|
|
80
|
+
unretriableError = new Error(response.statusText);
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
if (response.ok) {
|
|
84
|
+
return response;
|
|
85
|
+
} else {
|
|
86
|
+
throw new Error(`Failed with status ${response.status}`);
|
|
87
|
+
}
|
|
88
|
+
} catch (error) {
|
|
89
|
+
retries++;
|
|
90
|
+
await new Promise((resolve) => setTimeout(resolve, retryPeriod));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (unretriableError) {
|
|
94
|
+
throw unretriableError;
|
|
95
|
+
}
|
|
96
|
+
throw new Error("Failed to fetch after multiple retries");
|
|
97
|
+
};
|
|
98
|
+
var fixUrl = (adserverUrl, ad) => {
|
|
99
|
+
if (ad.content) {
|
|
100
|
+
ad.content = ad.content.replace("/ad/", `${adserverUrl}/ad/`);
|
|
101
|
+
}
|
|
102
|
+
return { ...ad, url: `${adserverUrl}${ad.url}` };
|
|
103
|
+
};
|
|
104
|
+
var mergeAds = ({
|
|
105
|
+
initAds,
|
|
106
|
+
preloadAds,
|
|
107
|
+
streamAds,
|
|
108
|
+
viewedAds
|
|
109
|
+
}) => {
|
|
110
|
+
const ads = [...initAds];
|
|
111
|
+
if (Array.isArray(preloadAds)) {
|
|
112
|
+
for (const ad of preloadAds) {
|
|
113
|
+
const index = ads.findIndex((a) => a.code === ad.code);
|
|
114
|
+
if (index !== -1) {
|
|
115
|
+
ads[index] = ad;
|
|
116
|
+
} else {
|
|
117
|
+
ads.push(ad);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (Array.isArray(streamAds)) {
|
|
122
|
+
for (const ad of streamAds) {
|
|
123
|
+
const index = ads.findIndex(
|
|
124
|
+
(a) => a.code === ad.code && a.messageId === ad.messageId
|
|
125
|
+
);
|
|
126
|
+
if (index !== -1) {
|
|
127
|
+
ads[index] = ad;
|
|
128
|
+
} else {
|
|
129
|
+
ads.push(ad);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
for (const ad of ads) {
|
|
134
|
+
ad.viewed = viewedAds.includes(ad?.id || "placeholder");
|
|
135
|
+
}
|
|
136
|
+
return ads;
|
|
137
|
+
};
|
|
138
|
+
function mergeStyles(propStyles, defaultStyles, overridingStyles) {
|
|
139
|
+
function deepMerge(target, source) {
|
|
140
|
+
if (typeof target !== "object" || typeof source !== "object") {
|
|
141
|
+
return source;
|
|
142
|
+
}
|
|
143
|
+
const result = { ...target };
|
|
144
|
+
for (const key in source) {
|
|
145
|
+
if (source.hasOwnProperty(key)) {
|
|
146
|
+
if (typeof source[key] === "object" && !Array.isArray(source[key])) {
|
|
147
|
+
result[key] = deepMerge(target[key], source[key]);
|
|
148
|
+
} else {
|
|
149
|
+
result[key] = source[key];
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
155
|
+
let mergedStyles = deepMerge({ ...defaultStyles }, propStyles || {});
|
|
156
|
+
mergedStyles = deepMerge(mergedStyles, overridingStyles || {});
|
|
157
|
+
return mergedStyles;
|
|
158
|
+
}
|
|
159
|
+
var parseMessageText = (text) => {
|
|
160
|
+
const parts = [];
|
|
161
|
+
let start = 0;
|
|
162
|
+
const regex = /(\*(.*?)\*)|\[([^\]]+)\]\(([^)]+)\)/g;
|
|
163
|
+
let match;
|
|
164
|
+
while ((match = regex.exec(text)) !== null) {
|
|
165
|
+
if (match.index > start) {
|
|
166
|
+
parts.push({ text: text.substring(start, match.index), textType: "normal" });
|
|
167
|
+
}
|
|
168
|
+
if (match[1]) {
|
|
169
|
+
const themedText = match[2];
|
|
170
|
+
const innerMatches = themedText.match(/\[([^\]]+)\]\(([^)]+)\)/g);
|
|
171
|
+
if (innerMatches) {
|
|
172
|
+
let innerStart = 0;
|
|
173
|
+
innerMatches.forEach((linkMatch) => {
|
|
174
|
+
const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/;
|
|
175
|
+
const innerLink = linkMatch.match(linkRegex);
|
|
176
|
+
const linkIndex = themedText.indexOf(linkMatch, innerStart);
|
|
177
|
+
if (linkIndex > innerStart) {
|
|
178
|
+
parts.push({ text: themedText.substring(innerStart, linkIndex), textType: "themed" });
|
|
179
|
+
}
|
|
180
|
+
parts.push({ text: innerLink[1], textType: "link", url: innerLink[2] });
|
|
181
|
+
innerStart = linkIndex + linkMatch.length;
|
|
182
|
+
});
|
|
183
|
+
if (innerStart < themedText.length) {
|
|
184
|
+
parts.push({ text: themedText.substring(innerStart), textType: "themed" });
|
|
185
|
+
}
|
|
186
|
+
} else {
|
|
187
|
+
parts.push({ text: themedText, textType: "themed" });
|
|
188
|
+
}
|
|
189
|
+
} else if (match[3] && match[4]) {
|
|
190
|
+
parts.push({ text: match[3], textType: "link", url: match[4] });
|
|
191
|
+
}
|
|
192
|
+
start = match.index + match[0].length;
|
|
193
|
+
}
|
|
194
|
+
if (start < text.length) {
|
|
195
|
+
parts.push({ text: text.substring(start), textType: "normal" });
|
|
196
|
+
}
|
|
197
|
+
return parts;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
// src/hooks/useInitializeAds.tsx
|
|
201
|
+
var import_loglevel = __toESM(require("loglevel"));
|
|
202
|
+
|
|
203
|
+
// package.json
|
|
204
|
+
var version = "0.0.4";
|
|
205
|
+
|
|
206
|
+
// src/hooks/useInitializeAds.tsx
|
|
207
|
+
async function initialize(adServerUrl, publisherToken, userId, conversationId, legacyVisitorId, character) {
|
|
208
|
+
import_loglevel.default.log("[BRAIN] init ads started");
|
|
209
|
+
const response = await fetchRetry(
|
|
210
|
+
`${adServerUrl}/init`,
|
|
211
|
+
{
|
|
212
|
+
timeout: 1e4,
|
|
213
|
+
method: "POST",
|
|
214
|
+
body: JSON.stringify({
|
|
215
|
+
publisherToken,
|
|
216
|
+
userId,
|
|
217
|
+
visitorId: userId,
|
|
218
|
+
conversationId,
|
|
219
|
+
sdkVersion: `native-${version}`,
|
|
220
|
+
legacyVisitorId,
|
|
221
|
+
character
|
|
222
|
+
})
|
|
223
|
+
},
|
|
224
|
+
3,
|
|
225
|
+
1e3
|
|
226
|
+
);
|
|
227
|
+
const {
|
|
228
|
+
sessionId,
|
|
229
|
+
enabledPlacements,
|
|
230
|
+
ads,
|
|
231
|
+
sessionDisabled,
|
|
232
|
+
streamAdServer,
|
|
233
|
+
onlyStream,
|
|
234
|
+
defaultReactNativeStyles,
|
|
235
|
+
overridingReactNativeStyles
|
|
236
|
+
} = await response.json();
|
|
237
|
+
const fixedAds = ads.map((ad) => {
|
|
238
|
+
return fixUrl(adServerUrl, ad);
|
|
239
|
+
});
|
|
240
|
+
import_loglevel.default.log("[BRAIN] init ads done");
|
|
241
|
+
return {
|
|
242
|
+
sessionDisabled,
|
|
243
|
+
sessionId,
|
|
244
|
+
enabledPlacements,
|
|
245
|
+
ads: fixedAds,
|
|
246
|
+
streamAdServer,
|
|
247
|
+
onlyStream,
|
|
248
|
+
defaultReactNativeStylesResponse: defaultReactNativeStyles,
|
|
249
|
+
overridingReactNativeStylesResponse: overridingReactNativeStyles
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function useInitializeAds({
|
|
253
|
+
adServerUrl,
|
|
254
|
+
publisherToken,
|
|
255
|
+
userId,
|
|
256
|
+
conversationId,
|
|
257
|
+
isDisabled,
|
|
258
|
+
character,
|
|
259
|
+
legacyVisitorId
|
|
260
|
+
}) {
|
|
261
|
+
const [enabledPlacements, setEnabledPlacements] = (0, import_react.useState)([]);
|
|
262
|
+
const [ads, setAds] = (0, import_react.useState)([]);
|
|
263
|
+
const [error, setError] = (0, import_react.useState)();
|
|
264
|
+
const [streamAdServerUrl, setStreamAdServerUrl] = (0, import_react.useState)();
|
|
265
|
+
const [onlyStream, setOnlyStream] = (0, import_react.useState)(false);
|
|
266
|
+
const [sessionId, setSessionId] = (0, import_react.useState)();
|
|
267
|
+
const [defaultReactNativeStyles, setDefaultReactNativeStyles] = (0, import_react.useState)();
|
|
268
|
+
const [overridingReactNativeStyles, setOverridingReactNativeStyles] = (0, import_react.useState)();
|
|
269
|
+
(0, import_react.useEffect)(() => {
|
|
270
|
+
setSessionId(void 0);
|
|
271
|
+
if (!isDisabled && userId) {
|
|
272
|
+
import_loglevel.default.debug("[BRAIN] Initializing ads.");
|
|
273
|
+
initialize(
|
|
274
|
+
adServerUrl,
|
|
275
|
+
publisherToken,
|
|
276
|
+
userId,
|
|
277
|
+
conversationId,
|
|
278
|
+
legacyVisitorId,
|
|
279
|
+
character
|
|
280
|
+
).then(
|
|
281
|
+
({
|
|
282
|
+
sessionId: sessionId2,
|
|
283
|
+
enabledPlacements: enabledPlacements2,
|
|
284
|
+
ads: ads2,
|
|
285
|
+
sessionDisabled,
|
|
286
|
+
streamAdServer,
|
|
287
|
+
onlyStream: onlyStream2,
|
|
288
|
+
defaultReactNativeStylesResponse,
|
|
289
|
+
overridingReactNativeStylesResponse
|
|
290
|
+
}) => {
|
|
291
|
+
if (!sessionDisabled) {
|
|
292
|
+
setSessionId(sessionId2);
|
|
293
|
+
setEnabledPlacements(enabledPlacements2);
|
|
294
|
+
setAds(ads2);
|
|
295
|
+
setStreamAdServerUrl(streamAdServer);
|
|
296
|
+
setOnlyStream(onlyStream2);
|
|
297
|
+
setDefaultReactNativeStyles(defaultReactNativeStylesResponse);
|
|
298
|
+
setOverridingReactNativeStyles(overridingReactNativeStylesResponse);
|
|
299
|
+
} else {
|
|
300
|
+
import_loglevel.default.debug("[BRAIN] Session is disabled by server.");
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
).catch((e) => {
|
|
304
|
+
import_loglevel.default.error("[BRAIN] Error initializing ads", e);
|
|
305
|
+
setError(e.message);
|
|
306
|
+
});
|
|
307
|
+
} else {
|
|
308
|
+
import_loglevel.default.debug("[BRAIN] Ads are disabled.");
|
|
309
|
+
}
|
|
310
|
+
}, [
|
|
311
|
+
adServerUrl,
|
|
312
|
+
publisherToken,
|
|
313
|
+
userId,
|
|
314
|
+
conversationId,
|
|
315
|
+
isDisabled
|
|
316
|
+
]);
|
|
317
|
+
return {
|
|
318
|
+
ads,
|
|
319
|
+
sessionId,
|
|
320
|
+
enabledPlacements,
|
|
321
|
+
error,
|
|
322
|
+
streamAdServerUrl,
|
|
323
|
+
onlyStream,
|
|
324
|
+
defaultReactNativeStyles,
|
|
325
|
+
overridingReactNativeStyles
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// src/hooks/usePreloadAds.tsx
|
|
330
|
+
var import_react2 = require("react");
|
|
331
|
+
var import_loglevel2 = __toESM(require("loglevel"));
|
|
332
|
+
function usePreloadAds({
|
|
333
|
+
userId,
|
|
334
|
+
sessionId,
|
|
335
|
+
conversationId,
|
|
336
|
+
adserverUrl,
|
|
337
|
+
messages,
|
|
338
|
+
publisherToken,
|
|
339
|
+
character,
|
|
340
|
+
onlyStream
|
|
341
|
+
}) {
|
|
342
|
+
const [preloadDone, setPreloadDone] = (0, import_react2.useState)(false);
|
|
343
|
+
const [ads, setAds] = (0, import_react2.useState)([]);
|
|
344
|
+
const userMessagesContent = messages.filter((m) => m.role === "user").map((m) => m.content).join(" ");
|
|
345
|
+
(0, import_react2.useEffect)(() => {
|
|
346
|
+
if (onlyStream) {
|
|
347
|
+
setPreloadDone(true);
|
|
348
|
+
import_loglevel2.default.log("[BRAIN] skipping preload ads");
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
async function preload() {
|
|
352
|
+
setPreloadDone(false);
|
|
353
|
+
if (!sessionId) {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
import_loglevel2.default.log("[BRAIN] preload ads started");
|
|
357
|
+
try {
|
|
358
|
+
const response = await fetchRetry(
|
|
359
|
+
`${adserverUrl}/preload`,
|
|
360
|
+
{
|
|
361
|
+
timeout: 1e4,
|
|
362
|
+
method: "POST",
|
|
363
|
+
body: JSON.stringify({
|
|
364
|
+
messages,
|
|
365
|
+
sessionId,
|
|
366
|
+
userId,
|
|
367
|
+
visitorId: userId,
|
|
368
|
+
publisherToken,
|
|
369
|
+
conversationId,
|
|
370
|
+
character,
|
|
371
|
+
sdkVersion: "native"
|
|
372
|
+
})
|
|
373
|
+
},
|
|
374
|
+
3,
|
|
375
|
+
1e3
|
|
376
|
+
);
|
|
377
|
+
const { ads: ads2 } = await response.json();
|
|
378
|
+
setAds((oldAds) => {
|
|
379
|
+
const newAds = ads2.map((ad) => fixUrl(adserverUrl, ad));
|
|
380
|
+
return [...oldAds, ...newAds];
|
|
381
|
+
});
|
|
382
|
+
setPreloadDone(true);
|
|
383
|
+
import_loglevel2.default.log("[BRAIN] preload ads finished");
|
|
384
|
+
} catch (e) {
|
|
385
|
+
import_loglevel2.default.error("[BRAIN] Error preloading ads", e);
|
|
386
|
+
setPreloadDone(true);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
preload();
|
|
390
|
+
}, [sessionId, userMessagesContent, conversationId]);
|
|
391
|
+
return { preloadDone, ads };
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// src/hooks/useStreamAds.tsx
|
|
395
|
+
var import_react3 = require("react");
|
|
396
|
+
|
|
397
|
+
// src/hooks/data-stream.ts
|
|
398
|
+
var textStreamPart = {
|
|
399
|
+
code: "0",
|
|
400
|
+
name: "text",
|
|
401
|
+
parse: (value) => {
|
|
402
|
+
if (typeof value !== "string") {
|
|
403
|
+
throw new Error('"text" parts expect a string value.');
|
|
404
|
+
}
|
|
405
|
+
return { type: "text", value };
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
var functionCallStreamPart = {
|
|
409
|
+
code: "1",
|
|
410
|
+
name: "function_call",
|
|
411
|
+
parse: (value) => {
|
|
412
|
+
if (value == null || typeof value !== "object" || !("function_call" in value) || typeof value.function_call !== "object" || value.function_call == null || !("name" in value.function_call) || !("arguments" in value.function_call) || typeof value.function_call.name !== "string" || typeof value.function_call.arguments !== "string") {
|
|
413
|
+
throw new Error(
|
|
414
|
+
'"function_call" parts expect an object with a "function_call" property.'
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
return {
|
|
418
|
+
type: "function_call",
|
|
419
|
+
value
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
var dataStreamPart = {
|
|
424
|
+
code: "2",
|
|
425
|
+
name: "data",
|
|
426
|
+
parse: (value) => {
|
|
427
|
+
if (!Array.isArray(value)) {
|
|
428
|
+
throw new Error('"data" parts expect an array value.');
|
|
429
|
+
}
|
|
430
|
+
return { type: "data", value };
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
var errorStreamPart = {
|
|
434
|
+
code: "3",
|
|
435
|
+
name: "error",
|
|
436
|
+
parse: (value) => {
|
|
437
|
+
if (typeof value !== "string") {
|
|
438
|
+
throw new Error('"error" parts expect a string value.');
|
|
439
|
+
}
|
|
440
|
+
return { type: "error", value };
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
var assistantMessageStreamPart = {
|
|
444
|
+
code: "4",
|
|
445
|
+
name: "assistant_message",
|
|
446
|
+
parse: (value) => {
|
|
447
|
+
if (value == null || typeof value !== "object" || !("id" in value) || !("role" in value) || !("content" in value) || typeof value.id !== "string" || typeof value.role !== "string" || value.role !== "assistant" || !Array.isArray(value.content) || !value.content.every(
|
|
448
|
+
(item) => item != null && typeof item === "object" && "type" in item && item.type === "text" && "text" in item && item.text != null && typeof item.text === "object" && "value" in item.text && typeof item.text.value === "string"
|
|
449
|
+
)) {
|
|
450
|
+
throw new Error(
|
|
451
|
+
'"assistant_message" parts expect an object with an "id", "role", and "content" property.'
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
return {
|
|
455
|
+
type: "assistant_message",
|
|
456
|
+
value
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
var assistantControlDataStreamPart = {
|
|
461
|
+
code: "5",
|
|
462
|
+
name: "assistant_control_data",
|
|
463
|
+
parse: (value) => {
|
|
464
|
+
if (value == null || typeof value !== "object" || !("threadId" in value) || !("messageId" in value) || typeof value.threadId !== "string" || typeof value.messageId !== "string") {
|
|
465
|
+
throw new Error(
|
|
466
|
+
'"assistant_control_data" parts expect an object with a "threadId" and "messageId" property.'
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
return {
|
|
470
|
+
type: "assistant_control_data",
|
|
471
|
+
value: {
|
|
472
|
+
threadId: value.threadId,
|
|
473
|
+
messageId: value.messageId
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
};
|
|
478
|
+
var dataMessageStreamPart = {
|
|
479
|
+
code: "6",
|
|
480
|
+
name: "data_message",
|
|
481
|
+
parse: (value) => {
|
|
482
|
+
if (value == null || typeof value !== "object" || !("role" in value) || !("data" in value) || typeof value.role !== "string" || value.role !== "data") {
|
|
483
|
+
throw new Error(
|
|
484
|
+
'"data_message" parts expect an object with a "role" and "data" property.'
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
return {
|
|
488
|
+
type: "data_message",
|
|
489
|
+
value
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
var toolCallStreamPart = {
|
|
494
|
+
code: "7",
|
|
495
|
+
name: "tool_calls",
|
|
496
|
+
parse: (value) => {
|
|
497
|
+
if (value == null || typeof value !== "object" || !("tool_calls" in value) || typeof value.tool_calls !== "object" || value.tool_calls == null || !Array.isArray(value.tool_calls) || value.tool_calls.some((tc) => {
|
|
498
|
+
tc == null || typeof tc !== "object" || !("id" in tc) || typeof tc.id !== "string" || !("type" in tc) || typeof tc.type !== "string" || !("function" in tc) || tc.function == null || typeof tc.function !== "object" || !("arguments" in tc.function) || typeof tc.function.name !== "string" || typeof tc.function.arguments !== "string";
|
|
499
|
+
})) {
|
|
500
|
+
throw new Error(
|
|
501
|
+
'"tool_calls" parts expect an object with a ToolCallPayload.'
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
return {
|
|
505
|
+
type: "tool_calls",
|
|
506
|
+
value
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
var messageAnnotationsStreamPart = {
|
|
511
|
+
code: "8",
|
|
512
|
+
name: "message_annotations",
|
|
513
|
+
parse: (value) => {
|
|
514
|
+
if (!Array.isArray(value)) {
|
|
515
|
+
throw new Error('"message_annotations" parts expect an array value.');
|
|
516
|
+
}
|
|
517
|
+
return { type: "message_annotations", value };
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
var streamParts = [
|
|
521
|
+
textStreamPart,
|
|
522
|
+
functionCallStreamPart,
|
|
523
|
+
dataStreamPart,
|
|
524
|
+
errorStreamPart,
|
|
525
|
+
assistantMessageStreamPart,
|
|
526
|
+
assistantControlDataStreamPart,
|
|
527
|
+
dataMessageStreamPart,
|
|
528
|
+
toolCallStreamPart,
|
|
529
|
+
messageAnnotationsStreamPart
|
|
530
|
+
];
|
|
531
|
+
var streamPartsByCode = {
|
|
532
|
+
[textStreamPart.code]: textStreamPart,
|
|
533
|
+
[functionCallStreamPart.code]: functionCallStreamPart,
|
|
534
|
+
[dataStreamPart.code]: dataStreamPart,
|
|
535
|
+
[errorStreamPart.code]: errorStreamPart,
|
|
536
|
+
[assistantMessageStreamPart.code]: assistantMessageStreamPart,
|
|
537
|
+
[assistantControlDataStreamPart.code]: assistantControlDataStreamPart,
|
|
538
|
+
[dataMessageStreamPart.code]: dataMessageStreamPart,
|
|
539
|
+
[toolCallStreamPart.code]: toolCallStreamPart,
|
|
540
|
+
[messageAnnotationsStreamPart.code]: messageAnnotationsStreamPart
|
|
541
|
+
};
|
|
542
|
+
var StreamStringPrefixes = {
|
|
543
|
+
[textStreamPart.name]: textStreamPart.code,
|
|
544
|
+
[functionCallStreamPart.name]: functionCallStreamPart.code,
|
|
545
|
+
[dataStreamPart.name]: dataStreamPart.code,
|
|
546
|
+
[errorStreamPart.name]: errorStreamPart.code,
|
|
547
|
+
[assistantMessageStreamPart.name]: assistantMessageStreamPart.code,
|
|
548
|
+
[assistantControlDataStreamPart.name]: assistantControlDataStreamPart.code,
|
|
549
|
+
[dataMessageStreamPart.name]: dataMessageStreamPart.code,
|
|
550
|
+
[toolCallStreamPart.name]: toolCallStreamPart.code,
|
|
551
|
+
[messageAnnotationsStreamPart.name]: messageAnnotationsStreamPart.code
|
|
552
|
+
};
|
|
553
|
+
var validCodes = streamParts.map((part) => part.code);
|
|
554
|
+
var parseStreamPart = (line) => {
|
|
555
|
+
const firstSeparatorIndex = line.indexOf(":");
|
|
556
|
+
if (firstSeparatorIndex === -1) {
|
|
557
|
+
throw new Error("Failed to parse stream string. No separator found.");
|
|
558
|
+
}
|
|
559
|
+
const prefix = line.slice(0, firstSeparatorIndex);
|
|
560
|
+
if (!validCodes.includes(prefix)) {
|
|
561
|
+
throw new Error(`Failed to parse stream string. Invalid code ${prefix}.`);
|
|
562
|
+
}
|
|
563
|
+
const code = prefix;
|
|
564
|
+
const textValue = line.slice(firstSeparatorIndex + 1);
|
|
565
|
+
const any = JSON.parse(textValue);
|
|
566
|
+
return streamPartsByCode[code].parse(any);
|
|
567
|
+
};
|
|
568
|
+
var NEWLINE = "\n".charCodeAt(0);
|
|
569
|
+
function concatChunks(chunks, totalLength) {
|
|
570
|
+
const concatenatedChunks = new Uint8Array(totalLength);
|
|
571
|
+
let offset = 0;
|
|
572
|
+
for (const chunk of chunks) {
|
|
573
|
+
concatenatedChunks.set(chunk, offset);
|
|
574
|
+
offset += chunk.length;
|
|
575
|
+
}
|
|
576
|
+
chunks.length = 0;
|
|
577
|
+
return concatenatedChunks;
|
|
578
|
+
}
|
|
579
|
+
async function* readDataStream(reader, {
|
|
580
|
+
isAborted
|
|
581
|
+
} = {}) {
|
|
582
|
+
const decoder = new TextDecoder();
|
|
583
|
+
const chunks = [];
|
|
584
|
+
let totalLength = 0;
|
|
585
|
+
while (true) {
|
|
586
|
+
const { value } = await reader.read();
|
|
587
|
+
if (value) {
|
|
588
|
+
chunks.push(value);
|
|
589
|
+
totalLength += value.length;
|
|
590
|
+
if (value[value.length - 1] !== NEWLINE) {
|
|
591
|
+
continue;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
if (chunks.length === 0) {
|
|
595
|
+
break;
|
|
596
|
+
}
|
|
597
|
+
const concatenatedChunks = concatChunks(chunks, totalLength);
|
|
598
|
+
totalLength = 0;
|
|
599
|
+
const streamParts2 = decoder.decode(concatenatedChunks, { stream: true }).split("\n").filter((line) => line !== "").map(parseStreamPart);
|
|
600
|
+
let i = 0;
|
|
601
|
+
for (const streamPart of streamParts2) {
|
|
602
|
+
if (!(i % 5)) {
|
|
603
|
+
await new Promise((resolve) => setTimeout(resolve));
|
|
604
|
+
}
|
|
605
|
+
i++;
|
|
606
|
+
yield streamPart;
|
|
607
|
+
}
|
|
608
|
+
if (isAborted?.()) {
|
|
609
|
+
reader.cancel();
|
|
610
|
+
break;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// src/hooks/useStreamAds.tsx
|
|
616
|
+
var import_encoding = require("react-native-polyfill-globals/src/encoding");
|
|
617
|
+
var import_readable_stream = require("react-native-polyfill-globals/src/readable-stream");
|
|
618
|
+
var import_react_native = require("react-native");
|
|
619
|
+
var import_loglevel3 = __toESM(require("loglevel"));
|
|
620
|
+
var patchFetch = fetch;
|
|
621
|
+
if (import_react_native.Platform.OS !== "web") {
|
|
622
|
+
(0, import_encoding.polyfill)();
|
|
623
|
+
(0, import_readable_stream.polyfill)();
|
|
624
|
+
patchFetch = require("react-native-fetch-api").fetch;
|
|
625
|
+
}
|
|
626
|
+
ErrorUtils.setGlobalHandler((error, isFatal) => {
|
|
627
|
+
import_loglevel3.default.error(error);
|
|
628
|
+
});
|
|
629
|
+
function useStreamAds({
|
|
630
|
+
userId,
|
|
631
|
+
sessionId,
|
|
632
|
+
conversationId,
|
|
633
|
+
adserverUrl,
|
|
634
|
+
messages,
|
|
635
|
+
isLoading,
|
|
636
|
+
preloadDone,
|
|
637
|
+
enabledPlacements,
|
|
638
|
+
character,
|
|
639
|
+
publisherToken
|
|
640
|
+
}) {
|
|
641
|
+
const [ads, setAds] = (0, import_react3.useState)([]);
|
|
642
|
+
const fetchStream = async (messages2, code) => {
|
|
643
|
+
const lastAssistantMessage = [...messages2].reverse().find((m) => m.role === "assistant");
|
|
644
|
+
if (!lastAssistantMessage) {
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
setAds((oldAds) => {
|
|
648
|
+
const newAds = [...oldAds];
|
|
649
|
+
newAds.push({
|
|
650
|
+
isLoading: true,
|
|
651
|
+
code,
|
|
652
|
+
messageId: lastAssistantMessage.id
|
|
653
|
+
});
|
|
654
|
+
return newAds;
|
|
655
|
+
});
|
|
656
|
+
const body = JSON.stringify({
|
|
657
|
+
userId,
|
|
658
|
+
visitorId: userId,
|
|
659
|
+
sessionId,
|
|
660
|
+
code,
|
|
661
|
+
messages: messages2,
|
|
662
|
+
publisherToken,
|
|
663
|
+
character,
|
|
664
|
+
conversationId,
|
|
665
|
+
sdkVersion: "native"
|
|
666
|
+
});
|
|
667
|
+
try {
|
|
668
|
+
const response = await patchFetch(`${adserverUrl}/stream`, {
|
|
669
|
+
method: "POST",
|
|
670
|
+
body,
|
|
671
|
+
headers: {
|
|
672
|
+
"Content-Type": "application/json"
|
|
673
|
+
},
|
|
674
|
+
reactNative: { textStreaming: true }
|
|
675
|
+
});
|
|
676
|
+
if (!response.ok) {
|
|
677
|
+
throw new Error("Error streaming ad");
|
|
678
|
+
}
|
|
679
|
+
if (!response.body) {
|
|
680
|
+
throw new Error("Response body is not a readable stream");
|
|
681
|
+
}
|
|
682
|
+
setAds(
|
|
683
|
+
(oldAds) => oldAds.map((ad) => ad.messageId === lastAssistantMessage.id && ad.code === code ? { ...ad, isLoading: false, isStreaming: true } : ad)
|
|
684
|
+
);
|
|
685
|
+
let data = "";
|
|
686
|
+
let adData = {};
|
|
687
|
+
import_loglevel3.default.log(`[BRAIN] streaming ${code} ad started`);
|
|
688
|
+
const reader = response.body.getReader();
|
|
689
|
+
for await (const { type, value } of readDataStream(reader)) {
|
|
690
|
+
switch (type) {
|
|
691
|
+
case "text":
|
|
692
|
+
data += value;
|
|
693
|
+
break;
|
|
694
|
+
case "data": {
|
|
695
|
+
let val = value;
|
|
696
|
+
if (Array.isArray(val)) {
|
|
697
|
+
val = val[0];
|
|
698
|
+
}
|
|
699
|
+
adData = { ...adData, ...val };
|
|
700
|
+
break;
|
|
701
|
+
}
|
|
702
|
+
case "error":
|
|
703
|
+
throw new Error(`Error streaming ad ${value}`);
|
|
704
|
+
}
|
|
705
|
+
setAds(
|
|
706
|
+
(oldAds) => oldAds.map((ad) => {
|
|
707
|
+
if (ad.messageId === lastAssistantMessage.id && ad.code === code) {
|
|
708
|
+
const content = adData.content ?? data;
|
|
709
|
+
const newAd = fixUrl(adserverUrl, {
|
|
710
|
+
...ad,
|
|
711
|
+
...adData,
|
|
712
|
+
isLoading: false,
|
|
713
|
+
isStreaming: true,
|
|
714
|
+
content: content.replace(
|
|
715
|
+
new RegExp(`/ad/${adData.product.id}/redirect`, "g"),
|
|
716
|
+
`/ad/${adData.id}/redirect`
|
|
717
|
+
)
|
|
718
|
+
});
|
|
719
|
+
return newAd;
|
|
720
|
+
}
|
|
721
|
+
return ad;
|
|
722
|
+
})
|
|
723
|
+
);
|
|
724
|
+
}
|
|
725
|
+
setAds(
|
|
726
|
+
(oldAds) => oldAds.map(
|
|
727
|
+
(ad) => ad.messageId === lastAssistantMessage.id && ad.code === code ? { ...ad, isStreaming: false } : ad
|
|
728
|
+
)
|
|
729
|
+
);
|
|
730
|
+
import_loglevel3.default.log(`[BRAIN] streaming ${code} ad done`);
|
|
731
|
+
} catch (e) {
|
|
732
|
+
import_loglevel3.default.error("[BRAIN] Error streaming ad", e);
|
|
733
|
+
setAds((oldAds) => [...oldAds, { isError: true, code }]);
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
(0, import_react3.useEffect)(() => {
|
|
737
|
+
if (!isLoading && sessionId && messages.length > 2 && messages[messages.length - 1]?.role === "assistant" && preloadDone) {
|
|
738
|
+
enabledPlacements.forEach((placement) => {
|
|
739
|
+
if (["QUERY_STREAM", "INLINE_AD", "BOX_AD"].includes(placement.format)) {
|
|
740
|
+
fetchStream(messages, String(placement.code));
|
|
741
|
+
}
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
}, [isLoading, preloadDone, sessionId, messages[messages.length - 1]?.role]);
|
|
745
|
+
return { ads };
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// src/context/AdsProvider.tsx
|
|
749
|
+
var import_loglevel4 = __toESM(require("loglevel"));
|
|
750
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
751
|
+
var AdsContext = import_react4.default.createContext(null);
|
|
752
|
+
AdsContext.displayName = "AdsContext";
|
|
753
|
+
var VISITOR_ID_KEY = "brain-visitor-id";
|
|
754
|
+
var AdsProvider = ({
|
|
755
|
+
children,
|
|
756
|
+
messages,
|
|
757
|
+
publisherToken,
|
|
758
|
+
isLoading,
|
|
759
|
+
adserverUrl,
|
|
760
|
+
isDisabled,
|
|
761
|
+
character,
|
|
762
|
+
conversationId,
|
|
763
|
+
userId,
|
|
764
|
+
logLevel,
|
|
765
|
+
onAdView,
|
|
766
|
+
onAdClick,
|
|
767
|
+
...props
|
|
768
|
+
}) => {
|
|
769
|
+
const [viewedAds, setViewedAds] = (0, import_react4.useState)([]);
|
|
770
|
+
const adServerUrlOrDefault = adserverUrl || "https://server.megabrain.co";
|
|
771
|
+
import_loglevel4.default.setLevel(logLevel || "ERROR");
|
|
772
|
+
const {
|
|
773
|
+
ads: initAds,
|
|
774
|
+
sessionId,
|
|
775
|
+
enabledPlacements,
|
|
776
|
+
error,
|
|
777
|
+
streamAdServerUrl,
|
|
778
|
+
onlyStream,
|
|
779
|
+
defaultReactNativeStyles,
|
|
780
|
+
overridingReactNativeStyles
|
|
781
|
+
} = useInitializeAds({
|
|
782
|
+
adServerUrl: adServerUrlOrDefault,
|
|
783
|
+
publisherToken,
|
|
784
|
+
userId,
|
|
785
|
+
legacyVisitorId: void 0,
|
|
786
|
+
conversationId,
|
|
787
|
+
character,
|
|
788
|
+
isDisabled: !!isDisabled
|
|
789
|
+
});
|
|
790
|
+
const styles = mergeStyles(props.styles, defaultReactNativeStyles, overridingReactNativeStyles);
|
|
791
|
+
const isInitialised = !!isDisabled || !!error || !!sessionId;
|
|
792
|
+
const { preloadDone, ads: preloadAds } = usePreloadAds({
|
|
793
|
+
sessionId,
|
|
794
|
+
adserverUrl: adServerUrlOrDefault,
|
|
795
|
+
messages,
|
|
796
|
+
publisherToken,
|
|
797
|
+
conversationId,
|
|
798
|
+
userId,
|
|
799
|
+
character,
|
|
800
|
+
onlyStream
|
|
801
|
+
});
|
|
802
|
+
const { ads: streamAds } = useStreamAds({
|
|
803
|
+
userId,
|
|
804
|
+
sessionId,
|
|
805
|
+
adserverUrl: onlyStream && streamAdServerUrl ? streamAdServerUrl : adServerUrlOrDefault,
|
|
806
|
+
conversationId,
|
|
807
|
+
messages,
|
|
808
|
+
isLoading,
|
|
809
|
+
character,
|
|
810
|
+
preloadDone,
|
|
811
|
+
enabledPlacements,
|
|
812
|
+
publisherToken
|
|
813
|
+
});
|
|
814
|
+
const ads = mergeAds({ initAds, preloadAds, streamAds, viewedAds });
|
|
815
|
+
const markAdAsViewed = (ad) => {
|
|
816
|
+
import_loglevel4.default.debug("[Brain] Calling onAdView");
|
|
817
|
+
onAdView && onAdView({
|
|
818
|
+
id: ad.id,
|
|
819
|
+
code: ad.code,
|
|
820
|
+
messageId: ad.messageId,
|
|
821
|
+
content: ad.content
|
|
822
|
+
});
|
|
823
|
+
if (!ad.id) return;
|
|
824
|
+
if (viewedAds.includes(ad.id)) return;
|
|
825
|
+
setViewedAds((old) => [...old, ad.id]);
|
|
826
|
+
};
|
|
827
|
+
const onAdClickInternal = (ad) => {
|
|
828
|
+
import_loglevel4.default.debug("[Brain] Calling onAdClick");
|
|
829
|
+
onAdClick && onAdClick({
|
|
830
|
+
id: ad.id,
|
|
831
|
+
code: ad.code,
|
|
832
|
+
messageId: ad.messageId,
|
|
833
|
+
content: ad.content
|
|
834
|
+
});
|
|
835
|
+
};
|
|
836
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
837
|
+
AdsContext.Provider,
|
|
838
|
+
{
|
|
839
|
+
value: {
|
|
840
|
+
adserverUrl: adServerUrlOrDefault,
|
|
841
|
+
streamAdServerUrl,
|
|
842
|
+
children,
|
|
843
|
+
messages,
|
|
844
|
+
publisherToken,
|
|
845
|
+
isLoading,
|
|
846
|
+
ads,
|
|
847
|
+
sessionId,
|
|
848
|
+
isInitialised,
|
|
849
|
+
isDisabled,
|
|
850
|
+
onAdClickInternal,
|
|
851
|
+
enabledPlacements,
|
|
852
|
+
userId,
|
|
853
|
+
markAdAsViewed,
|
|
854
|
+
onlyStream,
|
|
855
|
+
styles
|
|
856
|
+
},
|
|
857
|
+
children
|
|
858
|
+
}
|
|
859
|
+
);
|
|
860
|
+
};
|
|
861
|
+
|
|
862
|
+
// src/hooks/useAdViewed.tsx
|
|
863
|
+
var import_react6 = require("react");
|
|
864
|
+
var import_loglevel5 = __toESM(require("loglevel"));
|
|
865
|
+
function useAdViewed(ad) {
|
|
866
|
+
const context = (0, import_react6.useContext)(AdsContext);
|
|
867
|
+
const [stillMounted, setStillMounted] = (0, import_react5.useState)(false);
|
|
868
|
+
const sendRequest = async () => {
|
|
869
|
+
if (!context?.adserverUrl || !ad?.id) {
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
context.markAdAsViewed(ad);
|
|
873
|
+
let serverUrl = context.adserverUrl;
|
|
874
|
+
if (context.onlyStream && context.streamAdServerUrl) {
|
|
875
|
+
serverUrl = context.streamAdServerUrl;
|
|
876
|
+
}
|
|
877
|
+
try {
|
|
878
|
+
const response = await fetch(`${serverUrl}/ad/${ad.id}/view`, {
|
|
879
|
+
method: "POST"
|
|
880
|
+
});
|
|
881
|
+
if (!response.ok) {
|
|
882
|
+
throw new Error("Error sending view request");
|
|
883
|
+
}
|
|
884
|
+
import_loglevel5.default.log("[BRAIN] ad marked as viewed", ad.id, ad.code);
|
|
885
|
+
} catch (e) {
|
|
886
|
+
import_loglevel5.default.error("[BRAIN] Error sending view request", e);
|
|
887
|
+
}
|
|
888
|
+
};
|
|
889
|
+
(0, import_react5.useEffect)(() => {
|
|
890
|
+
if (!ad || ad.isError || ad.isLoading || ad.isStreaming || ad.viewed || stillMounted) {
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
import_loglevel5.default.log("[BRAIN] setting timeout", ad.id, ad.code);
|
|
894
|
+
setTimeout(() => {
|
|
895
|
+
import_loglevel5.default.log("[BRAIN] setting setStillMounted", ad.id, ad.code);
|
|
896
|
+
setStillMounted(true);
|
|
897
|
+
}, 1e3);
|
|
898
|
+
}, [ad]);
|
|
899
|
+
(0, import_react5.useEffect)(() => {
|
|
900
|
+
if (stillMounted) {
|
|
901
|
+
import_loglevel5.default.log("[BRAIN] sending request");
|
|
902
|
+
sendRequest();
|
|
903
|
+
}
|
|
904
|
+
}, [stillMounted]);
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
// src/formats/InlineAd.tsx
|
|
908
|
+
var import_loglevel8 = __toESM(require("loglevel"));
|
|
909
|
+
|
|
910
|
+
// src/components/MarkdownText.tsx
|
|
911
|
+
var import_loglevel6 = __toESM(require("loglevel"));
|
|
912
|
+
var import_react7 = require("react");
|
|
913
|
+
var import_react_native2 = require("react-native");
|
|
914
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
915
|
+
function MarkdownText({
|
|
916
|
+
content,
|
|
917
|
+
onLinkClick
|
|
918
|
+
}) {
|
|
919
|
+
const context = (0, import_react7.useContext)(AdsContext);
|
|
920
|
+
const textParts = parseMessageText(content);
|
|
921
|
+
const styles = context?.styles?.markdownText;
|
|
922
|
+
const linkClickHandler = (href) => {
|
|
923
|
+
onLinkClick();
|
|
924
|
+
import_react_native2.Linking.openURL(href).catch(
|
|
925
|
+
(err) => import_loglevel6.default.error("Failed to open URL:", err)
|
|
926
|
+
);
|
|
927
|
+
return false;
|
|
928
|
+
};
|
|
929
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: textParts.map((t, i) => {
|
|
930
|
+
if (t.textType === "link") {
|
|
931
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
932
|
+
import_react_native2.Text,
|
|
933
|
+
{
|
|
934
|
+
style: styles?.link,
|
|
935
|
+
onPress: () => {
|
|
936
|
+
linkClickHandler(t.url);
|
|
937
|
+
},
|
|
938
|
+
children: t.text
|
|
939
|
+
},
|
|
940
|
+
i + "link"
|
|
941
|
+
);
|
|
942
|
+
} else if (t.textType === "themed") {
|
|
943
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: styles?.em, children: t.text }, i + "em");
|
|
944
|
+
} else {
|
|
945
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: styles?.normal, children: t.text }, i + "normal");
|
|
946
|
+
}
|
|
947
|
+
}) });
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
// src/components/VideoPlayer.tsx
|
|
951
|
+
var import_expo_av = require("expo-av");
|
|
952
|
+
var import_loglevel7 = __toESM(require("loglevel"));
|
|
953
|
+
var import_react9 = require("react");
|
|
954
|
+
var import_react_native4 = require("react-native");
|
|
955
|
+
|
|
956
|
+
// src/components/VideoProgressBar.tsx
|
|
957
|
+
var import_react8 = require("react");
|
|
958
|
+
var import_react_native3 = require("react-native");
|
|
959
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
960
|
+
function VideoProgressBar({ videoRef }) {
|
|
961
|
+
const [progress, setProgress] = (0, import_react8.useState)(0);
|
|
962
|
+
const styles = (0, import_react8.useContext)(AdsContext)?.styles?.videoPlayer?.videoProgress;
|
|
963
|
+
(0, import_react8.useEffect)(() => {
|
|
964
|
+
const interval = setInterval(() => {
|
|
965
|
+
if (!videoRef.current) {
|
|
966
|
+
return;
|
|
967
|
+
}
|
|
968
|
+
videoRef.current.getStatusAsync().then((status) => {
|
|
969
|
+
if (status.isLoaded) {
|
|
970
|
+
const currentTime = status.positionMillis;
|
|
971
|
+
const duration = status.durationMillis;
|
|
972
|
+
const percentage = currentTime / duration * 100;
|
|
973
|
+
setProgress(percentage);
|
|
974
|
+
}
|
|
975
|
+
});
|
|
976
|
+
}, 1e3);
|
|
977
|
+
return () => clearInterval(interval);
|
|
978
|
+
}, [videoRef.current]);
|
|
979
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: styles?.progressContainer, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: [styles?.progressBar, { width: `${progress}%` }] }) });
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
// src/components/VideoPlayer.tsx
|
|
983
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
984
|
+
var VideoPlayer = ({
|
|
985
|
+
src,
|
|
986
|
+
onProgress,
|
|
987
|
+
endText,
|
|
988
|
+
badgeText,
|
|
989
|
+
videoPosterUrl,
|
|
990
|
+
videoOrientation,
|
|
991
|
+
ctaText,
|
|
992
|
+
clickUrl,
|
|
993
|
+
mediaPlacement,
|
|
994
|
+
onLinkClick
|
|
995
|
+
}) => {
|
|
996
|
+
const videoRef = (0, import_react9.useRef)(null);
|
|
997
|
+
const [isPlaying, setIsPlaying] = (0, import_react9.useState)(false);
|
|
998
|
+
const [isPaused, setIsPaused] = (0, import_react9.useState)(true);
|
|
999
|
+
const [isEnded, setIsEnded] = (0, import_react9.useState)(false);
|
|
1000
|
+
const [showDownloadBadge, setShowDownloadBadge] = (0, import_react9.useState)(false);
|
|
1001
|
+
const lastCallbackTimeRef = (0, import_react9.useRef)(0);
|
|
1002
|
+
const styles = (0, import_react9.useContext)(AdsContext)?.styles?.videoPlayer;
|
|
1003
|
+
(0, import_react9.useEffect)(() => {
|
|
1004
|
+
const interval = setInterval(() => {
|
|
1005
|
+
if (videoRef.current) {
|
|
1006
|
+
videoRef.current.getStatusAsync().then((status) => {
|
|
1007
|
+
if (status.isLoaded) {
|
|
1008
|
+
const progress = status.positionMillis / status.durationMillis * 100;
|
|
1009
|
+
const now = Date.now();
|
|
1010
|
+
if (now - lastCallbackTimeRef.current >= 3e3) {
|
|
1011
|
+
lastCallbackTimeRef.current = now;
|
|
1012
|
+
onProgress(progress);
|
|
1013
|
+
}
|
|
1014
|
+
if (status.positionMillis >= 5e3 && !showDownloadBadge) {
|
|
1015
|
+
setShowDownloadBadge(true);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
}, 1e3);
|
|
1021
|
+
return () => clearInterval(interval);
|
|
1022
|
+
}, [showDownloadBadge]);
|
|
1023
|
+
const handlePlayPause = () => {
|
|
1024
|
+
if (videoRef.current) {
|
|
1025
|
+
if (isPlaying) {
|
|
1026
|
+
videoRef.current.pauseAsync();
|
|
1027
|
+
setIsPaused(true);
|
|
1028
|
+
handleLinkClick(clickUrl);
|
|
1029
|
+
} else {
|
|
1030
|
+
videoRef.current.playAsync();
|
|
1031
|
+
setIsPaused(false);
|
|
1032
|
+
setIsEnded(false);
|
|
1033
|
+
}
|
|
1034
|
+
setIsPlaying(!isPlaying);
|
|
1035
|
+
}
|
|
1036
|
+
};
|
|
1037
|
+
const handleBadgeClick = () => {
|
|
1038
|
+
if (isPlaying) {
|
|
1039
|
+
videoRef.current?.pauseAsync();
|
|
1040
|
+
setIsPaused(true);
|
|
1041
|
+
setIsPlaying(false);
|
|
1042
|
+
}
|
|
1043
|
+
handleLinkClick(clickUrl);
|
|
1044
|
+
};
|
|
1045
|
+
const handleLinkClick = (url) => {
|
|
1046
|
+
if (url) {
|
|
1047
|
+
import_react_native4.Linking.openURL(url).catch((err) => import_loglevel7.default.error("Failed to open URL:", err));
|
|
1048
|
+
onLinkClick();
|
|
1049
|
+
}
|
|
1050
|
+
};
|
|
1051
|
+
const handleEnded = () => {
|
|
1052
|
+
setIsPlaying(false);
|
|
1053
|
+
setIsEnded(true);
|
|
1054
|
+
setIsPaused(false);
|
|
1055
|
+
onProgress(100);
|
|
1056
|
+
};
|
|
1057
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.View, { style: [videoOrientation === "horizontal" ? styles?.videoOuterContainerHorizontal : styles?.videoOuterContainerVertical, mediaPlacement === "top" && styles?.videoPlacementBottom, mediaPlacement === "bottom" && styles?.videoPlacementBottom], children: [
|
|
1058
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1059
|
+
import_expo_av.Video,
|
|
1060
|
+
{
|
|
1061
|
+
ref: videoRef,
|
|
1062
|
+
source: { uri: src },
|
|
1063
|
+
resizeMode: import_expo_av.ResizeMode.CONTAIN,
|
|
1064
|
+
shouldPlay: false,
|
|
1065
|
+
onTouchStart: handlePlayPause,
|
|
1066
|
+
style: videoOrientation === "horizontal" ? styles?.videoInnerContainerHorizontal : styles?.videoInnerContainerVertical,
|
|
1067
|
+
videoStyle: videoOrientation === "horizontal" ? styles?.videoElementHorizontal : styles?.videoElementVertical,
|
|
1068
|
+
onPlaybackStatusUpdate: (status) => {
|
|
1069
|
+
if (status.isLoaded && status.didJustFinish && !status.isLooping) {
|
|
1070
|
+
handleEnded();
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
),
|
|
1075
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(VideoProgressBar, { videoRef }),
|
|
1076
|
+
!isEnded && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.TouchableOpacity, { style: isPlaying ? styles?.playOverlay : styles?.pauseOverlay, onPress: handlePlayPause, children: !isPlaying && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: styles?.playButton, children: "\u25B6" }) }),
|
|
1077
|
+
(isPaused && !isEnded || showDownloadBadge && !isEnded) && badgeText && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.TouchableOpacity, { style: styles?.videoBadge, onPress: handleBadgeClick, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: styles?.videoBadgeText, children: showDownloadBadge && !isPaused && !isEnded ? ctaText : badgeText }) }),
|
|
1078
|
+
isEnded && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.View, { style: styles?.endOverlay, children: [
|
|
1079
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.TouchableOpacity, { style: styles?.endOverlayTouch, onPress: () => handleLinkClick(clickUrl), children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: styles?.endButton, children: ctaText }) }),
|
|
1080
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: styles?.endText, children: endText })
|
|
1081
|
+
] })
|
|
1082
|
+
] });
|
|
1083
|
+
};
|
|
1084
|
+
|
|
1085
|
+
// src/hooks/useAd.tsx
|
|
1086
|
+
var import_react10 = require("react");
|
|
1087
|
+
function useAd({ code, messageId }) {
|
|
1088
|
+
const context = (0, import_react10.useContext)(AdsContext);
|
|
1089
|
+
if (!context) {
|
|
1090
|
+
return null;
|
|
1091
|
+
}
|
|
1092
|
+
const ad = context.ads.find((ad2) => {
|
|
1093
|
+
if (messageId) {
|
|
1094
|
+
return ad2.code === code && ad2.messageId === messageId;
|
|
1095
|
+
}
|
|
1096
|
+
return ad2.code === code;
|
|
1097
|
+
});
|
|
1098
|
+
if (!ad) {
|
|
1099
|
+
return null;
|
|
1100
|
+
}
|
|
1101
|
+
const placement = context.enabledPlacements.find((p) => p.code === code);
|
|
1102
|
+
if (!placement) {
|
|
1103
|
+
return null;
|
|
1104
|
+
}
|
|
1105
|
+
const dependencies = placement.config.dependencies?.split(",").map((d) => d.trim()).filter((d) => d);
|
|
1106
|
+
if (!dependencies || dependencies.length === 0) {
|
|
1107
|
+
return ad;
|
|
1108
|
+
}
|
|
1109
|
+
const dependencyAds = context.enabledPlacements.filter(
|
|
1110
|
+
(p) => dependencies.includes(p.code)
|
|
1111
|
+
);
|
|
1112
|
+
if (!dependencyAds.length) {
|
|
1113
|
+
return ad;
|
|
1114
|
+
}
|
|
1115
|
+
const allAdsLoadedAndNotStreaming = dependencyAds.every((p) => {
|
|
1116
|
+
let a;
|
|
1117
|
+
if (messageId) {
|
|
1118
|
+
a = context.ads.find(
|
|
1119
|
+
(ad2) => ad2.code === p.code && ad2.messageId === messageId
|
|
1120
|
+
);
|
|
1121
|
+
} else {
|
|
1122
|
+
a = context.ads.find((ad2) => ad2.code === p.code);
|
|
1123
|
+
}
|
|
1124
|
+
return a && !a.isLoading && !a.isStreaming || a && a.isError;
|
|
1125
|
+
});
|
|
1126
|
+
if (!allAdsLoadedAndNotStreaming) {
|
|
1127
|
+
return null;
|
|
1128
|
+
}
|
|
1129
|
+
return ad;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
// src/formats/InlineAd.tsx
|
|
1133
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1134
|
+
var InlineAd = ({ code, messageId, wrapper }) => {
|
|
1135
|
+
const ad = useAd({ code, messageId });
|
|
1136
|
+
const [linkIncluded, setLinkIncluded] = (0, import_react11.useState)(false);
|
|
1137
|
+
const context = (0, import_react11.useContext)(AdsContext);
|
|
1138
|
+
const styles = context?.styles?.inlineAd;
|
|
1139
|
+
useAdViewed(ad);
|
|
1140
|
+
(0, import_react11.useEffect)(() => {
|
|
1141
|
+
if (ad?.content && !ad.isStreaming && !ad.content.match(/\[.*?\]\(.*?\)/) && !linkIncluded) {
|
|
1142
|
+
setLinkIncluded(true);
|
|
1143
|
+
}
|
|
1144
|
+
}, [ad]);
|
|
1145
|
+
if (!ad || !ad.content || context?.isDisabled) return null;
|
|
1146
|
+
if (ad.isLoading || ad.content?.trim().toLowerCase().includes("none"))
|
|
1147
|
+
return null;
|
|
1148
|
+
const onProgress = (progress) => {
|
|
1149
|
+
import_loglevel8.default.log(`Progress: ${progress}`);
|
|
1150
|
+
};
|
|
1151
|
+
const handleImageClick = (url) => {
|
|
1152
|
+
if (url) {
|
|
1153
|
+
context?.onAdClickInternal(ad);
|
|
1154
|
+
if (import_react_native5.Platform.OS === "web") {
|
|
1155
|
+
window.open(url, "_blank");
|
|
1156
|
+
} else {
|
|
1157
|
+
import_react_native5.Linking.openURL(url).catch(
|
|
1158
|
+
(err) => import_loglevel8.default.error("Failed to open URL:", err)
|
|
1159
|
+
);
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
};
|
|
1163
|
+
const content = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native5.View, { style: styles?.container, children: [
|
|
1164
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.View, { style: styles?.adBadgeContainer, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: styles?.adBadgeText, children: "Ad" }) }),
|
|
1165
|
+
ad.imageUrl && ad.mediaPlacement === "top" && !ad.isStreaming && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1166
|
+
import_react_native5.TouchableOpacity,
|
|
1167
|
+
{
|
|
1168
|
+
style: styles?.imageContainer,
|
|
1169
|
+
onPress: () => handleImageClick(ad.url),
|
|
1170
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Image, { source: { uri: ad.imageUrl }, style: styles?.image })
|
|
1171
|
+
}
|
|
1172
|
+
),
|
|
1173
|
+
ad.videoUrl && ad.mediaPlacement === "top" && !ad.isStreaming && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1174
|
+
VideoPlayer,
|
|
1175
|
+
{
|
|
1176
|
+
src: ad.videoUrl,
|
|
1177
|
+
onProgress,
|
|
1178
|
+
endText: ad.videoEndText,
|
|
1179
|
+
badgeText: ad.videoBadgeText,
|
|
1180
|
+
videoPosterUrl: ad.videoPosterUrl,
|
|
1181
|
+
videoOrientation: ad.videoOrientation,
|
|
1182
|
+
mediaPlacement: ad.mediaPlacement,
|
|
1183
|
+
ctaText: ad.videoCtaText,
|
|
1184
|
+
onLinkClick: () => context?.onAdClickInternal(ad),
|
|
1185
|
+
clickUrl: ad.url
|
|
1186
|
+
}
|
|
1187
|
+
),
|
|
1188
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: styles?.contentContainer, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1189
|
+
MarkdownText,
|
|
1190
|
+
{
|
|
1191
|
+
onLinkClick: () => context?.onAdClickInternal(ad),
|
|
1192
|
+
content: ad.content
|
|
1193
|
+
}
|
|
1194
|
+
) }),
|
|
1195
|
+
ad.imageUrl && ad.mediaPlacement === "bottom" && !ad.isStreaming && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1196
|
+
import_react_native5.TouchableOpacity,
|
|
1197
|
+
{
|
|
1198
|
+
style: styles?.imageContainer,
|
|
1199
|
+
onPress: () => handleImageClick(ad.url),
|
|
1200
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Image, { source: { uri: ad.imageUrl }, style: styles?.image })
|
|
1201
|
+
}
|
|
1202
|
+
),
|
|
1203
|
+
ad.videoUrl && ad.mediaPlacement === "bottom" && !ad.isStreaming && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1204
|
+
VideoPlayer,
|
|
1205
|
+
{
|
|
1206
|
+
src: ad.videoUrl,
|
|
1207
|
+
onProgress,
|
|
1208
|
+
endText: ad.videoEndText,
|
|
1209
|
+
badgeText: ad.videoBadgeText,
|
|
1210
|
+
videoPosterUrl: ad.videoPosterUrl,
|
|
1211
|
+
videoOrientation: ad.videoOrientation,
|
|
1212
|
+
mediaPlacement: ad.mediaPlacement,
|
|
1213
|
+
ctaText: ad.videoCtaText,
|
|
1214
|
+
onLinkClick: () => context?.onAdClickInternal(ad),
|
|
1215
|
+
clickUrl: ad.url
|
|
1216
|
+
}
|
|
1217
|
+
)
|
|
1218
|
+
] });
|
|
1219
|
+
return wrapper ? wrapper(content) : content;
|
|
1220
|
+
};
|
|
1221
|
+
var InlineAd_default = InlineAd;
|
|
1222
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1223
|
+
0 && (module.exports = {
|
|
1224
|
+
AdsContext,
|
|
1225
|
+
AdsProvider,
|
|
1226
|
+
InlineAd,
|
|
1227
|
+
VISITOR_ID_KEY,
|
|
1228
|
+
useAd
|
|
1229
|
+
});
|