@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.
Files changed (228) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +1227 -11
  3. package/dist/InviteFormCore-D4HkMMo0.d.mts +721 -0
  4. package/dist/InviteFormCore-D9oUCbu7.d.ts +721 -0
  5. package/dist/VortexClient.js +192 -0
  6. package/dist/VortexClient.js.map +1 -0
  7. package/dist/VortexDeferredLinks.js +127 -0
  8. package/dist/VortexDeferredLinks.js.map +1 -0
  9. package/dist/clientInfo.js +45 -0
  10. package/dist/clientInfo.js.map +1 -0
  11. package/dist/components/ContactsPickerModal.js +182 -0
  12. package/dist/components/ContactsPickerModal.js.map +1 -0
  13. package/dist/components/InviteFormCore.js +2141 -0
  14. package/dist/components/InviteFormCore.js.map +1 -0
  15. package/dist/components/InviteFormMobile.js +463 -0
  16. package/dist/components/InviteFormMobile.js.map +1 -0
  17. package/dist/components/InviteFormWeb.js +295 -0
  18. package/dist/components/InviteFormWeb.js.map +1 -0
  19. package/dist/components/PlacedItemToolbar.js +147 -0
  20. package/dist/components/PlacedItemToolbar.js.map +1 -0
  21. package/dist/components/ShareButtons.js +1 -0
  22. package/dist/components/ShareButtons.js.map +1 -0
  23. package/dist/components/VrtxContactsImport.js +234 -0
  24. package/dist/components/VrtxContactsImport.js.map +1 -0
  25. package/dist/components/VrtxEmailInvitations.js +341 -0
  26. package/dist/components/VrtxEmailInvitations.js.map +1 -0
  27. package/dist/components/VrtxFindFriends.js +400 -0
  28. package/dist/components/VrtxFindFriends.js.map +1 -0
  29. package/dist/components/VrtxHeading.js +58 -0
  30. package/dist/components/VrtxHeading.js.map +1 -0
  31. package/dist/components/VrtxIncomingInvitations.js +657 -0
  32. package/dist/components/VrtxIncomingInvitations.js.map +1 -0
  33. package/dist/components/VrtxInvitationSuggestions.js +506 -0
  34. package/dist/components/VrtxInvitationSuggestions.js.map +1 -0
  35. package/dist/components/VrtxInviteContacts.js +512 -0
  36. package/dist/components/VrtxInviteContacts.js.map +1 -0
  37. package/dist/components/VrtxOutgoingInvitations.js +572 -0
  38. package/dist/components/VrtxOutgoingInvitations.js.map +1 -0
  39. package/dist/components/VrtxSearchBox.js +487 -0
  40. package/dist/components/VrtxSearchBox.js.map +1 -0
  41. package/dist/components/VrtxSelect.js +27 -0
  42. package/dist/components/VrtxSelect.js.map +1 -0
  43. package/dist/components/VrtxShareOptions.js +435 -0
  44. package/dist/components/VrtxShareOptions.js.map +1 -0
  45. package/dist/components/VrtxSubmit.js +132 -0
  46. package/dist/components/VrtxSubmit.js.map +1 -0
  47. package/dist/components/VrtxText.js +146 -0
  48. package/dist/components/VrtxText.js.map +1 -0
  49. package/dist/constants/mockData.d.mts +7 -0
  50. package/dist/constants/mockData.d.ts +7 -0
  51. package/dist/constants/mockData.js +48 -0
  52. package/dist/constants/mockData.js.map +1 -0
  53. package/dist/constants/mockData.mjs +22 -0
  54. package/dist/constants/mockData.mjs.map +1 -0
  55. package/dist/context/VortexModulesContext.js +135 -0
  56. package/dist/context/VortexModulesContext.js.map +1 -0
  57. package/dist/hooks/useInvitationFormLogic.d.mts +2 -0
  58. package/dist/hooks/useInvitationFormLogic.d.ts +2 -0
  59. package/dist/hooks/useInvitationFormLogic.js +300 -0
  60. package/dist/hooks/useInvitationFormLogic.js.map +1 -0
  61. package/dist/hooks/useInvitationFormLogic.mjs +276 -0
  62. package/dist/hooks/useInvitationFormLogic.mjs.map +1 -0
  63. package/dist/hooks/usePrefetchWidgetConfiguration.js +117 -0
  64. package/dist/hooks/usePrefetchWidgetConfiguration.js.map +1 -0
  65. package/dist/hooks/useThemeStyles.js +2 -0
  66. package/dist/hooks/useThemeStyles.js.map +1 -0
  67. package/dist/hooks/useVortexInvite.js +467 -56
  68. package/dist/hooks/useVortexInvite.js.map +1 -0
  69. package/dist/index-web.d.mts +93 -0
  70. package/dist/index-web.d.ts +93 -0
  71. package/dist/index-web.js +7397 -0
  72. package/dist/index-web.js.map +1 -0
  73. package/dist/index-web.mjs +7445 -0
  74. package/dist/index-web.mjs.map +1 -0
  75. package/dist/index.d.mts +656 -0
  76. package/dist/index.d.ts +656 -0
  77. package/dist/index.js +10205 -4
  78. package/dist/index.js.map +1 -0
  79. package/dist/index.mjs +10244 -0
  80. package/dist/index.mjs.map +1 -0
  81. package/dist/types/VortexClient.d.ts +106 -0
  82. package/dist/types/VortexClient.d.ts.map +1 -0
  83. package/dist/types/VortexDeferredLinks.d.ts +73 -0
  84. package/dist/types/VortexDeferredLinks.d.ts.map +1 -0
  85. package/dist/types/clientInfo.d.ts +5 -0
  86. package/dist/types/clientInfo.d.ts.map +1 -0
  87. package/dist/types/components/ContactsPickerModal.d.ts +18 -0
  88. package/dist/types/components/ContactsPickerModal.d.ts.map +1 -0
  89. package/dist/types/components/InviteFormCore.d.ts +166 -0
  90. package/dist/types/components/InviteFormCore.d.ts.map +1 -0
  91. package/dist/types/components/InviteFormMobile.d.ts +42 -0
  92. package/dist/types/components/InviteFormMobile.d.ts.map +1 -0
  93. package/dist/types/components/InviteFormWeb.d.ts +87 -0
  94. package/dist/types/components/InviteFormWeb.d.ts.map +1 -0
  95. package/dist/types/components/PlacedItemToolbar.d.ts +16 -0
  96. package/dist/types/components/PlacedItemToolbar.d.ts.map +1 -0
  97. package/dist/types/components/VrtxContactsImport.d.ts +14 -0
  98. package/dist/types/components/VrtxContactsImport.d.ts.map +1 -0
  99. package/dist/types/components/VrtxEmailInvitations.d.ts +31 -0
  100. package/dist/types/components/VrtxEmailInvitations.d.ts.map +1 -0
  101. package/dist/types/components/VrtxFindFriends.d.ts +25 -0
  102. package/dist/types/components/VrtxFindFriends.d.ts.map +1 -0
  103. package/dist/types/components/VrtxHeading.d.ts +6 -0
  104. package/dist/types/components/VrtxHeading.d.ts.map +1 -0
  105. package/dist/types/components/VrtxIncomingInvitations.d.ts +27 -0
  106. package/dist/types/components/VrtxIncomingInvitations.d.ts.map +1 -0
  107. package/dist/types/components/VrtxInvitationSuggestions.d.ts +25 -0
  108. package/dist/types/components/VrtxInvitationSuggestions.d.ts.map +1 -0
  109. package/dist/types/components/VrtxInviteContacts.d.ts +24 -0
  110. package/dist/types/components/VrtxInviteContacts.d.ts.map +1 -0
  111. package/dist/types/components/VrtxOutgoingInvitations.d.ts +27 -0
  112. package/dist/types/components/VrtxOutgoingInvitations.d.ts.map +1 -0
  113. package/dist/types/components/VrtxSearchBox.d.ts +28 -0
  114. package/dist/types/components/VrtxSearchBox.d.ts.map +1 -0
  115. package/dist/types/components/VrtxSelect.d.ts +6 -0
  116. package/dist/types/components/VrtxSelect.d.ts.map +1 -0
  117. package/dist/types/components/VrtxShareOptions.d.ts +41 -0
  118. package/dist/types/components/VrtxShareOptions.d.ts.map +1 -0
  119. package/dist/types/components/VrtxSubmit.d.ts +18 -0
  120. package/dist/types/components/VrtxSubmit.d.ts.map +1 -0
  121. package/dist/types/components/VrtxText.d.ts +8 -0
  122. package/dist/types/components/VrtxText.d.ts.map +1 -0
  123. package/dist/types/constants/mockData.d.ts +4 -0
  124. package/dist/types/constants/mockData.d.ts.map +1 -0
  125. package/dist/types/context/VortexModulesContext.d.ts +238 -0
  126. package/dist/types/context/VortexModulesContext.d.ts.map +1 -0
  127. package/dist/types/findFriends.js +10 -0
  128. package/dist/types/findFriends.js.map +1 -0
  129. package/dist/types/hooks/useInvitationFormLogic.d.ts +55 -0
  130. package/dist/types/hooks/useInvitationFormLogic.d.ts.map +1 -0
  131. package/dist/types/hooks/usePrefetchWidgetConfiguration.d.ts +39 -0
  132. package/dist/types/hooks/usePrefetchWidgetConfiguration.d.ts.map +1 -0
  133. package/dist/types/hooks/useThemeStyles.d.ts +1 -0
  134. package/dist/types/hooks/useThemeStyles.d.ts.map +1 -1
  135. package/dist/types/hooks/useVortexInvite.d.ts +48 -6
  136. package/dist/types/hooks/useVortexInvite.d.ts.map +1 -1
  137. package/dist/types/index-web.d.ts +23 -0
  138. package/dist/types/index-web.d.ts.map +1 -0
  139. package/dist/types/index.d.ts +21 -0
  140. package/dist/types/index.d.ts.map +1 -1
  141. package/dist/types/invitations.js +13 -0
  142. package/dist/types/invitations.js.map +1 -0
  143. package/dist/types/inviteContacts.js +14 -0
  144. package/dist/types/inviteContacts.js.map +1 -0
  145. package/dist/{shared/InvitationResult.js → types/platformOperations.js} +1 -0
  146. package/dist/types/platformOperations.js.map +1 -0
  147. package/dist/types/searchBox.js +11 -0
  148. package/dist/types/searchBox.js.map +1 -0
  149. package/dist/types/types/findFriends.d.ts +101 -0
  150. package/dist/types/types/findFriends.d.ts.map +1 -0
  151. package/dist/types/types/invitations.d.ts +301 -0
  152. package/dist/types/types/invitations.d.ts.map +1 -0
  153. package/dist/types/types/inviteContacts.d.ts +86 -0
  154. package/dist/types/types/inviteContacts.d.ts.map +1 -0
  155. package/dist/types/types/platformOperations.d.ts +185 -0
  156. package/dist/types/types/platformOperations.d.ts.map +1 -0
  157. package/dist/types/types/searchBox.d.ts +69 -0
  158. package/dist/types/types/searchBox.d.ts.map +1 -0
  159. package/dist/types/types/unfurlConfig.d.ts +34 -0
  160. package/dist/types/types/unfurlConfig.d.ts.map +1 -0
  161. package/dist/types/unfurlConfig.js +21 -0
  162. package/dist/types/unfurlConfig.js.map +1 -0
  163. package/dist/types/utils/analytics.d.ts +54 -0
  164. package/dist/types/utils/analytics.d.ts.map +1 -0
  165. package/dist/types/utils/configCache.d.ts +34 -0
  166. package/dist/types/utils/configCache.d.ts.map +1 -0
  167. package/dist/types/utils/contactUtils.d.ts +9 -0
  168. package/dist/types/utils/contactUtils.d.ts.map +1 -0
  169. package/dist/types/utils/featureWarnings.d.ts +56 -0
  170. package/dist/types/utils/featureWarnings.d.ts.map +1 -0
  171. package/dist/types/utils/formUtils.d.ts +11 -3
  172. package/dist/types/utils/formUtils.d.ts.map +1 -1
  173. package/dist/types/utils/gradientUtils.d.ts +67 -0
  174. package/dist/types/utils/gradientUtils.d.ts.map +1 -0
  175. package/dist/types/utils/invitationEvents.d.ts +21 -0
  176. package/dist/types/utils/invitationEvents.d.ts.map +1 -0
  177. package/dist/types/utils/moduleLoaders.d.ts +115 -0
  178. package/dist/types/utils/moduleLoaders.d.ts.map +1 -0
  179. package/dist/types/utils/moduleLoaders.web.d.ts +73 -0
  180. package/dist/types/utils/moduleLoaders.web.d.ts.map +1 -0
  181. package/dist/types/utils/nameUtils.d.ts +15 -0
  182. package/dist/types/utils/nameUtils.d.ts.map +1 -0
  183. package/dist/types/utils/themeUtils.d.ts +3 -1
  184. package/dist/types/utils/themeUtils.d.ts.map +1 -1
  185. package/dist/types/vortexInvite.d.ts +145 -5
  186. package/dist/types/vortexInvite.d.ts.map +1 -1
  187. package/dist/useInvitationFormLogic-Ct73M19B.d.mts +242 -0
  188. package/dist/useInvitationFormLogic-Ct73M19B.d.ts +242 -0
  189. package/dist/utils/analytics.js +92 -0
  190. package/dist/utils/analytics.js.map +1 -0
  191. package/dist/utils/configCache.js +68 -0
  192. package/dist/utils/configCache.js.map +1 -0
  193. package/dist/utils/contactUtils.d.mts +12 -0
  194. package/dist/utils/contactUtils.d.ts +12 -0
  195. package/dist/utils/contactUtils.js +37 -0
  196. package/dist/utils/contactUtils.js.map +1 -0
  197. package/dist/utils/contactUtils.mjs +12 -0
  198. package/dist/utils/contactUtils.mjs.map +1 -0
  199. package/dist/utils/featureWarnings.js +214 -0
  200. package/dist/utils/featureWarnings.js.map +1 -0
  201. package/dist/utils/formUtils.js +161 -51
  202. package/dist/utils/formUtils.js.map +1 -0
  203. package/dist/utils/gradientUtils.js +120 -0
  204. package/dist/utils/gradientUtils.js.map +1 -0
  205. package/dist/utils/invitationEvents.js +45 -0
  206. package/dist/utils/invitationEvents.js.map +1 -0
  207. package/dist/utils/moduleLoaders.js +275 -0
  208. package/dist/utils/moduleLoaders.js.map +1 -0
  209. package/dist/utils/moduleLoaders.web.js +72 -0
  210. package/dist/utils/moduleLoaders.web.js.map +1 -0
  211. package/dist/utils/nameUtils.js +51 -0
  212. package/dist/utils/nameUtils.js.map +1 -0
  213. package/dist/utils/themeUtils.js +117 -32
  214. package/dist/utils/themeUtils.js.map +1 -0
  215. package/dist/vortexInvite.js +78 -167
  216. package/dist/vortexInvite.js.map +1 -0
  217. package/package.json +69 -31
  218. package/dist/components/Clipboard.js +0 -64
  219. package/dist/shared/api.js +0 -90
  220. package/dist/tests/TestVortexInvite.js +0 -134
  221. package/dist/types/components/Clipboard.d.ts +0 -16
  222. package/dist/types/components/Clipboard.d.ts.map +0 -1
  223. package/dist/types/shared/InvitationResult.d.ts +0 -24
  224. package/dist/types/shared/InvitationResult.d.ts.map +0 -1
  225. package/dist/types/shared/api.d.ts +0 -14
  226. package/dist/types/shared/api.d.ts.map +0 -1
  227. package/dist/types/tests/TestVortexInvite.d.ts +0 -4
  228. 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
- function useVortexInvite({ widgetId, environmentId, vortexApiUrl, contentTokens, isLoading = false, jwt, onSuccess, onError, }) {
25
- const vortexClient = (0, react_1.useMemo)(() => new vortex_shared_ui_1.VortexClient(vortexApiUrl, react_native_uuid_1.default.v4()), [vortexApiUrl]);
26
- const [widgetConfiguration, setWidgetConfiguration] = (0, react_1.useState)();
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)(true);
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 needed
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
- // Case: If `widgetConfiguration` is already set, do nothing
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
- console.log('[Vortex] Invite Fetching Data...');
100
- setFetching(true);
101
- setLoading(true);
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(widgetId, jwt, environmentId);
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
- setWidgetConfiguration(result.data.widgetConfiguration);
107
- console.log('[Vortex] Invite Successfully fetched widgetConfiguration', JSON.stringify(result.data.widgetConfiguration));
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 = (result === null || result === void 0 ? void 0 : result.error) || 'No configuration data found.';
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 (err instanceof Error) {
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
- setFetching(false);
133
- setLoading(false);
320
+ if (shouldShowLoading) {
321
+ setFetching(false);
322
+ setLoading(false);
323
+ }
134
324
  }
135
325
  });
136
326
  fetchData();
137
- }, [widgetId, jwt, environmentId, vortexClient, widgetConfiguration]);
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
- widgetId,
150
- environmentId,
350
+ componentId,
151
351
  });
152
- if (!jwt) {
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
- const body = yield vortexClient.createInvite(jwt, widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.id, environmentId, payload);
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(/{{vortex-share-link}}/g, shareableLink));
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(/{{vortex-share-link}}/g, shareableLink));
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 (!jwt || !(widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.id) || !vortexClient) {
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
- const invite = yield vortexClient.createShareableInvite(jwt, widgetConfiguration.id, environmentId);
238
- if (!invite || !invite.data || !invite.data.invitation.id) {
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 = `${vortexApiUrl}/noauth/invite/${invite.data.invitation.id}`;
248
- console.log(`getShareableInviteLink: ${link}`);
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 shareableLink = yield getShareableInviteLink(contentTokens);
255
- if (!shareableLink) {
523
+ const link = yield getShareableInviteLink();
524
+ if (!link) {
256
525
  throw new Error('No shareable link available');
257
526
  }
258
- yield react_native_1.Share.share({
259
- message: shareableLink,
260
- title: 'Share Invite Link',
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
- if (onSuccess) {
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
- if (onError) {
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
- yield Clipboard_1.Clipboard.setString(shareableLink);
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