@teamvortexsoftware/vortex-react-native 0.0.13 → 1.0.1
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/LICENSE +201 -0
- package/README.md +1227 -11
- package/dist/InviteFormCore-D4HkMMo0.d.mts +721 -0
- package/dist/InviteFormCore-D9oUCbu7.d.ts +721 -0
- package/dist/VortexClient.js +192 -0
- package/dist/VortexClient.js.map +1 -0
- package/dist/VortexDeferredLinks.js +127 -0
- package/dist/VortexDeferredLinks.js.map +1 -0
- package/dist/clientInfo.js +45 -0
- package/dist/clientInfo.js.map +1 -0
- package/dist/components/ContactsPickerModal.js +182 -0
- package/dist/components/ContactsPickerModal.js.map +1 -0
- package/dist/components/InviteFormCore.js +2141 -0
- package/dist/components/InviteFormCore.js.map +1 -0
- package/dist/components/InviteFormMobile.js +463 -0
- package/dist/components/InviteFormMobile.js.map +1 -0
- package/dist/components/InviteFormWeb.js +295 -0
- package/dist/components/InviteFormWeb.js.map +1 -0
- package/dist/components/PlacedItemToolbar.js +147 -0
- package/dist/components/PlacedItemToolbar.js.map +1 -0
- package/dist/components/ShareButtons.js +1 -0
- package/dist/components/ShareButtons.js.map +1 -0
- package/dist/components/VrtxContactsImport.js +234 -0
- package/dist/components/VrtxContactsImport.js.map +1 -0
- package/dist/components/VrtxEmailInvitations.js +341 -0
- package/dist/components/VrtxEmailInvitations.js.map +1 -0
- package/dist/components/VrtxFindFriends.js +400 -0
- package/dist/components/VrtxFindFriends.js.map +1 -0
- package/dist/components/VrtxHeading.js +58 -0
- package/dist/components/VrtxHeading.js.map +1 -0
- package/dist/components/VrtxIncomingInvitations.js +657 -0
- package/dist/components/VrtxIncomingInvitations.js.map +1 -0
- package/dist/components/VrtxInvitationSuggestions.js +506 -0
- package/dist/components/VrtxInvitationSuggestions.js.map +1 -0
- package/dist/components/VrtxInviteContacts.js +512 -0
- package/dist/components/VrtxInviteContacts.js.map +1 -0
- package/dist/components/VrtxOutgoingInvitations.js +572 -0
- package/dist/components/VrtxOutgoingInvitations.js.map +1 -0
- package/dist/components/VrtxSearchBox.js +487 -0
- package/dist/components/VrtxSearchBox.js.map +1 -0
- package/dist/components/VrtxSelect.js +27 -0
- package/dist/components/VrtxSelect.js.map +1 -0
- package/dist/components/VrtxShareOptions.js +435 -0
- package/dist/components/VrtxShareOptions.js.map +1 -0
- package/dist/components/VrtxSubmit.js +132 -0
- package/dist/components/VrtxSubmit.js.map +1 -0
- package/dist/components/VrtxText.js +146 -0
- package/dist/components/VrtxText.js.map +1 -0
- package/dist/constants/mockData.d.mts +7 -0
- package/dist/constants/mockData.d.ts +7 -0
- package/dist/constants/mockData.js +48 -0
- package/dist/constants/mockData.js.map +1 -0
- package/dist/constants/mockData.mjs +22 -0
- package/dist/constants/mockData.mjs.map +1 -0
- package/dist/context/VortexModulesContext.js +135 -0
- package/dist/context/VortexModulesContext.js.map +1 -0
- package/dist/hooks/useInvitationFormLogic.d.mts +2 -0
- package/dist/hooks/useInvitationFormLogic.d.ts +2 -0
- package/dist/hooks/useInvitationFormLogic.js +300 -0
- package/dist/hooks/useInvitationFormLogic.js.map +1 -0
- package/dist/hooks/useInvitationFormLogic.mjs +276 -0
- package/dist/hooks/useInvitationFormLogic.mjs.map +1 -0
- package/dist/hooks/usePrefetchWidgetConfiguration.js +117 -0
- package/dist/hooks/usePrefetchWidgetConfiguration.js.map +1 -0
- package/dist/hooks/useThemeStyles.js +2 -0
- package/dist/hooks/useThemeStyles.js.map +1 -0
- package/dist/hooks/useVortexInvite.js +467 -56
- package/dist/hooks/useVortexInvite.js.map +1 -0
- package/dist/index-web.d.mts +93 -0
- package/dist/index-web.d.ts +93 -0
- package/dist/index-web.js +7397 -0
- package/dist/index-web.js.map +1 -0
- package/dist/index-web.mjs +7445 -0
- package/dist/index-web.mjs.map +1 -0
- package/dist/index.d.mts +656 -0
- package/dist/index.d.ts +656 -0
- package/dist/index.js +10205 -4
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +10244 -0
- package/dist/index.mjs.map +1 -0
- package/dist/types/VortexClient.d.ts +106 -0
- package/dist/types/VortexClient.d.ts.map +1 -0
- package/dist/types/VortexDeferredLinks.d.ts +73 -0
- package/dist/types/VortexDeferredLinks.d.ts.map +1 -0
- package/dist/types/clientInfo.d.ts +5 -0
- package/dist/types/clientInfo.d.ts.map +1 -0
- package/dist/types/components/ContactsPickerModal.d.ts +18 -0
- package/dist/types/components/ContactsPickerModal.d.ts.map +1 -0
- package/dist/types/components/InviteFormCore.d.ts +166 -0
- package/dist/types/components/InviteFormCore.d.ts.map +1 -0
- package/dist/types/components/InviteFormMobile.d.ts +42 -0
- package/dist/types/components/InviteFormMobile.d.ts.map +1 -0
- package/dist/types/components/InviteFormWeb.d.ts +87 -0
- package/dist/types/components/InviteFormWeb.d.ts.map +1 -0
- package/dist/types/components/PlacedItemToolbar.d.ts +16 -0
- package/dist/types/components/PlacedItemToolbar.d.ts.map +1 -0
- package/dist/types/components/VrtxContactsImport.d.ts +14 -0
- package/dist/types/components/VrtxContactsImport.d.ts.map +1 -0
- package/dist/types/components/VrtxEmailInvitations.d.ts +31 -0
- package/dist/types/components/VrtxEmailInvitations.d.ts.map +1 -0
- package/dist/types/components/VrtxFindFriends.d.ts +25 -0
- package/dist/types/components/VrtxFindFriends.d.ts.map +1 -0
- package/dist/types/components/VrtxHeading.d.ts +6 -0
- package/dist/types/components/VrtxHeading.d.ts.map +1 -0
- package/dist/types/components/VrtxIncomingInvitations.d.ts +27 -0
- package/dist/types/components/VrtxIncomingInvitations.d.ts.map +1 -0
- package/dist/types/components/VrtxInvitationSuggestions.d.ts +25 -0
- package/dist/types/components/VrtxInvitationSuggestions.d.ts.map +1 -0
- package/dist/types/components/VrtxInviteContacts.d.ts +24 -0
- package/dist/types/components/VrtxInviteContacts.d.ts.map +1 -0
- package/dist/types/components/VrtxOutgoingInvitations.d.ts +27 -0
- package/dist/types/components/VrtxOutgoingInvitations.d.ts.map +1 -0
- package/dist/types/components/VrtxSearchBox.d.ts +28 -0
- package/dist/types/components/VrtxSearchBox.d.ts.map +1 -0
- package/dist/types/components/VrtxSelect.d.ts +6 -0
- package/dist/types/components/VrtxSelect.d.ts.map +1 -0
- package/dist/types/components/VrtxShareOptions.d.ts +41 -0
- package/dist/types/components/VrtxShareOptions.d.ts.map +1 -0
- package/dist/types/components/VrtxSubmit.d.ts +18 -0
- package/dist/types/components/VrtxSubmit.d.ts.map +1 -0
- package/dist/types/components/VrtxText.d.ts +8 -0
- package/dist/types/components/VrtxText.d.ts.map +1 -0
- package/dist/types/constants/mockData.d.ts +4 -0
- package/dist/types/constants/mockData.d.ts.map +1 -0
- package/dist/types/context/VortexModulesContext.d.ts +238 -0
- package/dist/types/context/VortexModulesContext.d.ts.map +1 -0
- package/dist/types/findFriends.js +10 -0
- package/dist/types/findFriends.js.map +1 -0
- package/dist/types/hooks/useInvitationFormLogic.d.ts +55 -0
- package/dist/types/hooks/useInvitationFormLogic.d.ts.map +1 -0
- package/dist/types/hooks/usePrefetchWidgetConfiguration.d.ts +39 -0
- package/dist/types/hooks/usePrefetchWidgetConfiguration.d.ts.map +1 -0
- package/dist/types/hooks/useThemeStyles.d.ts +1 -0
- package/dist/types/hooks/useThemeStyles.d.ts.map +1 -1
- package/dist/types/hooks/useVortexInvite.d.ts +48 -6
- package/dist/types/hooks/useVortexInvite.d.ts.map +1 -1
- package/dist/types/index-web.d.ts +23 -0
- package/dist/types/index-web.d.ts.map +1 -0
- package/dist/types/index.d.ts +21 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/invitations.js +13 -0
- package/dist/types/invitations.js.map +1 -0
- package/dist/types/inviteContacts.js +14 -0
- package/dist/types/inviteContacts.js.map +1 -0
- package/dist/{shared/InvitationResult.js → types/platformOperations.js} +1 -0
- package/dist/types/platformOperations.js.map +1 -0
- package/dist/types/searchBox.js +11 -0
- package/dist/types/searchBox.js.map +1 -0
- package/dist/types/types/findFriends.d.ts +101 -0
- package/dist/types/types/findFriends.d.ts.map +1 -0
- package/dist/types/types/invitations.d.ts +301 -0
- package/dist/types/types/invitations.d.ts.map +1 -0
- package/dist/types/types/inviteContacts.d.ts +86 -0
- package/dist/types/types/inviteContacts.d.ts.map +1 -0
- package/dist/types/types/platformOperations.d.ts +185 -0
- package/dist/types/types/platformOperations.d.ts.map +1 -0
- package/dist/types/types/searchBox.d.ts +69 -0
- package/dist/types/types/searchBox.d.ts.map +1 -0
- package/dist/types/types/unfurlConfig.d.ts +34 -0
- package/dist/types/types/unfurlConfig.d.ts.map +1 -0
- package/dist/types/unfurlConfig.js +21 -0
- package/dist/types/unfurlConfig.js.map +1 -0
- package/dist/types/utils/analytics.d.ts +54 -0
- package/dist/types/utils/analytics.d.ts.map +1 -0
- package/dist/types/utils/configCache.d.ts +34 -0
- package/dist/types/utils/configCache.d.ts.map +1 -0
- package/dist/types/utils/contactUtils.d.ts +9 -0
- package/dist/types/utils/contactUtils.d.ts.map +1 -0
- package/dist/types/utils/featureWarnings.d.ts +56 -0
- package/dist/types/utils/featureWarnings.d.ts.map +1 -0
- package/dist/types/utils/formUtils.d.ts +11 -3
- package/dist/types/utils/formUtils.d.ts.map +1 -1
- package/dist/types/utils/gradientUtils.d.ts +67 -0
- package/dist/types/utils/gradientUtils.d.ts.map +1 -0
- package/dist/types/utils/invitationEvents.d.ts +21 -0
- package/dist/types/utils/invitationEvents.d.ts.map +1 -0
- package/dist/types/utils/moduleLoaders.d.ts +115 -0
- package/dist/types/utils/moduleLoaders.d.ts.map +1 -0
- package/dist/types/utils/moduleLoaders.web.d.ts +73 -0
- package/dist/types/utils/moduleLoaders.web.d.ts.map +1 -0
- package/dist/types/utils/nameUtils.d.ts +15 -0
- package/dist/types/utils/nameUtils.d.ts.map +1 -0
- package/dist/types/utils/themeUtils.d.ts +3 -1
- package/dist/types/utils/themeUtils.d.ts.map +1 -1
- package/dist/types/vortexInvite.d.ts +145 -5
- package/dist/types/vortexInvite.d.ts.map +1 -1
- package/dist/useInvitationFormLogic-Ct73M19B.d.mts +242 -0
- package/dist/useInvitationFormLogic-Ct73M19B.d.ts +242 -0
- package/dist/utils/analytics.js +92 -0
- package/dist/utils/analytics.js.map +1 -0
- package/dist/utils/configCache.js +68 -0
- package/dist/utils/configCache.js.map +1 -0
- package/dist/utils/contactUtils.d.mts +12 -0
- package/dist/utils/contactUtils.d.ts +12 -0
- package/dist/utils/contactUtils.js +37 -0
- package/dist/utils/contactUtils.js.map +1 -0
- package/dist/utils/contactUtils.mjs +12 -0
- package/dist/utils/contactUtils.mjs.map +1 -0
- package/dist/utils/featureWarnings.js +214 -0
- package/dist/utils/featureWarnings.js.map +1 -0
- package/dist/utils/formUtils.js +161 -51
- package/dist/utils/formUtils.js.map +1 -0
- package/dist/utils/gradientUtils.js +120 -0
- package/dist/utils/gradientUtils.js.map +1 -0
- package/dist/utils/invitationEvents.js +45 -0
- package/dist/utils/invitationEvents.js.map +1 -0
- package/dist/utils/moduleLoaders.js +275 -0
- package/dist/utils/moduleLoaders.js.map +1 -0
- package/dist/utils/moduleLoaders.web.js +72 -0
- package/dist/utils/moduleLoaders.web.js.map +1 -0
- package/dist/utils/nameUtils.js +51 -0
- package/dist/utils/nameUtils.js.map +1 -0
- package/dist/utils/themeUtils.js +117 -32
- package/dist/utils/themeUtils.js.map +1 -0
- package/dist/vortexInvite.js +78 -167
- package/dist/vortexInvite.js.map +1 -0
- package/package.json +69 -31
- package/dist/components/Clipboard.js +0 -64
- package/dist/shared/api.js +0 -90
- package/dist/tests/TestVortexInvite.js +0 -134
- package/dist/types/components/Clipboard.d.ts +0 -16
- package/dist/types/components/Clipboard.d.ts.map +0 -1
- package/dist/types/shared/InvitationResult.d.ts +0 -24
- package/dist/types/shared/InvitationResult.d.ts.map +0 -1
- package/dist/types/shared/api.d.ts +0 -14
- package/dist/types/shared/api.d.ts.map +0 -1
- package/dist/types/tests/TestVortexInvite.d.ts +0 -4
- package/dist/types/tests/TestVortexInvite.d.ts.map +0 -1
|
@@ -16,22 +16,166 @@ exports.useVortexInvite = useVortexInvite;
|
|
|
16
16
|
const react_1 = require("react");
|
|
17
17
|
const react_native_1 = require("react-native");
|
|
18
18
|
const themeUtils_1 = require("../utils/themeUtils");
|
|
19
|
-
const Clipboard_1 = require("../components/Clipboard");
|
|
20
19
|
const useThemeStyles_1 = require("./useThemeStyles");
|
|
21
20
|
const react_native_uuid_1 = __importDefault(require("react-native-uuid"));
|
|
22
21
|
const formUtils_1 = require("../utils/formUtils");
|
|
23
22
|
const vortex_shared_ui_1 = require("@teamvortexsoftware/vortex-shared-ui");
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
const configCache_1 = require("../utils/configCache");
|
|
24
|
+
const analytics_client_1 = require("@teamvortexsoftware/analytics-client");
|
|
25
|
+
const analytics_1 = require("../utils/analytics");
|
|
26
|
+
function useVortexInvite({ componentId, vortexApiUrl, isLoading = false, jwt, user, onSuccess, onError, group, groups, scope, scopeType, widgetConfiguration: initialWidgetConfiguration, analyticsBaseURL, onEvent, analyticsSegmentation, locale, }) {
|
|
27
|
+
// Check FIRST, before any hooks are called
|
|
28
|
+
if (!componentId) {
|
|
29
|
+
throw Error('[Vortex Invite] componentId is required to fetch configuration');
|
|
30
|
+
}
|
|
31
|
+
// Create effective groups with fallback logic: group > scope+scopeType
|
|
32
|
+
const effectiveGroup = (0, react_1.useMemo)(() => {
|
|
33
|
+
if (group)
|
|
34
|
+
return group;
|
|
35
|
+
if (scope && scopeType) {
|
|
36
|
+
return { type: scopeType, groupId: scope, name: scope };
|
|
37
|
+
}
|
|
38
|
+
return undefined;
|
|
39
|
+
}, [group, scope, scopeType]);
|
|
40
|
+
// If user is provided, construct the insecure JWT token
|
|
41
|
+
const effectiveJwt = (0, react_1.useMemo)(() => {
|
|
42
|
+
if (jwt)
|
|
43
|
+
return jwt;
|
|
44
|
+
if (user) {
|
|
45
|
+
if (typeof user === 'string') {
|
|
46
|
+
return user;
|
|
47
|
+
}
|
|
48
|
+
const payload = Object.assign(Object.assign({}, user), { componentId });
|
|
49
|
+
const jsonString = JSON.stringify(payload);
|
|
50
|
+
const base64Encoded = Buffer.from(jsonString).toString('base64');
|
|
51
|
+
return `raw-data:${base64Encoded}`;
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}, [jwt, user, componentId]);
|
|
55
|
+
const vortexClient = (0, react_1.useMemo)(() => {
|
|
56
|
+
const clientInfo = (0, vortex_shared_ui_1.getClientInfo)();
|
|
57
|
+
const sessionId = react_native_uuid_1.default.v4();
|
|
58
|
+
return new vortex_shared_ui_1.VortexClient(vortexApiUrl, clientInfo.name, clientInfo.version, sessionId);
|
|
59
|
+
}, [vortexApiUrl]);
|
|
60
|
+
// Initialize from cache first, then fall back to initialWidgetConfiguration prop
|
|
61
|
+
const cachedConfig = configCache_1.configCache.get(componentId);
|
|
62
|
+
const [widgetConfiguration, setWidgetConfiguration] = (0, react_1.useState)(cachedConfig || initialWidgetConfiguration);
|
|
27
63
|
const [error, setError] = (0, react_1.useState)(null);
|
|
28
64
|
const [fetching, setFetching] = (0, react_1.useState)(isLoading);
|
|
29
|
-
const [loading, setLoading] = (0, react_1.useState)(
|
|
65
|
+
const [loading, setLoading] = (0, react_1.useState)(!cachedConfig && !initialWidgetConfiguration); // Start as false if we have cached or prefetched config
|
|
30
66
|
const [email, setEmail] = (0, react_1.useState)('');
|
|
31
67
|
const [shareableLink, setShareableLink] = (0, react_1.useState)();
|
|
68
|
+
const [role, setRole] = (0, react_1.useState)(undefined);
|
|
32
69
|
const [inviteLoading, setInviteLoading] = (0, react_1.useState)(false);
|
|
33
70
|
const [showSuccessMessage, setShowSuccessMessage] = (0, react_1.useState)(false);
|
|
34
71
|
const opacity = new react_native_1.Animated.Value(0.3);
|
|
72
|
+
// Analytics state
|
|
73
|
+
const [deploymentId, setDeploymentId] = (0, react_1.useState)(null);
|
|
74
|
+
const [sessionId] = (0, react_1.useState)(() => react_native_uuid_1.default.v4());
|
|
75
|
+
const [widgetRenderTracked, setWidgetRenderTracked] = (0, react_1.useState)(false);
|
|
76
|
+
const [formRenderTime, setFormRenderTime] = (0, react_1.useState)(null);
|
|
77
|
+
// Track when config has been fetched through VortexClient (which sets sessionAttestation)
|
|
78
|
+
const [configFetchedViaClient, setConfigFetchedViaClient] = (0, react_1.useState)(false);
|
|
79
|
+
// Create analytics client (memoized to avoid recreation)
|
|
80
|
+
const analyticsClient = (0, react_1.useMemo)(() => {
|
|
81
|
+
if (!effectiveJwt)
|
|
82
|
+
return null;
|
|
83
|
+
const analyticsUrl = analyticsBaseURL || analytics_1.DEFAULT_ANALYTICS_URL;
|
|
84
|
+
const clientInfo = (0, vortex_shared_ui_1.getClientInfo)();
|
|
85
|
+
return new analytics_client_1.AnalyticsClient(analyticsUrl, effectiveJwt, sessionId, clientInfo.name, clientInfo.version);
|
|
86
|
+
}, [effectiveJwt, analyticsBaseURL, sessionId]);
|
|
87
|
+
// Compute effective groups for analytics
|
|
88
|
+
const effectiveGroups = (0, react_1.useMemo)(() => {
|
|
89
|
+
if (groups && groups.length > 0)
|
|
90
|
+
return groups;
|
|
91
|
+
if (effectiveGroup) {
|
|
92
|
+
return [{
|
|
93
|
+
type: effectiveGroup.type,
|
|
94
|
+
id: effectiveGroup.groupId || effectiveGroup.id || '',
|
|
95
|
+
name: effectiveGroup.name,
|
|
96
|
+
}];
|
|
97
|
+
}
|
|
98
|
+
return undefined;
|
|
99
|
+
}, [groups, effectiveGroup]);
|
|
100
|
+
// Core event tracking function
|
|
101
|
+
const trackEvent = (0, react_1.useCallback)((name, payload) => __awaiter(this, void 0, void 0, function* () {
|
|
102
|
+
const event = {
|
|
103
|
+
name,
|
|
104
|
+
widgetConfigurationId: (widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.id) || '',
|
|
105
|
+
deploymentId: deploymentId || '',
|
|
106
|
+
environmentId: '', // Not currently used (matches web SDK)
|
|
107
|
+
platform: react_native_1.Platform.OS, // 'ios' or 'android'
|
|
108
|
+
timestamp: Math.floor(Date.now() / 1000), // Unix seconds
|
|
109
|
+
sessionId,
|
|
110
|
+
useragent: (0, analytics_1.getDeviceInfo)(),
|
|
111
|
+
foreignUserId: (0, analytics_1.extractForeignUserId)(effectiveJwt),
|
|
112
|
+
segmentation: analyticsSegmentation,
|
|
113
|
+
payload,
|
|
114
|
+
groups: effectiveGroups,
|
|
115
|
+
};
|
|
116
|
+
// Call user's onEvent callback
|
|
117
|
+
if (onEvent) {
|
|
118
|
+
onEvent(event);
|
|
119
|
+
}
|
|
120
|
+
// Send to analytics backend (fire-and-forget, non-blocking)
|
|
121
|
+
if (analyticsClient && deploymentId && widgetConfiguration) {
|
|
122
|
+
analyticsClient.track(event).catch(() => {
|
|
123
|
+
// Silently ignore errors - analytics should never impact user experience
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}), [
|
|
127
|
+
widgetConfiguration,
|
|
128
|
+
deploymentId,
|
|
129
|
+
sessionId,
|
|
130
|
+
effectiveJwt,
|
|
131
|
+
analyticsSegmentation,
|
|
132
|
+
effectiveGroups,
|
|
133
|
+
onEvent,
|
|
134
|
+
analyticsClient,
|
|
135
|
+
]);
|
|
136
|
+
// Track widget render once per hook instance (on initial render only)
|
|
137
|
+
// The flag resets when component unmounts, so remounting fires a new event (expected behavior)
|
|
138
|
+
const trackWidgetRender = (0, react_1.useCallback)(() => {
|
|
139
|
+
if (widgetRenderTracked)
|
|
140
|
+
return;
|
|
141
|
+
setWidgetRenderTracked(true);
|
|
142
|
+
setFormRenderTime(Date.now());
|
|
143
|
+
trackEvent(analytics_client_1.EventNames.INVITE_FORM_RENDER_SUCCEEDED);
|
|
144
|
+
}, [widgetRenderTracked, trackEvent]);
|
|
145
|
+
// Track widget error
|
|
146
|
+
const trackWidgetError = (0, react_1.useCallback)((errorMessage) => {
|
|
147
|
+
trackEvent(analytics_client_1.EventNames.INVITE_FORM_RENDER_FAILED, { error: errorMessage });
|
|
148
|
+
}, [trackEvent]);
|
|
149
|
+
// Track share link clicks
|
|
150
|
+
const trackShareLinkClick = (0, react_1.useCallback)((clickName) => {
|
|
151
|
+
trackEvent(analytics_client_1.EventNames.SHARING_DESTINATION_BUTTON_CLICKED, { clickName });
|
|
152
|
+
}, [trackEvent]);
|
|
153
|
+
// Track email field focus
|
|
154
|
+
const trackEmailFieldFocus = (0, react_1.useCallback)(() => {
|
|
155
|
+
const timestamp = formRenderTime ? Date.now() - formRenderTime : 0;
|
|
156
|
+
trackEvent(analytics_client_1.EventNames.EMAIL_FIELD_FOCUSSED, { timestamp });
|
|
157
|
+
}, [formRenderTime, trackEvent]);
|
|
158
|
+
// Track email field blur
|
|
159
|
+
const trackEmailFieldBlur = (0, react_1.useCallback)(() => {
|
|
160
|
+
const timestamp = formRenderTime ? Date.now() - formRenderTime : 0;
|
|
161
|
+
trackEvent(analytics_client_1.EventNames.EMAIL_FIELD_BLURRED, { timestamp });
|
|
162
|
+
}, [formRenderTime, trackEvent]);
|
|
163
|
+
// Track email validation
|
|
164
|
+
const trackEmailValidation = (0, react_1.useCallback)((emailValue, isValid) => {
|
|
165
|
+
trackEvent(analytics_client_1.EventNames.EMAIL_SUBMISSION_VALIDATED, { email: emailValue, isValid });
|
|
166
|
+
}, [trackEvent]);
|
|
167
|
+
// Track form submission
|
|
168
|
+
const trackEmailInvitationsSubmitted = (0, react_1.useCallback)((formData) => {
|
|
169
|
+
trackEvent(analytics_client_1.EventNames.EMAIL_FIELD_SUBMISSION_SUCCEEDED, { formData });
|
|
170
|
+
}, [trackEvent]);
|
|
171
|
+
// Track email validation error (now uses WIDGET_EMAIL_VALIDATION with error payload)
|
|
172
|
+
const trackEmailValidationError = (0, react_1.useCallback)((formData) => {
|
|
173
|
+
trackEvent(analytics_client_1.EventNames.EMAIL_SUBMISSION_VALIDATED, { formData, hasError: true });
|
|
174
|
+
}, [trackEvent]);
|
|
175
|
+
// Track email submit error
|
|
176
|
+
const trackEmailSubmitError = (0, react_1.useCallback)((errorMessage) => {
|
|
177
|
+
trackEvent(analytics_client_1.EventNames.EMAIL_FIELD_SUBMISSION_FAILED, { error: errorMessage });
|
|
178
|
+
}, [trackEvent]);
|
|
35
179
|
// Extract theme colors and feature flags using utility functions
|
|
36
180
|
const themeColors = (0, react_1.useMemo)(() => (0, themeUtils_1.extractThemeColors)(widgetConfiguration), [widgetConfiguration]);
|
|
37
181
|
// Extract form configuration
|
|
@@ -52,6 +196,14 @@ function useVortexInvite({ widgetId, environmentId, vortexApiUrl, contentTokens,
|
|
|
52
196
|
hasSeparateColumns: (0, formUtils_1.hasEmailAndShareInSeparateColumns)(formConfig),
|
|
53
197
|
formConfig,
|
|
54
198
|
}), [formConfig]);
|
|
199
|
+
// Extract role options and initialize default role
|
|
200
|
+
const roleOptions = (0, react_1.useMemo)(() => (0, formUtils_1.getRoleOptions)(formConfig), [formConfig]);
|
|
201
|
+
(0, react_1.useEffect)(() => {
|
|
202
|
+
var _a;
|
|
203
|
+
if (!role && roleOptions.length > 0) {
|
|
204
|
+
setRole((_a = roleOptions[0]) === null || _a === void 0 ? void 0 : _a.value);
|
|
205
|
+
}
|
|
206
|
+
}, [role, roleOptions]);
|
|
55
207
|
const has = (0, react_1.useMemo)(() => {
|
|
56
208
|
const flags = (0, themeUtils_1.extractFeatureFlags)(widgetConfiguration);
|
|
57
209
|
console.log('[Vortex] Invite has', flags);
|
|
@@ -63,8 +215,12 @@ function useVortexInvite({ widgetId, environmentId, vortexApiUrl, contentTokens,
|
|
|
63
215
|
console.warn('[Vortex] Invite Using localhost as host');
|
|
64
216
|
}
|
|
65
217
|
}, [vortexApiUrl]);
|
|
66
|
-
// Loading animation effect
|
|
218
|
+
// Loading animation effect (skip if we have prefetched configuration)
|
|
67
219
|
(0, react_1.useEffect)(() => {
|
|
220
|
+
// If we already have configuration, no need for loading animation
|
|
221
|
+
if (initialWidgetConfiguration) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
68
224
|
const animation = react_native_1.Animated.loop(react_native_1.Animated.sequence([
|
|
69
225
|
react_native_1.Animated.timing(opacity, {
|
|
70
226
|
toValue: 1,
|
|
@@ -82,38 +238,65 @@ function useVortexInvite({ widgetId, environmentId, vortexApiUrl, contentTokens,
|
|
|
82
238
|
setLoading(false);
|
|
83
239
|
animation.stop();
|
|
84
240
|
}, 2000);
|
|
85
|
-
}, [opacity]);
|
|
86
|
-
// Fetch widget configuration if
|
|
241
|
+
}, [opacity, initialWidgetConfiguration]);
|
|
242
|
+
// Fetch widget configuration (always fetch to get latest, but don't show loading if we have prefetched data)
|
|
87
243
|
(0, react_1.useEffect)(() => {
|
|
88
|
-
|
|
89
|
-
if (widgetConfiguration) {
|
|
90
|
-
console.log('[Vortex] Invite Already has widgetConfiguration, skipping fetch');
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
if (!jwt) {
|
|
244
|
+
if (!effectiveJwt) {
|
|
94
245
|
console.log('[Vortex] Invite JWT is required');
|
|
95
246
|
return;
|
|
96
247
|
}
|
|
97
248
|
const fetchData = () => __awaiter(this, void 0, void 0, function* () {
|
|
98
249
|
var _a;
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
250
|
+
// Only show loading state if we don't have ANY configuration yet
|
|
251
|
+
const shouldShowLoading = !widgetConfiguration;
|
|
252
|
+
if (shouldShowLoading) {
|
|
253
|
+
console.log('[Vortex] Invite Fetching Data (first load)...');
|
|
254
|
+
setFetching(true);
|
|
255
|
+
setLoading(true);
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
console.log('[Vortex] Invite Refreshing configuration in background...');
|
|
259
|
+
}
|
|
102
260
|
setError(null);
|
|
103
261
|
try {
|
|
104
|
-
const result = yield vortexClient.getWidgetConfiguration(
|
|
262
|
+
const result = yield vortexClient.getWidgetConfiguration(componentId, effectiveJwt, undefined, locale);
|
|
105
263
|
if ((_a = result === null || result === void 0 ? void 0 : result.data) === null || _a === void 0 ? void 0 : _a.widgetConfiguration) {
|
|
106
|
-
|
|
107
|
-
|
|
264
|
+
const freshConfig = result.data.widgetConfiguration;
|
|
265
|
+
setWidgetConfiguration(freshConfig);
|
|
266
|
+
configCache_1.configCache.set(componentId, freshConfig); // Update shared cache
|
|
267
|
+
// Mark that config was fetched via VortexClient (sessionAttestation is now set)
|
|
268
|
+
setConfigFetchedViaClient(true);
|
|
269
|
+
// Extract deploymentId from API response (CRITICAL for analytics)
|
|
270
|
+
if (result.data.deploymentId) {
|
|
271
|
+
setDeploymentId(result.data.deploymentId);
|
|
272
|
+
console.log('[Vortex] Invite deploymentId:', result.data.deploymentId);
|
|
273
|
+
}
|
|
274
|
+
console.log('[Vortex] Invite Successfully fetched widgetConfiguration', JSON.stringify(freshConfig));
|
|
108
275
|
}
|
|
109
276
|
else {
|
|
110
|
-
const error =
|
|
277
|
+
const error = 'No configuration data found.';
|
|
111
278
|
console.error(`[Vortex] Invite ${error}`);
|
|
112
279
|
throw new Error(error);
|
|
113
280
|
}
|
|
114
281
|
}
|
|
115
282
|
catch (err) {
|
|
116
|
-
if
|
|
283
|
+
// Check if this is a VortexWidgetError with structured error information
|
|
284
|
+
if (vortex_shared_ui_1.VortexWidgetError.isVortexWidgetError(err)) {
|
|
285
|
+
console.error('[Vortex] Invite Widget Error:', {
|
|
286
|
+
code: err.code,
|
|
287
|
+
userMessage: err.userMessage,
|
|
288
|
+
developerMessage: err.developerMessage,
|
|
289
|
+
context: err.context,
|
|
290
|
+
statusCode: err.statusCode,
|
|
291
|
+
});
|
|
292
|
+
setError({
|
|
293
|
+
message: err.userMessage || err.message,
|
|
294
|
+
code: err.code,
|
|
295
|
+
userMessage: err.userMessage,
|
|
296
|
+
developerMessage: err.developerMessage,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
else if (err instanceof Error) {
|
|
117
300
|
const fetchError = err;
|
|
118
301
|
console.error('[Vortex] Invite Error Details:', {
|
|
119
302
|
message: fetchError.message,
|
|
@@ -122,19 +305,37 @@ function useVortexInvite({ widgetId, environmentId, vortexApiUrl, contentTokens,
|
|
|
122
305
|
body: fetchError.body,
|
|
123
306
|
stack: fetchError.stack,
|
|
124
307
|
});
|
|
308
|
+
setError({
|
|
309
|
+
message: err.message || 'Something went wrong!',
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
console.error('[Vortex] Invite Error', err);
|
|
314
|
+
setError({
|
|
315
|
+
message: 'Something went wrong!',
|
|
316
|
+
});
|
|
125
317
|
}
|
|
126
|
-
console.error('[Vortex] Invite Error', err);
|
|
127
|
-
setError({
|
|
128
|
-
message: err.message || 'Something went wrong!',
|
|
129
|
-
});
|
|
130
318
|
}
|
|
131
319
|
finally {
|
|
132
|
-
|
|
133
|
-
|
|
320
|
+
if (shouldShowLoading) {
|
|
321
|
+
setFetching(false);
|
|
322
|
+
setLoading(false);
|
|
323
|
+
}
|
|
134
324
|
}
|
|
135
325
|
});
|
|
136
326
|
fetchData();
|
|
137
|
-
}, [
|
|
327
|
+
}, [componentId, effectiveJwt, vortexClient, locale]); // Removed widgetConfiguration from deps to allow refetch
|
|
328
|
+
// Prefetch the shareable invitation link to avoid UI delays when user taps Copy Link or QR Code
|
|
329
|
+
// IMPORTANT: Only prefetch after config has been fetched via VortexClient, which sets sessionAttestation
|
|
330
|
+
(0, react_1.useEffect)(() => {
|
|
331
|
+
if ((widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.id) && effectiveJwt && vortexClient && !shareableLink && configFetchedViaClient) {
|
|
332
|
+
console.log('[Vortex] Prefetching shareable invitation link...');
|
|
333
|
+
getShareableInviteLink().catch((error) => {
|
|
334
|
+
console.warn('[Vortex] Failed to prefetch shareable link:', error);
|
|
335
|
+
// Silent fail - user can still trigger it manually later
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}, [widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.id, effectiveJwt, vortexClient, shareableLink, configFetchedViaClient]);
|
|
138
339
|
const handleInviteClick = (contentTokens) => __awaiter(this, void 0, void 0, function* () {
|
|
139
340
|
try {
|
|
140
341
|
setInviteLoading(true);
|
|
@@ -146,17 +347,24 @@ function useVortexInvite({ widgetId, environmentId, vortexApiUrl, contentTokens,
|
|
|
146
347
|
// const url = `${host}/api/v1/no-auth/components/widget-configuration/${widgetConfigurationId}/invite`; // TODO should we use options.widgetHost?.value ?
|
|
147
348
|
console.log('[Vortex] Invite Sending invitation', {
|
|
148
349
|
email,
|
|
149
|
-
|
|
150
|
-
environmentId,
|
|
350
|
+
componentId,
|
|
151
351
|
});
|
|
152
|
-
if (!
|
|
352
|
+
if (!effectiveJwt) {
|
|
153
353
|
throw new Error('[Vortex Invite] JWT is required');
|
|
154
354
|
}
|
|
155
355
|
const payload = Object.assign(Object.assign({}, contentTokens), { email: {
|
|
156
356
|
value: email,
|
|
157
357
|
type: 'email',
|
|
158
358
|
} });
|
|
159
|
-
|
|
359
|
+
if (role) {
|
|
360
|
+
payload.role = {
|
|
361
|
+
value: role,
|
|
362
|
+
type: 'string',
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
// Use groups parameter instead of adding group to payload
|
|
366
|
+
const groups = effectiveGroup ? [{ type: effectiveGroup.type, id: effectiveGroup.groupId || effectiveGroup.id, name: effectiveGroup.name }] : undefined;
|
|
367
|
+
const body = yield vortexClient.createInvite(effectiveJwt, widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.id, payload, 'email', groups);
|
|
160
368
|
console.log('[Vortex] Invite Response', body);
|
|
161
369
|
if (onSuccess) {
|
|
162
370
|
onSuccess({
|
|
@@ -185,6 +393,41 @@ function useVortexInvite({ widgetId, environmentId, vortexApiUrl, contentTokens,
|
|
|
185
393
|
const getShareableLink = () => {
|
|
186
394
|
return vortexClient.getShareableLinkFormatted();
|
|
187
395
|
};
|
|
396
|
+
/**
|
|
397
|
+
* Create an invitation with internal ID target type.
|
|
398
|
+
* Used by Find Friends component when user taps Connect.
|
|
399
|
+
* Matches iOS SDK payload structure: { type: 'internal', value: { value, name, avatarUrl? } }
|
|
400
|
+
*/
|
|
401
|
+
const createUserIdInvitation = (userId, name, avatarUrl, metadata) => __awaiter(this, void 0, void 0, function* () {
|
|
402
|
+
if (!effectiveJwt) {
|
|
403
|
+
throw new Error('[Vortex Invite] JWT is required');
|
|
404
|
+
}
|
|
405
|
+
if (!(widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.id)) {
|
|
406
|
+
throw new Error('[Vortex Invite] Widget configuration is required');
|
|
407
|
+
}
|
|
408
|
+
console.log('[Vortex] Creating user ID invitation', { userId, name, avatarUrl });
|
|
409
|
+
// Build the target value object matching iOS SDK structure
|
|
410
|
+
const targetValue = {
|
|
411
|
+
value: userId,
|
|
412
|
+
};
|
|
413
|
+
if (name) {
|
|
414
|
+
targetValue.name = name;
|
|
415
|
+
}
|
|
416
|
+
if (avatarUrl) {
|
|
417
|
+
targetValue.avatarUrl = avatarUrl;
|
|
418
|
+
}
|
|
419
|
+
const payload = {
|
|
420
|
+
internalId: {
|
|
421
|
+
type: 'internal',
|
|
422
|
+
value: targetValue,
|
|
423
|
+
},
|
|
424
|
+
};
|
|
425
|
+
const groups = effectiveGroup
|
|
426
|
+
? [{ type: effectiveGroup.type, id: effectiveGroup.groupId || effectiveGroup.id, name: effectiveGroup.name }]
|
|
427
|
+
: undefined;
|
|
428
|
+
yield vortexClient.createInvite(effectiveJwt, widgetConfiguration.id, payload, 'other', groups, undefined, metadata || undefined);
|
|
429
|
+
console.log('[Vortex] Internal ID invitation created successfully');
|
|
430
|
+
});
|
|
188
431
|
const handleSmsShare = () => __awaiter(this, void 0, void 0, function* () {
|
|
189
432
|
var _a, _b, _c;
|
|
190
433
|
try {
|
|
@@ -194,7 +437,7 @@ function useVortexInvite({ widgetId, environmentId, vortexApiUrl, contentTokens,
|
|
|
194
437
|
const shareableLink = yield getShareableInviteLink();
|
|
195
438
|
if (!shareableLink)
|
|
196
439
|
throw new Error('No shareable link available');
|
|
197
|
-
const body = encodeURIComponent(template.replace(/{{
|
|
440
|
+
const body = encodeURIComponent(template.replace(/{{vortex_share_link}}/g, shareableLink));
|
|
198
441
|
const smsUrl = `sms:?body=${body}`;
|
|
199
442
|
yield react_native_1.Linking.openURL(smsUrl);
|
|
200
443
|
}
|
|
@@ -214,7 +457,7 @@ function useVortexInvite({ widgetId, environmentId, vortexApiUrl, contentTokens,
|
|
|
214
457
|
const shareableLink = yield getShareableInviteLink();
|
|
215
458
|
if (!shareableLink)
|
|
216
459
|
throw new Error('No shareable link available');
|
|
217
|
-
const body = encodeURIComponent(template.replace(/{{
|
|
460
|
+
const body = encodeURIComponent(template.replace(/{{vortex_share_link}}/g, shareableLink));
|
|
218
461
|
const mailtoUrl = `mailto:?subject=${encodeURIComponent(subject)}&body=${body}`;
|
|
219
462
|
yield react_native_1.Linking.openURL(mailtoUrl);
|
|
220
463
|
}
|
|
@@ -224,18 +467,44 @@ function useVortexInvite({ widgetId, environmentId, vortexApiUrl, contentTokens,
|
|
|
224
467
|
onError(error instanceof Error ? error : new Error(String(error)), 'share');
|
|
225
468
|
}
|
|
226
469
|
});
|
|
470
|
+
const handleWhatsAppShare = () => __awaiter(this, void 0, void 0, function* () {
|
|
471
|
+
var _a, _b, _c;
|
|
472
|
+
try {
|
|
473
|
+
const template = (_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.components.share.template.body']) === null || _c === void 0 ? void 0 : _c.value;
|
|
474
|
+
if (!template)
|
|
475
|
+
throw new Error('No template available');
|
|
476
|
+
const shareableLink = yield getShareableInviteLink();
|
|
477
|
+
if (!shareableLink)
|
|
478
|
+
throw new Error('No shareable link available');
|
|
479
|
+
const message = encodeURIComponent(template.replace(/{{vortex_share_link}}/g, shareableLink));
|
|
480
|
+
const whatsappUrl = `whatsapp://send?text=${message}`;
|
|
481
|
+
yield react_native_1.Linking.openURL(whatsappUrl).catch(() => {
|
|
482
|
+
console.warn('WhatsApp not installed');
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
catch (error) {
|
|
486
|
+
console.error('[Vortex] Failed to open WhatsApp:', error);
|
|
487
|
+
if (onError)
|
|
488
|
+
onError(error instanceof Error ? error : new Error(String(error)), 'share');
|
|
489
|
+
}
|
|
490
|
+
});
|
|
227
491
|
const getShareableInviteLink = (contentTokens) => __awaiter(this, void 0, void 0, function* () {
|
|
228
492
|
// if (this.isLoading || !this.vortex) {
|
|
229
493
|
// return;
|
|
230
494
|
// }
|
|
231
|
-
if (!
|
|
495
|
+
if (!effectiveJwt || !(widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.id) || !vortexClient) {
|
|
232
496
|
return;
|
|
233
497
|
}
|
|
234
498
|
if (shareableLink) {
|
|
235
499
|
return shareableLink;
|
|
236
500
|
}
|
|
237
|
-
|
|
238
|
-
|
|
501
|
+
// Use groups parameter for shareable invite as well
|
|
502
|
+
const groups = effectiveGroup ? [{ type: effectiveGroup.type, id: effectiveGroup.groupId || effectiveGroup.id, name: effectiveGroup.name }] : undefined;
|
|
503
|
+
const invite = yield vortexClient.createShareableInvite(effectiveJwt, widgetConfiguration.id, groups);
|
|
504
|
+
if (!invite ||
|
|
505
|
+
!invite.data ||
|
|
506
|
+
!invite.data.invitation.id ||
|
|
507
|
+
!invite.data.invitation.shortLink) {
|
|
239
508
|
console.error('No shareable link found');
|
|
240
509
|
onError === null || onError === void 0 ? void 0 : onError(new Error('No shareable link found'), 'share');
|
|
241
510
|
// this.error = {
|
|
@@ -244,43 +513,86 @@ function useVortexInvite({ widgetId, environmentId, vortexApiUrl, contentTokens,
|
|
|
244
513
|
// this.host.requestUpdate();
|
|
245
514
|
return;
|
|
246
515
|
}
|
|
247
|
-
const link =
|
|
248
|
-
|
|
249
|
-
setShareableLink(link); // TODO - need to differentiate invite host vs api host even though they are the same for now)
|
|
516
|
+
const link = invite.data.invitation.shortLink;
|
|
517
|
+
setShareableLink(link);
|
|
250
518
|
return link;
|
|
251
519
|
});
|
|
252
520
|
const handleShareLink = () => __awaiter(this, void 0, void 0, function* () {
|
|
521
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
253
522
|
try {
|
|
254
|
-
const
|
|
255
|
-
if (!
|
|
523
|
+
const link = yield getShareableInviteLink();
|
|
524
|
+
if (!link) {
|
|
256
525
|
throw new Error('No shareable link available');
|
|
257
526
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
527
|
+
const template = (_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.components.share.template.body']) === null || _c === void 0 ? void 0 : _c.value;
|
|
528
|
+
const subject = (_g = (_f = (_e = (_d = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _d === void 0 ? void 0 : _d.props) === null || _e === void 0 ? void 0 : _e['vortex.components.share.template.subject']) === null || _f === void 0 ? void 0 : _f.value) === null || _g === void 0 ? void 0 : _g.trim();
|
|
529
|
+
// Compose message
|
|
530
|
+
let message = '';
|
|
531
|
+
if (typeof template === 'string' && template.length > 0) {
|
|
532
|
+
if (template.includes('{{vortex_share_link}}')) {
|
|
533
|
+
message = template.replace(/{{vortex_share_link}}/g, link);
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
message = `${template}${template.endsWith(' ') ? '' : ' '}${link}`;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
message = link;
|
|
541
|
+
}
|
|
542
|
+
// Informative log about iOS share sheet branding
|
|
543
|
+
if (react_native_1.Platform.OS === 'ios') {
|
|
544
|
+
console.log('[Vortex] iOS share sheet branding note: The icon/name come from the host app bundle; link preview (if any) is pulled from the shared URL\'s Open Graph/Twitter meta tags. Customizing the header/logo via RN Share is not supported.');
|
|
545
|
+
}
|
|
546
|
+
const content = Object.assign({ message, url: link, title: subject || 'Share Invite Link' }, (subject ? { subject } : {}));
|
|
547
|
+
const options = react_native_1.Platform.select({
|
|
548
|
+
android: {
|
|
549
|
+
dialogTitle: subject || 'Share Invite Link',
|
|
550
|
+
},
|
|
551
|
+
ios: Object.assign({}, (subject ? { subject } : {})),
|
|
552
|
+
default: {},
|
|
261
553
|
});
|
|
554
|
+
yield react_native_1.Share.share(content, options);
|
|
262
555
|
console.log('[Vortex] handleShareLink', 'The invite link has been shared.');
|
|
263
|
-
|
|
264
|
-
onSuccess({
|
|
265
|
-
type: 'share',
|
|
266
|
-
data: 'Sharable link used',
|
|
267
|
-
});
|
|
268
|
-
}
|
|
556
|
+
onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess({ type: 'share', data: 'Sharable link used' });
|
|
269
557
|
}
|
|
270
558
|
catch (error) {
|
|
271
559
|
console.error('[Vortex] Failed to share link:', error);
|
|
272
|
-
|
|
273
|
-
onError(error instanceof Error ? error : new Error(String(error)), 'share');
|
|
274
|
-
}
|
|
560
|
+
onError === null || onError === void 0 ? void 0 : onError(error instanceof Error ? error : new Error(String(error)), 'share');
|
|
275
561
|
}
|
|
276
562
|
});
|
|
277
563
|
const handleCopyLink = () => __awaiter(this, void 0, void 0, function* () {
|
|
564
|
+
var _a;
|
|
278
565
|
try {
|
|
279
566
|
const shareableLink = yield getShareableInviteLink();
|
|
280
567
|
if (!shareableLink) {
|
|
281
568
|
throw new Error('No shareable link available');
|
|
282
569
|
}
|
|
283
|
-
|
|
570
|
+
// Try expo-clipboard first, then @react-native-clipboard/clipboard, then web clipboard
|
|
571
|
+
let copied = false;
|
|
572
|
+
try {
|
|
573
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
574
|
+
const expoClipboard = require('expo-clipboard');
|
|
575
|
+
yield expoClipboard.setStringAsync(shareableLink);
|
|
576
|
+
copied = true;
|
|
577
|
+
}
|
|
578
|
+
catch (e) {
|
|
579
|
+
try {
|
|
580
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
581
|
+
const rnClipboard = require('@react-native-clipboard/clipboard');
|
|
582
|
+
rnClipboard.default.setString(shareableLink);
|
|
583
|
+
copied = true;
|
|
584
|
+
}
|
|
585
|
+
catch (e2) {
|
|
586
|
+
// Fall back to web clipboard API
|
|
587
|
+
if (typeof navigator !== 'undefined' && ((_a = navigator.clipboard) === null || _a === void 0 ? void 0 : _a.writeText)) {
|
|
588
|
+
yield navigator.clipboard.writeText(shareableLink);
|
|
589
|
+
copied = true;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
if (!copied) {
|
|
594
|
+
throw new Error('No clipboard implementation available');
|
|
595
|
+
}
|
|
284
596
|
console.log('[Vortex] handleCopyLink', 'The invite link has been copied.');
|
|
285
597
|
if (onSuccess) {
|
|
286
598
|
onSuccess({
|
|
@@ -296,6 +608,87 @@ function useVortexInvite({ widgetId, environmentId, vortexApiUrl, contentTokens,
|
|
|
296
608
|
}
|
|
297
609
|
}
|
|
298
610
|
});
|
|
611
|
+
// Contacts import: dynamically use expo-contacts when available (iOS & Android)
|
|
612
|
+
const fetchContactsWithEmailsIOS = () => __awaiter(this, void 0, void 0, function* () {
|
|
613
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
614
|
+
try {
|
|
615
|
+
let Contacts = null;
|
|
616
|
+
try {
|
|
617
|
+
// Dynamic require so the library does not hard depend on expo-contacts
|
|
618
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
619
|
+
Contacts = require('expo-contacts');
|
|
620
|
+
}
|
|
621
|
+
catch (e) {
|
|
622
|
+
throw new Error('Contacts feature is not available. Please ensure expo-contacts is installed.');
|
|
623
|
+
}
|
|
624
|
+
// First check current permission status
|
|
625
|
+
let currentStatus;
|
|
626
|
+
try {
|
|
627
|
+
const currentPerm = yield (Contacts.getPermissionsAsync
|
|
628
|
+
? Contacts.getPermissionsAsync()
|
|
629
|
+
: (_a = Contacts.getPermissionAsync) === null || _a === void 0 ? void 0 : _a.call(Contacts));
|
|
630
|
+
currentStatus = currentPerm === null || currentPerm === void 0 ? void 0 : currentPerm.status;
|
|
631
|
+
}
|
|
632
|
+
catch (permErr) {
|
|
633
|
+
console.warn('[Vortex] Failed to check contacts permission:', permErr);
|
|
634
|
+
}
|
|
635
|
+
// If already denied, inform user they need to go to Settings
|
|
636
|
+
if (currentStatus === 'denied') {
|
|
637
|
+
throw new Error('Contacts access was denied. Please enable Contacts access in your device Settings to import contacts.');
|
|
638
|
+
}
|
|
639
|
+
// Request permission (either undetermined or checking again)
|
|
640
|
+
let status;
|
|
641
|
+
try {
|
|
642
|
+
const perm = yield (Contacts.requestPermissionsAsync
|
|
643
|
+
? Contacts.requestPermissionsAsync()
|
|
644
|
+
: (_b = Contacts.requestPermissionAsync) === null || _b === void 0 ? void 0 : _b.call(Contacts));
|
|
645
|
+
status = perm === null || perm === void 0 ? void 0 : perm.status;
|
|
646
|
+
}
|
|
647
|
+
catch (permErr) {
|
|
648
|
+
console.warn('[Vortex] Contacts permission request failed:', permErr);
|
|
649
|
+
throw new Error('Failed to request contacts permission. Please try again.');
|
|
650
|
+
}
|
|
651
|
+
if (status === 'denied') {
|
|
652
|
+
throw new Error('Contacts access was denied. Please enable Contacts access in your device Settings to import contacts.');
|
|
653
|
+
}
|
|
654
|
+
if (status !== 'granted') {
|
|
655
|
+
throw new Error('Contacts access is required to import contacts.');
|
|
656
|
+
}
|
|
657
|
+
// Fetch contacts with emails and images
|
|
658
|
+
const fields = Contacts.Fields
|
|
659
|
+
? [Contacts.Fields.Emails, Contacts.Fields.Image]
|
|
660
|
+
: ['emails', 'image'];
|
|
661
|
+
const args = { fields, pageSize: 500 };
|
|
662
|
+
if ((_c = Contacts.SortTypes) === null || _c === void 0 ? void 0 : _c.FirstName) {
|
|
663
|
+
args.sort = Contacts.SortTypes.FirstName;
|
|
664
|
+
}
|
|
665
|
+
const result = yield Contacts.getContactsAsync(args);
|
|
666
|
+
const data = (result === null || result === void 0 ? void 0 : result.data) || [];
|
|
667
|
+
if (!Array.isArray(data) || data.length === 0) {
|
|
668
|
+
return [];
|
|
669
|
+
}
|
|
670
|
+
const normalizeEmail = (e) => (e === null || e === void 0 ? void 0 : e.email) || (e === null || e === void 0 ? void 0 : e.address) || (typeof e === 'string' ? e : undefined);
|
|
671
|
+
const items = [];
|
|
672
|
+
for (const c of data) {
|
|
673
|
+
const emailsSrc = ((c === null || c === void 0 ? void 0 : c.emails) || (c === null || c === void 0 ? void 0 : c.emailAddresses) || []);
|
|
674
|
+
const emails = emailsSrc.map(normalizeEmail).filter(Boolean);
|
|
675
|
+
if (emails.length > 0) {
|
|
676
|
+
const name = ((_d = ((c === null || c === void 0 ? void 0 : c.name) || `${(c === null || c === void 0 ? void 0 : c.firstName) || ''} ${(c === null || c === void 0 ? void 0 : c.lastName) || ''}`)) === null || _d === void 0 ? void 0 : _d.trim()) || 'Unknown';
|
|
677
|
+
const imageUri = ((_e = c === null || c === void 0 ? void 0 : c.image) === null || _e === void 0 ? void 0 : _e.uri) || (c === null || c === void 0 ? void 0 : c.imageAvailable) ? (_f = c === null || c === void 0 ? void 0 : c.image) === null || _f === void 0 ? void 0 : _f.uri : undefined;
|
|
678
|
+
items.push({ id: String((_g = c === null || c === void 0 ? void 0 : c.id) !== null && _g !== void 0 ? _g : Math.random()), name, emails, imageUri });
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
return items;
|
|
682
|
+
}
|
|
683
|
+
catch (err) {
|
|
684
|
+
// Re-throw errors with messages so UI can display them properly
|
|
685
|
+
if (err instanceof Error) {
|
|
686
|
+
throw err;
|
|
687
|
+
}
|
|
688
|
+
console.error('[Vortex] Failed to fetch contacts:', err);
|
|
689
|
+
throw new Error('An unexpected error occurred while accessing contacts.');
|
|
690
|
+
}
|
|
691
|
+
});
|
|
299
692
|
return {
|
|
300
693
|
widgetConfiguration,
|
|
301
694
|
error,
|
|
@@ -303,6 +696,9 @@ function useVortexInvite({ widgetId, environmentId, vortexApiUrl, contentTokens,
|
|
|
303
696
|
loading,
|
|
304
697
|
email,
|
|
305
698
|
setEmail,
|
|
699
|
+
role,
|
|
700
|
+
setRole,
|
|
701
|
+
roleOptions,
|
|
306
702
|
opacity,
|
|
307
703
|
themeColors,
|
|
308
704
|
themeStyles,
|
|
@@ -315,7 +711,22 @@ function useVortexInvite({ widgetId, environmentId, vortexApiUrl, contentTokens,
|
|
|
315
711
|
handleCopyLink,
|
|
316
712
|
handleSmsShare,
|
|
317
713
|
handleEmailShare,
|
|
714
|
+
handleWhatsAppShare,
|
|
318
715
|
getShareableLink,
|
|
716
|
+
getShareableInviteLink,
|
|
717
|
+
createUserIdInvitation,
|
|
319
718
|
formLayout,
|
|
719
|
+
fetchContactsWithEmailsIOS,
|
|
720
|
+
// Analytics tracking functions
|
|
721
|
+
trackWidgetRender,
|
|
722
|
+
trackWidgetError,
|
|
723
|
+
trackShareLinkClick,
|
|
724
|
+
trackEmailFieldFocus,
|
|
725
|
+
trackEmailFieldBlur,
|
|
726
|
+
trackEmailValidation,
|
|
727
|
+
trackEmailInvitationsSubmitted,
|
|
728
|
+
trackEmailValidationError,
|
|
729
|
+
trackEmailSubmitError,
|
|
320
730
|
};
|
|
321
731
|
}
|
|
732
|
+
//# sourceMappingURL=useVortexInvite.js.map
|