@adminide-stack/yantra-mobile 12.0.68 → 12.0.69-alpha.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/lib/compute.js +8 -3
- package/lib/compute.js.map +1 -1
- package/lib/features/apps/mobileAppCatalog.js +9 -0
- package/lib/features/apps/mobileAppCatalog.js.map +1 -1
- package/lib/features/messaging/RegistrationScreen.js +358 -0
- package/lib/features/messaging/RegistrationScreen.js.map +1 -0
- package/lib/features/messaging/chatOperations.js +20 -0
- package/lib/features/messaging/chatOperations.js.map +1 -0
- package/lib/features/messaging/chatRegistrationStorage.js +1 -0
- package/lib/features/messaging/chatRegistrationStorage.js.map +1 -0
- package/lib/features/messaging/phoneNumbers.js +102 -0
- package/lib/features/messaging/phoneNumbers.js.map +1 -0
- package/lib/features/messaging/useChatViewer.js +16 -20
- package/lib/features/messaging/useChatViewer.js.map +1 -1
- package/lib/features/messaging/usePhoneVerification.js +147 -0
- package/lib/features/messaging/usePhoneVerification.js.map +1 -0
- package/lib/features/messaging/useYantraChatMembership.js +75 -0
- package/lib/features/messaging/useYantraChatMembership.js.map +1 -0
- package/lib/features/messaging/yantraChatOrg.js +17 -0
- package/lib/features/messaging/yantraChatOrg.js.map +1 -0
- package/lib/hooks/usePrerequisiteIds.js +5 -6
- package/lib/hooks/usePrerequisiteIds.js.map +1 -1
- package/lib/routes.json +8 -3
- package/lib/screens/ChatNative/index.js +72 -20
- package/lib/screens/ChatNative/index.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import {jsxs,jsx,Fragment}from'react/jsx-runtime';import React from'react';import {StyleSheet,Platform,View,TouchableOpacity,Text,ScrollView,ActivityIndicator,TextInput,Modal}from'react-native';import {SafeAreaView}from'react-native-safe-area-context';import {Ionicons}from'@expo/vector-icons';import {usePhoneVerification,CHAT_REGISTRATION_TEST_OTP}from'./usePhoneVerification.js';function canShowDevTestOtpUi() {
|
|
2
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) return true;
|
|
3
|
+
return process.env.EXPO_PUBLIC_CHAT_ALLOW_TEST_OTP === "true";
|
|
4
|
+
}
|
|
5
|
+
const COUNTRIES = [{
|
|
6
|
+
code: "US",
|
|
7
|
+
name: "United States",
|
|
8
|
+
callingCode: "+1"
|
|
9
|
+
}, {
|
|
10
|
+
code: "IN",
|
|
11
|
+
name: "India",
|
|
12
|
+
callingCode: "+91"
|
|
13
|
+
}, {
|
|
14
|
+
code: "GB",
|
|
15
|
+
name: "United Kingdom",
|
|
16
|
+
callingCode: "+44"
|
|
17
|
+
}, {
|
|
18
|
+
code: "CA",
|
|
19
|
+
name: "Canada",
|
|
20
|
+
callingCode: "+1"
|
|
21
|
+
}, {
|
|
22
|
+
code: "AU",
|
|
23
|
+
name: "Australia",
|
|
24
|
+
callingCode: "+61"
|
|
25
|
+
}, {
|
|
26
|
+
code: "FR",
|
|
27
|
+
name: "France",
|
|
28
|
+
callingCode: "+33"
|
|
29
|
+
}, {
|
|
30
|
+
code: "DE",
|
|
31
|
+
name: "Germany",
|
|
32
|
+
callingCode: "+49"
|
|
33
|
+
}, {
|
|
34
|
+
code: "ES",
|
|
35
|
+
name: "Spain",
|
|
36
|
+
callingCode: "+34"
|
|
37
|
+
}, {
|
|
38
|
+
code: "IT",
|
|
39
|
+
name: "Italy",
|
|
40
|
+
callingCode: "+39"
|
|
41
|
+
}, {
|
|
42
|
+
code: "BR",
|
|
43
|
+
name: "Brazil",
|
|
44
|
+
callingCode: "+55"
|
|
45
|
+
}, {
|
|
46
|
+
code: "MX",
|
|
47
|
+
name: "Mexico",
|
|
48
|
+
callingCode: "+52"
|
|
49
|
+
}, {
|
|
50
|
+
code: "SG",
|
|
51
|
+
name: "Singapore",
|
|
52
|
+
callingCode: "+65"
|
|
53
|
+
}, {
|
|
54
|
+
code: "JP",
|
|
55
|
+
name: "Japan",
|
|
56
|
+
callingCode: "+81"
|
|
57
|
+
}, {
|
|
58
|
+
code: "CN",
|
|
59
|
+
name: "China",
|
|
60
|
+
callingCode: "+86"
|
|
61
|
+
}];
|
|
62
|
+
function PhoneRow({
|
|
63
|
+
countryCode,
|
|
64
|
+
callingCode,
|
|
65
|
+
phone,
|
|
66
|
+
onCountryPress,
|
|
67
|
+
onChangePhone,
|
|
68
|
+
disabled,
|
|
69
|
+
theme
|
|
70
|
+
}) {
|
|
71
|
+
var _a;
|
|
72
|
+
return /* @__PURE__ */ jsxs(View, { style: [styles.phoneRow, {
|
|
73
|
+
flexDirection: "row",
|
|
74
|
+
alignItems: "center",
|
|
75
|
+
borderColor: theme.colors.border,
|
|
76
|
+
backgroundColor: (_a = theme.colors.card) != null ? _a : "#fff"
|
|
77
|
+
}], children: [
|
|
78
|
+
/* @__PURE__ */ jsxs(TouchableOpacity, { onPress: onCountryPress, disabled, style: [styles.countryChip, {
|
|
79
|
+
flexDirection: "row",
|
|
80
|
+
alignItems: "center"
|
|
81
|
+
}], accessibilityRole: "button", accessibilityLabel: "Select country code", children: [
|
|
82
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.callingCode, {
|
|
83
|
+
color: theme.colors.text
|
|
84
|
+
}], children: callingCode }),
|
|
85
|
+
/* @__PURE__ */ jsx(Ionicons, { name: "chevron-down", size: 18, color: theme.colors.textSecondary })
|
|
86
|
+
] }),
|
|
87
|
+
/* @__PURE__ */ jsx(View, { style: [styles.phoneDivider, {
|
|
88
|
+
backgroundColor: theme.colors.border
|
|
89
|
+
}] }),
|
|
90
|
+
/* @__PURE__ */ jsx(TextInput, { style: [styles.phoneInput, {
|
|
91
|
+
color: theme.colors.text
|
|
92
|
+
}], placeholder: "", placeholderTextColor: theme.colors.placeholder, keyboardType: "phone-pad", textContentType: "telephoneNumber", value: phone, onChangeText: onChangePhone, editable: !disabled, maxLength: 15, testID: "chat-registration-phone" })
|
|
93
|
+
] });
|
|
94
|
+
}
|
|
95
|
+
function RegistrationScreen({
|
|
96
|
+
onRegistrationComplete,
|
|
97
|
+
theme,
|
|
98
|
+
testID
|
|
99
|
+
}) {
|
|
100
|
+
var _a;
|
|
101
|
+
const phoneVerification = usePhoneVerification("US");
|
|
102
|
+
const selectedCountry = COUNTRIES.find((c) => c.code === phoneVerification.countryCode) || COUNTRIES[0];
|
|
103
|
+
const [countryModalOpen, setCountryModalOpen] = React.useState(false);
|
|
104
|
+
const accent = theme.colors.primary;
|
|
105
|
+
const otpDigits = phoneVerification.otp.replace(/\D/g, "");
|
|
106
|
+
const usingTestOtp = phoneVerification.testOtpMode || canShowDevTestOtpUi() && otpDigits === CHAT_REGISTRATION_TEST_OTP;
|
|
107
|
+
React.useEffect(() => {
|
|
108
|
+
if (phoneVerification.stage === "verified" && phoneVerification.verifiedPhone) {
|
|
109
|
+
void Promise.resolve(onRegistrationComplete(phoneVerification.verifiedPhone));
|
|
110
|
+
}
|
|
111
|
+
}, [phoneVerification.stage, phoneVerification.verifiedPhone, onRegistrationComplete]);
|
|
112
|
+
const handleBack = React.useCallback(() => {
|
|
113
|
+
if (phoneVerification.stage === "otp") {
|
|
114
|
+
phoneVerification.resetToInput();
|
|
115
|
+
}
|
|
116
|
+
}, [phoneVerification]);
|
|
117
|
+
const handleSendOtp = React.useCallback(async () => {
|
|
118
|
+
await phoneVerification.sendOtp();
|
|
119
|
+
}, [phoneVerification]);
|
|
120
|
+
const handleVerifyOtp = React.useCallback(async () => {
|
|
121
|
+
await phoneVerification.verifyOtp();
|
|
122
|
+
}, [phoneVerification]);
|
|
123
|
+
const handleResend = React.useCallback(async () => {
|
|
124
|
+
await phoneVerification.sendOtp();
|
|
125
|
+
}, [phoneVerification]);
|
|
126
|
+
return /* @__PURE__ */ jsxs(SafeAreaView, { style: [styles.container, {
|
|
127
|
+
backgroundColor: theme.colors.background
|
|
128
|
+
}], edges: ["top", "left", "right"], testID, children: [
|
|
129
|
+
/* @__PURE__ */ jsxs(View, { style: [styles.header, {
|
|
130
|
+
flexDirection: "row",
|
|
131
|
+
alignItems: "center"
|
|
132
|
+
}], children: [
|
|
133
|
+
phoneVerification.stage === "otp" ? /* @__PURE__ */ jsx(TouchableOpacity, { onPress: handleBack, hitSlop: {
|
|
134
|
+
top: 10,
|
|
135
|
+
bottom: 10,
|
|
136
|
+
left: 10,
|
|
137
|
+
right: 10
|
|
138
|
+
}, children: /* @__PURE__ */ jsx(Ionicons, { name: "chevron-back", size: 28, color: theme.colors.text }) }) : /* @__PURE__ */ jsx(View, { style: {
|
|
139
|
+
width: 28
|
|
140
|
+
} }),
|
|
141
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.title, {
|
|
142
|
+
color: theme.colors.text,
|
|
143
|
+
flex: 1,
|
|
144
|
+
textAlign: "center"
|
|
145
|
+
}], children: "Yantra Chat" }),
|
|
146
|
+
/* @__PURE__ */ jsx(View, { style: {
|
|
147
|
+
width: 28
|
|
148
|
+
} })
|
|
149
|
+
] }),
|
|
150
|
+
/* @__PURE__ */ jsx(ScrollView, { contentContainerStyle: styles.content, showsVerticalScrollIndicator: false, keyboardShouldPersistTaps: "handled", children: phoneVerification.stage === "input" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
151
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.subtitle, {
|
|
152
|
+
color: theme.colors.text
|
|
153
|
+
}], children: "Register to Chat" }),
|
|
154
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.description, {
|
|
155
|
+
color: theme.colors.textSecondary
|
|
156
|
+
}], children: "Enter your mobile number to get started. You'll receive an OTP to verify." }),
|
|
157
|
+
/* @__PURE__ */ jsxs(View, { style: styles.form, children: [
|
|
158
|
+
/* @__PURE__ */ jsx(PhoneRow, { countryCode: phoneVerification.countryCode, callingCode: selectedCountry.callingCode, phone: phoneVerification.inputPhone, onCountryPress: () => setCountryModalOpen(true), onChangePhone: phoneVerification.setInputPhone, disabled: phoneVerification.loading, theme }),
|
|
159
|
+
phoneVerification.error ? /* @__PURE__ */ jsxs(View, { style: [styles.errorBox, {
|
|
160
|
+
backgroundColor: theme.colors.error + "10"
|
|
161
|
+
}], children: [
|
|
162
|
+
/* @__PURE__ */ jsx(Ionicons, { name: "alert-circle", size: 16, color: theme.colors.error }),
|
|
163
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.errorText, {
|
|
164
|
+
color: theme.colors.error
|
|
165
|
+
}], children: phoneVerification.error })
|
|
166
|
+
] }) : null,
|
|
167
|
+
/* @__PURE__ */ jsx(TouchableOpacity, { onPress: phoneVerification.continueWithTestOtp, children: /* @__PURE__ */ jsx(Text, { style: [styles.linkText, {
|
|
168
|
+
color: accent
|
|
169
|
+
}], children: "Continue with test OTP" }) }),
|
|
170
|
+
/* @__PURE__ */ jsx(TouchableOpacity, { onPress: handleSendOtp, disabled: phoneVerification.loading || !phoneVerification.inputPhone.trim(), style: [styles.button, {
|
|
171
|
+
backgroundColor: phoneVerification.inputPhone.trim() ? accent : theme.colors.border
|
|
172
|
+
}], children: phoneVerification.loading ? /* @__PURE__ */ jsx(ActivityIndicator, { color: "#fff", size: "small" }) : /* @__PURE__ */ jsx(Text, { style: styles.buttonText, children: "Send OTP" }) })
|
|
173
|
+
] })
|
|
174
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
175
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.subtitle, {
|
|
176
|
+
color: theme.colors.text
|
|
177
|
+
}], children: "Verify your number" }),
|
|
178
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.description, {
|
|
179
|
+
color: theme.colors.textSecondary
|
|
180
|
+
}], children: usingTestOtp ? `Test mode: use code ${CHAT_REGISTRATION_TEST_OTP} and tap Continue.` : `Enter the 6-digit code sent to ${selectedCountry.callingCode} ${phoneVerification.inputPhone}` }),
|
|
181
|
+
/* @__PURE__ */ jsxs(View, { style: styles.form, children: [
|
|
182
|
+
/* @__PURE__ */ jsx(View, { style: [styles.otpInputContainer, {
|
|
183
|
+
flexDirection: "row",
|
|
184
|
+
alignItems: "center",
|
|
185
|
+
borderColor: theme.colors.border,
|
|
186
|
+
backgroundColor: (_a = theme.colors.card) != null ? _a : "#fff"
|
|
187
|
+
}], children: /* @__PURE__ */ jsx(TextInput, { style: [styles.otpInput, {
|
|
188
|
+
color: theme.colors.text,
|
|
189
|
+
flex: 1
|
|
190
|
+
}], placeholder: "", keyboardType: "number-pad", value: phoneVerification.otp, onChangeText: phoneVerification.setOtp, editable: !phoneVerification.loading, maxLength: 6, testID: "chat-registration-otp" }) }),
|
|
191
|
+
phoneVerification.error ? /* @__PURE__ */ jsxs(View, { style: [styles.errorBox, {
|
|
192
|
+
backgroundColor: theme.colors.error + "10"
|
|
193
|
+
}], children: [
|
|
194
|
+
/* @__PURE__ */ jsx(Ionicons, { name: "alert-circle", size: 16, color: theme.colors.error }),
|
|
195
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.errorText, {
|
|
196
|
+
color: theme.colors.error
|
|
197
|
+
}], children: phoneVerification.error })
|
|
198
|
+
] }) : null,
|
|
199
|
+
/* @__PURE__ */ jsx(TouchableOpacity, { onPress: handleVerifyOtp, disabled: phoneVerification.loading || otpDigits.length < 6, style: [styles.button, {
|
|
200
|
+
backgroundColor: otpDigits.length >= 6 ? accent : theme.colors.border
|
|
201
|
+
}], children: phoneVerification.loading ? /* @__PURE__ */ jsx(ActivityIndicator, { color: "#fff", size: "small" }) : /* @__PURE__ */ jsx(Text, { style: styles.buttonText, children: usingTestOtp ? "Continue" : "Verify & Register" }) }),
|
|
202
|
+
!usingTestOtp ? /* @__PURE__ */ jsx(TouchableOpacity, { onPress: handleResend, disabled: phoneVerification.loading, children: /* @__PURE__ */ jsx(Text, { style: [styles.linkText, {
|
|
203
|
+
color: accent
|
|
204
|
+
}], children: "Didn't get the code? Resend" }) }) : null
|
|
205
|
+
] })
|
|
206
|
+
] }) }),
|
|
207
|
+
/* @__PURE__ */ jsx(Modal, { visible: countryModalOpen, animationType: "slide", presentationStyle: "pageSheet", children: /* @__PURE__ */ jsxs(SafeAreaView, { style: [styles.modalFill, {
|
|
208
|
+
backgroundColor: theme.colors.background
|
|
209
|
+
}], children: [
|
|
210
|
+
/* @__PURE__ */ jsxs(View, { style: [styles.modalHeader, {
|
|
211
|
+
flexDirection: "row",
|
|
212
|
+
alignItems: "center"
|
|
213
|
+
}], children: [
|
|
214
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.modalTitle, {
|
|
215
|
+
color: theme.colors.text
|
|
216
|
+
}], children: "Country code" }),
|
|
217
|
+
/* @__PURE__ */ jsx(TouchableOpacity, { onPress: () => setCountryModalOpen(false), children: /* @__PURE__ */ jsx(Ionicons, { name: "close", size: 26, color: theme.colors.text }) })
|
|
218
|
+
] }),
|
|
219
|
+
/* @__PURE__ */ jsx(ScrollView, { children: COUNTRIES.map((country) => /* @__PURE__ */ jsxs(TouchableOpacity, { onPress: () => {
|
|
220
|
+
phoneVerification.setCountryCode(country.code);
|
|
221
|
+
setCountryModalOpen(false);
|
|
222
|
+
}, style: [styles.countryListItem, phoneVerification.countryCode === country.code && {
|
|
223
|
+
backgroundColor: accent + "18"
|
|
224
|
+
}], children: [
|
|
225
|
+
/* @__PURE__ */ jsxs(Text, { style: [styles.countryListText, {
|
|
226
|
+
color: theme.colors.text
|
|
227
|
+
}], children: [
|
|
228
|
+
country.name,
|
|
229
|
+
" (",
|
|
230
|
+
country.callingCode,
|
|
231
|
+
")"
|
|
232
|
+
] }),
|
|
233
|
+
phoneVerification.countryCode === country.code ? /* @__PURE__ */ jsx(Ionicons, { name: "checkmark", size: 20, color: accent }) : null
|
|
234
|
+
] }, country.code)) })
|
|
235
|
+
] }) })
|
|
236
|
+
] });
|
|
237
|
+
}
|
|
238
|
+
const styles = StyleSheet.create({
|
|
239
|
+
container: {
|
|
240
|
+
flex: 1
|
|
241
|
+
},
|
|
242
|
+
header: {
|
|
243
|
+
justifyContent: "space-between",
|
|
244
|
+
paddingHorizontal: 16,
|
|
245
|
+
paddingVertical: 8
|
|
246
|
+
},
|
|
247
|
+
title: {
|
|
248
|
+
fontSize: 17,
|
|
249
|
+
fontWeight: "600"
|
|
250
|
+
},
|
|
251
|
+
content: {
|
|
252
|
+
padding: 20,
|
|
253
|
+
paddingBottom: 40
|
|
254
|
+
},
|
|
255
|
+
subtitle: {
|
|
256
|
+
fontSize: 22,
|
|
257
|
+
fontWeight: "700",
|
|
258
|
+
marginBottom: 8
|
|
259
|
+
},
|
|
260
|
+
description: {
|
|
261
|
+
fontSize: 14,
|
|
262
|
+
marginBottom: 24,
|
|
263
|
+
lineHeight: 20
|
|
264
|
+
},
|
|
265
|
+
form: {
|
|
266
|
+
gap: 14
|
|
267
|
+
},
|
|
268
|
+
phoneRow: {
|
|
269
|
+
borderWidth: 1,
|
|
270
|
+
borderRadius: 10,
|
|
271
|
+
minHeight: 48,
|
|
272
|
+
overflow: "hidden"
|
|
273
|
+
},
|
|
274
|
+
countryChip: {
|
|
275
|
+
paddingHorizontal: 12,
|
|
276
|
+
paddingVertical: 12
|
|
277
|
+
},
|
|
278
|
+
callingCode: {
|
|
279
|
+
fontSize: 16,
|
|
280
|
+
fontWeight: "600",
|
|
281
|
+
marginRight: 4
|
|
282
|
+
},
|
|
283
|
+
phoneDivider: {
|
|
284
|
+
width: StyleSheet.hairlineWidth,
|
|
285
|
+
alignSelf: "stretch"
|
|
286
|
+
},
|
|
287
|
+
phoneInput: {
|
|
288
|
+
flex: 1,
|
|
289
|
+
paddingHorizontal: 12,
|
|
290
|
+
paddingVertical: Platform.OS === "ios" ? 12 : 10,
|
|
291
|
+
fontSize: 16
|
|
292
|
+
},
|
|
293
|
+
otpInputContainer: {
|
|
294
|
+
borderWidth: 1,
|
|
295
|
+
borderRadius: 10,
|
|
296
|
+
paddingHorizontal: 16
|
|
297
|
+
},
|
|
298
|
+
otpInput: {
|
|
299
|
+
paddingVertical: 14,
|
|
300
|
+
fontSize: 22,
|
|
301
|
+
fontWeight: "600",
|
|
302
|
+
letterSpacing: 6,
|
|
303
|
+
textAlign: "center"
|
|
304
|
+
},
|
|
305
|
+
button: {
|
|
306
|
+
borderRadius: 10,
|
|
307
|
+
paddingVertical: 14,
|
|
308
|
+
alignItems: "center",
|
|
309
|
+
justifyContent: "center",
|
|
310
|
+
marginTop: 4
|
|
311
|
+
},
|
|
312
|
+
buttonText: {
|
|
313
|
+
color: "#fff",
|
|
314
|
+
fontSize: 16,
|
|
315
|
+
fontWeight: "600"
|
|
316
|
+
},
|
|
317
|
+
errorBox: {
|
|
318
|
+
flexDirection: "row",
|
|
319
|
+
alignItems: "center",
|
|
320
|
+
borderRadius: 8,
|
|
321
|
+
paddingHorizontal: 12,
|
|
322
|
+
paddingVertical: 10,
|
|
323
|
+
gap: 8
|
|
324
|
+
},
|
|
325
|
+
errorText: {
|
|
326
|
+
fontSize: 13,
|
|
327
|
+
flex: 1
|
|
328
|
+
},
|
|
329
|
+
linkText: {
|
|
330
|
+
fontSize: 14,
|
|
331
|
+
textAlign: "center",
|
|
332
|
+
fontWeight: "500"
|
|
333
|
+
},
|
|
334
|
+
modalFill: {
|
|
335
|
+
flex: 1
|
|
336
|
+
},
|
|
337
|
+
modalHeader: {
|
|
338
|
+
justifyContent: "space-between",
|
|
339
|
+
paddingHorizontal: 16,
|
|
340
|
+
paddingVertical: 12
|
|
341
|
+
},
|
|
342
|
+
modalTitle: {
|
|
343
|
+
fontSize: 18,
|
|
344
|
+
fontWeight: "700"
|
|
345
|
+
},
|
|
346
|
+
countryListItem: {
|
|
347
|
+
flexDirection: "row",
|
|
348
|
+
alignItems: "center",
|
|
349
|
+
justifyContent: "space-between",
|
|
350
|
+
paddingHorizontal: 16,
|
|
351
|
+
paddingVertical: 14,
|
|
352
|
+
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
353
|
+
borderBottomColor: "rgba(0,0,0,0.06)"
|
|
354
|
+
},
|
|
355
|
+
countryListText: {
|
|
356
|
+
fontSize: 16
|
|
357
|
+
}
|
|
358
|
+
});export{RegistrationScreen};//# sourceMappingURL=RegistrationScreen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RegistrationScreen.js","sources":["../../../src/features/messaging/RegistrationScreen.tsx"],"sourcesContent":["/**\n * Registration screen for Yantra Chat.\n *\n * First-time users are prompted to register with their phone number.\n * The flow:\n * 1. User enters mobile number (country code in the same row)\n * 2. OTP is sent to their number\n * 3. User enters the OTP code to verify\n * 4. Phone number becomes their member ID in the Yantra Chat organization\n *\n * Follows the design from issue #1312.\n */\nimport React from 'react';\nimport {\n StyleSheet,\n View,\n Text,\n TouchableOpacity,\n TextInput,\n ActivityIndicator,\n ScrollView,\n Modal,\n Platform,\n} from 'react-native';\nimport { SafeAreaView } from 'react-native-safe-area-context';\nimport { Ionicons } from '@expo/vector-icons';\nimport { CHAT_REGISTRATION_TEST_OTP, usePhoneVerification } from './usePhoneVerification';\n\nfunction canShowDevTestOtpUi(): boolean {\n if (typeof __DEV__ !== 'undefined' && __DEV__) return true;\n return process.env.EXPO_PUBLIC_CHAT_ALLOW_TEST_OTP === 'true';\n}\n\ninterface RegistrationScreenProps {\n /** Called when registration completes successfully. */\n onRegistrationComplete: (phoneNumber: string) => void;\n /** Theme configuration. */\n theme: { colors: Record<string, string>; fonts: Record<string, Record<string, string>> };\n testID?: string;\n}\n\ninterface CountryOption {\n code: string;\n name: string;\n callingCode: string;\n}\n\nconst COUNTRIES: CountryOption[] = [\n { code: 'US', name: 'United States', callingCode: '+1' },\n { code: 'IN', name: 'India', callingCode: '+91' },\n { code: 'GB', name: 'United Kingdom', callingCode: '+44' },\n { code: 'CA', name: 'Canada', callingCode: '+1' },\n { code: 'AU', name: 'Australia', callingCode: '+61' },\n { code: 'FR', name: 'France', callingCode: '+33' },\n { code: 'DE', name: 'Germany', callingCode: '+49' },\n { code: 'ES', name: 'Spain', callingCode: '+34' },\n { code: 'IT', name: 'Italy', callingCode: '+39' },\n { code: 'BR', name: 'Brazil', callingCode: '+55' },\n { code: 'MX', name: 'Mexico', callingCode: '+52' },\n { code: 'SG', name: 'Singapore', callingCode: '+65' },\n { code: 'JP', name: 'Japan', callingCode: '+81' },\n { code: 'CN', name: 'China', callingCode: '+86' },\n];\n\nfunction PhoneRow({\n countryCode,\n callingCode,\n phone,\n onCountryPress,\n onChangePhone,\n disabled,\n theme,\n}: {\n countryCode: string;\n callingCode: string;\n phone: string;\n onCountryPress: () => void;\n onChangePhone: (v: string) => void;\n disabled: boolean;\n theme: RegistrationScreenProps['theme'];\n}) {\n return (\n <View\n style={[\n styles.phoneRow,\n {\n flexDirection: 'row',\n alignItems: 'center',\n borderColor: theme.colors.border,\n backgroundColor: theme.colors.card ?? '#fff',\n },\n ]}\n >\n <TouchableOpacity\n onPress={onCountryPress}\n disabled={disabled}\n style={[styles.countryChip, { flexDirection: 'row', alignItems: 'center' }]}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Select country code\"\n >\n <Text style={[styles.callingCode, { color: theme.colors.text }]}>{callingCode}</Text>\n <Ionicons name=\"chevron-down\" size={18} color={theme.colors.textSecondary} />\n </TouchableOpacity>\n <View style={[styles.phoneDivider, { backgroundColor: theme.colors.border }]} />\n <TextInput\n style={[styles.phoneInput, { color: theme.colors.text }]}\n placeholder=\"\"\n placeholderTextColor={theme.colors.placeholder}\n keyboardType=\"phone-pad\"\n textContentType=\"telephoneNumber\"\n value={phone}\n onChangeText={onChangePhone}\n editable={!disabled}\n maxLength={15}\n testID=\"chat-registration-phone\"\n />\n </View>\n );\n}\n\nexport function RegistrationScreen({ onRegistrationComplete, theme, testID }: RegistrationScreenProps) {\n const phoneVerification = usePhoneVerification('US');\n const selectedCountry = COUNTRIES.find((c) => c.code === phoneVerification.countryCode) || COUNTRIES[0];\n const [countryModalOpen, setCountryModalOpen] = React.useState(false);\n const accent = theme.colors.primary;\n const otpDigits = phoneVerification.otp.replace(/\\D/g, '');\n const usingTestOtp =\n phoneVerification.testOtpMode || (canShowDevTestOtpUi() && otpDigits === CHAT_REGISTRATION_TEST_OTP);\n\n React.useEffect(() => {\n if (phoneVerification.stage === 'verified' && phoneVerification.verifiedPhone) {\n void Promise.resolve(onRegistrationComplete(phoneVerification.verifiedPhone));\n }\n }, [phoneVerification.stage, phoneVerification.verifiedPhone, onRegistrationComplete]);\n\n const handleBack = React.useCallback(() => {\n if (phoneVerification.stage === 'otp') {\n phoneVerification.resetToInput();\n }\n }, [phoneVerification]);\n\n const handleSendOtp = React.useCallback(async () => {\n await phoneVerification.sendOtp();\n }, [phoneVerification]);\n\n const handleVerifyOtp = React.useCallback(async () => {\n await phoneVerification.verifyOtp();\n }, [phoneVerification]);\n\n const handleResend = React.useCallback(async () => {\n await phoneVerification.sendOtp();\n }, [phoneVerification]);\n\n return (\n <SafeAreaView\n style={[styles.container, { backgroundColor: theme.colors.background }]}\n edges={['top', 'left', 'right']}\n testID={testID}\n >\n <View style={[styles.header, { flexDirection: 'row', alignItems: 'center' }]}>\n {phoneVerification.stage === 'otp' ? (\n <TouchableOpacity onPress={handleBack} hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}>\n <Ionicons name=\"chevron-back\" size={28} color={theme.colors.text} />\n </TouchableOpacity>\n ) : (\n <View style={{ width: 28 }} />\n )}\n <Text style={[styles.title, { color: theme.colors.text, flex: 1, textAlign: 'center' }]}>\n Yantra Chat\n </Text>\n <View style={{ width: 28 }} />\n </View>\n\n <ScrollView\n contentContainerStyle={styles.content}\n showsVerticalScrollIndicator={false}\n keyboardShouldPersistTaps=\"handled\"\n >\n {phoneVerification.stage === 'input' ? (\n <>\n <Text style={[styles.subtitle, { color: theme.colors.text }]}>Register to Chat</Text>\n <Text style={[styles.description, { color: theme.colors.textSecondary }]}>\n Enter your mobile number to get started. You'll receive an OTP to verify.\n </Text>\n\n <View style={styles.form}>\n <PhoneRow\n countryCode={phoneVerification.countryCode}\n callingCode={selectedCountry.callingCode}\n phone={phoneVerification.inputPhone}\n onCountryPress={() => setCountryModalOpen(true)}\n onChangePhone={phoneVerification.setInputPhone}\n disabled={phoneVerification.loading}\n theme={theme}\n />\n\n {phoneVerification.error ? (\n <View style={[styles.errorBox, { backgroundColor: theme.colors.error + '10' }]}>\n <Ionicons name=\"alert-circle\" size={16} color={theme.colors.error} />\n <Text style={[styles.errorText, { color: theme.colors.error }]}>\n {phoneVerification.error}\n </Text>\n </View>\n ) : null}\n\n <TouchableOpacity onPress={phoneVerification.continueWithTestOtp}>\n <Text style={[styles.linkText, { color: accent }]}>Continue with test OTP</Text>\n </TouchableOpacity>\n\n <TouchableOpacity\n onPress={handleSendOtp}\n disabled={phoneVerification.loading || !phoneVerification.inputPhone.trim()}\n style={[\n styles.button,\n {\n backgroundColor: phoneVerification.inputPhone.trim()\n ? accent\n : theme.colors.border,\n },\n ]}\n >\n {phoneVerification.loading ? (\n <ActivityIndicator color=\"#fff\" size=\"small\" />\n ) : (\n <Text style={styles.buttonText}>Send OTP</Text>\n )}\n </TouchableOpacity>\n </View>\n </>\n ) : (\n <>\n <Text style={[styles.subtitle, { color: theme.colors.text }]}>Verify your number</Text>\n <Text style={[styles.description, { color: theme.colors.textSecondary }]}>\n {usingTestOtp\n ? `Test mode: use code ${CHAT_REGISTRATION_TEST_OTP} and tap Continue.`\n : `Enter the 6-digit code sent to ${selectedCountry.callingCode} ${phoneVerification.inputPhone}`}\n </Text>\n\n <View style={styles.form}>\n <View\n style={[\n styles.otpInputContainer,\n {\n flexDirection: 'row',\n alignItems: 'center',\n borderColor: theme.colors.border,\n backgroundColor: theme.colors.card ?? '#fff',\n },\n ]}\n >\n <TextInput\n style={[styles.otpInput, { color: theme.colors.text, flex: 1 }]}\n placeholder=\"\"\n keyboardType=\"number-pad\"\n value={phoneVerification.otp}\n onChangeText={phoneVerification.setOtp}\n editable={!phoneVerification.loading}\n maxLength={6}\n testID=\"chat-registration-otp\"\n />\n </View>\n\n {phoneVerification.error ? (\n <View style={[styles.errorBox, { backgroundColor: theme.colors.error + '10' }]}>\n <Ionicons name=\"alert-circle\" size={16} color={theme.colors.error} />\n <Text style={[styles.errorText, { color: theme.colors.error }]}>\n {phoneVerification.error}\n </Text>\n </View>\n ) : null}\n\n <TouchableOpacity\n onPress={handleVerifyOtp}\n disabled={phoneVerification.loading || otpDigits.length < 6}\n style={[\n styles.button,\n {\n backgroundColor: otpDigits.length >= 6 ? accent : theme.colors.border,\n },\n ]}\n >\n {phoneVerification.loading ? (\n <ActivityIndicator color=\"#fff\" size=\"small\" />\n ) : (\n <Text style={styles.buttonText}>\n {usingTestOtp ? 'Continue' : 'Verify & Register'}\n </Text>\n )}\n </TouchableOpacity>\n\n {!usingTestOtp ? (\n <TouchableOpacity onPress={handleResend} disabled={phoneVerification.loading}>\n <Text style={[styles.linkText, { color: accent }]}>\n Didn't get the code? Resend\n </Text>\n </TouchableOpacity>\n ) : null}\n </View>\n </>\n )}\n </ScrollView>\n\n <Modal visible={countryModalOpen} animationType=\"slide\" presentationStyle=\"pageSheet\">\n <SafeAreaView style={[styles.modalFill, { backgroundColor: theme.colors.background }]}>\n <View style={[styles.modalHeader, { flexDirection: 'row', alignItems: 'center' }]}>\n <Text style={[styles.modalTitle, { color: theme.colors.text }]}>Country code</Text>\n <TouchableOpacity onPress={() => setCountryModalOpen(false)}>\n <Ionicons name=\"close\" size={26} color={theme.colors.text} />\n </TouchableOpacity>\n </View>\n <ScrollView>\n {COUNTRIES.map((country) => (\n <TouchableOpacity\n key={country.code}\n onPress={() => {\n phoneVerification.setCountryCode(country.code);\n setCountryModalOpen(false);\n }}\n style={[\n styles.countryListItem,\n phoneVerification.countryCode === country.code && {\n backgroundColor: accent + '18',\n },\n ]}\n >\n <Text style={[styles.countryListText, { color: theme.colors.text }]}>\n {country.name} ({country.callingCode})\n </Text>\n {phoneVerification.countryCode === country.code ? (\n <Ionicons name=\"checkmark\" size={20} color={accent} />\n ) : null}\n </TouchableOpacity>\n ))}\n </ScrollView>\n </SafeAreaView>\n </Modal>\n </SafeAreaView>\n );\n}\n\nconst styles = StyleSheet.create({\n container: { flex: 1 },\n header: {\n justifyContent: 'space-between',\n paddingHorizontal: 16,\n paddingVertical: 8,\n },\n title: { fontSize: 17, fontWeight: '600' },\n content: { padding: 20, paddingBottom: 40 },\n subtitle: { fontSize: 22, fontWeight: '700', marginBottom: 8 },\n description: { fontSize: 14, marginBottom: 24, lineHeight: 20 },\n form: { gap: 14 },\n phoneRow: {\n borderWidth: 1,\n borderRadius: 10,\n minHeight: 48,\n overflow: 'hidden',\n },\n countryChip: {\n paddingHorizontal: 12,\n paddingVertical: 12,\n },\n callingCode: { fontSize: 16, fontWeight: '600', marginRight: 4 },\n phoneDivider: { width: StyleSheet.hairlineWidth, alignSelf: 'stretch' },\n phoneInput: {\n flex: 1,\n paddingHorizontal: 12,\n paddingVertical: Platform.OS === 'ios' ? 12 : 10,\n fontSize: 16,\n },\n otpInputContainer: {\n borderWidth: 1,\n borderRadius: 10,\n paddingHorizontal: 16,\n },\n otpInput: {\n paddingVertical: 14,\n fontSize: 22,\n fontWeight: '600',\n letterSpacing: 6,\n textAlign: 'center',\n },\n button: {\n borderRadius: 10,\n paddingVertical: 14,\n alignItems: 'center',\n justifyContent: 'center',\n marginTop: 4,\n },\n buttonText: { color: '#fff', fontSize: 16, fontWeight: '600' },\n errorBox: {\n flexDirection: 'row',\n alignItems: 'center',\n borderRadius: 8,\n paddingHorizontal: 12,\n paddingVertical: 10,\n gap: 8,\n },\n errorText: { fontSize: 13, flex: 1 },\n linkText: { fontSize: 14, textAlign: 'center', fontWeight: '500' },\n modalFill: { flex: 1 },\n modalHeader: {\n justifyContent: 'space-between',\n paddingHorizontal: 16,\n paddingVertical: 12,\n },\n modalTitle: { fontSize: 18, fontWeight: '700' },\n countryListItem: {\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'space-between',\n paddingHorizontal: 16,\n paddingVertical: 14,\n borderBottomWidth: StyleSheet.hairlineWidth,\n borderBottomColor: 'rgba(0,0,0,0.06)',\n },\n countryListText: { fontSize: 16 },\n});\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const YANTRA_CHAT_REGISTRATION_STATUS_QUERY = `
|
|
2
|
+
query YantraChatRegistrationStatus {
|
|
3
|
+
yantraChatRegistrationStatus {
|
|
4
|
+
registered
|
|
5
|
+
phoneE164
|
|
6
|
+
memberId
|
|
7
|
+
organizationId
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
`;
|
|
11
|
+
const REGISTER_YANTRA_CHAT_MEMBER_MUTATION = `
|
|
12
|
+
mutation RegisterYantraChatMember($phoneE164: String!, $displayName: String) {
|
|
13
|
+
registerYantraChatMember(phoneE164: $phoneE164, displayName: $displayName) {
|
|
14
|
+
registered
|
|
15
|
+
phoneE164
|
|
16
|
+
memberId
|
|
17
|
+
organizationId
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
`;export{REGISTER_YANTRA_CHAT_MEMBER_MUTATION,YANTRA_CHAT_REGISTRATION_STATUS_QUERY};//# sourceMappingURL=chatOperations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chatOperations.js","sources":["../../../src/features/messaging/chatOperations.ts"],"sourcesContent":["/**\n * Every GraphQL operation the chat remote sends, as plain strings.\n *\n * Strings, not `gql` documents, because the remote must not import\n * @apollo/client or graphql - the host parses and executes these on the app's\n * own client (see `ChatGraphQLSeam`). The upside beyond bundle size is that the\n * QUERY SHAPE ships over the air: a missing field or a wrong argument is a\n * surface deploy, not an App Store release.\n *\n * HARD REQUIREMENT - every operation is NAMED, and every subscription's name\n * ends in `_WS`. `portable-devices/mobile/src/config/base-apollo-client.ts`\n * routes ws-vs-http with `operationName.endsWith('_WS')`; an anonymous\n * operation makes that `undefined.endsWith(...)`, which throws for EVERY\n * operation the app sends, not just this one. `chatOperations.test.ts` asserts\n * both properties over every export in this file, so a new operation cannot\n * quietly break the app's transport.\n */\n\n/**\n * Shared field set for a message.\n *\n * `author` and `creator` are both selected on purpose: messenger-box populates\n * `author` on some write paths and `creator` on others (`sendMessage` sets\n * creator), and a transcript that reads only one shows \"Unknown\" for half the\n * messages. `chatModel.pickAuthor` collapses them.\n */\nexport const POST_FIELDS = `\n id\n message\n type\n createdAt\n updatedAt\n editedAt\n deletedAt\n rootId\n parentId\n filenames\n props\n author { id givenName familyName username email picture }\n creator { id givenName familyName username email picture }\n`;\n\nconst CHANNEL_FIELDS = `\n id\n type\n orgName\n title\n displayName\n topic\n lastPostAt\n totalMsgCount\n createdAt\n lastMessage { ${POST_FIELDS} }\n creator { id givenName familyName username email picture }\n members { id user { id givenName familyName username email picture } msgCount lastViewedAt }\n`;\n\n/**\n * The conversation list.\n *\n * `criteria.members.$elemMatch.user` is NOT sent: the server derives membership\n * from the session (`accountId`) and a client-supplied member filter is exactly\n * the kind of thing that lets a caller read someone else's list. Org scoping is\n * likewise server-side now (`channelOrgScope`). Native Messages passes\n * `criteria: { type: DIRECT, orgName: yantra-chat }` via `nativeChatChannelQuery`.\n */\nexport const CHANNELS_QUERY = `\n query ChatChannels($limit: Int, $skip: Int, $criteria: AnyObject, $sort: Sort) {\n channelsByUser(limit: $limit, skip: $skip, criteria: $criteria, sort: $sort) {\n ${CHANNEL_FIELDS}\n }\n }\n`;\n\n/**\n * One page of a transcript, newest first.\n *\n * Paged with `skip`, which is what the server exposes. That is offset paging and\n * it can repeat or drop a row if messages arrive mid-scroll; `chatModel.mergePosts`\n * de-duplicates by id so the visible effect is bounded. A cursor (`before: id`)\n * would be strictly better and is worth adding server-side later.\n */\nexport const MESSAGES_QUERY = `\n query ChatMessages($channelId: ID, $limit: Int! = 30, $skip: Int! = 0, $sort: Sort) {\n messages(channelId: $channelId, limit: $limit, skip: $skip, sort: $sort) {\n totalCount\n data {\n ${POST_FIELDS}\n }\n }\n }\n`;\n\nexport const SEND_MESSAGE_MUTATION = `\n mutation ChatSendMessage($channelId: String!, $messageInput: MessageInput!) {\n sendMessage(channelId: $channelId, messageInput: $messageInput) {\n ${POST_FIELDS}\n }\n }\n`;\n\n/** Marks the channel read for the caller; drives the unread badge to zero. */\nexport const VIEW_CHANNEL_MUTATION = `\n mutation ChatViewChannel($id: ID!) {\n viewChannel(id: $id)\n }\n`;\n\n/**\n * Opens (or returns) the 1:1 conversation with `receiver`.\n *\n * Idempotent server-side: an existing DM for the pair comes back rather than a\n * duplicate, so the UI can call this every time someone taps a contact without\n * checking first.\n */\nexport const CREATE_DIRECT_CHANNEL_MUTATION = `\n mutation ChatCreateDirectChannel($receiver: [ID]!, $displayName: String!) {\n createDirectChannel(receiver: $receiver, displayName: $displayName) {\n ${CHANNEL_FIELDS}\n }\n }\n`;\n\n/**\n * Live messages for one channel.\n *\n * `_WS` suffix is load-bearing - see the file header. `directTo` is left unset:\n * the server treats it as a second, user-scoped topic and passing the wrong one\n * silently delivers nothing.\n */\nexport const MESSAGE_ADDED_SUBSCRIPTION_WS = `\n subscription ChatMessageAdded_WS($channelId: String) {\n chatMessageAdded(channelId: $channelId) {\n ${POST_FIELDS}\n }\n }\n`;\n\n/**\n * Which of these phone numbers already have an account.\n *\n * Sends SHA-256 hashes of E.164 numbers, never the numbers - the server never\n * receives a plaintext address book. Returns AnyObject: a list of\n * `{phoneHash, userId, username, displayName, picture}`.\n */\nexport const CONTACT_MATCHES_QUERY = `\n query ChatContactMatches($phoneHashes: [String!]!) {\n chatContactMatches(phoneHashes: $phoneHashes)\n }\n`;\n\nexport const YANTRA_CHAT_REGISTRATION_STATUS_QUERY = `\n query YantraChatRegistrationStatus {\n yantraChatRegistrationStatus {\n registered\n phoneE164\n memberId\n organizationId\n }\n }\n`;\n\nexport const REGISTER_YANTRA_CHAT_MEMBER_MUTATION = `\n mutation RegisterYantraChatMember($phoneE164: String!, $displayName: String) {\n registerYantraChatMember(phoneE164: $phoneE164, displayName: $displayName) {\n registered\n phoneE164\n memberId\n organizationId\n }\n }\n`;\n\nexport const MATCH_YANTRA_CHAT_MEMBERS_QUERY = `\n query MatchYantraChatMembersByPhone($phones: [String!]!, $viewerPhoneE164: String) {\n matchYantraChatMembersByPhone(phones: $phones, viewerPhoneE164: $viewerPhoneE164) {\n phoneE164\n userId\n displayName\n avatarUrl\n }\n }\n`;\n\nexport const YANTRA_CHAT_MEMBERS_QUERY = `\n query YantraChatMembers {\n yantraChatMembers {\n phoneE164\n userId\n displayName\n avatarUrl\n }\n }\n`;\n\n/** Composer draft persistence, server-side and per (member, channel). */\nexport const SET_DRAFT_MUTATION = `\n mutation ChatSetDraft($channelId: ID!, $text: String!) {\n setChannelDraft(channelId: $channelId, text: $text)\n }\n`;\n\nexport const DRAFT_QUERY = `\n query ChatDraft($channelId: ID!) {\n channelDraft(channelId: $channelId)\n }\n`;\n\n/** Every operation, for the contract test and for host-side preflight. */\nexport const CHAT_OPERATIONS: Readonly<Record<string, string>> = Object.freeze({\n CHANNELS_QUERY,\n CONTACT_MATCHES_QUERY,\n YANTRA_CHAT_REGISTRATION_STATUS_QUERY,\n REGISTER_YANTRA_CHAT_MEMBER_MUTATION,\n MATCH_YANTRA_CHAT_MEMBERS_QUERY,\n YANTRA_CHAT_MEMBERS_QUERY,\n MESSAGES_QUERY,\n SEND_MESSAGE_MUTATION,\n VIEW_CHANNEL_MUTATION,\n CREATE_DIRECT_CHANNEL_MUTATION,\n MESSAGE_ADDED_SUBSCRIPTION_WS,\n SET_DRAFT_MUTATION,\n DRAFT_QUERY,\n});\n\n/**\n * The operation name inside a document string.\n *\n * Used by the contract test and by the host seam for logging. Intentionally a\n * regex rather than a parse: pulling in `graphql` to read a name would defeat\n * the whole point of shipping strings.\n */\nexport function operationNameOf(document: string): string | null {\n const match = /\\b(?:query|mutation|subscription)\\s+([A-Za-z_][A-Za-z0-9_]*)/.exec(document);\n return match ? match[1] : null;\n}\n\nexport function operationTypeOf(document: string): 'query' | 'mutation' | 'subscription' | null {\n const match = /\\b(query|mutation|subscription)\\s+[A-Za-z_]/.exec(document);\n return match ? (match[1] as 'query' | 'mutation' | 'subscription') : null;\n}\n"],"names":[],"mappings":"AAoJO,MAAM,qCAAwC,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAU9C,MAAM,oCAAuC,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const YANTRA_CHAT_PHONE_STORAGE_KEY = "yantra-chat-registered-phone-e164";export{YANTRA_CHAT_PHONE_STORAGE_KEY};//# sourceMappingURL=chatRegistrationStorage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chatRegistrationStorage.js","sources":["../../../src/features/messaging/chatRegistrationStorage.ts"],"sourcesContent":["/**\n * AsyncStorage cache for Yantra Chat registration (issue #1312).\n * Mongo `organizationMembers` is the source of truth (`yantraChatRegistrationStatus`);\n * this key only avoids flashing the registration screen while the status query runs.\n * It is not cleared on network/GraphQL errors (Metro reload, backend restart).\n */\nexport const YANTRA_CHAT_PHONE_STORAGE_KEY = 'yantra-chat-registered-phone-e164';\n"],"names":[],"mappings":"AAMO,MAAM,6BAAgC,GAAA"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
const TRUNK_PREFIXES = {
|
|
2
|
+
// The overwhelmingly common case: a single leading 0.
|
|
3
|
+
DEFAULT: "0",
|
|
4
|
+
// Italy is the notable exception - the leading 0 is part of the number.
|
|
5
|
+
IT: ""
|
|
6
|
+
};
|
|
7
|
+
const CALLING_CODES = {
|
|
8
|
+
US: "1",
|
|
9
|
+
CA: "1",
|
|
10
|
+
GB: "44",
|
|
11
|
+
IE: "353",
|
|
12
|
+
FR: "33",
|
|
13
|
+
DE: "49",
|
|
14
|
+
ES: "34",
|
|
15
|
+
IT: "39",
|
|
16
|
+
NL: "31",
|
|
17
|
+
BE: "32",
|
|
18
|
+
CH: "41",
|
|
19
|
+
AT: "43",
|
|
20
|
+
SE: "46",
|
|
21
|
+
NO: "47",
|
|
22
|
+
DK: "45",
|
|
23
|
+
FI: "358",
|
|
24
|
+
PL: "48",
|
|
25
|
+
PT: "351",
|
|
26
|
+
GR: "30",
|
|
27
|
+
CZ: "420",
|
|
28
|
+
RO: "40",
|
|
29
|
+
HU: "36",
|
|
30
|
+
UA: "380",
|
|
31
|
+
RU: "7",
|
|
32
|
+
TR: "90",
|
|
33
|
+
IL: "972",
|
|
34
|
+
AE: "971",
|
|
35
|
+
SA: "966",
|
|
36
|
+
QA: "974",
|
|
37
|
+
KW: "965",
|
|
38
|
+
IN: "91",
|
|
39
|
+
PK: "92",
|
|
40
|
+
BD: "880",
|
|
41
|
+
LK: "94",
|
|
42
|
+
NP: "977",
|
|
43
|
+
CN: "86",
|
|
44
|
+
JP: "81",
|
|
45
|
+
KR: "82",
|
|
46
|
+
TW: "886",
|
|
47
|
+
HK: "852",
|
|
48
|
+
SG: "65",
|
|
49
|
+
MY: "60",
|
|
50
|
+
TH: "66",
|
|
51
|
+
VN: "84",
|
|
52
|
+
PH: "63",
|
|
53
|
+
ID: "62",
|
|
54
|
+
AU: "61",
|
|
55
|
+
NZ: "64",
|
|
56
|
+
ZA: "27",
|
|
57
|
+
NG: "234",
|
|
58
|
+
KE: "254",
|
|
59
|
+
EG: "20",
|
|
60
|
+
GH: "233",
|
|
61
|
+
BR: "55",
|
|
62
|
+
MX: "52",
|
|
63
|
+
AR: "54",
|
|
64
|
+
CL: "56",
|
|
65
|
+
CO: "57",
|
|
66
|
+
PE: "51"
|
|
67
|
+
};
|
|
68
|
+
const MIN_E164_DIGITS = 7;
|
|
69
|
+
const MAX_E164_DIGITS = 15;
|
|
70
|
+
function toE164(raw, defaultRegion) {
|
|
71
|
+
if (typeof raw !== "string") return null;
|
|
72
|
+
const cleaned = raw.replace(/[\s\-().]/g, "");
|
|
73
|
+
if (!cleaned) return null;
|
|
74
|
+
const withoutExt = cleaned.split(/[,;xX#]/)[0];
|
|
75
|
+
if (!withoutExt) return null;
|
|
76
|
+
let digits;
|
|
77
|
+
if (withoutExt.startsWith("+")) {
|
|
78
|
+
digits = withoutExt.slice(1).replace(/\D/g, "");
|
|
79
|
+
} else if (withoutExt.startsWith("00")) {
|
|
80
|
+
digits = withoutExt.slice(2).replace(/\D/g, "");
|
|
81
|
+
} else {
|
|
82
|
+
const region = (defaultRegion != null ? defaultRegion : "").toUpperCase();
|
|
83
|
+
const cc = CALLING_CODES[region];
|
|
84
|
+
if (!cc) return null;
|
|
85
|
+
let national = withoutExt.replace(/\D/g, "");
|
|
86
|
+
const trunk = region in TRUNK_PREFIXES ? TRUNK_PREFIXES[region] : TRUNK_PREFIXES.DEFAULT;
|
|
87
|
+
if (trunk && national.startsWith(trunk)) national = national.slice(trunk.length);
|
|
88
|
+
digits = national.startsWith(cc) && national.length >= MIN_E164_DIGITS + cc.length - 1 ? national : cc + national;
|
|
89
|
+
}
|
|
90
|
+
if (digits.length < MIN_E164_DIGITS || digits.length > MAX_E164_DIGITS) return null;
|
|
91
|
+
if (digits.startsWith("0")) return null;
|
|
92
|
+
return `+${digits}`;
|
|
93
|
+
}
|
|
94
|
+
function formatPhoneNumber(input, defaultRegion) {
|
|
95
|
+
return toE164(input, defaultRegion);
|
|
96
|
+
}
|
|
97
|
+
function isValidPhoneNumber(phoneNumber) {
|
|
98
|
+
if (!phoneNumber) return false;
|
|
99
|
+
if (!phoneNumber.startsWith("+")) return false;
|
|
100
|
+
const digits = phoneNumber.slice(1).replace(/\D/g, "");
|
|
101
|
+
return digits.length >= MIN_E164_DIGITS && digits.length <= MAX_E164_DIGITS;
|
|
102
|
+
}export{formatPhoneNumber,isValidPhoneNumber,toE164};//# sourceMappingURL=phoneNumbers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phoneNumbers.js","sources":["../../../src/features/messaging/phoneNumbers.ts"],"sourcesContent":["/**\n * Turning what the address book gives you into something matchable.\n *\n * This lives in the REMOTE, not the host, on purpose: it is pure logic with no\n * native dependency, it is where contact-matching actually goes wrong, and\n * keeping it here means a normalisation bug is a surface deploy rather than an\n * App Store release. The `contacts` seam hands over RAW strings plus a region\n * hint precisely so this can own the problem.\n *\n * Deliberately NOT libphonenumber. That library is ~500KB and every byte ships\n * in the remote bundle on every cold launch. What follows handles the cases\n * that actually occur in an address book; anything it cannot confidently place\n * is DROPPED rather than guessed, because a wrong match shows someone a\n * stranger's name attached to their friend's number, which is far worse than\n * simply not finding them.\n */\n\n/** National trunk prefixes stripped before applying a country code. */\nconst TRUNK_PREFIXES: Record<string, string> = {\n // The overwhelmingly common case: a single leading 0.\n DEFAULT: '0',\n // Italy is the notable exception - the leading 0 is part of the number.\n IT: '',\n};\n\n/** Country calling codes for the regions a device is likely to report. */\nconst CALLING_CODES: Record<string, string> = {\n US: '1',\n CA: '1',\n GB: '44',\n IE: '353',\n FR: '33',\n DE: '49',\n ES: '34',\n IT: '39',\n NL: '31',\n BE: '32',\n CH: '41',\n AT: '43',\n SE: '46',\n NO: '47',\n DK: '45',\n FI: '358',\n PL: '48',\n PT: '351',\n GR: '30',\n CZ: '420',\n RO: '40',\n HU: '36',\n UA: '380',\n RU: '7',\n TR: '90',\n IL: '972',\n AE: '971',\n SA: '966',\n QA: '974',\n KW: '965',\n IN: '91',\n PK: '92',\n BD: '880',\n LK: '94',\n NP: '977',\n CN: '86',\n JP: '81',\n KR: '82',\n TW: '886',\n HK: '852',\n SG: '65',\n MY: '60',\n TH: '66',\n VN: '84',\n PH: '63',\n ID: '62',\n AU: '61',\n NZ: '64',\n ZA: '27',\n NG: '234',\n KE: '254',\n EG: '20',\n GH: '233',\n BR: '55',\n MX: '52',\n AR: '54',\n CL: '56',\n CO: '57',\n PE: '51',\n};\n\n/** Longest calling code is 4 digits; shortest E.164 subscriber part is ~4. */\nconst MIN_E164_DIGITS = 7;\nconst MAX_E164_DIGITS = 15;\n\n/**\n * Normalise one raw address-book string to E.164 (`+<digits>`), or null.\n *\n * Returns null rather than a best guess whenever the result would be\n * speculative - see the file header for why a miss beats a wrong match.\n */\nexport function toE164(raw: string, defaultRegion?: string | null): string | null {\n if (typeof raw !== 'string') return null;\n\n // Strip everything a human might type: spaces, dashes, dots, parens,\n // and the invisible bidi marks iOS inserts around RTL-locale numbers.\n const cleaned = raw.replace(/[\\s\\-().]/g, '');\n if (!cleaned) return null;\n\n // Extensions are not diallable identity - \"555123456x89\" is one person.\n const withoutExt = cleaned.split(/[,;xX#]/)[0];\n if (!withoutExt) return null;\n\n let digits: string;\n if (withoutExt.startsWith('+')) {\n digits = withoutExt.slice(1).replace(/\\D/g, '');\n } else if (withoutExt.startsWith('00')) {\n // The international prefix used across most of Europe and Asia.\n digits = withoutExt.slice(2).replace(/\\D/g, '');\n } else {\n const region = (defaultRegion ?? '').toUpperCase();\n const cc = CALLING_CODES[region];\n // No region and no international prefix: the number is meaningless\n // without knowing where it was dialled from. Drop it.\n if (!cc) return null;\n\n let national = withoutExt.replace(/\\D/g, '');\n const trunk = region in TRUNK_PREFIXES ? TRUNK_PREFIXES[region] : TRUNK_PREFIXES.DEFAULT;\n if (trunk && national.startsWith(trunk)) national = national.slice(trunk.length);\n\n // A number that already begins with its own country code and is\n // plausibly full-length is international-without-the-plus, not a\n // national number that happens to start with those digits.\n digits =\n national.startsWith(cc) && national.length >= MIN_E164_DIGITS + cc.length - 1 ? national : cc + national;\n }\n\n if (digits.length < MIN_E164_DIGITS || digits.length > MAX_E164_DIGITS) return null;\n // A leading zero after the country code means the trunk prefix survived.\n if (digits.startsWith('0')) return null;\n\n return `+${digits}`;\n}\n\n/**\n * Region implied by an E.164 number's calling code (longest match wins), or null.\n * `+1` resolves to US, which parses identically to CA.\n */\nexport function regionFromE164(e164: string | null | undefined): string | null {\n if (!e164 || !e164.startsWith('+')) return null;\n const digits = e164.slice(1);\n let best: { region: string; len: number } | null = null;\n for (const [region, cc] of Object.entries(CALLING_CODES)) {\n if (digits.startsWith(cc) && (!best || cc.length > best.len)) best = { region, len: cc.length };\n }\n return best?.region ?? null;\n}\n\n/**\n * Every distinct E.164 number a contact has, in address-book order.\n *\n * De-duplicated because the same person routinely appears with the same number\n * written two ways (\"+1 555…\" and \"(555) …\"), and matching the same number\n * twice would show them twice in the results.\n */\nexport function contactE164s(\n phoneNumbers: readonly string[],\n defaultRegion?: string | null | readonly (string | null | undefined)[],\n): string[] {\n // A number saved without a country code is ambiguous, so each candidate region\n // contributes an interpretation. The server only matches verified member numbers,\n // so extra candidates cannot produce a match that is not real.\n const regions = Array.isArray(defaultRegion) ? defaultRegion : [defaultRegion as string | null | undefined];\n const seen = new Set<string>();\n for (const raw of phoneNumbers) {\n for (const region of regions) {\n const e164 = toE164(raw, region);\n if (e164) seen.add(e164);\n }\n }\n return Array.from(seen);\n}\n\nexport interface NormalizedContact {\n /** The device's stable id, echoed back so a match can be re-attached. */\n id: string;\n displayName: string;\n imageUri?: string | null;\n /** Distinct E.164 candidates; never empty (contacts without any are dropped). */\n e164: string[];\n /** Numbers exactly as saved in the address book, for display and SMS. */\n rawNumbers?: string[];\n}\n\n/**\n * Normalise a whole address book, dropping anything unmatchable.\n *\n * Also drops the viewer's OWN number, so nobody is offered a chat with\n * themselves - which happens on essentially every real device, because people\n * save their own contact card.\n */\nexport function normalizeContacts(\n contacts: ReadonlyArray<{ id: string; displayName: string; phoneNumbers: string[]; imageUri?: string | null }>,\n defaultRegion?: string | null | readonly (string | null | undefined)[],\n viewerE164?: string | null,\n): NormalizedContact[] {\n const mine = new Set(viewerE164 ? contactE164s([viewerE164], defaultRegion) : []);\n const out: NormalizedContact[] = [];\n for (const c of contacts) {\n const e164 = contactE164s(c.phoneNumbers ?? [], defaultRegion);\n // Any interpretation equal to the viewer's own number means this is their own card.\n if (!e164.length || e164.some((n) => mine.has(n))) continue;\n out.push({\n id: c.id,\n displayName: c.displayName,\n imageUri: c.imageUri ?? null,\n e164,\n rawNumbers: (c.phoneNumbers ?? []).map((n) => n.trim()).filter(Boolean),\n });\n }\n return out;\n}\n\n/**\n * The full set of numbers to ask the server about, capped.\n *\n * Address books of several thousand entries are ordinary, and the cap keeps one\n * request from becoming megabytes. Callers page through the rest.\n */\nexport function collectLookupNumbers(contacts: readonly NormalizedContact[], limit = 1000): string[] {\n const seen = new Set<string>();\n for (const c of contacts) {\n for (const n of c.e164) {\n seen.add(n);\n if (seen.size >= limit) return Array.from(seen);\n }\n }\n return Array.from(seen);\n}\n\n/**\n * Format a phone number string (user input) to E.164 format.\n * Used by the registration screen to normalize phone input before verification.\n */\nexport function formatPhoneNumber(input: string, defaultRegion?: string): string | null {\n return toE164(input, defaultRegion);\n}\n\n/**\n * Validate if a phone number string is a valid E.164 number.\n * Returns true if the number can be normalized to E.164 format.\n */\nexport function isValidPhoneNumber(phoneNumber: string | null): boolean {\n if (!phoneNumber) return false;\n if (!phoneNumber.startsWith('+')) return false;\n const digits = phoneNumber.slice(1).replace(/\\D/g, '');\n return digits.length >= MIN_E164_DIGITS && digits.length <= MAX_E164_DIGITS;\n}\n"],"names":[],"mappings":"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"}
|