@getdevteam/analytics-react-native 0.2.0 → 0.3.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 +40 -0
- package/dist/index.cjs +564 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -3
- package/dist/index.d.ts +41 -3
- package/dist/index.js +587 -3
- package/dist/index.js.map +1 -1
- package/package.json +22 -3
package/dist/index.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
1
8
|
// src/index.ts
|
|
2
9
|
import {
|
|
3
10
|
createClient
|
|
@@ -42,6 +49,57 @@ function collectReactNativeContext() {
|
|
|
42
49
|
return context;
|
|
43
50
|
}
|
|
44
51
|
|
|
52
|
+
// src/feedback.ts
|
|
53
|
+
import {
|
|
54
|
+
uuidV7
|
|
55
|
+
} from "@getdevteam/analytics-core";
|
|
56
|
+
var FEEDBACK_DEFAULT_COLORS = ["#f44336", "#4caf50", "#2196f3", "#ffeb3b"];
|
|
57
|
+
var FEEDBACK_DEFAULT_ACCENT = "#5b9cf5";
|
|
58
|
+
var MAX_TEXT_LENGTH = 1e4;
|
|
59
|
+
var MAX_PAGE_URL_LENGTH = 2048;
|
|
60
|
+
function createFeedbackSender(config) {
|
|
61
|
+
const url = `${String(config.host ?? "").replace(/\/+$/, "")}/v1/ingest/feedback`;
|
|
62
|
+
return async (input) => {
|
|
63
|
+
try {
|
|
64
|
+
const text = typeof input?.text === "string" ? input.text.trim() : "";
|
|
65
|
+
if (text.length === 0) {
|
|
66
|
+
config.onError(new Error("feedback text must be a non-empty string"));
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
const body = {
|
|
70
|
+
feedback_id: uuidV7(),
|
|
71
|
+
text: text.slice(0, MAX_TEXT_LENGTH)
|
|
72
|
+
};
|
|
73
|
+
if (typeof input.screenshot === "string" && input.screenshot.length > 0) {
|
|
74
|
+
body.screenshot = input.screenshot;
|
|
75
|
+
body.screenshot_content_type = input.screenshotContentType ?? "image/png";
|
|
76
|
+
}
|
|
77
|
+
const distinctId = config.getDistinctId();
|
|
78
|
+
if (distinctId) body.distinct_id = distinctId;
|
|
79
|
+
const sessionId = config.getSessionId();
|
|
80
|
+
if (sessionId) body.session_id = sessionId;
|
|
81
|
+
if (typeof input.pageUrl === "string" && input.pageUrl.length > 0) {
|
|
82
|
+
body.page_url = input.pageUrl.slice(0, MAX_PAGE_URL_LENGTH);
|
|
83
|
+
}
|
|
84
|
+
body.context = config.getContext();
|
|
85
|
+
if (!config.fetch) throw new Error("fetch is not available in this environment");
|
|
86
|
+
const response = await config.fetch(url, {
|
|
87
|
+
method: "POST",
|
|
88
|
+
headers: { "Content-Type": "application/json", "X-DevTeam-Key": config.key },
|
|
89
|
+
body: JSON.stringify(body)
|
|
90
|
+
});
|
|
91
|
+
if (!response.ok) {
|
|
92
|
+
config.onError(new Error(`feedback ingest responded ${response.status}`));
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
return true;
|
|
96
|
+
} catch (error) {
|
|
97
|
+
config.onError(error);
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
45
103
|
// src/storage.ts
|
|
46
104
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
47
105
|
function createAsyncStorageAdapter() {
|
|
@@ -68,30 +126,556 @@ function createAsyncStorageAdapter() {
|
|
|
68
126
|
};
|
|
69
127
|
}
|
|
70
128
|
|
|
129
|
+
// src/feedbackProvider.ts
|
|
130
|
+
import {
|
|
131
|
+
createElement,
|
|
132
|
+
Fragment,
|
|
133
|
+
useCallback,
|
|
134
|
+
useEffect,
|
|
135
|
+
useRef,
|
|
136
|
+
useState
|
|
137
|
+
} from "react";
|
|
138
|
+
import {
|
|
139
|
+
Dimensions,
|
|
140
|
+
Image,
|
|
141
|
+
Modal,
|
|
142
|
+
PanResponder,
|
|
143
|
+
Pressable,
|
|
144
|
+
StyleSheet,
|
|
145
|
+
Text,
|
|
146
|
+
TextInput,
|
|
147
|
+
View
|
|
148
|
+
} from "react-native";
|
|
149
|
+
|
|
150
|
+
// src/optionalPeers.ts
|
|
151
|
+
var testLoaderOverride;
|
|
152
|
+
function requireViewShot() {
|
|
153
|
+
if (typeof __require !== "function") return void 0;
|
|
154
|
+
try {
|
|
155
|
+
return __require("react-native-view-shot");
|
|
156
|
+
} catch {
|
|
157
|
+
return void 0;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function requireSvg() {
|
|
161
|
+
if (typeof __require !== "function") return void 0;
|
|
162
|
+
try {
|
|
163
|
+
return __require("react-native-svg");
|
|
164
|
+
} catch {
|
|
165
|
+
return void 0;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function loadOptionalModule(name, requirePeer) {
|
|
169
|
+
if (testLoaderOverride) {
|
|
170
|
+
try {
|
|
171
|
+
return testLoaderOverride(name);
|
|
172
|
+
} catch {
|
|
173
|
+
return void 0;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return requirePeer();
|
|
177
|
+
}
|
|
178
|
+
function loadViewShot() {
|
|
179
|
+
const loaded = loadOptionalModule("react-native-view-shot", requireViewShot);
|
|
180
|
+
const captureRef = loaded?.captureRef ?? loaded?.default?.captureRef;
|
|
181
|
+
if (typeof captureRef !== "function") return void 0;
|
|
182
|
+
return { captureRef };
|
|
183
|
+
}
|
|
184
|
+
function loadSvg() {
|
|
185
|
+
const loaded = loadOptionalModule("react-native-svg", requireSvg);
|
|
186
|
+
const svg = loaded?.Svg ?? loaded?.default;
|
|
187
|
+
const polyline = loaded?.Polyline;
|
|
188
|
+
if (svg == null || polyline == null) return void 0;
|
|
189
|
+
return {
|
|
190
|
+
Svg: svg,
|
|
191
|
+
Polyline: polyline
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// src/feedbackProvider.ts
|
|
196
|
+
var PEN_WIDTH = 5;
|
|
197
|
+
var SUCCESS_CLOSE_DELAY_MS = 1600;
|
|
198
|
+
function FeedbackProvider(props) {
|
|
199
|
+
const options = props.client.feedbackOptions ?? {};
|
|
200
|
+
const colors = options.colors && options.colors.length > 0 ? options.colors : FEEDBACK_DEFAULT_COLORS;
|
|
201
|
+
const firstColor = colors[0] ?? FEEDBACK_DEFAULT_COLORS[0];
|
|
202
|
+
const accent = options.accentColor ?? FEEDBACK_DEFAULT_ACCENT;
|
|
203
|
+
const [visible, setVisible] = useState(false);
|
|
204
|
+
const [screenshot, setScreenshot] = useState(void 0);
|
|
205
|
+
const [screenshotSize, setScreenshotSize] = useState(void 0);
|
|
206
|
+
const [comment, setComment] = useState("");
|
|
207
|
+
const [sending, setSending] = useState(false);
|
|
208
|
+
const [sent, setSent] = useState(false);
|
|
209
|
+
const [errorMessage, setErrorMessage] = useState(void 0);
|
|
210
|
+
const [activeColor, setActiveColor] = useState(firstColor);
|
|
211
|
+
const [, setStrokeVersion] = useState(0);
|
|
212
|
+
const contentRef = useRef(null);
|
|
213
|
+
const previewRef = useRef(null);
|
|
214
|
+
const strokesRef = useRef([]);
|
|
215
|
+
const activeStrokeRef = useRef(void 0);
|
|
216
|
+
const activeColorRef = useRef(firstColor);
|
|
217
|
+
const closeTimerRef = useRef(void 0);
|
|
218
|
+
const sessionRef = useRef(0);
|
|
219
|
+
useEffect(
|
|
220
|
+
() => () => {
|
|
221
|
+
if (closeTimerRef.current !== void 0) clearTimeout(closeTimerRef.current);
|
|
222
|
+
},
|
|
223
|
+
[]
|
|
224
|
+
);
|
|
225
|
+
const bumpStrokes = useCallback(() => setStrokeVersion((version) => version + 1), []);
|
|
226
|
+
const panResponderRef = useRef(
|
|
227
|
+
PanResponder.create({
|
|
228
|
+
onStartShouldSetPanResponder: () => true,
|
|
229
|
+
onMoveShouldSetPanResponder: () => true,
|
|
230
|
+
onPanResponderGrant: (event) => {
|
|
231
|
+
const { locationX, locationY } = event.nativeEvent;
|
|
232
|
+
const stroke = {
|
|
233
|
+
points: [{ x: locationX, y: locationY }],
|
|
234
|
+
color: activeColorRef.current,
|
|
235
|
+
width: PEN_WIDTH
|
|
236
|
+
};
|
|
237
|
+
activeStrokeRef.current = stroke;
|
|
238
|
+
strokesRef.current.push(stroke);
|
|
239
|
+
bumpStrokes();
|
|
240
|
+
},
|
|
241
|
+
onPanResponderMove: (event) => {
|
|
242
|
+
const stroke = activeStrokeRef.current;
|
|
243
|
+
if (!stroke) return;
|
|
244
|
+
stroke.points.push({ x: event.nativeEvent.locationX, y: event.nativeEvent.locationY });
|
|
245
|
+
bumpStrokes();
|
|
246
|
+
},
|
|
247
|
+
onPanResponderRelease: () => {
|
|
248
|
+
activeStrokeRef.current = void 0;
|
|
249
|
+
},
|
|
250
|
+
onPanResponderTerminate: () => {
|
|
251
|
+
activeStrokeRef.current = void 0;
|
|
252
|
+
}
|
|
253
|
+
})
|
|
254
|
+
);
|
|
255
|
+
const close = useCallback(() => {
|
|
256
|
+
sessionRef.current += 1;
|
|
257
|
+
if (closeTimerRef.current !== void 0) {
|
|
258
|
+
clearTimeout(closeTimerRef.current);
|
|
259
|
+
closeTimerRef.current = void 0;
|
|
260
|
+
}
|
|
261
|
+
setVisible(false);
|
|
262
|
+
setScreenshot(void 0);
|
|
263
|
+
setScreenshotSize(void 0);
|
|
264
|
+
setComment("");
|
|
265
|
+
setSending(false);
|
|
266
|
+
setSent(false);
|
|
267
|
+
setErrorMessage(void 0);
|
|
268
|
+
strokesRef.current = [];
|
|
269
|
+
activeStrokeRef.current = void 0;
|
|
270
|
+
}, []);
|
|
271
|
+
const open = useCallback(async () => {
|
|
272
|
+
sessionRef.current += 1;
|
|
273
|
+
try {
|
|
274
|
+
strokesRef.current = [];
|
|
275
|
+
activeStrokeRef.current = void 0;
|
|
276
|
+
activeColorRef.current = firstColor;
|
|
277
|
+
setActiveColor(firstColor);
|
|
278
|
+
setComment("");
|
|
279
|
+
setSent(false);
|
|
280
|
+
setSending(false);
|
|
281
|
+
setErrorMessage(void 0);
|
|
282
|
+
let shot;
|
|
283
|
+
const viewShot = loadViewShot();
|
|
284
|
+
if (viewShot && contentRef.current) {
|
|
285
|
+
try {
|
|
286
|
+
shot = await viewShot.captureRef(contentRef.current, {
|
|
287
|
+
format: "png",
|
|
288
|
+
quality: 0.9,
|
|
289
|
+
result: "base64"
|
|
290
|
+
});
|
|
291
|
+
} catch (captureError) {
|
|
292
|
+
props.client.reportError(captureError);
|
|
293
|
+
shot = void 0;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
setScreenshotSize(shot ? await capturedImageSize(shot) : void 0);
|
|
297
|
+
setScreenshot(shot);
|
|
298
|
+
setVisible(true);
|
|
299
|
+
} catch (openError) {
|
|
300
|
+
props.client.reportError(openError);
|
|
301
|
+
setScreenshotSize(void 0);
|
|
302
|
+
setScreenshot(void 0);
|
|
303
|
+
setVisible(true);
|
|
304
|
+
}
|
|
305
|
+
}, [firstColor, props.client]);
|
|
306
|
+
const submit = useCallback(async () => {
|
|
307
|
+
if (sending || sent) return;
|
|
308
|
+
const text = comment.trim();
|
|
309
|
+
if (text.length === 0) {
|
|
310
|
+
setErrorMessage("Please add a short comment before sending.");
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
const session = sessionRef.current;
|
|
314
|
+
setSending(true);
|
|
315
|
+
setErrorMessage(void 0);
|
|
316
|
+
try {
|
|
317
|
+
let finalScreenshot = screenshot;
|
|
318
|
+
const viewShot = loadViewShot();
|
|
319
|
+
if (screenshot && strokesRef.current.length > 0 && viewShot && previewRef.current) {
|
|
320
|
+
try {
|
|
321
|
+
finalScreenshot = await viewShot.captureRef(previewRef.current, {
|
|
322
|
+
format: "png",
|
|
323
|
+
quality: 0.9,
|
|
324
|
+
result: "base64"
|
|
325
|
+
});
|
|
326
|
+
} catch (flattenError) {
|
|
327
|
+
props.client.reportError(flattenError);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
const submission = { text };
|
|
331
|
+
if (finalScreenshot) {
|
|
332
|
+
submission.screenshot = finalScreenshot;
|
|
333
|
+
submission.screenshotContentType = "image/png";
|
|
334
|
+
}
|
|
335
|
+
const ok = await props.client.submitFeedback(submission);
|
|
336
|
+
if (sessionRef.current !== session) return;
|
|
337
|
+
if (ok) {
|
|
338
|
+
setSent(true);
|
|
339
|
+
closeTimerRef.current = setTimeout(close, SUCCESS_CLOSE_DELAY_MS);
|
|
340
|
+
} else {
|
|
341
|
+
setErrorMessage("Could not send feedback. Please try again.");
|
|
342
|
+
}
|
|
343
|
+
} catch {
|
|
344
|
+
if (sessionRef.current === session) {
|
|
345
|
+
setErrorMessage("Could not send feedback. Please try again.");
|
|
346
|
+
}
|
|
347
|
+
} finally {
|
|
348
|
+
if (sessionRef.current === session) {
|
|
349
|
+
setSending(false);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}, [close, comment, props.client, screenshot, sending, sent]);
|
|
353
|
+
const undo = useCallback(() => {
|
|
354
|
+
if (activeStrokeRef.current) return;
|
|
355
|
+
strokesRef.current.pop();
|
|
356
|
+
bumpStrokes();
|
|
357
|
+
}, [bumpStrokes]);
|
|
358
|
+
const clear = useCallback(() => {
|
|
359
|
+
if (activeStrokeRef.current) return;
|
|
360
|
+
strokesRef.current = [];
|
|
361
|
+
bumpStrokes();
|
|
362
|
+
}, [bumpStrokes]);
|
|
363
|
+
if (props.client.userFeedbackAllowed !== true || typeof props.client.submitFeedback !== "function") {
|
|
364
|
+
return createElement(Fragment, null, props.children);
|
|
365
|
+
}
|
|
366
|
+
const svg = loadSvg();
|
|
367
|
+
const window = Dimensions.get("window");
|
|
368
|
+
const previewAspectRatio = screenshotSize && screenshotSize.height > 0 ? screenshotSize.width / screenshotSize.height : window.height > 0 ? window.width / window.height : 1;
|
|
369
|
+
const positionStyle = options.position === "bottom-left" ? styles.fabLeft : styles.fabRight;
|
|
370
|
+
const preview = screenshot ? createElement(
|
|
371
|
+
View,
|
|
372
|
+
{
|
|
373
|
+
style: [styles.preview, { aspectRatio: previewAspectRatio }],
|
|
374
|
+
testID: "devteam-feedback-preview-frame"
|
|
375
|
+
},
|
|
376
|
+
createElement(
|
|
377
|
+
View,
|
|
378
|
+
{ ref: previewRef, collapsable: false, style: styles.previewSurface },
|
|
379
|
+
createElement(Image, {
|
|
380
|
+
source: { uri: `data:image/png;base64,${screenshot}` },
|
|
381
|
+
style: styles.previewImage,
|
|
382
|
+
resizeMode: "stretch",
|
|
383
|
+
testID: "devteam-feedback-preview"
|
|
384
|
+
}),
|
|
385
|
+
svg ? createElement(
|
|
386
|
+
View,
|
|
387
|
+
{ style: styles.drawLayer, ...panResponderRef.current.panHandlers },
|
|
388
|
+
createElement(
|
|
389
|
+
svg.Svg,
|
|
390
|
+
{ width: "100%", height: "100%" },
|
|
391
|
+
...strokesRef.current.map(
|
|
392
|
+
(stroke, index) => createElement(svg.Polyline, {
|
|
393
|
+
key: String(index),
|
|
394
|
+
points: stroke.points.map((point) => `${point.x},${point.y}`).join(" "),
|
|
395
|
+
fill: "none",
|
|
396
|
+
stroke: stroke.color,
|
|
397
|
+
strokeWidth: stroke.width,
|
|
398
|
+
strokeLinecap: "round",
|
|
399
|
+
strokeLinejoin: "round"
|
|
400
|
+
})
|
|
401
|
+
)
|
|
402
|
+
)
|
|
403
|
+
) : null
|
|
404
|
+
)
|
|
405
|
+
) : null;
|
|
406
|
+
const tools = screenshot && svg ? createElement(
|
|
407
|
+
View,
|
|
408
|
+
{ style: styles.tools },
|
|
409
|
+
...colors.map(
|
|
410
|
+
(color) => createElement(Pressable, {
|
|
411
|
+
key: color,
|
|
412
|
+
accessibilityRole: "button",
|
|
413
|
+
testID: `devteam-feedback-color-${color}`,
|
|
414
|
+
onPress: () => {
|
|
415
|
+
activeColorRef.current = color;
|
|
416
|
+
setActiveColor(color);
|
|
417
|
+
},
|
|
418
|
+
style: [
|
|
419
|
+
styles.swatch,
|
|
420
|
+
{ backgroundColor: color },
|
|
421
|
+
activeColor === color ? styles.swatchActive : null
|
|
422
|
+
]
|
|
423
|
+
})
|
|
424
|
+
),
|
|
425
|
+
createElement(
|
|
426
|
+
Pressable,
|
|
427
|
+
{
|
|
428
|
+
accessibilityRole: "button",
|
|
429
|
+
testID: "devteam-feedback-undo",
|
|
430
|
+
onPress: undo,
|
|
431
|
+
style: styles.toolButton
|
|
432
|
+
},
|
|
433
|
+
createElement(Text, { style: styles.toolLabel }, "Undo")
|
|
434
|
+
),
|
|
435
|
+
createElement(
|
|
436
|
+
Pressable,
|
|
437
|
+
{
|
|
438
|
+
accessibilityRole: "button",
|
|
439
|
+
testID: "devteam-feedback-clear",
|
|
440
|
+
onPress: clear,
|
|
441
|
+
style: styles.toolButton
|
|
442
|
+
},
|
|
443
|
+
createElement(Text, { style: styles.toolLabel }, "Clear")
|
|
444
|
+
)
|
|
445
|
+
) : null;
|
|
446
|
+
const modal = createElement(
|
|
447
|
+
Modal,
|
|
448
|
+
{ visible, transparent: true, animationType: "fade", onRequestClose: close },
|
|
449
|
+
createElement(
|
|
450
|
+
View,
|
|
451
|
+
{ style: styles.backdrop },
|
|
452
|
+
createElement(
|
|
453
|
+
View,
|
|
454
|
+
{ style: styles.panel },
|
|
455
|
+
createElement(Text, { style: styles.title }, options.title ?? "Send feedback"),
|
|
456
|
+
preview,
|
|
457
|
+
tools,
|
|
458
|
+
createElement(TextInput, {
|
|
459
|
+
multiline: true,
|
|
460
|
+
value: comment,
|
|
461
|
+
onChangeText: setComment,
|
|
462
|
+
placeholder: options.commentPlaceholder ?? "What went wrong, or what could be better?",
|
|
463
|
+
style: styles.input,
|
|
464
|
+
testID: "devteam-feedback-comment"
|
|
465
|
+
}),
|
|
466
|
+
errorMessage ? createElement(Text, { style: styles.error, testID: "devteam-feedback-error" }, errorMessage) : null,
|
|
467
|
+
sent ? createElement(
|
|
468
|
+
Text,
|
|
469
|
+
{ style: styles.success, testID: "devteam-feedback-success" },
|
|
470
|
+
options.successMessage ?? "Thanks for the feedback!"
|
|
471
|
+
) : null,
|
|
472
|
+
createElement(
|
|
473
|
+
View,
|
|
474
|
+
{ style: styles.actions },
|
|
475
|
+
createElement(
|
|
476
|
+
Pressable,
|
|
477
|
+
{
|
|
478
|
+
accessibilityRole: "button",
|
|
479
|
+
testID: "devteam-feedback-cancel",
|
|
480
|
+
onPress: close,
|
|
481
|
+
style: styles.cancelButton
|
|
482
|
+
},
|
|
483
|
+
createElement(Text, { style: styles.cancelLabel }, "Cancel")
|
|
484
|
+
),
|
|
485
|
+
createElement(
|
|
486
|
+
Pressable,
|
|
487
|
+
{
|
|
488
|
+
accessibilityRole: "button",
|
|
489
|
+
testID: "devteam-feedback-submit",
|
|
490
|
+
disabled: sending || sent,
|
|
491
|
+
onPress: () => {
|
|
492
|
+
void submit();
|
|
493
|
+
},
|
|
494
|
+
style: [styles.submitButton, { backgroundColor: accent }]
|
|
495
|
+
},
|
|
496
|
+
createElement(Text, { style: styles.submitLabel }, options.submitLabel ?? "Send")
|
|
497
|
+
)
|
|
498
|
+
)
|
|
499
|
+
)
|
|
500
|
+
)
|
|
501
|
+
);
|
|
502
|
+
return createElement(
|
|
503
|
+
View,
|
|
504
|
+
{ style: styles.root },
|
|
505
|
+
createElement(
|
|
506
|
+
View,
|
|
507
|
+
{ ref: contentRef, collapsable: false, style: styles.content },
|
|
508
|
+
props.children
|
|
509
|
+
),
|
|
510
|
+
createElement(
|
|
511
|
+
Pressable,
|
|
512
|
+
{
|
|
513
|
+
accessibilityRole: "button",
|
|
514
|
+
testID: "devteam-feedback-button",
|
|
515
|
+
onPress: () => {
|
|
516
|
+
void open();
|
|
517
|
+
},
|
|
518
|
+
style: [styles.fab, positionStyle, { backgroundColor: accent }]
|
|
519
|
+
},
|
|
520
|
+
createElement(Text, { style: styles.fabLabel }, options.buttonLabel ?? "Feedback")
|
|
521
|
+
),
|
|
522
|
+
modal
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
function capturedImageSize(base64) {
|
|
526
|
+
return new Promise((resolve) => {
|
|
527
|
+
try {
|
|
528
|
+
Image.getSize(
|
|
529
|
+
`data:image/png;base64,${base64}`,
|
|
530
|
+
(width, height) => resolve(width > 0 && height > 0 ? { width, height } : void 0),
|
|
531
|
+
() => resolve(void 0)
|
|
532
|
+
);
|
|
533
|
+
} catch {
|
|
534
|
+
resolve(void 0);
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
var styles = StyleSheet.create({
|
|
539
|
+
root: { flex: 1 },
|
|
540
|
+
content: { flex: 1 },
|
|
541
|
+
fab: {
|
|
542
|
+
position: "absolute",
|
|
543
|
+
bottom: 24,
|
|
544
|
+
borderRadius: 999,
|
|
545
|
+
paddingVertical: 10,
|
|
546
|
+
paddingHorizontal: 18,
|
|
547
|
+
elevation: 4
|
|
548
|
+
},
|
|
549
|
+
fabRight: { right: 20 },
|
|
550
|
+
fabLeft: { left: 20 },
|
|
551
|
+
fabLabel: { color: "#ffffff", fontSize: 14, fontWeight: "600" },
|
|
552
|
+
backdrop: {
|
|
553
|
+
flex: 1,
|
|
554
|
+
backgroundColor: "rgba(15, 23, 42, 0.55)",
|
|
555
|
+
alignItems: "center",
|
|
556
|
+
justifyContent: "center",
|
|
557
|
+
padding: 16
|
|
558
|
+
},
|
|
559
|
+
panel: {
|
|
560
|
+
width: "100%",
|
|
561
|
+
maxWidth: 560,
|
|
562
|
+
backgroundColor: "#ffffff",
|
|
563
|
+
borderRadius: 12,
|
|
564
|
+
padding: 16,
|
|
565
|
+
gap: 10
|
|
566
|
+
},
|
|
567
|
+
title: { fontSize: 16, fontWeight: "600", color: "#1f2933" },
|
|
568
|
+
preview: {
|
|
569
|
+
width: "100%",
|
|
570
|
+
borderRadius: 8,
|
|
571
|
+
borderWidth: 1,
|
|
572
|
+
borderColor: "#d3dce6",
|
|
573
|
+
overflow: "hidden"
|
|
574
|
+
},
|
|
575
|
+
previewSurface: { width: "100%", height: "100%" },
|
|
576
|
+
previewImage: { width: "100%", height: "100%" },
|
|
577
|
+
drawLayer: { position: "absolute", top: 0, right: 0, bottom: 0, left: 0 },
|
|
578
|
+
tools: { flexDirection: "row", alignItems: "center", gap: 8 },
|
|
579
|
+
swatch: { width: 24, height: 24, borderRadius: 12, borderWidth: 2, borderColor: "transparent" },
|
|
580
|
+
swatchActive: { borderColor: "#1f2933" },
|
|
581
|
+
toolButton: {
|
|
582
|
+
borderWidth: 1,
|
|
583
|
+
borderColor: "#d3dce6",
|
|
584
|
+
borderRadius: 6,
|
|
585
|
+
paddingVertical: 4,
|
|
586
|
+
paddingHorizontal: 10
|
|
587
|
+
},
|
|
588
|
+
toolLabel: { fontSize: 13, color: "#1f2933" },
|
|
589
|
+
input: {
|
|
590
|
+
minHeight: 72,
|
|
591
|
+
borderWidth: 1,
|
|
592
|
+
borderColor: "#d3dce6",
|
|
593
|
+
borderRadius: 8,
|
|
594
|
+
padding: 10,
|
|
595
|
+
color: "#1f2933",
|
|
596
|
+
textAlignVertical: "top"
|
|
597
|
+
},
|
|
598
|
+
error: { color: "#b91c1c", fontSize: 13 },
|
|
599
|
+
success: { color: "#15803d", fontSize: 14, fontWeight: "600" },
|
|
600
|
+
actions: { flexDirection: "row", justifyContent: "flex-end", gap: 8 },
|
|
601
|
+
cancelButton: {
|
|
602
|
+
borderWidth: 1,
|
|
603
|
+
borderColor: "#d3dce6",
|
|
604
|
+
borderRadius: 8,
|
|
605
|
+
paddingVertical: 8,
|
|
606
|
+
paddingHorizontal: 14
|
|
607
|
+
},
|
|
608
|
+
cancelLabel: { fontSize: 14, color: "#1f2933" },
|
|
609
|
+
submitButton: { borderRadius: 8, paddingVertical: 8, paddingHorizontal: 16 },
|
|
610
|
+
submitLabel: { fontSize: 14, fontWeight: "600", color: "#ffffff" }
|
|
611
|
+
});
|
|
612
|
+
|
|
71
613
|
// src/index.ts
|
|
72
614
|
var REACT_NATIVE_SDK_NAME = "@getdevteam/analytics-react-native";
|
|
73
|
-
var REACT_NATIVE_SDK_VERSION = "0.
|
|
615
|
+
var REACT_NATIVE_SDK_VERSION = "0.3.0";
|
|
74
616
|
function createAnalytics(config) {
|
|
75
617
|
const client = createClient({
|
|
76
618
|
...config,
|
|
77
619
|
storage: config.storage ?? createAsyncStorageAdapter()
|
|
78
620
|
});
|
|
79
|
-
|
|
621
|
+
const reactNativeContext = {
|
|
80
622
|
sdk_name: REACT_NATIVE_SDK_NAME,
|
|
81
623
|
sdk_version: REACT_NATIVE_SDK_VERSION,
|
|
82
624
|
...collectReactNativeContext(),
|
|
83
625
|
...config.context
|
|
84
|
-
}
|
|
626
|
+
};
|
|
627
|
+
client.setContext(reactNativeContext);
|
|
628
|
+
let currentContext = reactNativeContext;
|
|
629
|
+
const setContext = (partial) => {
|
|
630
|
+
client.setContext(partial);
|
|
631
|
+
try {
|
|
632
|
+
const next = { ...currentContext };
|
|
633
|
+
for (const [key, value] of Object.entries(partial)) {
|
|
634
|
+
if (value === void 0) {
|
|
635
|
+
delete next[key];
|
|
636
|
+
} else {
|
|
637
|
+
next[key] = value;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
currentContext = next;
|
|
641
|
+
} catch {
|
|
642
|
+
}
|
|
643
|
+
};
|
|
85
644
|
const removeAppStateFlush = installAppStateFlush(client);
|
|
645
|
+
const reportError = (error) => {
|
|
646
|
+
try {
|
|
647
|
+
config.onError?.(error);
|
|
648
|
+
} catch {
|
|
649
|
+
}
|
|
650
|
+
};
|
|
651
|
+
const submitFeedback = createFeedbackSender({
|
|
652
|
+
host: config.host,
|
|
653
|
+
key: config.key,
|
|
654
|
+
fetch: config.fetch ?? defaultReactNativeFetch(),
|
|
655
|
+
getDistinctId: () => client.getDistinctId(),
|
|
656
|
+
getSessionId: () => client.getSessionId(),
|
|
657
|
+
getContext: () => currentContext,
|
|
658
|
+
onError: reportError
|
|
659
|
+
});
|
|
86
660
|
return {
|
|
87
661
|
...client,
|
|
662
|
+
setContext,
|
|
663
|
+
submitFeedback,
|
|
664
|
+
reportError,
|
|
665
|
+
userFeedbackAllowed: config.allowUserFeedback === true,
|
|
666
|
+
feedbackOptions: config.feedback ?? {},
|
|
88
667
|
shutdown: async () => {
|
|
89
668
|
removeAppStateFlush();
|
|
90
669
|
await client.shutdown();
|
|
91
670
|
}
|
|
92
671
|
};
|
|
93
672
|
}
|
|
673
|
+
function defaultReactNativeFetch() {
|
|
674
|
+
if (typeof fetch !== "function") return void 0;
|
|
675
|
+
return (url, init) => fetch(url, init);
|
|
676
|
+
}
|
|
94
677
|
export {
|
|
678
|
+
FeedbackProvider,
|
|
95
679
|
REACT_NATIVE_SDK_NAME,
|
|
96
680
|
REACT_NATIVE_SDK_VERSION,
|
|
97
681
|
collectReactNativeContext,
|