@mushi-mushi/react-native 0.20.1 → 0.22.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 +16 -2
- package/dist/index.cjs +502 -62
- package/dist/index.d.cts +64 -3
- package/dist/index.d.ts +64 -3
- package/dist/index.js +511 -56
- package/package.json +23 -18
- package/CHANGELOG.md +0 -1039
- package/CODE_OF_CONDUCT.md +0 -51
- package/CONTRIBUTING.md +0 -218
- package/SECURITY.md +0 -287
package/dist/index.js
CHANGED
|
@@ -15,11 +15,12 @@ import {
|
|
|
15
15
|
useCallback as useCallback4,
|
|
16
16
|
useMemo
|
|
17
17
|
} from "react";
|
|
18
|
+
import { AppState, Platform as Platform5 } from "react-native";
|
|
18
19
|
import {
|
|
19
20
|
createApiClient,
|
|
20
21
|
createBreadcrumbBuffer,
|
|
21
22
|
DEFAULT_API_ENDPOINT,
|
|
22
|
-
newUuid,
|
|
23
|
+
newUuid as newUuid2,
|
|
23
24
|
parseIdentityToken,
|
|
24
25
|
scrubPii as scrubPii3
|
|
25
26
|
} from "@mushi-mushi/core";
|
|
@@ -474,7 +475,17 @@ import {
|
|
|
474
475
|
ScrollView,
|
|
475
476
|
useColorScheme
|
|
476
477
|
} from "react-native";
|
|
477
|
-
import {
|
|
478
|
+
import {
|
|
479
|
+
mushiPalette,
|
|
480
|
+
MUSHI_BANNER_NEON,
|
|
481
|
+
MUSHI_COPY,
|
|
482
|
+
MUSHI_CONTROL_DISABLED,
|
|
483
|
+
MUSHI_INVERSE,
|
|
484
|
+
MUSHI_RADIUS,
|
|
485
|
+
MUSHI_SHADOW_INK,
|
|
486
|
+
MUSHI_SPACING,
|
|
487
|
+
MUSHI_TYPE
|
|
488
|
+
} from "@mushi-mushi/core";
|
|
478
489
|
import { getLocale } from "@mushi-mushi/web/i18n";
|
|
479
490
|
|
|
480
491
|
// src/reporter-status.ts
|
|
@@ -540,6 +551,7 @@ var MushiBottomSheet = ({
|
|
|
540
551
|
const [category, setCategory] = useState(null);
|
|
541
552
|
const [description, setDescription] = useState("");
|
|
542
553
|
const [phase, setPhase] = useState("form");
|
|
554
|
+
const [submitError, setSubmitError] = useState(null);
|
|
543
555
|
const [sheetTab, setSheetTab] = useState("report");
|
|
544
556
|
const [inboxReports, setInboxReports] = useState([]);
|
|
545
557
|
const [inboxLoading, setInboxLoading] = useState(false);
|
|
@@ -613,6 +625,8 @@ var MushiBottomSheet = ({
|
|
|
613
625
|
...prev,
|
|
614
626
|
{ role: "bot", text: botText, options: reply.options }
|
|
615
627
|
]);
|
|
628
|
+
} catch {
|
|
629
|
+
setAssistantError("Could not reach the assistant. Try again.");
|
|
616
630
|
} finally {
|
|
617
631
|
setAssistantSending(false);
|
|
618
632
|
}
|
|
@@ -686,24 +700,46 @@ var MushiBottomSheet = ({
|
|
|
686
700
|
).current;
|
|
687
701
|
const handleSubmit = async () => {
|
|
688
702
|
if (!category || !description.trim() || !mushi) return;
|
|
703
|
+
const trimmed = description.trim();
|
|
704
|
+
if (trimmed.length < 20) {
|
|
705
|
+
setSubmitError("Please write at least 20 characters so we can understand the issue.");
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
setSubmitError(null);
|
|
689
709
|
setPhase("sending");
|
|
690
710
|
try {
|
|
691
|
-
await mushi.submitReport({
|
|
711
|
+
const outcome = await mushi.submitReport({
|
|
692
712
|
category,
|
|
693
|
-
description:
|
|
713
|
+
description: trimmed,
|
|
694
714
|
screenshotDataUrl: screenshotDataUrl && screenshotAttached ? screenshotDataUrl : void 0
|
|
695
715
|
});
|
|
716
|
+
if (!outcome.ok) {
|
|
717
|
+
if (outcome.failureKind === "credentials" || outcome.failureKind === "quota") {
|
|
718
|
+
setSubmitError("Could not send \u2014 check the project API key or plan quota.");
|
|
719
|
+
setPhase("error");
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
if (outcome.failureKind === "rate_limited") {
|
|
723
|
+
setSubmitError("Sending too fast \u2014 we queued this and will retry shortly.");
|
|
724
|
+
setPhase("error");
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
setSubmitError("Queued for retry \u2014 we will send it when the connection is back.");
|
|
728
|
+
setPhase("error");
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
696
731
|
setPhase("sent");
|
|
697
732
|
setTimeout(handleClose, 1400);
|
|
698
733
|
} catch {
|
|
699
|
-
|
|
734
|
+
setSubmitError("Something went wrong. Please try again.");
|
|
735
|
+
setPhase("error");
|
|
700
736
|
}
|
|
701
737
|
};
|
|
702
738
|
const activeScreenshot = screenshotDataUrl && screenshotAttached ? screenshotDataUrl : null;
|
|
703
739
|
const pal = mushiPalette(dark ? "dark" : "light");
|
|
704
|
-
const colors = dark ? { bg: pal.paper, text: pal.ink, sub: pal.inkMuted, card: pal.paperRaised, accent: MUSHI_BANNER_NEON.bg, accentInk: MUSHI_BANNER_NEON.fg, border: pal.ruleStrong, backdrop: "rgba(0,0,0,0.6)", disabled:
|
|
740
|
+
const colors = dark ? { bg: pal.paper, text: pal.ink, sub: pal.inkMuted, card: pal.paperRaised, accent: MUSHI_BANNER_NEON.bg, accentInk: MUSHI_BANNER_NEON.fg, border: pal.ruleStrong, backdrop: "rgba(0,0,0,0.6)", disabled: MUSHI_CONTROL_DISABLED.dark, disabledText: pal.inkFaint } : { bg: pal.paperRaised, text: pal.ink, sub: pal.inkMuted, card: pal.paper, accent: MUSHI_BANNER_NEON.bg, accentInk: MUSHI_BANNER_NEON.fg, border: pal.ruleStrong, backdrop: "rgba(0,0,0,0.35)", disabled: MUSHI_CONTROL_DISABLED.light, disabledText: pal.inkFaint };
|
|
705
741
|
const sheetTabs = ["report", "inbox", ...assistantEnabled ? ["assistant"] : []];
|
|
706
|
-
const canSubmit = !!category && description.trim().length
|
|
742
|
+
const canSubmit = !!category && description.trim().length >= 20 && (phase === "form" || phase === "error");
|
|
707
743
|
return /* @__PURE__ */ jsx(Modal, { visible, transparent: true, animationType: "none", statusBarTranslucent: true, children: /* @__PURE__ */ jsxs(
|
|
708
744
|
KeyboardAvoidingView,
|
|
709
745
|
{
|
|
@@ -731,7 +767,7 @@ var MushiBottomSheet = ({
|
|
|
731
767
|
/* @__PURE__ */ jsx(Text, { style: [s.brandEyebrow, { color: MUSHI_BANNER_NEON.fg }], children: "MUSHI \xB7 BETA" }),
|
|
732
768
|
/* @__PURE__ */ jsx(Text, { style: [s.brandTitle, { color: MUSHI_BANNER_NEON.fg }], children: sheetTab === "assistant" ? assistantLabel : sheetTab === "inbox" ? t.flows.reports.title : t.widget.title })
|
|
733
769
|
] }),
|
|
734
|
-
/* @__PURE__ */ jsx(View, { style: s.tabRow, children: sheetTabs.map((tab) => /* @__PURE__ */ jsx(
|
|
770
|
+
/* @__PURE__ */ jsx(View, { style: [s.tabRow, { borderBottomColor: colors.border }], children: sheetTabs.map((tab) => /* @__PURE__ */ jsx(
|
|
735
771
|
TouchableOpacity,
|
|
736
772
|
{
|
|
737
773
|
onPress: () => {
|
|
@@ -884,16 +920,22 @@ var MushiBottomSheet = ({
|
|
|
884
920
|
multiline: true,
|
|
885
921
|
textAlignVertical: "top",
|
|
886
922
|
value: description,
|
|
887
|
-
onChangeText:
|
|
888
|
-
|
|
923
|
+
onChangeText: (text) => {
|
|
924
|
+
setDescription(text.slice(0, 4e3));
|
|
925
|
+
if (submitError) setSubmitError(null);
|
|
926
|
+
if (phase === "error") setPhase("form");
|
|
927
|
+
},
|
|
928
|
+
editable: phase === "form" || phase === "error",
|
|
929
|
+
maxLength: 4e3
|
|
889
930
|
}
|
|
890
931
|
),
|
|
932
|
+
submitError ? /* @__PURE__ */ jsx(Text, { style: { color: colors.accent, marginTop: 6, fontSize: 12 }, children: submitError }) : null,
|
|
891
933
|
activeScreenshot ? /* @__PURE__ */ jsxs(View, { style: s.screenshotRow, children: [
|
|
892
934
|
/* @__PURE__ */ jsx(
|
|
893
935
|
Image,
|
|
894
936
|
{
|
|
895
937
|
source: { uri: activeScreenshot },
|
|
896
|
-
style: s.screenshotThumb,
|
|
938
|
+
style: [s.screenshotThumb, { backgroundColor: colors.text }],
|
|
897
939
|
accessibilityLabel: t.step3.screenshotPreviewAlt
|
|
898
940
|
}
|
|
899
941
|
),
|
|
@@ -954,10 +996,10 @@ var s = StyleSheet.create({
|
|
|
954
996
|
left: 0,
|
|
955
997
|
right: 0,
|
|
956
998
|
minHeight: SHEET_HEIGHT,
|
|
957
|
-
borderTopLeftRadius:
|
|
958
|
-
borderTopRightRadius:
|
|
959
|
-
paddingBottom: Platform2.OS === "ios" ? 34 :
|
|
960
|
-
shadowColor:
|
|
999
|
+
borderTopLeftRadius: MUSHI_RADIUS.sheet,
|
|
1000
|
+
borderTopRightRadius: MUSHI_RADIUS.sheet,
|
|
1001
|
+
paddingBottom: Platform2.OS === "ios" ? 34 : MUSHI_SPACING.roomy,
|
|
1002
|
+
shadowColor: MUSHI_SHADOW_INK,
|
|
961
1003
|
shadowOffset: { width: 0, height: -3 },
|
|
962
1004
|
shadowOpacity: 0.12,
|
|
963
1005
|
shadowRadius: 8,
|
|
@@ -965,22 +1007,22 @@ var s = StyleSheet.create({
|
|
|
965
1007
|
},
|
|
966
1008
|
handleArea: {
|
|
967
1009
|
alignItems: "center",
|
|
968
|
-
paddingVertical:
|
|
1010
|
+
paddingVertical: MUSHI_SPACING.comfy
|
|
969
1011
|
},
|
|
970
1012
|
handle: {
|
|
971
1013
|
width: 40,
|
|
972
1014
|
height: 5,
|
|
973
|
-
borderRadius:
|
|
1015
|
+
borderRadius: MUSHI_RADIUS.control,
|
|
974
1016
|
opacity: 0.5
|
|
975
1017
|
},
|
|
976
1018
|
brandHeader: {
|
|
977
|
-
paddingHorizontal:
|
|
978
|
-
paddingTop:
|
|
979
|
-
paddingBottom:
|
|
1019
|
+
paddingHorizontal: MUSHI_SPACING.lounge,
|
|
1020
|
+
paddingTop: MUSHI_SPACING.comfy,
|
|
1021
|
+
paddingBottom: MUSHI_SPACING.comfy,
|
|
980
1022
|
borderBottomWidth: 1.5
|
|
981
1023
|
},
|
|
982
1024
|
brandEyebrow: {
|
|
983
|
-
fontSize:
|
|
1025
|
+
fontSize: MUSHI_TYPE.sizeLabel,
|
|
984
1026
|
fontWeight: "700",
|
|
985
1027
|
letterSpacing: 1.2,
|
|
986
1028
|
marginBottom: 2,
|
|
@@ -992,59 +1034,61 @@ var s = StyleSheet.create({
|
|
|
992
1034
|
letterSpacing: -0.2
|
|
993
1035
|
},
|
|
994
1036
|
body: {
|
|
995
|
-
paddingHorizontal:
|
|
996
|
-
paddingTop:
|
|
1037
|
+
paddingHorizontal: MUSHI_SPACING.lounge,
|
|
1038
|
+
paddingTop: MUSHI_SPACING.tight
|
|
997
1039
|
},
|
|
998
1040
|
title: {
|
|
999
1041
|
fontSize: 20,
|
|
1000
1042
|
fontWeight: "700",
|
|
1001
|
-
marginBottom:
|
|
1043
|
+
marginBottom: MUSHI_SPACING.roomy
|
|
1002
1044
|
},
|
|
1003
1045
|
stepLabel: {
|
|
1004
|
-
fontSize:
|
|
1046
|
+
fontSize: MUSHI_TYPE.sizeLabel,
|
|
1005
1047
|
fontWeight: "600",
|
|
1006
1048
|
letterSpacing: 0.2,
|
|
1007
|
-
marginTop:
|
|
1008
|
-
marginBottom:
|
|
1049
|
+
marginTop: MUSHI_SPACING.snug,
|
|
1050
|
+
marginBottom: MUSHI_SPACING.comfy,
|
|
1009
1051
|
textTransform: "uppercase"
|
|
1010
1052
|
},
|
|
1011
1053
|
catRow: {
|
|
1012
1054
|
flexDirection: "row",
|
|
1013
1055
|
justifyContent: "space-between",
|
|
1014
|
-
marginBottom:
|
|
1056
|
+
marginBottom: MUSHI_SPACING.roomy
|
|
1015
1057
|
},
|
|
1016
1058
|
catBtn: {
|
|
1017
1059
|
alignItems: "center",
|
|
1018
|
-
paddingVertical:
|
|
1060
|
+
paddingVertical: MUSHI_SPACING.comfy,
|
|
1019
1061
|
paddingHorizontal: 6,
|
|
1020
|
-
borderRadius:
|
|
1062
|
+
borderRadius: MUSHI_RADIUS.card,
|
|
1021
1063
|
borderWidth: 1,
|
|
1022
1064
|
flex: 1,
|
|
1023
|
-
marginHorizontal: 3
|
|
1065
|
+
marginHorizontal: 3,
|
|
1066
|
+
minHeight: 44
|
|
1024
1067
|
},
|
|
1025
1068
|
catEmoji: {
|
|
1026
1069
|
fontSize: 22,
|
|
1027
|
-
marginBottom:
|
|
1070
|
+
marginBottom: MUSHI_SPACING.tight
|
|
1028
1071
|
},
|
|
1029
1072
|
catLabel: {
|
|
1030
|
-
fontSize:
|
|
1073
|
+
fontSize: MUSHI_TYPE.sizeLabel,
|
|
1031
1074
|
fontWeight: "600"
|
|
1032
1075
|
},
|
|
1033
1076
|
input: {
|
|
1034
1077
|
borderWidth: 1,
|
|
1035
|
-
borderRadius:
|
|
1078
|
+
borderRadius: MUSHI_RADIUS.card,
|
|
1036
1079
|
padding: 14,
|
|
1037
1080
|
fontSize: 15,
|
|
1038
1081
|
minHeight: 100,
|
|
1039
|
-
marginBottom:
|
|
1082
|
+
marginBottom: MUSHI_SPACING.roomy
|
|
1040
1083
|
},
|
|
1041
1084
|
submitBtn: {
|
|
1042
|
-
borderRadius:
|
|
1085
|
+
borderRadius: MUSHI_RADIUS.card,
|
|
1043
1086
|
paddingVertical: 14,
|
|
1044
|
-
alignItems: "center"
|
|
1087
|
+
alignItems: "center",
|
|
1088
|
+
minHeight: 44
|
|
1045
1089
|
},
|
|
1046
1090
|
submitText: {
|
|
1047
|
-
color:
|
|
1091
|
+
color: MUSHI_INVERSE,
|
|
1048
1092
|
fontSize: 16,
|
|
1049
1093
|
fontWeight: "700"
|
|
1050
1094
|
},
|
|
@@ -1075,7 +1119,6 @@ var s = StyleSheet.create({
|
|
|
1075
1119
|
width: 48,
|
|
1076
1120
|
height: 36,
|
|
1077
1121
|
borderRadius: 4,
|
|
1078
|
-
backgroundColor: "#000",
|
|
1079
1122
|
flexShrink: 0
|
|
1080
1123
|
},
|
|
1081
1124
|
screenshotMeta: {
|
|
@@ -1097,7 +1140,6 @@ var s = StyleSheet.create({
|
|
|
1097
1140
|
tabRow: {
|
|
1098
1141
|
flexDirection: "row",
|
|
1099
1142
|
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
1100
|
-
borderBottomColor: "#ccc",
|
|
1101
1143
|
marginBottom: 8
|
|
1102
1144
|
},
|
|
1103
1145
|
tabBtn: {
|
|
@@ -1216,7 +1258,8 @@ var MushiFloatingButton = ({
|
|
|
1216
1258
|
const currentX = useRef2(baseX);
|
|
1217
1259
|
const currentY = useRef2(0);
|
|
1218
1260
|
const isDragging = useRef2(false);
|
|
1219
|
-
const
|
|
1261
|
+
const pal = mushiPalette2(dark ? "dark" : "light");
|
|
1262
|
+
const accentColor = accent ?? pal.accent;
|
|
1220
1263
|
useEffect2(() => {
|
|
1221
1264
|
if (!persist) return;
|
|
1222
1265
|
void loadStoredPos().then((pos) => {
|
|
@@ -1319,7 +1362,7 @@ var MushiFloatingButton = ({
|
|
|
1319
1362
|
left: posX,
|
|
1320
1363
|
bottom: Animated2.add(new Animated2.Value(baseBottom), Animated2.multiply(posY, -1)),
|
|
1321
1364
|
backgroundColor: accentColor,
|
|
1322
|
-
shadowColor: dark ? accentColor :
|
|
1365
|
+
shadowColor: dark ? accentColor : pal.ink,
|
|
1323
1366
|
transform: [{ scale }]
|
|
1324
1367
|
}
|
|
1325
1368
|
],
|
|
@@ -1358,7 +1401,7 @@ import {
|
|
|
1358
1401
|
StyleSheet as StyleSheet3,
|
|
1359
1402
|
Platform as Platform4
|
|
1360
1403
|
} from "react-native";
|
|
1361
|
-
import { MUSHI_BANNER_NEON as MUSHI_BANNER_NEON2, MUSHI_COPY as MUSHI_COPY3, MUSHI_GEOMETRY as MUSHI_GEOMETRY2 } from "@mushi-mushi/core";
|
|
1404
|
+
import { MUSHI_BANNER_NEON as MUSHI_BANNER_NEON2, MUSHI_COPY as MUSHI_COPY3, MUSHI_GEOMETRY as MUSHI_GEOMETRY2, MUSHI_SPACING as MUSHI_SPACING2, MUSHI_TYPE as MUSHI_TYPE2, MUSHI_Z } from "@mushi-mushi/core";
|
|
1362
1405
|
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1363
1406
|
var MUSHI_BANNER_DEFAULT_HEIGHT = MUSHI_GEOMETRY2.bannerHeight;
|
|
1364
1407
|
var MushiBanner = ({
|
|
@@ -1414,7 +1457,7 @@ var s3 = StyleSheet3.create({
|
|
|
1414
1457
|
backgroundColor: MUSHI_BANNER_NEON2.bg,
|
|
1415
1458
|
borderTopWidth: 1,
|
|
1416
1459
|
borderTopColor: MUSHI_BANNER_NEON2.border,
|
|
1417
|
-
zIndex:
|
|
1460
|
+
zIndex: MUSHI_Z.banner,
|
|
1418
1461
|
elevation: 8,
|
|
1419
1462
|
paddingBottom: Platform4.OS === "ios" ? 0 : 0
|
|
1420
1463
|
},
|
|
@@ -1422,12 +1465,13 @@ var s3 = StyleSheet3.create({
|
|
|
1422
1465
|
flex: 1,
|
|
1423
1466
|
flexDirection: "row",
|
|
1424
1467
|
alignItems: "center",
|
|
1425
|
-
gap:
|
|
1426
|
-
paddingHorizontal:
|
|
1427
|
-
paddingVertical:
|
|
1468
|
+
gap: MUSHI_SPACING2.snug,
|
|
1469
|
+
paddingHorizontal: MUSHI_SPACING2.comfy,
|
|
1470
|
+
paddingVertical: MUSHI_SPACING2.snug,
|
|
1471
|
+
minHeight: 44
|
|
1428
1472
|
},
|
|
1429
1473
|
eyebrow: {
|
|
1430
|
-
fontSize:
|
|
1474
|
+
fontSize: MUSHI_TYPE2.sizeLabel,
|
|
1431
1475
|
fontWeight: "700",
|
|
1432
1476
|
letterSpacing: 1.2,
|
|
1433
1477
|
color: MUSHI_BANNER_NEON2.fg
|
|
@@ -1456,13 +1500,341 @@ var s3 = StyleSheet3.create({
|
|
|
1456
1500
|
}
|
|
1457
1501
|
});
|
|
1458
1502
|
|
|
1503
|
+
// src/analytics/event-tracker.ts
|
|
1504
|
+
import {
|
|
1505
|
+
EVENT_PROPERTY_LIMITS,
|
|
1506
|
+
isValidEventName,
|
|
1507
|
+
propertiesWithinByteLimit,
|
|
1508
|
+
sanitizeEventProperties
|
|
1509
|
+
} from "@mushi-mushi/core";
|
|
1510
|
+
var CONSENT_BUFFER_MAX = 50;
|
|
1511
|
+
var SPILL_MAX = 200;
|
|
1512
|
+
var SPILL_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
1513
|
+
var SURFACE = "mobile";
|
|
1514
|
+
async function defaultStorage() {
|
|
1515
|
+
try {
|
|
1516
|
+
const mod = await import("@react-native-async-storage/async-storage");
|
|
1517
|
+
return mod.default;
|
|
1518
|
+
} catch {
|
|
1519
|
+
return null;
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
function createRNEventTracker(opts) {
|
|
1523
|
+
const cfg = opts.config ?? {};
|
|
1524
|
+
const enabled = cfg.enabled !== false;
|
|
1525
|
+
const consentMode = cfg.consent ?? "implied";
|
|
1526
|
+
const allowlist = cfg.propertyAllowlist ?? [];
|
|
1527
|
+
const spillKey = `@mushi:events_spill_${opts.projectId}`;
|
|
1528
|
+
const consentKey = `@mushi:analytics_consent_${opts.projectId}`;
|
|
1529
|
+
let buffer = [];
|
|
1530
|
+
let consentBuffer = [];
|
|
1531
|
+
let consent = consentMode === "required" ? "pending" : "granted";
|
|
1532
|
+
let userId = null;
|
|
1533
|
+
let userTraits = null;
|
|
1534
|
+
let pendingIdentify = false;
|
|
1535
|
+
let flushing = false;
|
|
1536
|
+
let destroyed = false;
|
|
1537
|
+
let timer = null;
|
|
1538
|
+
let storage = null;
|
|
1539
|
+
const consentListeners = /* @__PURE__ */ new Set();
|
|
1540
|
+
const loadStorage = opts.storage ?? defaultStorage;
|
|
1541
|
+
const resolveClient = () => typeof opts.client === "function" ? opts.client() : opts.client;
|
|
1542
|
+
async function readSpill() {
|
|
1543
|
+
if (!storage) return [];
|
|
1544
|
+
try {
|
|
1545
|
+
const raw = await storage.getItem(spillKey);
|
|
1546
|
+
if (!raw) return [];
|
|
1547
|
+
const parsed = JSON.parse(raw);
|
|
1548
|
+
if (!parsed || Date.now() - parsed.at > SPILL_TTL_MS) {
|
|
1549
|
+
await storage.removeItem(spillKey);
|
|
1550
|
+
return [];
|
|
1551
|
+
}
|
|
1552
|
+
return Array.isArray(parsed.events) ? parsed.events.slice(0, SPILL_MAX) : [];
|
|
1553
|
+
} catch {
|
|
1554
|
+
return [];
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
async function writeSpill(events) {
|
|
1558
|
+
if (!storage) return;
|
|
1559
|
+
try {
|
|
1560
|
+
if (events.length === 0) await storage.removeItem(spillKey);
|
|
1561
|
+
else await storage.setItem(spillKey, JSON.stringify({ at: Date.now(), events: events.slice(-SPILL_MAX) }));
|
|
1562
|
+
} catch {
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
async function flushNow() {
|
|
1566
|
+
if (destroyed && buffer.length === 0) return;
|
|
1567
|
+
await ready;
|
|
1568
|
+
if (flushing || consent !== "granted") return;
|
|
1569
|
+
if (buffer.length === 0 && !pendingIdentify) return;
|
|
1570
|
+
const client = resolveClient();
|
|
1571
|
+
if (!client) return;
|
|
1572
|
+
flushing = true;
|
|
1573
|
+
const batch = buffer.splice(0, EVENT_PROPERTY_LIMITS.maxServerBatch);
|
|
1574
|
+
if (pendingIdentify) {
|
|
1575
|
+
batch.unshift({ name: "identify", ts: (/* @__PURE__ */ new Date()).toISOString(), properties: {} });
|
|
1576
|
+
pendingIdentify = false;
|
|
1577
|
+
}
|
|
1578
|
+
try {
|
|
1579
|
+
const anonId = await Promise.resolve(opts.getAnonId());
|
|
1580
|
+
const payload = {
|
|
1581
|
+
anon_id: anonId,
|
|
1582
|
+
user_id: userId ?? opts.getUserId?.() ?? null,
|
|
1583
|
+
user_traits: userTraits ?? opts.getUserTraits?.() ?? null,
|
|
1584
|
+
session_id: null,
|
|
1585
|
+
sdk_version: opts.sdkVersion ?? null,
|
|
1586
|
+
surface: SURFACE,
|
|
1587
|
+
events: batch
|
|
1588
|
+
};
|
|
1589
|
+
const res = await client.postProductEvents(payload);
|
|
1590
|
+
if (!res.ok) await writeSpill([...await readSpill(), ...batch.filter((e) => e.name !== "identify")]);
|
|
1591
|
+
else await writeSpill([]);
|
|
1592
|
+
} catch {
|
|
1593
|
+
await writeSpill([...await readSpill(), ...batch.filter((e) => e.name !== "identify")]);
|
|
1594
|
+
} finally {
|
|
1595
|
+
flushing = false;
|
|
1596
|
+
}
|
|
1597
|
+
if (buffer.length >= EVENT_PROPERTY_LIMITS.maxClientBatch) void flushNow();
|
|
1598
|
+
}
|
|
1599
|
+
function enqueue(ev) {
|
|
1600
|
+
if (consent === "denied") return;
|
|
1601
|
+
if (consent === "pending") {
|
|
1602
|
+
if (consentBuffer.length < CONSENT_BUFFER_MAX) consentBuffer.push(ev);
|
|
1603
|
+
return;
|
|
1604
|
+
}
|
|
1605
|
+
buffer.push(ev);
|
|
1606
|
+
if (buffer.length >= EVENT_PROPERTY_LIMITS.maxClientBatch) void flushNow();
|
|
1607
|
+
}
|
|
1608
|
+
const ready = enabled ? (async () => {
|
|
1609
|
+
storage = await loadStorage();
|
|
1610
|
+
if (destroyed) return;
|
|
1611
|
+
let stored = null;
|
|
1612
|
+
try {
|
|
1613
|
+
stored = storage ? await storage.getItem(consentKey) : null;
|
|
1614
|
+
} catch {
|
|
1615
|
+
stored = null;
|
|
1616
|
+
}
|
|
1617
|
+
if (stored === "denied") {
|
|
1618
|
+
consent = "denied";
|
|
1619
|
+
buffer = [];
|
|
1620
|
+
consentBuffer = [];
|
|
1621
|
+
pendingIdentify = false;
|
|
1622
|
+
await writeSpill([]);
|
|
1623
|
+
return;
|
|
1624
|
+
}
|
|
1625
|
+
if (stored === "granted" && consent === "pending") {
|
|
1626
|
+
consent = "granted";
|
|
1627
|
+
buffer.push(...consentBuffer);
|
|
1628
|
+
consentBuffer = [];
|
|
1629
|
+
}
|
|
1630
|
+
const spill = await readSpill();
|
|
1631
|
+
if (spill.length > 0) {
|
|
1632
|
+
buffer.unshift(...spill);
|
|
1633
|
+
await writeSpill([]);
|
|
1634
|
+
}
|
|
1635
|
+
})().catch(() => void 0) : Promise.resolve();
|
|
1636
|
+
if (enabled) {
|
|
1637
|
+
const interval = Math.max(1e3, cfg.flushIntervalMs ?? 5e3);
|
|
1638
|
+
timer = setInterval(() => {
|
|
1639
|
+
void flushNow();
|
|
1640
|
+
}, interval);
|
|
1641
|
+
}
|
|
1642
|
+
return {
|
|
1643
|
+
ready,
|
|
1644
|
+
track(name, properties, trackOpts) {
|
|
1645
|
+
if (!enabled || destroyed) return false;
|
|
1646
|
+
if (!isValidEventName(name)) return false;
|
|
1647
|
+
const { properties: props } = sanitizeEventProperties(properties, { allowlist, scrub: opts.scrub });
|
|
1648
|
+
let merged = props;
|
|
1649
|
+
if (trackOpts?.reserved) {
|
|
1650
|
+
const { properties: res } = sanitizeEventProperties(trackOpts.reserved, { allowReserved: true, scrub: opts.scrub });
|
|
1651
|
+
merged = { ...props, ...res };
|
|
1652
|
+
}
|
|
1653
|
+
merged.$surface = SURFACE;
|
|
1654
|
+
const route = opts.getRoute?.();
|
|
1655
|
+
if (route && merged.$route === void 0) merged.$route = route;
|
|
1656
|
+
if (!propertiesWithinByteLimit(merged)) return false;
|
|
1657
|
+
enqueue({
|
|
1658
|
+
name,
|
|
1659
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1660
|
+
properties: merged,
|
|
1661
|
+
...trackOpts?.dedupKey ? { dedup_key: trackOpts.dedupKey } : {}
|
|
1662
|
+
});
|
|
1663
|
+
return true;
|
|
1664
|
+
},
|
|
1665
|
+
setConsent(state) {
|
|
1666
|
+
void ready.then(() => storage?.setItem(consentKey, state)).catch(() => void 0);
|
|
1667
|
+
if (state === "denied") {
|
|
1668
|
+
consent = "denied";
|
|
1669
|
+
buffer = [];
|
|
1670
|
+
consentBuffer = [];
|
|
1671
|
+
pendingIdentify = false;
|
|
1672
|
+
void writeSpill([]);
|
|
1673
|
+
} else {
|
|
1674
|
+
const wasPending = consent === "pending";
|
|
1675
|
+
consent = "granted";
|
|
1676
|
+
if (wasPending && consentBuffer.length > 0) {
|
|
1677
|
+
buffer.push(...consentBuffer);
|
|
1678
|
+
consentBuffer = [];
|
|
1679
|
+
}
|
|
1680
|
+
if (buffer.length > 0 || pendingIdentify) void flushNow();
|
|
1681
|
+
}
|
|
1682
|
+
for (const listener of [...consentListeners]) {
|
|
1683
|
+
try {
|
|
1684
|
+
listener(state);
|
|
1685
|
+
} catch {
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
},
|
|
1689
|
+
setIdentity(id, traits) {
|
|
1690
|
+
userId = id;
|
|
1691
|
+
userTraits = traits ?? null;
|
|
1692
|
+
if (id && enabled && consent !== "denied") {
|
|
1693
|
+
pendingIdentify = true;
|
|
1694
|
+
void flushNow();
|
|
1695
|
+
}
|
|
1696
|
+
},
|
|
1697
|
+
consentState() {
|
|
1698
|
+
return enabled ? consent : "denied";
|
|
1699
|
+
},
|
|
1700
|
+
onConsentChange(listener) {
|
|
1701
|
+
consentListeners.add(listener);
|
|
1702
|
+
return () => {
|
|
1703
|
+
consentListeners.delete(listener);
|
|
1704
|
+
};
|
|
1705
|
+
},
|
|
1706
|
+
async flush() {
|
|
1707
|
+
await ready;
|
|
1708
|
+
await flushNow();
|
|
1709
|
+
},
|
|
1710
|
+
async destroy() {
|
|
1711
|
+
if (timer != null) {
|
|
1712
|
+
clearInterval(timer);
|
|
1713
|
+
timer = null;
|
|
1714
|
+
}
|
|
1715
|
+
await flushNow().catch(() => void 0);
|
|
1716
|
+
destroyed = true;
|
|
1717
|
+
buffer = [];
|
|
1718
|
+
consentBuffer = [];
|
|
1719
|
+
pendingIdentify = false;
|
|
1720
|
+
}
|
|
1721
|
+
};
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
// src/analytics/session-tracker.ts
|
|
1725
|
+
import { newUuid } from "@mushi-mushi/core";
|
|
1726
|
+
var HEARTBEAT_INTERVAL_MS = 6e4;
|
|
1727
|
+
var RESUME_WINDOW_MS = 30 * 6e4;
|
|
1728
|
+
function createRNSessionTracker(opts) {
|
|
1729
|
+
const clock = opts.now ?? Date.now;
|
|
1730
|
+
const resolveClient = () => typeof opts.client === "function" ? opts.client() : opts.client;
|
|
1731
|
+
let sessionId = opts.sessionId ?? `sess_${newUuid()}`;
|
|
1732
|
+
let pageViewCount = 0;
|
|
1733
|
+
let started = false;
|
|
1734
|
+
let allowed = false;
|
|
1735
|
+
let foreground = true;
|
|
1736
|
+
let destroyed = false;
|
|
1737
|
+
let backgroundedAt = null;
|
|
1738
|
+
let heartbeat = null;
|
|
1739
|
+
function route() {
|
|
1740
|
+
return opts.getRoute?.() ?? null;
|
|
1741
|
+
}
|
|
1742
|
+
function send(kind, extra) {
|
|
1743
|
+
const client = resolveClient();
|
|
1744
|
+
if (!client || !allowed || destroyed) return;
|
|
1745
|
+
const base = {
|
|
1746
|
+
kind,
|
|
1747
|
+
session_id: sessionId,
|
|
1748
|
+
ts: new Date(clock()).toISOString(),
|
|
1749
|
+
page_view_count: pageViewCount,
|
|
1750
|
+
user_agent: opts.userAgent ?? null,
|
|
1751
|
+
sdk_version: opts.sdkVersion
|
|
1752
|
+
};
|
|
1753
|
+
void Promise.resolve(opts.getReporterToken()).then((token) => client.postSessionEvent({ ...base, reporter_token_hash: token, ...extra })).catch(() => void 0);
|
|
1754
|
+
}
|
|
1755
|
+
function stopHeartbeat() {
|
|
1756
|
+
if (heartbeat != null) {
|
|
1757
|
+
clearInterval(heartbeat);
|
|
1758
|
+
heartbeat = null;
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
function startHeartbeat() {
|
|
1762
|
+
stopHeartbeat();
|
|
1763
|
+
heartbeat = setInterval(() => send("session_heartbeat", { route: route() }), HEARTBEAT_INTERVAL_MS);
|
|
1764
|
+
}
|
|
1765
|
+
function startSession() {
|
|
1766
|
+
started = true;
|
|
1767
|
+
pageViewCount = 1;
|
|
1768
|
+
send("session_start", { route: route() });
|
|
1769
|
+
startHeartbeat();
|
|
1770
|
+
}
|
|
1771
|
+
function activate() {
|
|
1772
|
+
if (allowed || destroyed) return;
|
|
1773
|
+
allowed = true;
|
|
1774
|
+
if (!foreground) return;
|
|
1775
|
+
if (!started) startSession();
|
|
1776
|
+
else startHeartbeat();
|
|
1777
|
+
}
|
|
1778
|
+
function deactivate() {
|
|
1779
|
+
allowed = false;
|
|
1780
|
+
stopHeartbeat();
|
|
1781
|
+
}
|
|
1782
|
+
const unsubscribe = opts.consent.onConsentChange(() => {
|
|
1783
|
+
if (opts.consent.consentState() === "granted") activate();
|
|
1784
|
+
else deactivate();
|
|
1785
|
+
});
|
|
1786
|
+
void opts.consent.ready.then(() => {
|
|
1787
|
+
if (opts.consent.consentState() === "granted") activate();
|
|
1788
|
+
}).catch(() => void 0);
|
|
1789
|
+
return {
|
|
1790
|
+
onAppStateChange(state) {
|
|
1791
|
+
if (destroyed) return;
|
|
1792
|
+
if (state === "active") {
|
|
1793
|
+
if (foreground) return;
|
|
1794
|
+
foreground = true;
|
|
1795
|
+
const away = backgroundedAt == null ? 0 : clock() - backgroundedAt;
|
|
1796
|
+
backgroundedAt = null;
|
|
1797
|
+
if (!allowed) return;
|
|
1798
|
+
if (!started || away >= RESUME_WINDOW_MS) {
|
|
1799
|
+
sessionId = `sess_${newUuid()}`;
|
|
1800
|
+
startSession();
|
|
1801
|
+
} else {
|
|
1802
|
+
send("session_heartbeat", { route: route() });
|
|
1803
|
+
startHeartbeat();
|
|
1804
|
+
}
|
|
1805
|
+
return;
|
|
1806
|
+
}
|
|
1807
|
+
if (state !== "background" || !foreground) return;
|
|
1808
|
+
foreground = false;
|
|
1809
|
+
backgroundedAt = clock();
|
|
1810
|
+
stopHeartbeat();
|
|
1811
|
+
if (started) send("session_end", { route: route() });
|
|
1812
|
+
},
|
|
1813
|
+
pageView(next) {
|
|
1814
|
+
if (!allowed || !started || !foreground) return;
|
|
1815
|
+
pageViewCount += 1;
|
|
1816
|
+
send("page_view", { route: next });
|
|
1817
|
+
},
|
|
1818
|
+
destroy() {
|
|
1819
|
+
destroyed = true;
|
|
1820
|
+
deactivate();
|
|
1821
|
+
unsubscribe();
|
|
1822
|
+
}
|
|
1823
|
+
};
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1459
1826
|
// src/version.ts
|
|
1460
1827
|
var MUSHI_SDK_PACKAGE = "@mushi-mushi/react-native";
|
|
1461
|
-
var MUSHI_SDK_VERSION = "0.
|
|
1828
|
+
var MUSHI_SDK_VERSION = "0.22.0" ? "0.22.0" : "0.17.0";
|
|
1462
1829
|
|
|
1463
1830
|
// src/provider.tsx
|
|
1464
1831
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1465
1832
|
var MushiContext = createContext(null);
|
|
1833
|
+
function nativeUserAgent() {
|
|
1834
|
+
const os = Platform5?.OS ?? "native";
|
|
1835
|
+
const version = Platform5?.Version;
|
|
1836
|
+
return `${MUSHI_SDK_PACKAGE}/${MUSHI_SDK_VERSION} (${version != null ? `${os} ${String(version)}` : os})`;
|
|
1837
|
+
}
|
|
1466
1838
|
var DEFAULT_SCREENSHOT_HINT = "Check the preview \u2014 remove it if any private info (balances, personal details) is visible.";
|
|
1467
1839
|
function resolveScreenshotHint(v) {
|
|
1468
1840
|
if (v === false) return null;
|
|
@@ -1505,7 +1877,7 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
1505
1877
|
const networkRef = useRef3(null);
|
|
1506
1878
|
const queueRef = useRef3(null);
|
|
1507
1879
|
const apiClientRef = useRef3(null);
|
|
1508
|
-
const sessionIdRef = useRef3(`sess_${
|
|
1880
|
+
const sessionIdRef = useRef3(`sess_${newUuid2()}`);
|
|
1509
1881
|
const breadcrumbsRef = useRef3(
|
|
1510
1882
|
createBreadcrumbBuffer({ max: config.capture?.maxBreadcrumbs ?? 50 })
|
|
1511
1883
|
);
|
|
@@ -1577,14 +1949,14 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
1577
1949
|
if (existing && existing.startsWith("mushi_")) {
|
|
1578
1950
|
reporterTokenRef.current = existing;
|
|
1579
1951
|
} else {
|
|
1580
|
-
const fresh = `mushi_${
|
|
1952
|
+
const fresh = `mushi_${newUuid2()}`;
|
|
1581
1953
|
reporterTokenRef.current = fresh;
|
|
1582
1954
|
await saveReporterToken(fresh, secureStorage);
|
|
1583
1955
|
}
|
|
1584
1956
|
resolveReporterTokenReady();
|
|
1585
1957
|
} catch {
|
|
1586
1958
|
if (reporterTokenRef.current === `rn-${config.projectId}-anon`) {
|
|
1587
|
-
reporterTokenRef.current = `mushi_${
|
|
1959
|
+
reporterTokenRef.current = `mushi_${newUuid2()}`;
|
|
1588
1960
|
}
|
|
1589
1961
|
resolveReporterTokenReady();
|
|
1590
1962
|
}
|
|
@@ -1713,7 +2085,7 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
1713
2085
|
}));
|
|
1714
2086
|
const fingerprintHash = getDeviceFingerprintHash(deviceInfo);
|
|
1715
2087
|
const report = {
|
|
1716
|
-
id:
|
|
2088
|
+
id: newUuid2(),
|
|
1717
2089
|
projectId: config.projectId,
|
|
1718
2090
|
category: data.category,
|
|
1719
2091
|
description: scrubPii3(data.description),
|
|
@@ -1748,21 +2120,34 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
1748
2120
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1749
2121
|
};
|
|
1750
2122
|
const client = apiClientRef.current;
|
|
1751
|
-
if (!client)
|
|
2123
|
+
if (!client) {
|
|
2124
|
+
console.error(
|
|
2125
|
+
"[Mushi] submitReport called before the SDK finished initializing. Ensure <MushiProvider> is mounted with a valid projectId and apiKey."
|
|
2126
|
+
);
|
|
2127
|
+
return { ok: false, failureKind: "permanent" };
|
|
2128
|
+
}
|
|
1752
2129
|
const result = await client.submitReport(report);
|
|
1753
2130
|
if (!result.ok) {
|
|
1754
|
-
const
|
|
2131
|
+
const code = result.error?.code ?? "";
|
|
2132
|
+
const isCredentialError = code === "HTTP_401" || code === "HTTP_403" || code.includes("UNAUTHORIZED") || code.includes("FORBIDDEN") || code.includes("INVALID_API_KEY");
|
|
1755
2133
|
if (isCredentialError) {
|
|
1756
2134
|
const where = apiEndpoint.replace(/\/$/, "") === DEFAULT_API_ENDPOINT ? "Get the correct values at: https://kensaur.us/mushi-mushi/admin/projects" : "Get the correct values from your Mushi console's Projects page.";
|
|
1757
2135
|
console.error(
|
|
1758
2136
|
'[Mushi] Credentials rejected. Check your projectId and apiKey (must have "report:write" scope). ' + where
|
|
1759
2137
|
);
|
|
1760
|
-
return;
|
|
2138
|
+
return { ok: false, failureKind: "credentials" };
|
|
1761
2139
|
}
|
|
2140
|
+
const isRateLimited = code === "HTTP_429" || code.includes("RATE_LIMIT");
|
|
1762
2141
|
await queueRef.current?.enqueue(report);
|
|
1763
2142
|
config.callbacks?.onReportQueued?.(report.id);
|
|
2143
|
+
return {
|
|
2144
|
+
ok: false,
|
|
2145
|
+
queued: true,
|
|
2146
|
+
failureKind: isRateLimited ? "rate_limited" : "retrying"
|
|
2147
|
+
};
|
|
1764
2148
|
} else {
|
|
1765
2149
|
config.callbacks?.onReportSubmitted?.(report.id);
|
|
2150
|
+
return { ok: true };
|
|
1766
2151
|
}
|
|
1767
2152
|
},
|
|
1768
2153
|
[config.projectId, config.callbacks, apiEndpoint]
|
|
@@ -1792,6 +2177,63 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
1792
2177
|
if (rewardsFlushRef.current) clearInterval(rewardsFlushRef.current);
|
|
1793
2178
|
};
|
|
1794
2179
|
}, [config.rewards?.enabled, config.rewards?.trackActivity, config.rewards?.flushIntervalMs]);
|
|
2180
|
+
const eventTrackerRef = useRef3(null);
|
|
2181
|
+
const sessionTrackerRef = useRef3(null);
|
|
2182
|
+
useEffect3(() => {
|
|
2183
|
+
const getRoute = () => screenRef.current?.route ?? screenRef.current?.name ?? null;
|
|
2184
|
+
const tracker = createRNEventTracker({
|
|
2185
|
+
projectId: config.projectId,
|
|
2186
|
+
client: () => apiClientRef.current,
|
|
2187
|
+
getAnonId: async () => {
|
|
2188
|
+
await reporterTokenReadyRef.current;
|
|
2189
|
+
return reporterTokenRef.current;
|
|
2190
|
+
},
|
|
2191
|
+
getUserId: () => userRef.current?.id ?? null,
|
|
2192
|
+
getRoute,
|
|
2193
|
+
sdkVersion: MUSHI_SDK_VERSION,
|
|
2194
|
+
config: config.analytics,
|
|
2195
|
+
scrub: scrubPii3
|
|
2196
|
+
});
|
|
2197
|
+
eventTrackerRef.current = tracker;
|
|
2198
|
+
const sessions = config.trackSessions === false ? null : createRNSessionTracker({
|
|
2199
|
+
client: () => apiClientRef.current,
|
|
2200
|
+
getReporterToken: async () => {
|
|
2201
|
+
await reporterTokenReadyRef.current;
|
|
2202
|
+
return reporterTokenRef.current;
|
|
2203
|
+
},
|
|
2204
|
+
getRoute,
|
|
2205
|
+
consent: tracker,
|
|
2206
|
+
sessionId: sessionIdRef.current,
|
|
2207
|
+
sdkVersion: MUSHI_SDK_VERSION,
|
|
2208
|
+
userAgent: nativeUserAgent()
|
|
2209
|
+
});
|
|
2210
|
+
sessionTrackerRef.current = sessions;
|
|
2211
|
+
let sub;
|
|
2212
|
+
try {
|
|
2213
|
+
if (typeof AppState?.addEventListener === "function") {
|
|
2214
|
+
sub = AppState.addEventListener("change", (state) => {
|
|
2215
|
+
sessions?.onAppStateChange(state);
|
|
2216
|
+
if (state !== "active") tracker.flush().catch(() => {
|
|
2217
|
+
});
|
|
2218
|
+
});
|
|
2219
|
+
}
|
|
2220
|
+
} catch {
|
|
2221
|
+
}
|
|
2222
|
+
return () => {
|
|
2223
|
+
sub?.remove();
|
|
2224
|
+
sessions?.destroy();
|
|
2225
|
+
sessionTrackerRef.current = null;
|
|
2226
|
+
eventTrackerRef.current = null;
|
|
2227
|
+
tracker.destroy().catch(() => {
|
|
2228
|
+
});
|
|
2229
|
+
};
|
|
2230
|
+
}, [
|
|
2231
|
+
config.projectId,
|
|
2232
|
+
config.analytics?.enabled,
|
|
2233
|
+
config.analytics?.consent,
|
|
2234
|
+
config.analytics?.flushIntervalMs,
|
|
2235
|
+
config.trackSessions
|
|
2236
|
+
]);
|
|
1795
2237
|
const instance = useMemo(
|
|
1796
2238
|
() => ({
|
|
1797
2239
|
open,
|
|
@@ -1809,6 +2251,7 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
1809
2251
|
name: traits?.name,
|
|
1810
2252
|
provider: traits?.provider
|
|
1811
2253
|
};
|
|
2254
|
+
eventTrackerRef.current?.setIdentity(userId, traits ?? null);
|
|
1812
2255
|
},
|
|
1813
2256
|
setUser(user) {
|
|
1814
2257
|
userRef.current = {
|
|
@@ -1817,6 +2260,7 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
1817
2260
|
name: user.name,
|
|
1818
2261
|
provider: user.provider
|
|
1819
2262
|
};
|
|
2263
|
+
eventTrackerRef.current?.setIdentity(user.id, { email: user.email, name: user.name, provider: user.provider });
|
|
1820
2264
|
},
|
|
1821
2265
|
identifyWithToken(token) {
|
|
1822
2266
|
userTokenRef.current = token && typeof token === "string" ? token : null;
|
|
@@ -1828,7 +2272,10 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
1828
2272
|
...claims.email ? { email: claims.email } : {},
|
|
1829
2273
|
...claims.name ? { name: claims.name } : {}
|
|
1830
2274
|
};
|
|
2275
|
+
eventTrackerRef.current?.setIdentity(claims.sub, null);
|
|
1831
2276
|
}
|
|
2277
|
+
} else {
|
|
2278
|
+
eventTrackerRef.current?.setIdentity(null, null);
|
|
1832
2279
|
}
|
|
1833
2280
|
},
|
|
1834
2281
|
publishPageContext(context) {
|
|
@@ -1841,6 +2288,7 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
1841
2288
|
const prev = screenRef.current?.name;
|
|
1842
2289
|
screenRef.current = screen;
|
|
1843
2290
|
if (screen.name && screen.name !== prev) {
|
|
2291
|
+
sessionTrackerRef.current?.pageView(screen.route ?? screen.name);
|
|
1844
2292
|
breadcrumbsRef.current.add({
|
|
1845
2293
|
category: "navigation",
|
|
1846
2294
|
level: "info",
|
|
@@ -1902,6 +2350,13 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
1902
2350
|
if (!config.rewards?.enabled) return;
|
|
1903
2351
|
activityQueueRef.current.push({ action, metadata });
|
|
1904
2352
|
},
|
|
2353
|
+
// Product analytics (Users & Funnels)
|
|
2354
|
+
track(event, properties) {
|
|
2355
|
+
return eventTrackerRef.current?.track(event, properties) ?? false;
|
|
2356
|
+
},
|
|
2357
|
+
setConsent(state) {
|
|
2358
|
+
eventTrackerRef.current?.setConsent(state);
|
|
2359
|
+
},
|
|
1905
2360
|
async getTier() {
|
|
1906
2361
|
const userId = userRef.current?.id;
|
|
1907
2362
|
const client = apiClientRef.current;
|