@teamvortexsoftware/vortex-react-native 0.0.13 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +1227 -11
- package/dist/InviteFormCore-D4HkMMo0.d.mts +721 -0
- package/dist/InviteFormCore-D9oUCbu7.d.ts +721 -0
- package/dist/VortexClient.js +192 -0
- package/dist/VortexClient.js.map +1 -0
- package/dist/VortexDeferredLinks.js +127 -0
- package/dist/VortexDeferredLinks.js.map +1 -0
- package/dist/clientInfo.js +45 -0
- package/dist/clientInfo.js.map +1 -0
- package/dist/components/ContactsPickerModal.js +182 -0
- package/dist/components/ContactsPickerModal.js.map +1 -0
- package/dist/components/InviteFormCore.js +2141 -0
- package/dist/components/InviteFormCore.js.map +1 -0
- package/dist/components/InviteFormMobile.js +463 -0
- package/dist/components/InviteFormMobile.js.map +1 -0
- package/dist/components/InviteFormWeb.js +295 -0
- package/dist/components/InviteFormWeb.js.map +1 -0
- package/dist/components/PlacedItemToolbar.js +147 -0
- package/dist/components/PlacedItemToolbar.js.map +1 -0
- package/dist/components/ShareButtons.js +1 -0
- package/dist/components/ShareButtons.js.map +1 -0
- package/dist/components/VrtxContactsImport.js +234 -0
- package/dist/components/VrtxContactsImport.js.map +1 -0
- package/dist/components/VrtxEmailInvitations.js +341 -0
- package/dist/components/VrtxEmailInvitations.js.map +1 -0
- package/dist/components/VrtxFindFriends.js +400 -0
- package/dist/components/VrtxFindFriends.js.map +1 -0
- package/dist/components/VrtxHeading.js +58 -0
- package/dist/components/VrtxHeading.js.map +1 -0
- package/dist/components/VrtxIncomingInvitations.js +657 -0
- package/dist/components/VrtxIncomingInvitations.js.map +1 -0
- package/dist/components/VrtxInvitationSuggestions.js +506 -0
- package/dist/components/VrtxInvitationSuggestions.js.map +1 -0
- package/dist/components/VrtxInviteContacts.js +512 -0
- package/dist/components/VrtxInviteContacts.js.map +1 -0
- package/dist/components/VrtxOutgoingInvitations.js +572 -0
- package/dist/components/VrtxOutgoingInvitations.js.map +1 -0
- package/dist/components/VrtxSearchBox.js +487 -0
- package/dist/components/VrtxSearchBox.js.map +1 -0
- package/dist/components/VrtxSelect.js +27 -0
- package/dist/components/VrtxSelect.js.map +1 -0
- package/dist/components/VrtxShareOptions.js +435 -0
- package/dist/components/VrtxShareOptions.js.map +1 -0
- package/dist/components/VrtxSubmit.js +132 -0
- package/dist/components/VrtxSubmit.js.map +1 -0
- package/dist/components/VrtxText.js +146 -0
- package/dist/components/VrtxText.js.map +1 -0
- package/dist/constants/mockData.d.mts +7 -0
- package/dist/constants/mockData.d.ts +7 -0
- package/dist/constants/mockData.js +48 -0
- package/dist/constants/mockData.js.map +1 -0
- package/dist/constants/mockData.mjs +22 -0
- package/dist/constants/mockData.mjs.map +1 -0
- package/dist/context/VortexModulesContext.js +135 -0
- package/dist/context/VortexModulesContext.js.map +1 -0
- package/dist/hooks/useInvitationFormLogic.d.mts +2 -0
- package/dist/hooks/useInvitationFormLogic.d.ts +2 -0
- package/dist/hooks/useInvitationFormLogic.js +300 -0
- package/dist/hooks/useInvitationFormLogic.js.map +1 -0
- package/dist/hooks/useInvitationFormLogic.mjs +276 -0
- package/dist/hooks/useInvitationFormLogic.mjs.map +1 -0
- package/dist/hooks/usePrefetchWidgetConfiguration.js +117 -0
- package/dist/hooks/usePrefetchWidgetConfiguration.js.map +1 -0
- package/dist/hooks/useThemeStyles.js +2 -0
- package/dist/hooks/useThemeStyles.js.map +1 -0
- package/dist/hooks/useVortexInvite.js +467 -56
- package/dist/hooks/useVortexInvite.js.map +1 -0
- package/dist/index-web.d.mts +93 -0
- package/dist/index-web.d.ts +93 -0
- package/dist/index-web.js +7397 -0
- package/dist/index-web.js.map +1 -0
- package/dist/index-web.mjs +7445 -0
- package/dist/index-web.mjs.map +1 -0
- package/dist/index.d.mts +656 -0
- package/dist/index.d.ts +656 -0
- package/dist/index.js +10205 -4
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +10244 -0
- package/dist/index.mjs.map +1 -0
- package/dist/types/VortexClient.d.ts +106 -0
- package/dist/types/VortexClient.d.ts.map +1 -0
- package/dist/types/VortexDeferredLinks.d.ts +73 -0
- package/dist/types/VortexDeferredLinks.d.ts.map +1 -0
- package/dist/types/clientInfo.d.ts +5 -0
- package/dist/types/clientInfo.d.ts.map +1 -0
- package/dist/types/components/ContactsPickerModal.d.ts +18 -0
- package/dist/types/components/ContactsPickerModal.d.ts.map +1 -0
- package/dist/types/components/InviteFormCore.d.ts +166 -0
- package/dist/types/components/InviteFormCore.d.ts.map +1 -0
- package/dist/types/components/InviteFormMobile.d.ts +42 -0
- package/dist/types/components/InviteFormMobile.d.ts.map +1 -0
- package/dist/types/components/InviteFormWeb.d.ts +87 -0
- package/dist/types/components/InviteFormWeb.d.ts.map +1 -0
- package/dist/types/components/PlacedItemToolbar.d.ts +16 -0
- package/dist/types/components/PlacedItemToolbar.d.ts.map +1 -0
- package/dist/types/components/VrtxContactsImport.d.ts +14 -0
- package/dist/types/components/VrtxContactsImport.d.ts.map +1 -0
- package/dist/types/components/VrtxEmailInvitations.d.ts +31 -0
- package/dist/types/components/VrtxEmailInvitations.d.ts.map +1 -0
- package/dist/types/components/VrtxFindFriends.d.ts +25 -0
- package/dist/types/components/VrtxFindFriends.d.ts.map +1 -0
- package/dist/types/components/VrtxHeading.d.ts +6 -0
- package/dist/types/components/VrtxHeading.d.ts.map +1 -0
- package/dist/types/components/VrtxIncomingInvitations.d.ts +27 -0
- package/dist/types/components/VrtxIncomingInvitations.d.ts.map +1 -0
- package/dist/types/components/VrtxInvitationSuggestions.d.ts +25 -0
- package/dist/types/components/VrtxInvitationSuggestions.d.ts.map +1 -0
- package/dist/types/components/VrtxInviteContacts.d.ts +24 -0
- package/dist/types/components/VrtxInviteContacts.d.ts.map +1 -0
- package/dist/types/components/VrtxOutgoingInvitations.d.ts +27 -0
- package/dist/types/components/VrtxOutgoingInvitations.d.ts.map +1 -0
- package/dist/types/components/VrtxSearchBox.d.ts +28 -0
- package/dist/types/components/VrtxSearchBox.d.ts.map +1 -0
- package/dist/types/components/VrtxSelect.d.ts +6 -0
- package/dist/types/components/VrtxSelect.d.ts.map +1 -0
- package/dist/types/components/VrtxShareOptions.d.ts +41 -0
- package/dist/types/components/VrtxShareOptions.d.ts.map +1 -0
- package/dist/types/components/VrtxSubmit.d.ts +18 -0
- package/dist/types/components/VrtxSubmit.d.ts.map +1 -0
- package/dist/types/components/VrtxText.d.ts +8 -0
- package/dist/types/components/VrtxText.d.ts.map +1 -0
- package/dist/types/constants/mockData.d.ts +4 -0
- package/dist/types/constants/mockData.d.ts.map +1 -0
- package/dist/types/context/VortexModulesContext.d.ts +238 -0
- package/dist/types/context/VortexModulesContext.d.ts.map +1 -0
- package/dist/types/findFriends.js +10 -0
- package/dist/types/findFriends.js.map +1 -0
- package/dist/types/hooks/useInvitationFormLogic.d.ts +55 -0
- package/dist/types/hooks/useInvitationFormLogic.d.ts.map +1 -0
- package/dist/types/hooks/usePrefetchWidgetConfiguration.d.ts +39 -0
- package/dist/types/hooks/usePrefetchWidgetConfiguration.d.ts.map +1 -0
- package/dist/types/hooks/useThemeStyles.d.ts +1 -0
- package/dist/types/hooks/useThemeStyles.d.ts.map +1 -1
- package/dist/types/hooks/useVortexInvite.d.ts +48 -6
- package/dist/types/hooks/useVortexInvite.d.ts.map +1 -1
- package/dist/types/index-web.d.ts +23 -0
- package/dist/types/index-web.d.ts.map +1 -0
- package/dist/types/index.d.ts +21 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/invitations.js +13 -0
- package/dist/types/invitations.js.map +1 -0
- package/dist/types/inviteContacts.js +14 -0
- package/dist/types/inviteContacts.js.map +1 -0
- package/dist/{shared/InvitationResult.js → types/platformOperations.js} +1 -0
- package/dist/types/platformOperations.js.map +1 -0
- package/dist/types/searchBox.js +11 -0
- package/dist/types/searchBox.js.map +1 -0
- package/dist/types/types/findFriends.d.ts +101 -0
- package/dist/types/types/findFriends.d.ts.map +1 -0
- package/dist/types/types/invitations.d.ts +301 -0
- package/dist/types/types/invitations.d.ts.map +1 -0
- package/dist/types/types/inviteContacts.d.ts +86 -0
- package/dist/types/types/inviteContacts.d.ts.map +1 -0
- package/dist/types/types/platformOperations.d.ts +185 -0
- package/dist/types/types/platformOperations.d.ts.map +1 -0
- package/dist/types/types/searchBox.d.ts +69 -0
- package/dist/types/types/searchBox.d.ts.map +1 -0
- package/dist/types/types/unfurlConfig.d.ts +34 -0
- package/dist/types/types/unfurlConfig.d.ts.map +1 -0
- package/dist/types/unfurlConfig.js +21 -0
- package/dist/types/unfurlConfig.js.map +1 -0
- package/dist/types/utils/analytics.d.ts +54 -0
- package/dist/types/utils/analytics.d.ts.map +1 -0
- package/dist/types/utils/configCache.d.ts +34 -0
- package/dist/types/utils/configCache.d.ts.map +1 -0
- package/dist/types/utils/contactUtils.d.ts +9 -0
- package/dist/types/utils/contactUtils.d.ts.map +1 -0
- package/dist/types/utils/featureWarnings.d.ts +56 -0
- package/dist/types/utils/featureWarnings.d.ts.map +1 -0
- package/dist/types/utils/formUtils.d.ts +11 -3
- package/dist/types/utils/formUtils.d.ts.map +1 -1
- package/dist/types/utils/gradientUtils.d.ts +67 -0
- package/dist/types/utils/gradientUtils.d.ts.map +1 -0
- package/dist/types/utils/invitationEvents.d.ts +21 -0
- package/dist/types/utils/invitationEvents.d.ts.map +1 -0
- package/dist/types/utils/moduleLoaders.d.ts +115 -0
- package/dist/types/utils/moduleLoaders.d.ts.map +1 -0
- package/dist/types/utils/moduleLoaders.web.d.ts +73 -0
- package/dist/types/utils/moduleLoaders.web.d.ts.map +1 -0
- package/dist/types/utils/nameUtils.d.ts +15 -0
- package/dist/types/utils/nameUtils.d.ts.map +1 -0
- package/dist/types/utils/themeUtils.d.ts +3 -1
- package/dist/types/utils/themeUtils.d.ts.map +1 -1
- package/dist/types/vortexInvite.d.ts +145 -5
- package/dist/types/vortexInvite.d.ts.map +1 -1
- package/dist/useInvitationFormLogic-Ct73M19B.d.mts +242 -0
- package/dist/useInvitationFormLogic-Ct73M19B.d.ts +242 -0
- package/dist/utils/analytics.js +92 -0
- package/dist/utils/analytics.js.map +1 -0
- package/dist/utils/configCache.js +68 -0
- package/dist/utils/configCache.js.map +1 -0
- package/dist/utils/contactUtils.d.mts +12 -0
- package/dist/utils/contactUtils.d.ts +12 -0
- package/dist/utils/contactUtils.js +37 -0
- package/dist/utils/contactUtils.js.map +1 -0
- package/dist/utils/contactUtils.mjs +12 -0
- package/dist/utils/contactUtils.mjs.map +1 -0
- package/dist/utils/featureWarnings.js +214 -0
- package/dist/utils/featureWarnings.js.map +1 -0
- package/dist/utils/formUtils.js +161 -51
- package/dist/utils/formUtils.js.map +1 -0
- package/dist/utils/gradientUtils.js +120 -0
- package/dist/utils/gradientUtils.js.map +1 -0
- package/dist/utils/invitationEvents.js +45 -0
- package/dist/utils/invitationEvents.js.map +1 -0
- package/dist/utils/moduleLoaders.js +275 -0
- package/dist/utils/moduleLoaders.js.map +1 -0
- package/dist/utils/moduleLoaders.web.js +72 -0
- package/dist/utils/moduleLoaders.web.js.map +1 -0
- package/dist/utils/nameUtils.js +51 -0
- package/dist/utils/nameUtils.js.map +1 -0
- package/dist/utils/themeUtils.js +117 -32
- package/dist/utils/themeUtils.js.map +1 -0
- package/dist/vortexInvite.js +78 -167
- package/dist/vortexInvite.js.map +1 -0
- package/package.json +69 -31
- package/dist/components/Clipboard.js +0 -64
- package/dist/shared/api.js +0 -90
- package/dist/tests/TestVortexInvite.js +0 -134
- package/dist/types/components/Clipboard.d.ts +0 -16
- package/dist/types/components/Clipboard.d.ts.map +0 -1
- package/dist/types/shared/InvitationResult.d.ts +0 -24
- package/dist/types/shared/InvitationResult.d.ts.map +0 -1
- package/dist/types/shared/api.d.ts +0 -14
- package/dist/types/shared/api.d.ts.map +0 -1
- package/dist/types/tests/TestVortexInvite.d.ts +0 -4
- package/dist/types/tests/TestVortexInvite.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,13 +1,1231 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Vortex React Native SDK
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Configuration-driven invitation forms for React Native apps.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The Vortex React Native SDK provides a React Native component for rendering customizable invitation forms in your React Native applications. The SDK fetches configuration from the [Vortex](https://vortexsoftware.com) platform and dynamically renders the appropriate UI.
|
|
8
|
+
|
|
9
|
+
## Requirements
|
|
10
|
+
|
|
11
|
+
- React 19+
|
|
12
|
+
- React Native 0.79.0+
|
|
4
13
|
|
|
5
14
|
## Installation
|
|
6
15
|
|
|
7
16
|
```bash
|
|
17
|
+
# Using pnpm (recommended)
|
|
8
18
|
pnpm add @teamvortexsoftware/vortex-react-native
|
|
19
|
+
|
|
20
|
+
# Using npm
|
|
21
|
+
npm install @teamvortexsoftware/vortex-react-native
|
|
22
|
+
|
|
23
|
+
# Using yarn
|
|
24
|
+
yarn add @teamvortexsoftware/vortex-react-native
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
### Basic Usage
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import { VortexInvite } from '@teamvortexsoftware/vortex-react-native';
|
|
33
|
+
import { useState, useEffect } from 'react';
|
|
34
|
+
|
|
35
|
+
function MyInviteScreen() {
|
|
36
|
+
const [jwt, setJwt] = useState<string | null>(null);
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
// Generate JWT for authenticated user
|
|
40
|
+
const generateJwt = async () => {
|
|
41
|
+
try {
|
|
42
|
+
const response = await fetch('https://your-api.com/api/vortex/jwt', {
|
|
43
|
+
method: 'POST',
|
|
44
|
+
headers: { 'Content-Type': 'application/json' },
|
|
45
|
+
});
|
|
46
|
+
const data = await response.json();
|
|
47
|
+
setJwt(data.jwt);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error('Failed to generate JWT:', error);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
generateJwt();
|
|
54
|
+
}, []);
|
|
55
|
+
|
|
56
|
+
if (!jwt) {
|
|
57
|
+
return <Text>Loading...</Text>;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<VortexInvite
|
|
62
|
+
componentId="my-component-id"
|
|
63
|
+
jwt={jwt}
|
|
64
|
+
scope="team-123"
|
|
65
|
+
scopeType="team"
|
|
66
|
+
onInvite={(data) => {
|
|
67
|
+
console.log('Invitation sent:', data);
|
|
68
|
+
}}
|
|
69
|
+
onError={(error) => {
|
|
70
|
+
console.error('Invitation error:', error);
|
|
71
|
+
}}
|
|
72
|
+
/>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### With Group Context
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
import { VortexInvite } from '@teamvortexsoftware/vortex-react-native';
|
|
81
|
+
|
|
82
|
+
function TeamInviteScreen({ teamId, teamName }: { teamId: string; teamName: string }) {
|
|
83
|
+
const { jwt, error } = useVortexAuth();
|
|
84
|
+
|
|
85
|
+
if (error) {
|
|
86
|
+
return <Text>Error: {error.message}</Text>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<VortexInvite
|
|
91
|
+
componentId={process.env.EXPO_PUBLIC_COMPONENT_ID}
|
|
92
|
+
jwt={jwt || ''}
|
|
93
|
+
scope={teamId}
|
|
94
|
+
scopeType="team"
|
|
95
|
+
onInvite={(result) => {
|
|
96
|
+
console.log('Invitations sent:', result);
|
|
97
|
+
}}
|
|
98
|
+
/>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Complete Example with Modal
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
import { useState, useEffect } from 'react';
|
|
107
|
+
import { Modal, View, Button, StyleSheet } from 'react-native';
|
|
108
|
+
import { VortexInvite } from '@teamvortexsoftware/vortex-react-native';
|
|
109
|
+
|
|
110
|
+
function InviteModal({
|
|
111
|
+
visible,
|
|
112
|
+
onClose,
|
|
113
|
+
workspaceId,
|
|
114
|
+
}: {
|
|
115
|
+
visible: boolean;
|
|
116
|
+
onClose: () => void;
|
|
117
|
+
workspaceId: string;
|
|
118
|
+
}) {
|
|
119
|
+
const [jwt, setJwt] = useState<string>('');
|
|
120
|
+
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
if (visible) {
|
|
123
|
+
// Generate JWT when modal opens
|
|
124
|
+
fetchJwt().then(setJwt);
|
|
125
|
+
}
|
|
126
|
+
}, [visible]);
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<Modal visible={visible} animationType="slide">
|
|
130
|
+
<View style={styles.container}>
|
|
131
|
+
<View style={styles.header}>
|
|
132
|
+
<Button title="Close" onPress={onClose} />
|
|
133
|
+
</View>
|
|
134
|
+
|
|
135
|
+
<VortexInvite
|
|
136
|
+
componentId={process.env.EXPO_PUBLIC_COMPONENT_ID}
|
|
137
|
+
jwt={jwt}
|
|
138
|
+
scope={workspaceId}
|
|
139
|
+
scopeType="workspace"
|
|
140
|
+
onInvite={(result) => {
|
|
141
|
+
console.log('Invitations sent successfully');
|
|
142
|
+
onClose();
|
|
143
|
+
}}
|
|
144
|
+
onError={(error) => {
|
|
145
|
+
console.error('Failed to send invitations:', error);
|
|
146
|
+
}}
|
|
147
|
+
/>
|
|
148
|
+
</View>
|
|
149
|
+
</Modal>
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const styles = StyleSheet.create({
|
|
154
|
+
container: {
|
|
155
|
+
flex: 1,
|
|
156
|
+
backgroundColor: '#fff',
|
|
157
|
+
},
|
|
158
|
+
header: {
|
|
159
|
+
padding: 16,
|
|
160
|
+
borderBottomWidth: 1,
|
|
161
|
+
borderBottomColor: '#e0e0e0',
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Invite Contacts
|
|
167
|
+
|
|
168
|
+
The Invite Contacts component displays a list of contacts that can be invited via SMS. Unlike the Contact Import feature (which fetches contacts from the device), Invite Contacts receives a pre-populated list of contacts from your app.
|
|
169
|
+
|
|
170
|
+
**Basic Usage:**
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
import { VortexInvite, InviteContactsConfig, InviteContactsContact } from '@teamvortexsoftware/vortex-react-native';
|
|
174
|
+
|
|
175
|
+
const contacts: InviteContactsContact[] = [
|
|
176
|
+
{ name: "Alice Johnson", phoneNumber: "+1 (555) 123-4567" },
|
|
177
|
+
{ name: "Bob Smith", phoneNumber: "+1 (555) 234-5678" },
|
|
178
|
+
{ name: "Carol Davis", phoneNumber: "+1 (555) 345-6789" }
|
|
179
|
+
];
|
|
180
|
+
|
|
181
|
+
<VortexInvite
|
|
182
|
+
componentId="your-component-id"
|
|
183
|
+
jwt={jwt}
|
|
184
|
+
inviteContactsConfig={{ contacts }}
|
|
185
|
+
onInvite={(result) => {
|
|
186
|
+
console.log('Invitations sent:', result);
|
|
187
|
+
}}
|
|
188
|
+
/>
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**How It Works:**
|
|
192
|
+
|
|
193
|
+
1. The component shows an "Invite your contacts" entry with a contact count
|
|
194
|
+
2. Tapping it navigates to a searchable list of contacts
|
|
195
|
+
3. Each contact has an "Invite" button that:
|
|
196
|
+
- Creates an SMS invitation via the Vortex API
|
|
197
|
+
- Opens the device's SMS app (on supported devices)
|
|
198
|
+
- Pre-fills the message with the invitation link
|
|
199
|
+
|
|
200
|
+
**InviteContactsContact Properties:**
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
// Simple usage - just name and phone number (ID is auto-generated)
|
|
204
|
+
{
|
|
205
|
+
name: string; // Display name
|
|
206
|
+
phoneNumber: string; // Phone number for SMS
|
|
207
|
+
avatarUrl?: string; // Optional avatar image URL
|
|
208
|
+
metadata?: Record<string, any>; // Optional custom metadata
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Full usage - with custom ID
|
|
212
|
+
{
|
|
213
|
+
id: string; // Custom unique identifier
|
|
214
|
+
name: string; // Display name
|
|
215
|
+
phoneNumber: string; // Phone number for SMS
|
|
216
|
+
avatarUrl?: string; // Optional avatar image URL
|
|
217
|
+
metadata?: Record<string, any>; // Optional custom metadata
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**InviteContactsConfig Properties:**
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
{
|
|
225
|
+
contacts: InviteContactsContact[]; // List of contacts to display
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Find Friends
|
|
230
|
+
|
|
231
|
+
The Find Friends component displays a list of contacts provided by your app. Each contact has a "Connect" button that creates an invitation with `targetType: internalId`.
|
|
232
|
+
|
|
233
|
+
**Basic Usage:**
|
|
234
|
+
|
|
235
|
+
```tsx
|
|
236
|
+
import { VortexInvite, FindFriendsConfig, FindFriendsContact } from '@teamvortexsoftware/vortex-react-native';
|
|
237
|
+
|
|
238
|
+
<VortexInvite
|
|
239
|
+
componentId="your-component-id"
|
|
240
|
+
jwt={jwt}
|
|
241
|
+
findFriendsConfig={{
|
|
242
|
+
contacts: [
|
|
243
|
+
{ internalId: "user-123", name: "Alice Johnson", subtitle: "@alice" },
|
|
244
|
+
{ internalId: "user-456", name: "Bob Smith", subtitle: "@bob" }
|
|
245
|
+
],
|
|
246
|
+
onInvitationCreated: (contact) => {
|
|
247
|
+
// Called after an invitation is successfully created
|
|
248
|
+
console.log('Invitation created for', contact.name);
|
|
249
|
+
}
|
|
250
|
+
}}
|
|
251
|
+
/>
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**How It Works:**
|
|
255
|
+
|
|
256
|
+
1. Your app provides a list of contacts with internal IDs (users already in your platform)
|
|
257
|
+
2. The component displays them with a "Connect" button (text configurable via widget config)
|
|
258
|
+
3. When the user taps "Connect", the SDK creates an invitation via the Vortex API with `targetType: internalId`
|
|
259
|
+
4. The `onInvitationCreated` callback is called after a successful invitation
|
|
260
|
+
5. The section is hidden when there are no contacts to display
|
|
261
|
+
|
|
262
|
+
**FindFriendsContact Properties:**
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
{
|
|
266
|
+
internalId: string; // Required: ID in your platform
|
|
267
|
+
name: string; // Required: Display name
|
|
268
|
+
subtitle?: string; // Optional: Secondary text (e.g., username)
|
|
269
|
+
avatarUrl?: string; // Optional: Avatar image URL
|
|
270
|
+
metadata?: Record<string, any>; // Optional: Custom metadata
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
**FindFriendsConfig Properties:**
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
{
|
|
278
|
+
contacts: FindFriendsContact[]; // Required: List of contacts to display
|
|
279
|
+
onInvitationCreated?: (contact: FindFriendsContact) => void; // Optional: Called after successful invitation
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Invitation Suggestions
|
|
284
|
+
|
|
285
|
+
The Invitation Suggestions component displays a list of suggested contacts provided by your app. Each contact has an "Invite" button and a dismiss (X) button. When the user taps "Invite", an invitation with `targetType: internalId` is created. The dismiss button removes the suggestion from the list.
|
|
286
|
+
|
|
287
|
+
**Basic Usage:**
|
|
288
|
+
|
|
289
|
+
```tsx
|
|
290
|
+
import { VortexInvite, InvitationSuggestionsConfig, InvitationSuggestionContact } from '@teamvortexsoftware/vortex-react-native';
|
|
291
|
+
|
|
292
|
+
<VortexInvite
|
|
293
|
+
componentId="your-component-id"
|
|
294
|
+
jwt={jwt}
|
|
295
|
+
invitationSuggestionsConfig={{
|
|
296
|
+
contacts: [
|
|
297
|
+
{
|
|
298
|
+
internalId: "user-123",
|
|
299
|
+
name: "Alice Johnson",
|
|
300
|
+
subtitle: "@alice",
|
|
301
|
+
avatarUrl: "https://example.com/alice-avatar.jpg"
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
internalId: "user-456",
|
|
305
|
+
name: "Bob Smith",
|
|
306
|
+
subtitle: "@bob"
|
|
307
|
+
}
|
|
308
|
+
],
|
|
309
|
+
onDismiss: (contact) => {
|
|
310
|
+
// Called when user taps the X button
|
|
311
|
+
console.log('Dismissed suggestion for', contact.name);
|
|
312
|
+
},
|
|
313
|
+
onInvitationCreated: (contact) => {
|
|
314
|
+
console.log('Invitation created for', contact.name);
|
|
315
|
+
},
|
|
316
|
+
onInvitationFailed: (contact, error) => {
|
|
317
|
+
console.error('Failed to invite', contact.name, error);
|
|
318
|
+
}
|
|
319
|
+
}}
|
|
320
|
+
/>
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
**How It Works:**
|
|
324
|
+
|
|
325
|
+
1. Your app provides a list of suggested contacts with internal IDs
|
|
326
|
+
2. The component displays them with an "Invite" button and a dismiss (X) button
|
|
327
|
+
3. When the user taps "Invite", the SDK creates an invitation via the Vortex API with `targetType: internalId`
|
|
328
|
+
4. The `onInvitationCreated` or `onInvitationFailed` callback is called based on the result
|
|
329
|
+
5. When the user taps the X button, the `onDismiss` callback is called and the contact is removed from the list
|
|
330
|
+
|
|
331
|
+
**InvitationSuggestionContact Properties:**
|
|
332
|
+
|
|
333
|
+
```typescript
|
|
334
|
+
{
|
|
335
|
+
internalId: string; // Required: ID in your platform
|
|
336
|
+
name: string; // Required: Display name
|
|
337
|
+
subtitle?: string; // Optional: Secondary text (e.g., username)
|
|
338
|
+
avatarUrl?: string; // Optional: Avatar image URL (rendered instead of initials if provided)
|
|
339
|
+
metadata?: Record<string, any>; // Optional: Custom metadata
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
**InvitationSuggestionsConfig Properties:**
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
{
|
|
347
|
+
contacts: InvitationSuggestionContact[]; // Required: List of contacts to display
|
|
348
|
+
onDismiss: (contact: InvitationSuggestionContact) => void; // Required: Called when user dismisses a suggestion
|
|
349
|
+
onInvitationCreated?: (contact: InvitationSuggestionContact) => void; // Called after successful invitation
|
|
350
|
+
onInvitationFailed?: (contact: InvitationSuggestionContact, error: Error) => void; // Called on failure
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### Search Box
|
|
355
|
+
|
|
356
|
+
The Search Box component displays a search input with a search button. When the user taps the search button, your app's `onSearch` callback is invoked to return matching contacts. The results are rendered below the search box with "Connect" buttons, identical to the Find Friends component.
|
|
357
|
+
|
|
358
|
+
**Basic Usage:**
|
|
359
|
+
|
|
360
|
+
```tsx
|
|
361
|
+
import { VortexInvite, SearchBoxConfig, SearchBoxContact } from '@teamvortexsoftware/vortex-react-native';
|
|
362
|
+
|
|
363
|
+
<VortexInvite
|
|
364
|
+
componentId="your-component-id"
|
|
365
|
+
jwt={jwt}
|
|
366
|
+
searchBoxConfig={{
|
|
367
|
+
onSearch: async (query) => {
|
|
368
|
+
// Return matching contacts from your backend or local data
|
|
369
|
+
const results = await myAPI.searchUsers(query);
|
|
370
|
+
return results.map(user => ({
|
|
371
|
+
internalId: user.id,
|
|
372
|
+
name: user.displayName,
|
|
373
|
+
subtitle: `@${user.username}`,
|
|
374
|
+
avatarUrl: user.avatarUrl
|
|
375
|
+
}));
|
|
376
|
+
},
|
|
377
|
+
onInvitationCreated: (contact) => {
|
|
378
|
+
console.log('Invitation created for', contact.name);
|
|
379
|
+
}
|
|
380
|
+
}}
|
|
381
|
+
/>
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
**How It Works:**
|
|
385
|
+
|
|
386
|
+
1. The component renders a search text field with a configurable placeholder and a search button
|
|
387
|
+
2. An optional title can be displayed above the search box (configured in the widget editor)
|
|
388
|
+
3. When the user taps the search button, your `onSearch` callback is called with the query string
|
|
389
|
+
4. Your callback returns a list of `SearchBoxContact` objects
|
|
390
|
+
5. The matching contacts are rendered below the search box with a "Connect" button next to each
|
|
391
|
+
6. If the search returns no results, a configurable "no results" message is displayed
|
|
392
|
+
7. When the user taps "Connect", the SDK creates an invitation via the Vortex API with `targetType: internalId` and the contact is removed from the list (identical to Find Friends behavior)
|
|
393
|
+
|
|
394
|
+
**SearchBoxConfig Properties:**
|
|
395
|
+
|
|
396
|
+
```typescript
|
|
397
|
+
{
|
|
398
|
+
onSearch: (query: string) => Promise<SearchBoxContact[]>; // Required: Search callback
|
|
399
|
+
onInvitationCreated?: (contact: SearchBoxContact) => void; // Optional: Called after successful invitation
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
> **Note:** The placeholder text, connect button text, no-results message, and title are all configurable from the widget editor.
|
|
404
|
+
|
|
405
|
+
### Incoming Invitations
|
|
406
|
+
|
|
407
|
+
The Incoming Invitations component displays invitations the user has received, with Accept and Delete actions.
|
|
408
|
+
|
|
409
|
+
**Basic Usage:**
|
|
410
|
+
|
|
411
|
+
```tsx
|
|
412
|
+
import { VortexInvite, IncomingInvitationsConfig } from '@teamvortexsoftware/vortex-react-native';
|
|
413
|
+
|
|
414
|
+
<VortexInvite
|
|
415
|
+
componentId="your-component-id"
|
|
416
|
+
jwt={jwt}
|
|
417
|
+
incomingInvitationsConfig={{
|
|
418
|
+
onAccept: async (invitation) => {
|
|
419
|
+
// Handle acceptance (return true to proceed with API call)
|
|
420
|
+
await myAPI.acceptInvitation(invitation.id);
|
|
421
|
+
return true;
|
|
422
|
+
},
|
|
423
|
+
onDelete: async (invitation) => {
|
|
424
|
+
// Handle deletion (return true to proceed with API call)
|
|
425
|
+
return true;
|
|
426
|
+
}
|
|
427
|
+
}}
|
|
428
|
+
/>
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
**With Internal Invitations:**
|
|
432
|
+
|
|
433
|
+
You can merge your app's invitations with Vortex API invitations:
|
|
434
|
+
|
|
435
|
+
```tsx
|
|
436
|
+
<VortexInvite
|
|
437
|
+
componentId="your-component-id"
|
|
438
|
+
jwt={jwt}
|
|
439
|
+
incomingInvitationsConfig={{
|
|
440
|
+
internalInvitations: [
|
|
441
|
+
{
|
|
442
|
+
id: "internal-1",
|
|
443
|
+
name: "Alice Johnson",
|
|
444
|
+
subtitle: "alice@example.com",
|
|
445
|
+
avatarUrl: "https://example.com/avatar.jpg"
|
|
446
|
+
}
|
|
447
|
+
],
|
|
448
|
+
onAccept: async (invitation) => {
|
|
449
|
+
if (invitation.isVortexInvitation) {
|
|
450
|
+
// Vortex invitation: return true to let SDK call the Vortex API
|
|
451
|
+
return true;
|
|
452
|
+
} else {
|
|
453
|
+
// Internal/app invitation: handle it yourself
|
|
454
|
+
await myAPI.acceptInvitation(invitation.id);
|
|
455
|
+
return true; // Return true to remove from list
|
|
456
|
+
}
|
|
457
|
+
},
|
|
458
|
+
onDelete: async (invitation) => {
|
|
459
|
+
if (invitation.isVortexInvitation) {
|
|
460
|
+
return true; // Let SDK handle the API call
|
|
461
|
+
} else {
|
|
462
|
+
await myAPI.deleteInvitation(invitation.id);
|
|
463
|
+
return true; // Return true to remove from list
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}}
|
|
467
|
+
/>
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
**Identifying Invitation Source:**
|
|
471
|
+
|
|
472
|
+
Use the `isVortexInvitation` property to determine where an invitation came from:
|
|
473
|
+
- `true`: Fetched from the Vortex API — the SDK will handle accept/delete API calls
|
|
474
|
+
- `false`: Provided by your app via `internalInvitations` — your app must handle the action
|
|
475
|
+
|
|
476
|
+
**IncomingInvitationsConfig Properties:**
|
|
477
|
+
|
|
478
|
+
```typescript
|
|
479
|
+
{
|
|
480
|
+
internalInvitations?: IncomingInvitationItem[]; // App-provided invitations (isVortexInvitation = false)
|
|
481
|
+
onAccept?: (invitation: IncomingInvitationItem) => Promise<boolean>; // Called when user accepts
|
|
482
|
+
onDelete?: (invitation: IncomingInvitationItem) => Promise<boolean>; // Called when user deletes
|
|
483
|
+
}
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
**Callback Return Values:**
|
|
487
|
+
|
|
488
|
+
| Invitation Source | Return `true` | Return `false` |
|
|
489
|
+
|-------------------|---------------|----------------|
|
|
490
|
+
| Vortex (`isVortexInvitation === true`) | SDK calls Vortex API, removes from list | Cancels the action |
|
|
491
|
+
| Internal (`isVortexInvitation === false`) | Removes from list (no API call) | Keeps in list |
|
|
492
|
+
|
|
493
|
+
### Outgoing Invitations
|
|
494
|
+
|
|
495
|
+
The Outgoing Invitations component displays invitations the user has sent, with a Cancel action.
|
|
496
|
+
|
|
497
|
+
**Basic Usage:**
|
|
498
|
+
|
|
499
|
+
```tsx
|
|
500
|
+
import { VortexInvite, OutgoingInvitationsConfig } from '@teamvortexsoftware/vortex-react-native';
|
|
501
|
+
|
|
502
|
+
<VortexInvite
|
|
503
|
+
componentId="your-component-id"
|
|
504
|
+
jwt={jwt}
|
|
505
|
+
outgoingInvitationsConfig={{
|
|
506
|
+
onCancel: async (invitation) => {
|
|
507
|
+
// Handle cancellation (return true to proceed with API call)
|
|
508
|
+
return true;
|
|
509
|
+
}
|
|
510
|
+
}}
|
|
511
|
+
/>
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
**With Internal Invitations:**
|
|
515
|
+
|
|
516
|
+
You can merge your app's outgoing invitations with Vortex API invitations:
|
|
517
|
+
|
|
518
|
+
```tsx
|
|
519
|
+
<VortexInvite
|
|
520
|
+
componentId="your-component-id"
|
|
521
|
+
jwt={jwt}
|
|
522
|
+
outgoingInvitationsConfig={{
|
|
523
|
+
internalInvitations: [
|
|
524
|
+
{
|
|
525
|
+
id: "internal-1",
|
|
526
|
+
name: "Bob Smith",
|
|
527
|
+
subtitle: "user-12345", // e.g., user ID or other identifier
|
|
528
|
+
avatarUrl: "https://example.com/avatar.jpg"
|
|
529
|
+
}
|
|
530
|
+
],
|
|
531
|
+
onCancel: async (invitation) => {
|
|
532
|
+
if (invitation.isVortexInvitation) {
|
|
533
|
+
// Vortex invitation: return true to let SDK call the Vortex API
|
|
534
|
+
return true;
|
|
535
|
+
} else {
|
|
536
|
+
// Internal/app invitation: handle it yourself
|
|
537
|
+
await myAPI.cancelInvitation(invitation.id);
|
|
538
|
+
return true; // Return true to remove from list
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}}
|
|
542
|
+
/>
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
**Identifying Invitation Source:**
|
|
546
|
+
|
|
547
|
+
Use the `isVortexInvitation` property to determine where an invitation came from:
|
|
548
|
+
- `true`: Fetched from the Vortex API — the SDK will handle cancel API calls
|
|
549
|
+
- `false`: Provided by your app via `internalInvitations` — your app must handle the action
|
|
550
|
+
|
|
551
|
+
**OutgoingInvitationsConfig Properties:**
|
|
552
|
+
|
|
553
|
+
```typescript
|
|
554
|
+
{
|
|
555
|
+
internalInvitations?: OutgoingInvitationItem[]; // App-provided invitations (isVortexInvitation = false)
|
|
556
|
+
onCancel?: (invitation: OutgoingInvitationItem) => Promise<boolean>; // Called when user cancels
|
|
557
|
+
}
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
**Callback Return Values:**
|
|
561
|
+
|
|
562
|
+
| Invitation Source | Return `true` | Return `false` |
|
|
563
|
+
|-------------------|---------------|----------------|
|
|
564
|
+
| Vortex (`isVortexInvitation === true`) | SDK calls Vortex API, removes from list | Cancels the action |
|
|
565
|
+
| Internal (`isVortexInvitation === false`) | Removes from list (no API call) | Keeps in list |
|
|
566
|
+
|
|
567
|
+
## Prefetch for Instant Rendering
|
|
568
|
+
|
|
569
|
+
The SDK supports prefetching widget configurations to eliminate loading delays when opening the invite form. This uses a **stale-while-revalidate** pattern: cached configurations are shown immediately while fresh data is fetched in the background.
|
|
570
|
+
|
|
571
|
+
### Basic Prefetch Example
|
|
572
|
+
|
|
573
|
+
```tsx
|
|
574
|
+
import { VortexInvite, usePrefetchWidgetConfiguration } from '@teamvortexsoftware/vortex-react-native';
|
|
575
|
+
import { useState, useEffect } from 'react';
|
|
576
|
+
|
|
577
|
+
function InviteModal({ onClose, groupId }) {
|
|
578
|
+
const [jwt, setJwt] = useState<string | undefined>();
|
|
579
|
+
|
|
580
|
+
// Fetch JWT on mount
|
|
581
|
+
useEffect(() => {
|
|
582
|
+
fetchJwt().then(setJwt);
|
|
583
|
+
}, []);
|
|
584
|
+
|
|
585
|
+
// Prefetch widget configuration as soon as JWT is available
|
|
586
|
+
const { widgetConfiguration } = usePrefetchWidgetConfiguration({
|
|
587
|
+
componentId: process.env.EXPO_PUBLIC_COMPONENT_ID,
|
|
588
|
+
vortexApiUrl: 'https://client-api.vortexsoftware.com',
|
|
589
|
+
jwt,
|
|
590
|
+
enabled: !!jwt, // Only prefetch when JWT is ready
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
return (
|
|
594
|
+
<VortexInvite
|
|
595
|
+
componentId={process.env.EXPO_PUBLIC_COMPONENT_ID}
|
|
596
|
+
jwt={jwt}
|
|
597
|
+
widgetConfiguration={widgetConfiguration} // Pass prefetched config
|
|
598
|
+
scope={groupId}
|
|
599
|
+
scopeType="team"
|
|
600
|
+
onClose={onClose}
|
|
601
|
+
/>
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
### How It Works
|
|
607
|
+
|
|
608
|
+
1. **Without Prefetching** (default behavior):
|
|
609
|
+
- Component mounts → Shows loading spinner
|
|
610
|
+
- Fetches configuration → Renders form
|
|
611
|
+
|
|
612
|
+
2. **With Prefetching** (optimized):
|
|
613
|
+
- Parent prefetches configuration early (when JWT becomes available)
|
|
614
|
+
- Component mounts → **Renders form immediately** (no loading spinner!)
|
|
615
|
+
- Fetches latest configuration in background
|
|
616
|
+
- Updates form if configuration changed
|
|
617
|
+
|
|
618
|
+
### Benefits
|
|
619
|
+
|
|
620
|
+
✅ **Instant rendering** - No loading spinner when modal/screen is shown
|
|
621
|
+
✅ **Always fresh** - Component still fetches latest configuration in background
|
|
622
|
+
✅ **Automatic updates** - Form updates if configuration changes on server
|
|
623
|
+
✅ **Backward compatible** - Works perfectly without prefetching (shows loading spinner as before)
|
|
624
|
+
|
|
625
|
+
## Optional Features
|
|
626
|
+
|
|
627
|
+
The SDK supports optional native features that enhance the user experience. These features require additional libraries that you can install based on your needs.
|
|
628
|
+
|
|
629
|
+
### Why Optional?
|
|
630
|
+
|
|
631
|
+
These features use native code that must be explicitly installed in your app. The SDK gracefully degrades when these libraries aren't installed:
|
|
632
|
+
|
|
633
|
+
- **Gradients** → Falls back to solid colors
|
|
634
|
+
- **Haptics** → Silently skipped
|
|
635
|
+
- **QR Codes** → Shows placeholder message
|
|
636
|
+
|
|
637
|
+
### Configuration
|
|
638
|
+
|
|
639
|
+
Specify which libraries you've installed via the unified `modules` prop on `VortexInvite`:
|
|
640
|
+
|
|
641
|
+
```tsx
|
|
642
|
+
<VortexInvite
|
|
643
|
+
componentId="your-component-id"
|
|
644
|
+
jwt={jwt}
|
|
645
|
+
// Optional feature modules (unified configuration)
|
|
646
|
+
modules={{
|
|
647
|
+
gradient: 'expo-linear-gradient',
|
|
648
|
+
haptics: 'expo-haptics',
|
|
649
|
+
qrCode: 'react-native-qrcode-svg',
|
|
650
|
+
}}
|
|
651
|
+
/>
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
### Available Features
|
|
655
|
+
|
|
656
|
+
| Feature | Property | Options | Description |
|
|
657
|
+
|---------|----------|---------|-------------|
|
|
658
|
+
| Gradients | `modules.gradient` | `'expo-linear-gradient'` or `'react-native-linear-gradient'` | Gradient backgrounds on buttons |
|
|
659
|
+
| Haptics | `modules.haptics` | `'expo-haptics'` | Haptic feedback on button presses |
|
|
660
|
+
| QR Codes | `modules.qrCode` | `'react-native-qrcode-svg'` or `'react-qr-code'` | QR code display for shareable links |
|
|
661
|
+
| Clipboard | `modules.clipboard` | `'expo-clipboard'` or `'@react-native-clipboard/clipboard'` | Enhanced clipboard operations |
|
|
662
|
+
| Sharing | `modules.sharing` | `'expo-sharing'` | Native share sheet |
|
|
663
|
+
|
|
664
|
+
### Installation by Project Type
|
|
665
|
+
|
|
666
|
+
#### Expo Projects (Recommended)
|
|
667
|
+
|
|
668
|
+
```bash
|
|
669
|
+
# Gradients
|
|
670
|
+
npx expo install expo-linear-gradient
|
|
671
|
+
|
|
672
|
+
# Haptics
|
|
673
|
+
npx expo install expo-haptics
|
|
674
|
+
|
|
675
|
+
# QR Codes (requires both packages)
|
|
676
|
+
npx expo install react-native-qrcode-svg react-native-svg
|
|
677
|
+
|
|
678
|
+
# Clipboard
|
|
679
|
+
npx expo install expo-clipboard
|
|
680
|
+
|
|
681
|
+
# Sharing
|
|
682
|
+
npx expo install expo-sharing
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
Then configure:
|
|
686
|
+
|
|
687
|
+
```tsx
|
|
688
|
+
<VortexInvite
|
|
689
|
+
componentId="your-component-id"
|
|
690
|
+
jwt={jwt}
|
|
691
|
+
modules={{
|
|
692
|
+
gradient: 'expo-linear-gradient',
|
|
693
|
+
haptics: 'expo-haptics',
|
|
694
|
+
qrCode: 'react-native-qrcode-svg',
|
|
695
|
+
clipboard: 'expo-clipboard',
|
|
696
|
+
sharing: 'expo-sharing',
|
|
697
|
+
}}
|
|
698
|
+
/>
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
#### Bare React Native Projects
|
|
702
|
+
|
|
703
|
+
```bash
|
|
704
|
+
# Gradients
|
|
705
|
+
npm install react-native-linear-gradient
|
|
706
|
+
cd ios && pod install
|
|
707
|
+
|
|
708
|
+
# QR Codes (requires both packages)
|
|
709
|
+
npm install react-native-qrcode-svg react-native-svg
|
|
710
|
+
cd ios && pod install
|
|
711
|
+
|
|
712
|
+
# Clipboard
|
|
713
|
+
npm install @react-native-clipboard/clipboard
|
|
714
|
+
cd ios && pod install
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
Then configure:
|
|
718
|
+
|
|
719
|
+
```tsx
|
|
720
|
+
<VortexInvite
|
|
721
|
+
componentId="your-component-id"
|
|
722
|
+
jwt={jwt}
|
|
723
|
+
modules={{
|
|
724
|
+
gradient: 'react-native-linear-gradient',
|
|
725
|
+
qrCode: 'react-native-qrcode-svg',
|
|
726
|
+
clipboard: '@react-native-clipboard/clipboard',
|
|
727
|
+
}}
|
|
728
|
+
/>
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
### Helpful Warnings
|
|
732
|
+
|
|
733
|
+
In development mode, the SDK will print helpful warnings when:
|
|
734
|
+
- A feature is detected in your widget configuration but no module is specified
|
|
735
|
+
- A module is specified but not installed
|
|
736
|
+
|
|
737
|
+
Example warning:
|
|
738
|
+
```
|
|
739
|
+
[Vortex] ⚠️ Gradient backgrounds detected in widget configuration, but no gradient module specified.
|
|
740
|
+
|
|
741
|
+
To enable gradients on native platforms, add the modules prop to <VortexInvite>:
|
|
742
|
+
|
|
743
|
+
For Expo projects:
|
|
744
|
+
<VortexInvite modules={{ gradient: 'expo-linear-gradient' }} ... />
|
|
745
|
+
Then run: npx expo install expo-linear-gradient
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
---
|
|
749
|
+
|
|
750
|
+
## Deferred Deep Linking
|
|
751
|
+
|
|
752
|
+
Deferred deep linking allows your app to retrieve invitation context even when a user installs the app after clicking an invitation link. When a user clicks an invitation link but doesn't have the app installed, they're redirected to the App Store or Play Store. After installation, the SDK can match the device fingerprint to retrieve the original invitation context.
|
|
753
|
+
|
|
754
|
+
### Basic Usage
|
|
755
|
+
|
|
756
|
+
Call `VortexDeferredLinks.retrieveDeferredDeepLink` when the user signs in or when the app session is restored:
|
|
757
|
+
|
|
758
|
+
```tsx
|
|
759
|
+
import { VortexDeferredLinks } from '@teamvortexsoftware/vortex-react-native';
|
|
760
|
+
|
|
761
|
+
// In your authentication manager or app initialization
|
|
762
|
+
async function onUserSignedIn(vortexJwt: string) {
|
|
763
|
+
try {
|
|
764
|
+
const result = await VortexDeferredLinks.retrieveDeferredDeepLink({ jwt: vortexJwt });
|
|
765
|
+
|
|
766
|
+
if (result.matched && result.deepLink) {
|
|
767
|
+
console.log('Found pending invitation!');
|
|
768
|
+
console.log('Invitation ID:', result.invitationId);
|
|
769
|
+
console.log('Deep Link:', result.deepLink);
|
|
770
|
+
console.log('Metadata:', result.metadata);
|
|
771
|
+
// Handle the invitation (e.g., show UI, auto-join group, navigate to specific screen)
|
|
772
|
+
}
|
|
773
|
+
} catch (error) {
|
|
774
|
+
console.error('Deferred link check failed:', error);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
### Response Types
|
|
780
|
+
|
|
781
|
+
**MatchFingerprintResponse:**
|
|
782
|
+
```typescript
|
|
783
|
+
interface MatchFingerprintResponse {
|
|
784
|
+
matched: boolean; // Whether a matching invitation was found
|
|
785
|
+
invitationId?: string; // The original invitation ID
|
|
786
|
+
deepLink?: string; // Deep link URL to handle
|
|
787
|
+
metadata?: Record<string, any>; // Additional metadata
|
|
788
|
+
}
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
### Best Practices
|
|
792
|
+
|
|
793
|
+
1. **Call on authentication**: Check for deferred deep links immediately after user sign-in or session restore
|
|
794
|
+
2. **Use Vortex JWT**: The endpoint requires a Vortex JWT token (not your app's auth token)
|
|
795
|
+
3. **Handle once**: Clear or mark the invitation as handled after processing to avoid showing it repeatedly
|
|
796
|
+
4. **Graceful degradation**: The check may fail (network issues, no match found) - handle errors gracefully
|
|
797
|
+
|
|
798
|
+
### Example Integration with Navigation
|
|
799
|
+
|
|
800
|
+
```tsx
|
|
801
|
+
import { useEffect } from 'react';
|
|
802
|
+
import { VortexDeferredLinks } from '@teamvortexsoftware/vortex-react-native';
|
|
803
|
+
|
|
804
|
+
function App() {
|
|
805
|
+
const [jwt, setJwt] = useState<string | null>(null);
|
|
806
|
+
const navigation = useNavigation();
|
|
807
|
+
|
|
808
|
+
useEffect(() => {
|
|
809
|
+
if (jwt) {
|
|
810
|
+
checkForPendingInvitations();
|
|
811
|
+
}
|
|
812
|
+
}, [jwt]);
|
|
813
|
+
|
|
814
|
+
async function checkForPendingInvitations() {
|
|
815
|
+
try {
|
|
816
|
+
const result = await VortexDeferredLinks.retrieveDeferredDeepLink({ jwt });
|
|
817
|
+
|
|
818
|
+
if (result.matched && result.deepLink) {
|
|
819
|
+
// Parse deep link and navigate
|
|
820
|
+
const params = parseDeepLink(result.deepLink);
|
|
821
|
+
navigation.navigate('TeamScreen', { teamId: params.teamId });
|
|
822
|
+
}
|
|
823
|
+
} catch (error) {
|
|
824
|
+
// Log error but don't block the user
|
|
825
|
+
console.error('Deferred deep link check failed:', error);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
return <YourApp />;
|
|
830
|
+
}
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
## Unfurl Configuration
|
|
834
|
+
|
|
835
|
+
When invitation links are shared on social platforms (iMessage, WhatsApp, Facebook, Twitter, etc.), the `UnfurlConfig` allows you to customize the Open Graph metadata that appears in the link preview card.
|
|
836
|
+
|
|
837
|
+
### Usage
|
|
838
|
+
|
|
839
|
+
```tsx
|
|
840
|
+
<VortexInvite
|
|
841
|
+
componentId="your-component-id"
|
|
842
|
+
jwt={jwt}
|
|
843
|
+
scope={teamId}
|
|
844
|
+
scopeType="team"
|
|
845
|
+
unfurlConfig={{
|
|
846
|
+
title: "Join our team!",
|
|
847
|
+
description: "You've been invited to collaborate on an exciting project",
|
|
848
|
+
image: "https://example.com/preview-image.png",
|
|
849
|
+
siteName: "My App",
|
|
850
|
+
type: "website"
|
|
851
|
+
}}
|
|
852
|
+
onInvite={(result) => {
|
|
853
|
+
console.log('Invitations sent:', result);
|
|
854
|
+
}}
|
|
855
|
+
/>
|
|
856
|
+
```
|
|
857
|
+
|
|
858
|
+
### UnfurlConfig Properties
|
|
859
|
+
|
|
860
|
+
| Property | Type | Description |
|
|
861
|
+
|----------|------|-------------|
|
|
862
|
+
| `title` | `string?` | The title shown in the link preview (`og:title`) |
|
|
863
|
+
| `description` | `string?` | The description shown in the link preview (`og:description`) |
|
|
864
|
+
| `image` | `string?` | URL to the image shown in the link preview (`og:image`). Must be a valid URL. |
|
|
865
|
+
| `siteName` | `string?` | The site name shown in the link preview (`og:site_name`) |
|
|
866
|
+
| `type` | `string?` | The Open Graph type (`og:type`). Defaults to "website" if not provided. |
|
|
867
|
+
|
|
868
|
+
### Priority Chain
|
|
869
|
+
|
|
870
|
+
The backend uses this priority for unfurl values:
|
|
871
|
+
1. **Invitation metadata** (values from `UnfurlConfig`) - highest priority
|
|
872
|
+
2. **Domain profile unfurlData** - fallback from account settings in Vortex dashboard
|
|
873
|
+
3. **Hardcoded defaults** - last resort
|
|
874
|
+
|
|
875
|
+
## Features
|
|
876
|
+
|
|
877
|
+
**Invitation Methods:**
|
|
878
|
+
- Email invitations with validation
|
|
879
|
+
- Copy shareable link to clipboard
|
|
880
|
+
- Native share sheet integration
|
|
881
|
+
- SMS sharing
|
|
882
|
+
- QR code generation
|
|
883
|
+
- WhatsApp, LINE, and other social messaging integrations
|
|
884
|
+
|
|
885
|
+
**Contact Import:**
|
|
886
|
+
- Device Contacts integration (iOS/Android)
|
|
887
|
+
- Google Contacts integration (via Google Sign-In)
|
|
888
|
+
|
|
889
|
+
**Advanced Components:**
|
|
890
|
+
- **Invite Contacts** - Display a list of contacts for SMS invitation
|
|
891
|
+
- **Find Friends** - Display contacts with "Connect" buttons for internal invitations
|
|
892
|
+
- **Invitation Suggestions** - Show suggested contacts with invite/dismiss actions
|
|
893
|
+
- **Search Box** - Search and invite users from your platform
|
|
894
|
+
- **Incoming Invitations** - Display and manage received invitations
|
|
895
|
+
- **Outgoing Invitations** - Display and manage sent invitations
|
|
896
|
+
|
|
897
|
+
**Core Capabilities:**
|
|
898
|
+
- Dynamic form rendering from server configuration
|
|
899
|
+
- JWT authentication
|
|
900
|
+
- Group/team/workspace context support
|
|
901
|
+
- Real-time loading states and error handling
|
|
902
|
+
- Customizable UI based on widget configuration
|
|
903
|
+
- Deferred deep linking via fingerprint matching
|
|
904
|
+
- Prefetching for instant rendering
|
|
905
|
+
- Open Graph unfurl metadata customization
|
|
906
|
+
|
|
907
|
+
**Optional Native Features:**
|
|
908
|
+
- Linear gradients (Expo or React Native)
|
|
909
|
+
- Haptic feedback (Expo)
|
|
910
|
+
- QR code generation (react-native-qrcode-svg or react-qr-code)
|
|
911
|
+
- Enhanced clipboard (Expo or React Native Community)
|
|
912
|
+
- Native share sheet (Expo)
|
|
913
|
+
|
|
914
|
+
---
|
|
915
|
+
|
|
916
|
+
## API Reference
|
|
917
|
+
|
|
918
|
+
### VortexInvite
|
|
919
|
+
|
|
920
|
+
The main React Native component for rendering invitation forms.
|
|
921
|
+
|
|
922
|
+
**Props:**
|
|
923
|
+
|
|
924
|
+
| Prop | Type | Required | Description |
|
|
925
|
+
|------|------|----------|-------------|
|
|
926
|
+
| `componentId` | `string` | ✅ | Unique identifier for the Vortex component |
|
|
927
|
+
| `jwt` | `string` | ✅ | JWT token for authenticated user |
|
|
928
|
+
| `scope` | `string` | ✅ | Scope identifier (e.g., team ID, workspace ID) |
|
|
929
|
+
| `scopeType` | `string` | ✅ | Type of scope (e.g., "team", "workspace", "project") |
|
|
930
|
+
| `widgetConfiguration` | `WidgetConfiguration` | ❌ | Pre-fetched widget configuration |
|
|
931
|
+
| `onInvite` | `(result: any) => void` | ❌ | Callback when invitations are successfully sent |
|
|
932
|
+
| `onError` | `(error: Error) => void` | ❌ | Callback when an error occurs |
|
|
933
|
+
| `onClose` | `() => void` | ❌ | Callback when the component is closed |
|
|
934
|
+
| `templateVariables` | `Record<string, string>` | ❌ | Variables for invitation templates |
|
|
935
|
+
| `analyticsSegmentation` | `Record<string, any>` | ❌ | Analytics tracking data |
|
|
936
|
+
| `modules` | `VortexModules` | ❌ | Unified configuration for optional native libraries (see below) |
|
|
937
|
+
| `inviteContactsConfig` | `InviteContactsConfig` | ❌ | Configuration for Invite Contacts feature |
|
|
938
|
+
| `findFriendsConfig` | `FindFriendsConfig` | ❌ | Configuration for Find Friends feature |
|
|
939
|
+
| `invitationSuggestionsConfig` | `InvitationSuggestionsConfig` | ❌ | Configuration for Invitation Suggestions feature |
|
|
940
|
+
| `searchBoxConfig` | `SearchBoxConfig` | ❌ | Configuration for Search Box feature |
|
|
941
|
+
| `incomingInvitationsConfig` | `IncomingInvitationsConfig` | ❌ | Configuration for Incoming Invitations feature |
|
|
942
|
+
| `outgoingInvitationsConfig` | `OutgoingInvitationsConfig` | ❌ | Configuration for Outgoing Invitations feature |
|
|
943
|
+
| `unfurlConfig` | `UnfurlConfig` | ❌ | Configuration for Open Graph unfurl metadata |
|
|
944
|
+
|
|
945
|
+
**`modules` Object Properties:**
|
|
946
|
+
|
|
947
|
+
| Property | Type | Description |
|
|
948
|
+
|----------|------|-------------|
|
|
949
|
+
| `gradient` | `'expo-linear-gradient'` \| `'react-native-linear-gradient'` | Gradient library for button backgrounds |
|
|
950
|
+
| `haptics` | `'expo-haptics'` | Haptics library for touch feedback |
|
|
951
|
+
| `qrCode` | `'react-native-qrcode-svg'` \| `'react-qr-code'` | QR code library |
|
|
952
|
+
| `clipboard` | `'expo-clipboard'` \| `'@react-native-clipboard/clipboard'` | Clipboard library |
|
|
953
|
+
| `sharing` | `'expo-sharing'` | Sharing library |
|
|
954
|
+
|
|
955
|
+
### usePrefetchWidgetConfiguration
|
|
956
|
+
|
|
957
|
+
Hook for prefetching widget configuration.
|
|
958
|
+
|
|
959
|
+
**Options:**
|
|
960
|
+
|
|
961
|
+
| Option | Type | Required | Description |
|
|
962
|
+
|--------|------|----------|-------------|
|
|
963
|
+
| `componentId` | `string` | ✅ | Your widget component ID |
|
|
964
|
+
| `vortexApiUrl` | `string` | ✅ | Vortex API URL |
|
|
965
|
+
| `jwt` | `string` | ❌ | JWT token (or use `user`) |
|
|
966
|
+
| `user` | `string \| object` | ❌ | User data for unsigned JWT |
|
|
967
|
+
| `enabled` | `boolean` | ❌ | Conditionally enable/disable prefetching (default: true) |
|
|
968
|
+
|
|
969
|
+
**Return Value:**
|
|
970
|
+
|
|
971
|
+
| Property | Type | Description |
|
|
972
|
+
|----------|------|-------------|
|
|
973
|
+
| `widgetConfiguration` | `WidgetConfiguration \| undefined` | The prefetched configuration |
|
|
974
|
+
| `isLoading` | `boolean` | True while fetching |
|
|
975
|
+
| `error` | `Error \| null` | Error if fetch failed |
|
|
976
|
+
|
|
977
|
+
### VortexDeferredLinks
|
|
978
|
+
|
|
979
|
+
Class for handling deferred deep linking - track invitation attribution even when the app wasn't installed at the time the invitation link was clicked.
|
|
980
|
+
|
|
981
|
+
**Methods:**
|
|
982
|
+
|
|
983
|
+
| Method | Parameters | Returns | Description |
|
|
984
|
+
|--------|------------|---------|-------------|
|
|
985
|
+
| `collectDeviceFingerprint()` | none | `DeviceFingerprint` | Collects device fingerprint data for matching |
|
|
986
|
+
| `retrieveDeferredDeepLink(options)` | `RetrieveDeferredDeepLinkOptions` | `Promise<MatchFingerprintResponse>` | Retrieves deferred deep link by matching fingerprint |
|
|
987
|
+
|
|
988
|
+
**Example:**
|
|
989
|
+
|
|
990
|
+
```tsx
|
|
991
|
+
import { VortexDeferredLinks } from '@teamvortexsoftware/vortex-react-native';
|
|
992
|
+
|
|
993
|
+
// On app first launch after install
|
|
994
|
+
async function checkDeferredDeepLink(jwt: string) {
|
|
995
|
+
const result = await VortexDeferredLinks.retrieveDeferredDeepLink({ jwt });
|
|
996
|
+
if (result.matched && result.deepLink) {
|
|
997
|
+
console.log('User came from invitation:', result.invitationId);
|
|
998
|
+
// Navigate to appropriate screen based on deepLink
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
```
|
|
1002
|
+
|
|
1003
|
+
**Types:**
|
|
1004
|
+
|
|
1005
|
+
```typescript
|
|
1006
|
+
interface DeviceFingerprint {
|
|
1007
|
+
platform: 'ios' | 'android';
|
|
1008
|
+
osVersion: string;
|
|
1009
|
+
deviceModel: string;
|
|
1010
|
+
deviceBrand: string;
|
|
1011
|
+
timezone: string;
|
|
1012
|
+
language: string;
|
|
1013
|
+
screenWidth: number;
|
|
1014
|
+
screenHeight: number;
|
|
1015
|
+
carrierName?: string;
|
|
1016
|
+
totalMemory?: number;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
interface MatchFingerprintResponse {
|
|
1020
|
+
matched: boolean;
|
|
1021
|
+
invitationId?: string;
|
|
1022
|
+
deepLink?: string;
|
|
1023
|
+
metadata?: Record<string, any>;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
interface RetrieveDeferredDeepLinkOptions {
|
|
1027
|
+
jwt: string;
|
|
1028
|
+
baseURL?: string; // Defaults to production
|
|
1029
|
+
}
|
|
9
1030
|
```
|
|
10
1031
|
|
|
1032
|
+
### Type Definitions
|
|
1033
|
+
|
|
1034
|
+
**InviteContactsConfig:**
|
|
1035
|
+
```typescript
|
|
1036
|
+
interface InviteContactsConfig {
|
|
1037
|
+
contacts: InviteContactsContact[];
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
interface InviteContactsContact {
|
|
1041
|
+
id?: string;
|
|
1042
|
+
name: string;
|
|
1043
|
+
phoneNumber: string;
|
|
1044
|
+
avatarUrl?: string;
|
|
1045
|
+
metadata?: Record<string, any>;
|
|
1046
|
+
}
|
|
1047
|
+
```
|
|
1048
|
+
|
|
1049
|
+
**FindFriendsConfig:**
|
|
1050
|
+
```typescript
|
|
1051
|
+
interface FindFriendsConfig {
|
|
1052
|
+
contacts: FindFriendsContact[];
|
|
1053
|
+
onInvitationCreated?: (contact: FindFriendsContact) => void;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
interface FindFriendsContact {
|
|
1057
|
+
internalId: string;
|
|
1058
|
+
name: string;
|
|
1059
|
+
subtitle?: string;
|
|
1060
|
+
avatarUrl?: string;
|
|
1061
|
+
metadata?: Record<string, any>;
|
|
1062
|
+
}
|
|
1063
|
+
```
|
|
1064
|
+
|
|
1065
|
+
**InvitationSuggestionsConfig:**
|
|
1066
|
+
```typescript
|
|
1067
|
+
interface InvitationSuggestionsConfig {
|
|
1068
|
+
contacts: InvitationSuggestionContact[];
|
|
1069
|
+
onDismiss: (contact: InvitationSuggestionContact) => void;
|
|
1070
|
+
onInvitationCreated?: (contact: InvitationSuggestionContact) => void;
|
|
1071
|
+
onInvitationFailed?: (contact: InvitationSuggestionContact, error: Error) => void;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
interface InvitationSuggestionContact {
|
|
1075
|
+
internalId: string;
|
|
1076
|
+
name: string;
|
|
1077
|
+
subtitle?: string;
|
|
1078
|
+
avatarUrl?: string;
|
|
1079
|
+
metadata?: Record<string, any>;
|
|
1080
|
+
}
|
|
1081
|
+
```
|
|
1082
|
+
|
|
1083
|
+
**SearchBoxConfig:**
|
|
1084
|
+
```typescript
|
|
1085
|
+
interface SearchBoxConfig {
|
|
1086
|
+
onSearch: (query: string) => Promise<SearchBoxContact[]>;
|
|
1087
|
+
onInvitationCreated?: (contact: SearchBoxContact) => void;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
interface SearchBoxContact {
|
|
1091
|
+
internalId: string;
|
|
1092
|
+
name: string;
|
|
1093
|
+
subtitle?: string;
|
|
1094
|
+
avatarUrl?: string;
|
|
1095
|
+
metadata?: Record<string, any>;
|
|
1096
|
+
}
|
|
1097
|
+
```
|
|
1098
|
+
|
|
1099
|
+
**IncomingInvitationsConfig:**
|
|
1100
|
+
```typescript
|
|
1101
|
+
interface IncomingInvitationsConfig {
|
|
1102
|
+
internalInvitations?: IncomingInvitationItem[];
|
|
1103
|
+
onAccept?: (invitation: IncomingInvitationItem) => Promise<boolean>;
|
|
1104
|
+
onDelete?: (invitation: IncomingInvitationItem) => Promise<boolean>;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
interface IncomingInvitationItem {
|
|
1108
|
+
id: string;
|
|
1109
|
+
name: string;
|
|
1110
|
+
subtitle?: string;
|
|
1111
|
+
avatarUrl?: string;
|
|
1112
|
+
isVortexInvitation: boolean;
|
|
1113
|
+
metadata?: Record<string, any>;
|
|
1114
|
+
}
|
|
1115
|
+
```
|
|
1116
|
+
|
|
1117
|
+
**OutgoingInvitationsConfig:**
|
|
1118
|
+
```typescript
|
|
1119
|
+
interface OutgoingInvitationsConfig {
|
|
1120
|
+
internalInvitations?: OutgoingInvitationItem[];
|
|
1121
|
+
onCancel?: (invitation: OutgoingInvitationItem) => Promise<boolean>;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
interface OutgoingInvitationItem {
|
|
1125
|
+
id: string;
|
|
1126
|
+
name: string;
|
|
1127
|
+
subtitle?: string;
|
|
1128
|
+
avatarUrl?: string;
|
|
1129
|
+
isVortexInvitation: boolean;
|
|
1130
|
+
metadata?: Record<string, any>;
|
|
1131
|
+
}
|
|
1132
|
+
```
|
|
1133
|
+
|
|
1134
|
+
**UnfurlConfig:**
|
|
1135
|
+
```typescript
|
|
1136
|
+
interface UnfurlConfig {
|
|
1137
|
+
title?: string;
|
|
1138
|
+
description?: string;
|
|
1139
|
+
image?: string;
|
|
1140
|
+
siteName?: string;
|
|
1141
|
+
type?: string;
|
|
1142
|
+
}
|
|
1143
|
+
```
|
|
1144
|
+
|
|
1145
|
+
## Authentication
|
|
1146
|
+
|
|
1147
|
+
Your backend should generate JWTs for authenticated users:
|
|
1148
|
+
|
|
1149
|
+
### Node.js Example
|
|
1150
|
+
|
|
1151
|
+
```typescript
|
|
1152
|
+
import { generateJwt } from '@teamvortexsoftware/vortex-node-22-sdk';
|
|
1153
|
+
|
|
1154
|
+
const jwt = generateJwt({
|
|
1155
|
+
user: {
|
|
1156
|
+
id: user.id,
|
|
1157
|
+
email: user.email,
|
|
1158
|
+
adminScopes: user.isAdmin ? ['autojoin'] : [],
|
|
1159
|
+
},
|
|
1160
|
+
apiKey: process.env.VORTEX_API_KEY,
|
|
1161
|
+
});
|
|
1162
|
+
```
|
|
1163
|
+
|
|
1164
|
+
### Express/Fastify Example
|
|
1165
|
+
|
|
1166
|
+
```typescript
|
|
1167
|
+
app.post('/api/vortex/jwt', async (req, res) => {
|
|
1168
|
+
const user = await getCurrentUser(req);
|
|
1169
|
+
|
|
1170
|
+
const jwt = generateJwt({
|
|
1171
|
+
user: {
|
|
1172
|
+
id: user.id,
|
|
1173
|
+
email: user.email,
|
|
1174
|
+
adminScopes: user.isAutojoinAdmin ? ['autojoin'] : [],
|
|
1175
|
+
},
|
|
1176
|
+
apiKey: process.env.VORTEX_API_KEY,
|
|
1177
|
+
});
|
|
1178
|
+
|
|
1179
|
+
res.json({ jwt });
|
|
1180
|
+
});
|
|
1181
|
+
```
|
|
1182
|
+
|
|
1183
|
+
## Configuration
|
|
1184
|
+
|
|
1185
|
+
The SDK automatically fetches and caches widget configuration from your Vortex backend. The configuration determines:
|
|
1186
|
+
|
|
1187
|
+
- Available invitation methods
|
|
1188
|
+
- Form fields and validation
|
|
1189
|
+
- UI theme and styling
|
|
1190
|
+
- Enabled features
|
|
1191
|
+
|
|
1192
|
+
### Environment Variables
|
|
1193
|
+
|
|
1194
|
+
Set up your environment variables in `.env`:
|
|
1195
|
+
|
|
1196
|
+
```bash
|
|
1197
|
+
EXPO_PUBLIC_COMPONENT_ID=your-component-id-here
|
|
1198
|
+
```
|
|
1199
|
+
|
|
1200
|
+
## Error Handling
|
|
1201
|
+
|
|
1202
|
+
The SDK provides error callbacks for handling failures:
|
|
1203
|
+
|
|
1204
|
+
```tsx
|
|
1205
|
+
<VortexInvite
|
|
1206
|
+
componentId="my-component-id"
|
|
1207
|
+
jwt={jwt}
|
|
1208
|
+
scope={teamId}
|
|
1209
|
+
scopeType="team"
|
|
1210
|
+
onError={(error) => {
|
|
1211
|
+
// Handle specific error types
|
|
1212
|
+
if (error.message.includes('network')) {
|
|
1213
|
+
Alert.alert('Network Error', 'Please check your connection and try again.');
|
|
1214
|
+
} else if (error.message.includes('unauthorized')) {
|
|
1215
|
+
Alert.alert('Authentication Error', 'Please log in again.');
|
|
1216
|
+
} else {
|
|
1217
|
+
Alert.alert('Error', error.message);
|
|
1218
|
+
}
|
|
1219
|
+
}}
|
|
1220
|
+
/>
|
|
1221
|
+
```
|
|
1222
|
+
|
|
1223
|
+
Common error scenarios:
|
|
1224
|
+
- **Network errors** - Connection issues or API unavailable
|
|
1225
|
+
- **Authentication errors** - Invalid or expired JWT
|
|
1226
|
+
- **Configuration errors** - Invalid component ID or missing configuration
|
|
1227
|
+
- **Validation errors** - Invalid form input (e.g., malformed email)
|
|
1228
|
+
|
|
11
1229
|
## Development
|
|
12
1230
|
|
|
13
1231
|
```bash
|
|
@@ -27,16 +1245,14 @@ pnpm run lint
|
|
|
27
1245
|
pnpm run dev
|
|
28
1246
|
```
|
|
29
1247
|
|
|
30
|
-
##
|
|
1248
|
+
## License
|
|
31
1249
|
|
|
32
|
-
|
|
33
|
-
import { VortexInvite } from '@teamvortexsoftware/vortex-react-native';
|
|
1250
|
+
Apache License 2.0 - see [LICENSE](LICENSE) file for details.
|
|
34
1251
|
|
|
35
|
-
|
|
36
|
-
```
|
|
1252
|
+
## Support
|
|
37
1253
|
|
|
38
|
-
|
|
1254
|
+
For questions or issues:
|
|
39
1255
|
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
1256
|
+
- 📚 Documentation: https://docs.vortexsoftware.com
|
|
1257
|
+
- 📧 Email: support@vortexsoftware.com
|
|
1258
|
+
- 🐛 GitHub Issues: https://github.com/teamvortexsoftware/vortex/issues
|