@teamvortexsoftware/vortex-react-native 1.0.0 → 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 (217) hide show
  1. package/dist/InviteFormCore-D4HkMMo0.d.mts +721 -0
  2. package/dist/InviteFormCore-D9oUCbu7.d.ts +721 -0
  3. package/dist/VortexClient.js +192 -0
  4. package/dist/VortexClient.js.map +1 -0
  5. package/dist/VortexDeferredLinks.js +127 -0
  6. package/dist/VortexDeferredLinks.js.map +1 -0
  7. package/dist/clientInfo.js +45 -0
  8. package/dist/clientInfo.js.map +1 -0
  9. package/dist/components/ContactsPickerModal.js +182 -0
  10. package/dist/components/ContactsPickerModal.js.map +1 -0
  11. package/dist/components/InviteFormCore.js +2141 -0
  12. package/dist/components/InviteFormCore.js.map +1 -0
  13. package/dist/components/InviteFormMobile.js +463 -0
  14. package/dist/components/InviteFormMobile.js.map +1 -0
  15. package/dist/components/InviteFormWeb.js +295 -0
  16. package/dist/components/InviteFormWeb.js.map +1 -0
  17. package/dist/components/PlacedItemToolbar.js +147 -0
  18. package/dist/components/PlacedItemToolbar.js.map +1 -0
  19. package/dist/components/ShareButtons.js +181 -0
  20. package/dist/components/ShareButtons.js.map +1 -0
  21. package/dist/components/VrtxContactsImport.js +234 -0
  22. package/dist/components/VrtxContactsImport.js.map +1 -0
  23. package/dist/components/VrtxEmailInvitations.js +341 -0
  24. package/dist/components/VrtxEmailInvitations.js.map +1 -0
  25. package/dist/components/VrtxFindFriends.js +400 -0
  26. package/dist/components/VrtxFindFriends.js.map +1 -0
  27. package/dist/components/VrtxHeading.js +58 -0
  28. package/dist/components/VrtxHeading.js.map +1 -0
  29. package/dist/components/VrtxIncomingInvitations.js +657 -0
  30. package/dist/components/VrtxIncomingInvitations.js.map +1 -0
  31. package/dist/components/VrtxInvitationSuggestions.js +506 -0
  32. package/dist/components/VrtxInvitationSuggestions.js.map +1 -0
  33. package/dist/components/VrtxInviteContacts.js +512 -0
  34. package/dist/components/VrtxInviteContacts.js.map +1 -0
  35. package/dist/components/VrtxOutgoingInvitations.js +572 -0
  36. package/dist/components/VrtxOutgoingInvitations.js.map +1 -0
  37. package/dist/components/VrtxSearchBox.js +487 -0
  38. package/dist/components/VrtxSearchBox.js.map +1 -0
  39. package/dist/components/VrtxSelect.js +27 -0
  40. package/dist/components/VrtxSelect.js.map +1 -0
  41. package/dist/components/VrtxShareOptions.js +435 -0
  42. package/dist/components/VrtxShareOptions.js.map +1 -0
  43. package/dist/components/VrtxSubmit.js +132 -0
  44. package/dist/components/VrtxSubmit.js.map +1 -0
  45. package/dist/components/VrtxText.js +146 -0
  46. package/dist/components/VrtxText.js.map +1 -0
  47. package/dist/constants/mockData.d.mts +7 -0
  48. package/dist/constants/mockData.d.ts +7 -0
  49. package/dist/constants/mockData.js +48 -0
  50. package/dist/constants/mockData.js.map +1 -0
  51. package/dist/constants/mockData.mjs +22 -0
  52. package/dist/constants/mockData.mjs.map +1 -0
  53. package/dist/context/VortexModulesContext.js +135 -0
  54. package/dist/context/VortexModulesContext.js.map +1 -0
  55. package/dist/hooks/useInvitationFormLogic.d.mts +2 -0
  56. package/dist/hooks/useInvitationFormLogic.d.ts +2 -0
  57. package/dist/hooks/useInvitationFormLogic.js +300 -0
  58. package/dist/hooks/useInvitationFormLogic.js.map +1 -0
  59. package/dist/hooks/useInvitationFormLogic.mjs +276 -0
  60. package/dist/hooks/useInvitationFormLogic.mjs.map +1 -0
  61. package/dist/hooks/usePrefetchWidgetConfiguration.js +117 -0
  62. package/dist/hooks/usePrefetchWidgetConfiguration.js.map +1 -0
  63. package/dist/hooks/useThemeStyles.js +41 -0
  64. package/dist/hooks/useThemeStyles.js.map +1 -0
  65. package/dist/hooks/useVortexInvite.js +732 -0
  66. package/dist/hooks/useVortexInvite.js.map +1 -0
  67. package/dist/index-web.d.mts +93 -0
  68. package/dist/index-web.d.ts +93 -0
  69. package/dist/index-web.js +7397 -0
  70. package/dist/index-web.js.map +1 -0
  71. package/dist/index-web.mjs +7445 -0
  72. package/dist/index-web.mjs.map +1 -0
  73. package/dist/index.d.mts +656 -0
  74. package/dist/index.d.ts +656 -0
  75. package/dist/index.js +10206 -0
  76. package/dist/index.js.map +1 -0
  77. package/dist/index.mjs +10244 -0
  78. package/dist/index.mjs.map +1 -0
  79. package/dist/types/VortexClient.d.ts +106 -0
  80. package/dist/types/VortexClient.d.ts.map +1 -0
  81. package/dist/types/VortexDeferredLinks.d.ts +73 -0
  82. package/dist/types/VortexDeferredLinks.d.ts.map +1 -0
  83. package/dist/types/clientInfo.d.ts +5 -0
  84. package/dist/types/clientInfo.d.ts.map +1 -0
  85. package/dist/types/components/ContactsPickerModal.d.ts +18 -0
  86. package/dist/types/components/ContactsPickerModal.d.ts.map +1 -0
  87. package/dist/types/components/InviteFormCore.d.ts +166 -0
  88. package/dist/types/components/InviteFormCore.d.ts.map +1 -0
  89. package/dist/types/components/InviteFormMobile.d.ts +42 -0
  90. package/dist/types/components/InviteFormMobile.d.ts.map +1 -0
  91. package/dist/types/components/InviteFormWeb.d.ts +87 -0
  92. package/dist/types/components/InviteFormWeb.d.ts.map +1 -0
  93. package/dist/types/components/PlacedItemToolbar.d.ts +16 -0
  94. package/dist/types/components/PlacedItemToolbar.d.ts.map +1 -0
  95. package/dist/types/components/ShareButtons.d.ts +29 -0
  96. package/dist/types/components/ShareButtons.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 +35 -0
  134. package/dist/types/hooks/useThemeStyles.d.ts.map +1 -0
  135. package/dist/types/hooks/useVortexInvite.d.ts +86 -0
  136. package/dist/types/hooks/useVortexInvite.d.ts.map +1 -0
  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 +23 -0
  140. package/dist/types/index.d.ts.map +1 -0
  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/types/platformOperations.js +3 -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 +93 -0
  172. package/dist/types/utils/formUtils.d.ts.map +1 -0
  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 +38 -0
  184. package/dist/types/utils/themeUtils.d.ts.map +1 -0
  185. package/dist/types/vortexInvite.d.ts +165 -0
  186. package/dist/types/vortexInvite.d.ts.map +1 -0
  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 +284 -0
  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 +141 -0
  214. package/dist/utils/themeUtils.js.map +1 -0
  215. package/dist/vortexInvite.js +83 -0
  216. package/dist/vortexInvite.js.map +1 -0
  217. package/package.json +21 -56
@@ -0,0 +1,192 @@
1
+ "use strict";
2
+ /**
3
+ * VortexClient provides API methods for invitation management.
4
+ *
5
+ * @example
6
+ * ```tsx
7
+ * import { VortexClient } from '@teamvortexsoftware/vortex-react-native';
8
+ *
9
+ * const client = new VortexClient({
10
+ * jwt: 'your-jwt-token',
11
+ * baseURL: 'https://client-api.vortexsoftware.com', // optional
12
+ * });
13
+ *
14
+ * // Revoke an outgoing invitation
15
+ * await client.revokeInvitation('invitation-id');
16
+ *
17
+ * // Accept an incoming invitation
18
+ * await client.acceptIncomingInvitation('invitation-id');
19
+ *
20
+ * // Delete an incoming invitation
21
+ * await client.deleteIncomingInvitation('invitation-id');
22
+ * ```
23
+ */
24
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
25
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
26
+ return new (P || (P = Promise))(function (resolve, reject) {
27
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
28
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
29
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
30
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
31
+ });
32
+ };
33
+ Object.defineProperty(exports, "__esModule", { value: true });
34
+ exports.VortexClient = void 0;
35
+ const DEFAULT_BASE_URL = 'https://client-api.vortexsoftware.com';
36
+ class VortexClient {
37
+ constructor(options) {
38
+ this.jwt = options.jwt;
39
+ this.baseURL = options.baseURL || DEFAULT_BASE_URL;
40
+ }
41
+ /**
42
+ * Updates the JWT token used for authentication.
43
+ */
44
+ setJwt(jwt) {
45
+ this.jwt = jwt;
46
+ }
47
+ /**
48
+ * Revokes an outgoing invitation that the user has sent.
49
+ *
50
+ * @param invitationId - The ID of the invitation to revoke
51
+ * @returns Promise resolving to the response
52
+ */
53
+ revokeInvitation(invitationId) {
54
+ return __awaiter(this, void 0, void 0, function* () {
55
+ try {
56
+ const response = yield fetch(`${this.baseURL}/api/v1/invitations/${invitationId}`, {
57
+ method: 'DELETE',
58
+ headers: {
59
+ 'Content-Type': 'application/json',
60
+ 'Authorization': `Bearer ${this.jwt}`,
61
+ },
62
+ });
63
+ if (!response.ok) {
64
+ const errorData = yield response.json().catch(() => ({}));
65
+ return {
66
+ success: false,
67
+ message: errorData.message || `Failed to revoke invitation: ${response.status}`,
68
+ };
69
+ }
70
+ const data = yield response.json().catch(() => ({}));
71
+ return {
72
+ success: true,
73
+ data,
74
+ };
75
+ }
76
+ catch (error) {
77
+ console.warn('[VortexClient] Error revoking invitation:', error);
78
+ return {
79
+ success: false,
80
+ message: error instanceof Error ? error.message : 'Unknown error',
81
+ };
82
+ }
83
+ });
84
+ }
85
+ /**
86
+ * Accepts an incoming invitation that the user has received.
87
+ *
88
+ * @param invitationId - The ID of the invitation to accept
89
+ * @returns Promise resolving to the response
90
+ */
91
+ acceptIncomingInvitation(invitationId) {
92
+ return __awaiter(this, void 0, void 0, function* () {
93
+ try {
94
+ const response = yield fetch(`${this.baseURL}/api/v1/invitations/accept`, {
95
+ method: 'POST',
96
+ headers: {
97
+ 'Content-Type': 'application/json',
98
+ 'Authorization': `Bearer ${this.jwt}`,
99
+ },
100
+ body: JSON.stringify({ invitationId }),
101
+ });
102
+ if (!response.ok) {
103
+ const errorData = yield response.json().catch(() => ({}));
104
+ return {
105
+ success: false,
106
+ message: errorData.message || `Failed to accept invitation: ${response.status}`,
107
+ };
108
+ }
109
+ const data = yield response.json().catch(() => ({}));
110
+ return {
111
+ success: true,
112
+ data,
113
+ };
114
+ }
115
+ catch (error) {
116
+ console.warn('[VortexClient] Error accepting invitation:', error);
117
+ return {
118
+ success: false,
119
+ message: error instanceof Error ? error.message : 'Unknown error',
120
+ };
121
+ }
122
+ });
123
+ }
124
+ /**
125
+ * Fetches incoming (open) invitations for the current user.
126
+ *
127
+ * @returns Promise resolving to the list of incoming invitations
128
+ */
129
+ getIncomingInvitations() {
130
+ return __awaiter(this, void 0, void 0, function* () {
131
+ try {
132
+ const response = yield fetch(`${this.baseURL}/api/v1/invitations`, {
133
+ method: 'GET',
134
+ headers: {
135
+ 'Content-Type': 'application/json',
136
+ 'Authorization': `Bearer ${this.jwt}`,
137
+ },
138
+ });
139
+ if (!response.ok) {
140
+ console.warn('[VortexClient] Failed to fetch incoming invitations:', response.status);
141
+ return [];
142
+ }
143
+ const data = yield response.json();
144
+ return data.data.invitations;
145
+ }
146
+ catch (error) {
147
+ console.warn('[VortexClient] Error fetching incoming invitations:', error);
148
+ return [];
149
+ }
150
+ });
151
+ }
152
+ /**
153
+ * Deletes/declines an incoming invitation that the user has received.
154
+ *
155
+ * @param invitationId - The ID of the invitation to delete
156
+ * @returns Promise resolving to the response
157
+ */
158
+ deleteIncomingInvitation(invitationId) {
159
+ return __awaiter(this, void 0, void 0, function* () {
160
+ try {
161
+ const response = yield fetch(`${this.baseURL}/api/v1/invitations/incoming/${invitationId}`, {
162
+ method: 'DELETE',
163
+ headers: {
164
+ 'Content-Type': 'application/json',
165
+ 'Authorization': `Bearer ${this.jwt}`,
166
+ },
167
+ });
168
+ if (!response.ok) {
169
+ const errorData = yield response.json().catch(() => ({}));
170
+ return {
171
+ success: false,
172
+ message: errorData.message || `Failed to delete invitation: ${response.status}`,
173
+ };
174
+ }
175
+ const data = yield response.json().catch(() => ({}));
176
+ return {
177
+ success: true,
178
+ data,
179
+ };
180
+ }
181
+ catch (error) {
182
+ console.warn('[VortexClient] Error deleting invitation:', error);
183
+ return {
184
+ success: false,
185
+ message: error instanceof Error ? error.message : 'Unknown error',
186
+ };
187
+ }
188
+ });
189
+ }
190
+ }
191
+ exports.VortexClient = VortexClient;
192
+ //# sourceMappingURL=VortexClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VortexClient.js","sourceRoot":"","sources":["../src/VortexClient.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * VortexClient provides API methods for invitation management.\n * \n * @example\n * ```tsx\n * import { VortexClient } from '@teamvortexsoftware/vortex-react-native';\n * \n * const client = new VortexClient({\n * jwt: 'your-jwt-token',\n * baseURL: 'https://client-api.vortexsoftware.com', // optional\n * });\n * \n * // Revoke an outgoing invitation\n * await client.revokeInvitation('invitation-id');\n * \n * // Accept an incoming invitation\n * await client.acceptIncomingInvitation('invitation-id');\n * \n * // Delete an incoming invitation\n * await client.deleteIncomingInvitation('invitation-id');\n * ```\n */\n\nconst DEFAULT_BASE_URL = 'https://client-api.vortexsoftware.com';\n\nexport interface VortexClientOptions {\n /** JWT token for authentication */\n jwt: string;\n /** Optional base URL override (defaults to production) */\n baseURL?: string;\n}\n\nexport interface InvitationResponse {\n success: boolean;\n message?: string;\n data?: any;\n}\n\n/** Target of an invitation (e.g., email or SMS recipient) */\nexport interface InvitationTarget {\n targetType: string;\n targetValue: string;\n targetName?: string;\n targetAvatarUrl?: string;\n}\n\n/** Individual incoming invitation from the API */\nexport interface IncomingInvitation {\n id: string;\n targets?: InvitationTarget[];\n senderIdentifier?: string;\n senderIdentifierType?: string;\n avatarUrl?: string;\n status?: string;\n createdAt?: string;\n source?: string;\n deliveryType?: string;\n /** Name of the user who created this invitation */\n creatorName?: string | null;\n /** Avatar URL of the user who created this invitation */\n creatorAvatarUrl?: string | null;\n /** Internal ID of the user who created this invitation */\n foreignCreatorId?: string | null;\n /** Optional metadata attached to the invitation */\n metadata?: Record<string, unknown>;\n}\n\n/** Response from GET /api/v1/invitations endpoint */\nexport interface IncomingInvitationsResponse {\n data: {\n invitations: IncomingInvitation[];\n nextCursor?: string;\n hasMore?: boolean;\n count?: number;\n };\n}\n\nexport class VortexClient {\n private jwt: string;\n private baseURL: string;\n\n constructor(options: VortexClientOptions) {\n this.jwt = options.jwt;\n this.baseURL = options.baseURL || DEFAULT_BASE_URL;\n }\n\n /**\n * Updates the JWT token used for authentication.\n */\n setJwt(jwt: string): void {\n this.jwt = jwt;\n }\n\n /**\n * Revokes an outgoing invitation that the user has sent.\n * \n * @param invitationId - The ID of the invitation to revoke\n * @returns Promise resolving to the response\n */\n async revokeInvitation(invitationId: string): Promise<InvitationResponse> {\n try {\n const response = await fetch(`${this.baseURL}/api/v1/invitations/${invitationId}`, {\n method: 'DELETE',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.jwt}`,\n },\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n return {\n success: false,\n message: errorData.message || `Failed to revoke invitation: ${response.status}`,\n };\n }\n\n const data = await response.json().catch(() => ({}));\n return {\n success: true,\n data,\n };\n } catch (error) {\n console.warn('[VortexClient] Error revoking invitation:', error);\n return {\n success: false,\n message: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n\n /**\n * Accepts an incoming invitation that the user has received.\n * \n * @param invitationId - The ID of the invitation to accept\n * @returns Promise resolving to the response\n */\n async acceptIncomingInvitation(invitationId: string): Promise<InvitationResponse> {\n try {\n const response = await fetch(`${this.baseURL}/api/v1/invitations/accept`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.jwt}`,\n },\n body: JSON.stringify({ invitationId }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n return {\n success: false,\n message: errorData.message || `Failed to accept invitation: ${response.status}`,\n };\n }\n\n const data = await response.json().catch(() => ({}));\n return {\n success: true,\n data,\n };\n } catch (error) {\n console.warn('[VortexClient] Error accepting invitation:', error);\n return {\n success: false,\n message: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n\n /**\n * Fetches incoming (open) invitations for the current user.\n * \n * @returns Promise resolving to the list of incoming invitations\n */\n async getIncomingInvitations(): Promise<IncomingInvitation[]> {\n try {\n const response = await fetch(`${this.baseURL}/api/v1/invitations`, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.jwt}`,\n },\n });\n\n if (!response.ok) {\n console.warn('[VortexClient] Failed to fetch incoming invitations:', response.status);\n return [];\n }\n\n const data: IncomingInvitationsResponse = await response.json();\n return data.data.invitations;\n } catch (error) {\n console.warn('[VortexClient] Error fetching incoming invitations:', error);\n return [];\n }\n }\n\n /**\n * Deletes/declines an incoming invitation that the user has received.\n * \n * @param invitationId - The ID of the invitation to delete\n * @returns Promise resolving to the response\n */\n async deleteIncomingInvitation(invitationId: string): Promise<InvitationResponse> {\n try {\n const response = await fetch(`${this.baseURL}/api/v1/invitations/incoming/${invitationId}`, {\n method: 'DELETE',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.jwt}`,\n },\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n return {\n success: false,\n message: errorData.message || `Failed to delete invitation: ${response.status}`,\n };\n }\n\n const data = await response.json().catch(() => ({}));\n return {\n success: true,\n data,\n };\n } catch (error) {\n console.warn('[VortexClient] Error deleting invitation:', error);\n return {\n success: false,\n message: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n}\n"]}
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.VortexDeferredLinks = void 0;
13
+ const react_native_1 = require("react-native");
14
+ const DEFAULT_BASE_URL = 'https://client-api.vortexsoftware.com';
15
+ /**
16
+ * VortexDeferredLinks provides deferred deep linking functionality.
17
+ *
18
+ * Deferred deep linking allows you to track invitation attribution even when
19
+ * the app wasn't installed at the time the invitation link was clicked.
20
+ *
21
+ * @example
22
+ * ```tsx
23
+ * import { VortexDeferredLinks } from '@teamvortexsoftware/vortex-react-native';
24
+ *
25
+ * // On app first launch after install
26
+ * async function checkDeferredDeepLink(jwt: string) {
27
+ * const result = await VortexDeferredLinks.retrieveDeferredDeepLink({ jwt });
28
+ * if (result.matched && result.deepLink) {
29
+ * // Handle the deferred deep link
30
+ * console.log('User came from invitation:', result.invitationId);
31
+ * // Navigate to appropriate screen
32
+ * }
33
+ * }
34
+ * ```
35
+ */
36
+ class VortexDeferredLinks {
37
+ /**
38
+ * Collects device fingerprint data for matching.
39
+ * This data is used to match the device against fingerprints collected
40
+ * when invitation links were clicked.
41
+ */
42
+ static collectDeviceFingerprint() {
43
+ var _a, _b, _c, _d, _e, _f, _g;
44
+ const { width, height } = react_native_1.Dimensions.get('screen');
45
+ const platformOS = react_native_1.Platform.OS === 'ios' ? 'ios' : 'android';
46
+ // Get OS version
47
+ const osVersion = ((_a = react_native_1.Platform.Version) === null || _a === void 0 ? void 0 : _a.toString()) || 'unknown';
48
+ // Get device model and brand
49
+ let deviceModel = 'unknown';
50
+ let deviceBrand = 'unknown';
51
+ if (react_native_1.Platform.OS === 'ios') {
52
+ // On iOS, we can get some info from Platform.constants
53
+ const constants = react_native_1.Platform.constants;
54
+ deviceModel = (constants === null || constants === void 0 ? void 0 : constants.systemName) || 'iPhone';
55
+ deviceBrand = 'Apple';
56
+ }
57
+ else if (react_native_1.Platform.OS === 'android') {
58
+ // On Android, we can access more device info
59
+ const constants = react_native_1.Platform.constants;
60
+ deviceModel = (constants === null || constants === void 0 ? void 0 : constants.Model) || 'unknown';
61
+ deviceBrand = (constants === null || constants === void 0 ? void 0 : constants.Brand) || (constants === null || constants === void 0 ? void 0 : constants.Manufacturer) || 'unknown';
62
+ }
63
+ // Get timezone
64
+ const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || 'unknown';
65
+ // Get language
66
+ const language = react_native_1.Platform.OS === 'ios'
67
+ ? (((_c = (_b = react_native_1.NativeModules.SettingsManager) === null || _b === void 0 ? void 0 : _b.settings) === null || _c === void 0 ? void 0 : _c.AppleLocale) ||
68
+ ((_f = (_e = (_d = react_native_1.NativeModules.SettingsManager) === null || _d === void 0 ? void 0 : _d.settings) === null || _e === void 0 ? void 0 : _e.AppleLanguages) === null || _f === void 0 ? void 0 : _f[0]) ||
69
+ 'en')
70
+ : (((_g = react_native_1.NativeModules.I18nManager) === null || _g === void 0 ? void 0 : _g.localeIdentifier) || 'en');
71
+ return {
72
+ platform: platformOS,
73
+ osVersion,
74
+ deviceModel,
75
+ deviceBrand,
76
+ timezone,
77
+ language,
78
+ screenWidth: Math.round(width),
79
+ screenHeight: Math.round(height),
80
+ };
81
+ }
82
+ /**
83
+ * Retrieves a deferred deep link by matching the device fingerprint.
84
+ *
85
+ * Call this method on first app launch after installation to check if
86
+ * the user came from an invitation link.
87
+ *
88
+ * @param options - Configuration options including JWT and optional base URL
89
+ * @returns Promise resolving to match result with optional deep link
90
+ */
91
+ static retrieveDeferredDeepLink(options) {
92
+ return __awaiter(this, void 0, void 0, function* () {
93
+ var _a;
94
+ const { jwt, baseURL = DEFAULT_BASE_URL } = options;
95
+ const fingerprint = VortexDeferredLinks.collectDeviceFingerprint();
96
+ try {
97
+ const response = yield fetch(`${baseURL}/api/v1/invitations/match-fingerprint`, {
98
+ method: 'POST',
99
+ headers: {
100
+ 'Content-Type': 'application/json',
101
+ 'Authorization': `Bearer ${jwt}`,
102
+ },
103
+ body: JSON.stringify(fingerprint),
104
+ });
105
+ if (!response.ok) {
106
+ // Non-blocking: return no match on error
107
+ console.warn('[VortexDeferredLinks] Failed to match fingerprint:', response.status);
108
+ return { matched: false };
109
+ }
110
+ const data = yield response.json();
111
+ return {
112
+ matched: (_a = data.matched) !== null && _a !== void 0 ? _a : false,
113
+ invitationId: data.invitationId,
114
+ deepLink: data.deepLink,
115
+ metadata: data.metadata,
116
+ };
117
+ }
118
+ catch (error) {
119
+ // Non-blocking: silently fail and return no match
120
+ console.warn('[VortexDeferredLinks] Error matching fingerprint:', error);
121
+ return { matched: false };
122
+ }
123
+ });
124
+ }
125
+ }
126
+ exports.VortexDeferredLinks = VortexDeferredLinks;
127
+ //# sourceMappingURL=VortexDeferredLinks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VortexDeferredLinks.js","sourceRoot":"","sources":["../src/VortexDeferredLinks.ts"],"names":[],"mappings":"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","sourcesContent":["import { Platform, Dimensions, NativeModules } from 'react-native';\n\n/**\n * Device fingerprint data collected for deferred deep linking.\n */\nexport interface DeviceFingerprint {\n platform: 'ios' | 'android';\n osVersion: string;\n deviceModel: string;\n deviceBrand: string;\n timezone: string;\n language: string;\n screenWidth: number;\n screenHeight: number;\n carrierName?: string;\n totalMemory?: number;\n}\n\n/**\n * Response from the match-fingerprint API endpoint.\n */\nexport interface MatchFingerprintResponse {\n matched: boolean;\n invitationId?: string;\n deepLink?: string;\n metadata?: Record<string, any>;\n}\n\n/**\n * Options for retrieving deferred deep links.\n */\nexport interface RetrieveDeferredDeepLinkOptions {\n /** JWT token for authentication */\n jwt: string;\n /** Optional base URL override (defaults to production) */\n baseURL?: string;\n}\n\nconst DEFAULT_BASE_URL = 'https://client-api.vortexsoftware.com';\n\n/**\n * VortexDeferredLinks provides deferred deep linking functionality.\n * \n * Deferred deep linking allows you to track invitation attribution even when\n * the app wasn't installed at the time the invitation link was clicked.\n * \n * @example\n * ```tsx\n * import { VortexDeferredLinks } from '@teamvortexsoftware/vortex-react-native';\n * \n * // On app first launch after install\n * async function checkDeferredDeepLink(jwt: string) {\n * const result = await VortexDeferredLinks.retrieveDeferredDeepLink({ jwt });\n * if (result.matched && result.deepLink) {\n * // Handle the deferred deep link\n * console.log('User came from invitation:', result.invitationId);\n * // Navigate to appropriate screen\n * }\n * }\n * ```\n */\nexport class VortexDeferredLinks {\n /**\n * Collects device fingerprint data for matching.\n * This data is used to match the device against fingerprints collected\n * when invitation links were clicked.\n */\n static collectDeviceFingerprint(): DeviceFingerprint {\n const { width, height } = Dimensions.get('screen');\n const platformOS = Platform.OS === 'ios' ? 'ios' : 'android';\n \n // Get OS version\n const osVersion = Platform.Version?.toString() || 'unknown';\n \n // Get device model and brand\n let deviceModel = 'unknown';\n let deviceBrand = 'unknown';\n \n if (Platform.OS === 'ios') {\n // On iOS, we can get some info from Platform.constants\n const constants = Platform.constants as any;\n deviceModel = constants?.systemName || 'iPhone';\n deviceBrand = 'Apple';\n } else if (Platform.OS === 'android') {\n // On Android, we can access more device info\n const constants = Platform.constants as any;\n deviceModel = constants?.Model || 'unknown';\n deviceBrand = constants?.Brand || constants?.Manufacturer || 'unknown';\n }\n \n // Get timezone\n const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || 'unknown';\n \n // Get language\n const language = Platform.OS === 'ios'\n ? (NativeModules.SettingsManager?.settings?.AppleLocale ||\n NativeModules.SettingsManager?.settings?.AppleLanguages?.[0] ||\n 'en')\n : (NativeModules.I18nManager?.localeIdentifier || 'en');\n \n return {\n platform: platformOS,\n osVersion,\n deviceModel,\n deviceBrand,\n timezone,\n language,\n screenWidth: Math.round(width),\n screenHeight: Math.round(height),\n };\n }\n\n /**\n * Retrieves a deferred deep link by matching the device fingerprint.\n * \n * Call this method on first app launch after installation to check if\n * the user came from an invitation link.\n * \n * @param options - Configuration options including JWT and optional base URL\n * @returns Promise resolving to match result with optional deep link\n */\n static async retrieveDeferredDeepLink(\n options: RetrieveDeferredDeepLinkOptions\n ): Promise<MatchFingerprintResponse> {\n const { jwt, baseURL = DEFAULT_BASE_URL } = options;\n \n const fingerprint = VortexDeferredLinks.collectDeviceFingerprint();\n \n try {\n const response = await fetch(`${baseURL}/api/v1/invitations/match-fingerprint`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${jwt}`,\n },\n body: JSON.stringify(fingerprint),\n });\n \n if (!response.ok) {\n // Non-blocking: return no match on error\n console.warn('[VortexDeferredLinks] Failed to match fingerprint:', response.status);\n return { matched: false };\n }\n \n const data = await response.json();\n return {\n matched: data.matched ?? false,\n invitationId: data.invitationId,\n deepLink: data.deepLink,\n metadata: data.metadata,\n };\n } catch (error) {\n // Non-blocking: silently fail and return no match\n console.warn('[VortexDeferredLinks] Error matching fingerprint:', error);\n return { matched: false };\n }\n }\n}\n"]}
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ // Static client info for React Native - imports package.json at runtime
3
+ // This file is committed and doesn't need to be generated
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.getReactNativeClientInfo = getReactNativeClientInfo;
6
+ let cachedClientInfo = null;
7
+ function getReactNativeClientInfo() {
8
+ // Return cached value if available
9
+ if (cachedClientInfo) {
10
+ return cachedClientInfo;
11
+ }
12
+ try {
13
+ // Try to import package.json - this works in React Native Metro bundler
14
+ const packageJson = require('../package.json');
15
+ // Extract client name from the full package name
16
+ const clientName = packageJson.name || 'vortex-react-native';
17
+ const clientVersion = packageJson.version || 'unknown';
18
+ cachedClientInfo = {
19
+ name: clientName,
20
+ version: clientVersion
21
+ };
22
+ // Set on globalThis for the shared-ui getClientInfo() function
23
+ if (typeof globalThis !== 'undefined') {
24
+ globalThis.__VORTEX_CLIENT_NAME__ = clientName;
25
+ globalThis.__VORTEX_CLIENT_VERSION__ = clientVersion;
26
+ }
27
+ return cachedClientInfo;
28
+ }
29
+ catch (error) {
30
+ console.warn('[Vortex] Could not load package.json for client info:', error);
31
+ // Fallback values
32
+ const fallback = {
33
+ name: 'vortex-react-native',
34
+ version: 'unknown'
35
+ };
36
+ cachedClientInfo = fallback;
37
+ // Set fallback on globalThis
38
+ if (typeof globalThis !== 'undefined') {
39
+ globalThis.__VORTEX_CLIENT_NAME__ = fallback.name;
40
+ globalThis.__VORTEX_CLIENT_VERSION__ = fallback.version;
41
+ }
42
+ return fallback;
43
+ }
44
+ }
45
+ //# sourceMappingURL=clientInfo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clientInfo.js","sourceRoot":"","sources":["../src/clientInfo.ts"],"names":[],"mappings":";AAAA,wEAAwE;AACxE,0DAA0D;;AAI1D,4DA6CC;AA/CD,IAAI,gBAAgB,GAA6C,IAAI,CAAC;AAEtE,SAAgB,wBAAwB;IACtC,mCAAmC;IACnC,IAAI,gBAAgB,EAAE,CAAC;QACrB,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IAED,IAAI,CAAC;QACH,wEAAwE;QACxE,MAAM,WAAW,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;QAE/C,iDAAiD;QACjD,MAAM,UAAU,GAAG,WAAW,CAAC,IAAI,IAAI,qBAAqB,CAAC;QAC7D,MAAM,aAAa,GAAG,WAAW,CAAC,OAAO,IAAI,SAAS,CAAC;QAEvD,gBAAgB,GAAG;YACjB,IAAI,EAAE,UAAU;YAChB,OAAO,EAAE,aAAa;SACvB,CAAC;QAEF,+DAA+D;QAC/D,IAAI,OAAO,UAAU,KAAK,WAAW,EAAE,CAAC;YACtC,UAAU,CAAC,sBAAsB,GAAG,UAAU,CAAC;YAC/C,UAAU,CAAC,yBAAyB,GAAG,aAAa,CAAC;QACvD,CAAC;QAED,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,uDAAuD,EAAE,KAAK,CAAC,CAAC;QAE7E,kBAAkB;QAClB,MAAM,QAAQ,GAAG;YACf,IAAI,EAAE,qBAAqB;YAC3B,OAAO,EAAE,SAAS;SACnB,CAAC;QAEF,gBAAgB,GAAG,QAAQ,CAAC;QAE5B,6BAA6B;QAC7B,IAAI,OAAO,UAAU,KAAK,WAAW,EAAE,CAAC;YACtC,UAAU,CAAC,sBAAsB,GAAG,QAAQ,CAAC,IAAI,CAAC;YAClD,UAAU,CAAC,yBAAyB,GAAG,QAAQ,CAAC,OAAO,CAAC;QAC1D,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;AACH,CAAC","sourcesContent":["// Static client info for React Native - imports package.json at runtime\n// This file is committed and doesn't need to be generated\n\nlet cachedClientInfo: { name: string; version: string } | null = null;\n\nexport function getReactNativeClientInfo(): { name: string; version: string } {\n // Return cached value if available\n if (cachedClientInfo) {\n return cachedClientInfo;\n }\n\n try {\n // Try to import package.json - this works in React Native Metro bundler\n const packageJson = require('../package.json');\n\n // Extract client name from the full package name\n const clientName = packageJson.name || 'vortex-react-native';\n const clientVersion = packageJson.version || 'unknown';\n\n cachedClientInfo = {\n name: clientName,\n version: clientVersion\n };\n\n // Set on globalThis for the shared-ui getClientInfo() function\n if (typeof globalThis !== 'undefined') {\n globalThis.__VORTEX_CLIENT_NAME__ = clientName;\n globalThis.__VORTEX_CLIENT_VERSION__ = clientVersion;\n }\n\n return cachedClientInfo;\n } catch (error) {\n console.warn('[Vortex] Could not load package.json for client info:', error);\n\n // Fallback values\n const fallback = {\n name: 'vortex-react-native',\n version: 'unknown'\n };\n\n cachedClientInfo = fallback;\n\n // Set fallback on globalThis\n if (typeof globalThis !== 'undefined') {\n globalThis.__VORTEX_CLIENT_NAME__ = fallback.name;\n globalThis.__VORTEX_CLIENT_VERSION__ = fallback.version;\n }\n\n return fallback;\n }\n}"]}
@@ -0,0 +1,182 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ContactsPickerModal = ContactsPickerModal;
37
+ const react_1 = __importStar(require("react"));
38
+ const react_native_1 = require("react-native");
39
+ function ContactsPickerModal({ visible, contacts, onCancel, onConfirm, themeStyles, themeColors, }) {
40
+ const [selectedIds, setSelectedIds] = (0, react_1.useState)(new Set());
41
+ const toggle = (id) => {
42
+ setSelectedIds((prev) => {
43
+ const next = new Set(prev);
44
+ if (next.has(id))
45
+ next.delete(id);
46
+ else
47
+ next.add(id);
48
+ return next;
49
+ });
50
+ };
51
+ const selectedCount = selectedIds.size;
52
+ const renderItem = ({ item }) => {
53
+ var _a;
54
+ const selected = selectedIds.has(item.id);
55
+ const primaryEmail = (_a = item.emails) === null || _a === void 0 ? void 0 : _a[0];
56
+ return (<react_native_1.Pressable onPress={() => toggle(item.id)} style={[styles.row, { borderColor: themeColors.containerBorder, backgroundColor: themeColors.containerBackground }]}>
57
+ <react_native_1.View style={styles.checkboxContainer}>
58
+ <react_native_1.View style={[
59
+ styles.checkbox,
60
+ { borderColor: themeColors.containerBorder, backgroundColor: selected ? themeColors.primaryButtonBackground : 'transparent' },
61
+ ]}/>
62
+ </react_native_1.View>
63
+ <react_native_1.View style={styles.info}>
64
+ <react_native_1.Text style={[styles.name, { color: themeColors.containerForeground }]} numberOfLines={1}>
65
+ {item.name}
66
+ </react_native_1.Text>
67
+ {!!primaryEmail && (<react_native_1.Text style={[styles.email, { color: themeColors.containerForeground }]} numberOfLines={1}>
68
+ {primaryEmail}
69
+ </react_native_1.Text>)}
70
+ </react_native_1.View>
71
+ </react_native_1.Pressable>);
72
+ };
73
+ const handleConfirm = () => {
74
+ const selected = contacts.filter((c) => selectedIds.has(c.id));
75
+ onConfirm(selected);
76
+ setSelectedIds(new Set());
77
+ };
78
+ const handleCancel = () => {
79
+ setSelectedIds(new Set());
80
+ onCancel();
81
+ };
82
+ const keyExtractor = (item) => item.id;
83
+ return (<react_native_1.Modal visible={visible} animationType="slide" transparent>
84
+ <react_native_1.View style={styles.backdrop}>
85
+ <react_native_1.View style={[styles.sheet, { backgroundColor: themeColors.containerBackground, borderColor: themeColors.containerBorder }]}>
86
+ <react_native_1.Text style={[styles.title, { color: themeColors.containerForeground }]}>Select contacts</react_native_1.Text>
87
+ <react_native_1.FlatList data={contacts} renderItem={renderItem} keyExtractor={keyExtractor} ItemSeparatorComponent={() => <react_native_1.View style={[styles.separator, { backgroundColor: themeColors.containerBackground }]}/>} style={styles.list} contentContainerStyle={contacts.length === 0 ? styles.emptyContainer : undefined} ListEmptyComponent={<react_native_1.Text style={{ color: themeColors.containerForeground }}>No contacts with emails</react_native_1.Text>}/>
88
+ <react_native_1.View style={styles.actions}>
89
+ <react_native_1.Pressable style={[styles.button, styles.secondaryBtn, { borderColor: themeColors.secondaryButtonBorder, backgroundColor: themeColors.secondaryButtonBackground }]} onPress={handleCancel}>
90
+ <react_native_1.Text style={[styles.buttonText, { color: themeColors.secondaryButtonForeground }]}>Cancel</react_native_1.Text>
91
+ </react_native_1.Pressable>
92
+ <react_native_1.Pressable style={[styles.button, styles.primaryBtn, { borderColor: themeColors.primaryButtonBorder, backgroundColor: themeColors.primaryButtonBackground, opacity: selectedCount === 0 ? 0.6 : 1 }]} onPress={handleConfirm} disabled={selectedCount === 0}>
93
+ <react_native_1.Text style={[styles.buttonText, { color: themeColors.primaryButtonForeground }]}>
94
+ Add{selectedCount > 0 ? ` ${selectedCount}` : ''}
95
+ </react_native_1.Text>
96
+ </react_native_1.Pressable>
97
+ </react_native_1.View>
98
+ </react_native_1.View>
99
+ </react_native_1.View>
100
+ </react_native_1.Modal>);
101
+ }
102
+ const styles = react_native_1.StyleSheet.create({
103
+ backdrop: {
104
+ flex: 1,
105
+ backgroundColor: 'rgba(0,0,0,0.35)',
106
+ justifyContent: 'flex-end',
107
+ },
108
+ sheet: {
109
+ maxHeight: '80%',
110
+ borderTopLeftRadius: 12,
111
+ borderTopRightRadius: 12,
112
+ borderWidth: 1,
113
+ paddingHorizontal: 12,
114
+ paddingTop: 12,
115
+ paddingBottom: 8,
116
+ },
117
+ title: {
118
+ fontSize: 18,
119
+ fontWeight: '600',
120
+ textAlign: 'center',
121
+ marginBottom: 8,
122
+ },
123
+ list: {
124
+ flexGrow: 0,
125
+ },
126
+ row: {
127
+ flexDirection: 'row',
128
+ alignItems: 'center',
129
+ paddingVertical: 10,
130
+ paddingHorizontal: 4,
131
+ borderWidth: 1,
132
+ borderRadius: 8,
133
+ },
134
+ checkboxContainer: {
135
+ paddingHorizontal: 8,
136
+ },
137
+ checkbox: {
138
+ width: 18,
139
+ height: 18,
140
+ borderRadius: 4,
141
+ borderWidth: 1,
142
+ },
143
+ info: {
144
+ flex: 1,
145
+ paddingLeft: 8,
146
+ },
147
+ name: {
148
+ fontSize: 16,
149
+ fontWeight: '500',
150
+ },
151
+ email: {
152
+ fontSize: 13,
153
+ opacity: 0.9,
154
+ },
155
+ separator: {
156
+ height: 8,
157
+ },
158
+ emptyContainer: {
159
+ alignItems: 'center',
160
+ paddingVertical: 16,
161
+ },
162
+ actions: {
163
+ flexDirection: 'row',
164
+ justifyContent: 'space-between',
165
+ paddingVertical: 12,
166
+ },
167
+ button: {
168
+ flex: 1,
169
+ paddingVertical: 10,
170
+ borderRadius: 8,
171
+ borderWidth: 1,
172
+ alignItems: 'center',
173
+ marginHorizontal: 6,
174
+ },
175
+ primaryBtn: {},
176
+ secondaryBtn: {},
177
+ buttonText: {
178
+ fontSize: 16,
179
+ fontWeight: '600',
180
+ },
181
+ });
182
+ //# sourceMappingURL=ContactsPickerModal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContactsPickerModal.js","sourceRoot":"","sources":["../../src/components/ContactsPickerModal.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useMemo, useState } from 'react';\nimport { Modal, View, Text, Pressable, StyleSheet, FlatList } from 'react-native';\nimport { ThemeColors } from '../utils/themeUtils';\n\nexport interface ContactItem {\n id: string;\n name: string;\n emails: string[];\n}\n\ninterface ContactsPickerModalProps {\n visible: boolean;\n contacts: ContactItem[];\n onCancel: () => void;\n onConfirm: (selected: ContactItem[]) => void;\n themeStyles: any;\n themeColors: ThemeColors;\n}\n\nexport function ContactsPickerModal({\n visible,\n contacts,\n onCancel,\n onConfirm,\n themeStyles,\n themeColors,\n}: ContactsPickerModalProps) {\n const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());\n\n const toggle = (id: string) => {\n setSelectedIds((prev) => {\n const next = new Set(prev);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n };\n\n const selectedCount = selectedIds.size;\n\n const renderItem = ({ item }: { item: ContactItem }) => {\n const selected = selectedIds.has(item.id);\n const primaryEmail = item.emails?.[0];\n return (\n <Pressable\n onPress={() => toggle(item.id)}\n style={[styles.row, { borderColor: themeColors.containerBorder, backgroundColor: themeColors.containerBackground }]}\n >\n <View style={styles.checkboxContainer}>\n <View\n style={[\n styles.checkbox,\n { borderColor: themeColors.containerBorder, backgroundColor: selected ? themeColors.primaryButtonBackground : 'transparent' },\n ]}\n />\n </View>\n <View style={styles.info}>\n <Text style={[styles.name, { color: themeColors.containerForeground }]} numberOfLines={1}>\n {item.name}\n </Text>\n {!!primaryEmail && (\n <Text style={[styles.email, { color: themeColors.containerForeground }]} numberOfLines={1}>\n {primaryEmail}\n </Text>\n )}\n </View>\n </Pressable>\n );\n };\n\n const handleConfirm = () => {\n const selected = contacts.filter((c) => selectedIds.has(c.id));\n onConfirm(selected);\n setSelectedIds(new Set());\n };\n\n const handleCancel = () => {\n setSelectedIds(new Set());\n onCancel();\n };\n\n const keyExtractor = (item: ContactItem) => item.id;\n\n return (\n <Modal visible={visible} animationType=\"slide\" transparent>\n <View style={styles.backdrop}>\n <View style={[styles.sheet, { backgroundColor: themeColors.containerBackground, borderColor: themeColors.containerBorder }]}> \n <Text style={[styles.title, { color: themeColors.containerForeground }]}>Select contacts</Text>\n <FlatList\n data={contacts}\n renderItem={renderItem}\n keyExtractor={keyExtractor}\n ItemSeparatorComponent={() => <View style={[styles.separator, { backgroundColor: themeColors.containerBackground }]} />}\n style={styles.list}\n contentContainerStyle={contacts.length === 0 ? styles.emptyContainer : undefined}\n ListEmptyComponent={<Text style={{ color: themeColors.containerForeground }}>No contacts with emails</Text>}\n />\n <View style={styles.actions}>\n <Pressable style={[styles.button, styles.secondaryBtn, { borderColor: themeColors.secondaryButtonBorder, backgroundColor: themeColors.secondaryButtonBackground }]} onPress={handleCancel}>\n <Text style={[styles.buttonText, { color: themeColors.secondaryButtonForeground }]}>Cancel</Text>\n </Pressable>\n <Pressable\n style={[styles.button, styles.primaryBtn, { borderColor: themeColors.primaryButtonBorder, backgroundColor: themeColors.primaryButtonBackground, opacity: selectedCount === 0 ? 0.6 : 1 }]}\n onPress={handleConfirm}\n disabled={selectedCount === 0}\n >\n <Text style={[styles.buttonText, { color: themeColors.primaryButtonForeground }]}>\n Add{selectedCount > 0 ? ` ${selectedCount}` : ''}\n </Text>\n </Pressable>\n </View>\n </View>\n </View>\n </Modal>\n );\n}\n\nconst styles = StyleSheet.create({\n backdrop: {\n flex: 1,\n backgroundColor: 'rgba(0,0,0,0.35)',\n justifyContent: 'flex-end',\n },\n sheet: {\n maxHeight: '80%',\n borderTopLeftRadius: 12,\n borderTopRightRadius: 12,\n borderWidth: 1,\n paddingHorizontal: 12,\n paddingTop: 12,\n paddingBottom: 8,\n },\n title: {\n fontSize: 18,\n fontWeight: '600',\n textAlign: 'center',\n marginBottom: 8,\n },\n list: {\n flexGrow: 0,\n },\n row: {\n flexDirection: 'row',\n alignItems: 'center',\n paddingVertical: 10,\n paddingHorizontal: 4,\n borderWidth: 1,\n borderRadius: 8,\n },\n checkboxContainer: {\n paddingHorizontal: 8,\n },\n checkbox: {\n width: 18,\n height: 18,\n borderRadius: 4,\n borderWidth: 1,\n },\n info: {\n flex: 1,\n paddingLeft: 8,\n },\n name: {\n fontSize: 16,\n fontWeight: '500',\n },\n email: {\n fontSize: 13,\n opacity: 0.9,\n },\n separator: {\n height: 8,\n },\n emptyContainer: {\n alignItems: 'center',\n paddingVertical: 16,\n },\n actions: {\n flexDirection: 'row',\n justifyContent: 'space-between',\n paddingVertical: 12,\n },\n button: {\n flex: 1,\n paddingVertical: 10,\n borderRadius: 8,\n borderWidth: 1,\n alignItems: 'center',\n marginHorizontal: 6,\n },\n primaryBtn: {},\n secondaryBtn: {},\n buttonText: {\n fontSize: 16,\n fontWeight: '600',\n },\n});\n"]}