@adminide-stack/yantra-mobile 12.0.69-alpha.20 → 12.0.69-alpha.21
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/features/messaging/ContactsScreen.js +61 -3
- package/lib/features/messaging/ContactsScreen.js.map +1 -1
- package/lib/features/messaging/MessageActions.js +11 -4
- package/lib/features/messaging/MessageActions.js.map +1 -1
- package/lib/features/messaging/NativeChatExperience.js +4 -1
- package/lib/features/messaging/NativeChatExperience.js.map +1 -1
- package/lib/features/messaging/chatInviteLink.js +2 -2
- package/lib/features/messaging/chatInviteLink.js.map +1 -1
- package/lib/features/messaging/emojiCatalog.js +26 -2
- package/lib/features/messaging/emojiCatalog.js.map +1 -1
- package/lib/features/messaging/emojiKeywords.js +1592 -0
- package/lib/features/messaging/emojiKeywords.js.map +1 -0
- package/lib/features/messaging/useContactMatches.js +13 -1
- package/lib/features/messaging/useContactMatches.js.map +1 -1
- package/package.json +4 -3
|
@@ -36,7 +36,9 @@ function ContactsScreen({
|
|
|
36
36
|
onOpenSettings,
|
|
37
37
|
onStartChat,
|
|
38
38
|
onInvite,
|
|
39
|
-
onDismissError
|
|
39
|
+
onDismissError,
|
|
40
|
+
limited = false,
|
|
41
|
+
onChooseMoreContacts
|
|
40
42
|
}) {
|
|
41
43
|
const [query, setQuery] = React.useState("");
|
|
42
44
|
const filterContact = React.useCallback((c) => {
|
|
@@ -97,9 +99,20 @@ function ContactsScreen({
|
|
|
97
99
|
}], children: "Checking your contacts\u2026" })
|
|
98
100
|
] }));
|
|
99
101
|
}
|
|
102
|
+
const listHeader = permission === "granted" && limited ? /* @__PURE__ */ jsxs(View, { children: [
|
|
103
|
+
/* @__PURE__ */ jsxs(View, { style: [styles.limitedNote, {
|
|
104
|
+
backgroundColor: theme.surface
|
|
105
|
+
}], children: [
|
|
106
|
+
/* @__PURE__ */ jsx(MaterialCommunityIcons, { name: "account-lock-outline", size: 20, color: theme.textMuted }),
|
|
107
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.limitedText, {
|
|
108
|
+
color: theme.textMuted
|
|
109
|
+
}], children: "You shared only some contacts with Yantra. People you did not share will not show here." })
|
|
110
|
+
] }),
|
|
111
|
+
onChooseMoreContacts ? /* @__PURE__ */ jsx(ActionRow, { icon: "account-multiple-plus-outline", label: "Choose more contacts", theme, onPress: onChooseMoreContacts, testID: "chat-contacts-choose-more" }) : null
|
|
112
|
+
] }) : null;
|
|
100
113
|
return shell(/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
101
114
|
/* @__PURE__ */ jsx(InlineBanner, { message: error, theme, onDismiss: onDismissError }),
|
|
102
|
-
/* @__PURE__ */ jsx(SectionList, { sections, keyExtractor: (item) => item.id, stickySectionHeadersEnabled: false, renderSectionHeader: ({
|
|
115
|
+
/* @__PURE__ */ jsx(SectionList, { ListHeaderComponent: listHeader, sections, keyExtractor: (item) => item.id, stickySectionHeadersEnabled: false, renderSectionHeader: ({
|
|
103
116
|
section
|
|
104
117
|
}) => /* @__PURE__ */ jsx(Text, { style: [styles.sectionTitle, {
|
|
105
118
|
color: theme.textMuted,
|
|
@@ -107,12 +120,35 @@ function ContactsScreen({
|
|
|
107
120
|
}], children: section.title }), renderItem: ({
|
|
108
121
|
item,
|
|
109
122
|
section
|
|
110
|
-
}) => /* @__PURE__ */ jsx(ContactRow, { contact: item, theme, busy: busyContactId === item.id, mode: section.key === "here" ? "chat" : "invite", onPress: () => section.key === "here" ? onStartChat(item) : onInvite(item) }), ListEmptyComponent: /* @__PURE__ */ jsx(View, { style: styles.centerFill, children: /* @__PURE__ */ jsx(EmptyState, { icon: "account-search-outline", title: "No contacts found", detail: "Add phone numbers to your contacts, or search with a different name.", theme, action: {
|
|
123
|
+
}) => /* @__PURE__ */ jsx(ContactRow, { contact: item, theme, busy: busyContactId === item.id, mode: section.key === "here" ? "chat" : "invite", onPress: () => section.key === "here" ? onStartChat(item) : onInvite(item) }), ListEmptyComponent: /* @__PURE__ */ jsx(View, { style: styles.centerFill, children: /* @__PURE__ */ jsx(EmptyState, { icon: "account-search-outline", title: "No contacts found", detail: limited && !query.trim() ? "Choose more contacts to share with Yantra." : "Add phone numbers to your contacts, or search with a different name.", theme, action: {
|
|
111
124
|
label: "Back to chats",
|
|
112
125
|
onPress: onBack
|
|
113
126
|
} }) }), contentContainerStyle: sections.length === 0 ? styles.listEmptyGrow : void 0, testID: "chat-contacts-list" })
|
|
114
127
|
] }));
|
|
115
128
|
}
|
|
129
|
+
function ActionRow({
|
|
130
|
+
icon,
|
|
131
|
+
label,
|
|
132
|
+
theme,
|
|
133
|
+
onPress,
|
|
134
|
+
testID
|
|
135
|
+
}) {
|
|
136
|
+
return /* @__PURE__ */ jsx(Pressable, { onPress, accessibilityRole: "button", accessibilityLabel: label, style: ({
|
|
137
|
+
pressed
|
|
138
|
+
}) => ({
|
|
139
|
+
width: "100%",
|
|
140
|
+
backgroundColor: pressed ? theme.surface : theme.bg
|
|
141
|
+
}), testID, children: /* @__PURE__ */ jsxs(View, { style: styles.row, children: [
|
|
142
|
+
/* @__PURE__ */ jsx(View, { style: [styles.actionIcon, {
|
|
143
|
+
backgroundColor: theme.surface
|
|
144
|
+
}], children: /* @__PURE__ */ jsx(MaterialCommunityIcons, { name: icon, size: 22, color: theme.accent }) }),
|
|
145
|
+
/* @__PURE__ */ jsx(View, { style: [styles.rowBody, {
|
|
146
|
+
borderBottomColor: theme.border
|
|
147
|
+
}], children: /* @__PURE__ */ jsx(Text, { style: [styles.rowName, {
|
|
148
|
+
color: theme.accent
|
|
149
|
+
}], children: label }) })
|
|
150
|
+
] }) });
|
|
151
|
+
}
|
|
116
152
|
function ContactRow({
|
|
117
153
|
contact,
|
|
118
154
|
theme,
|
|
@@ -210,6 +246,28 @@ const styles = StyleSheet.create({
|
|
|
210
246
|
fontSize: 13,
|
|
211
247
|
marginTop: 2
|
|
212
248
|
},
|
|
249
|
+
actionIcon: {
|
|
250
|
+
width: 44,
|
|
251
|
+
height: 44,
|
|
252
|
+
borderRadius: 22,
|
|
253
|
+
alignItems: "center",
|
|
254
|
+
justifyContent: "center"
|
|
255
|
+
},
|
|
256
|
+
limitedNote: {
|
|
257
|
+
flexDirection: "row",
|
|
258
|
+
alignItems: "flex-start",
|
|
259
|
+
marginHorizontal: 16,
|
|
260
|
+
marginTop: 4,
|
|
261
|
+
marginBottom: 4,
|
|
262
|
+
padding: 12,
|
|
263
|
+
borderRadius: 10
|
|
264
|
+
},
|
|
265
|
+
limitedText: {
|
|
266
|
+
flex: 1,
|
|
267
|
+
fontSize: 13,
|
|
268
|
+
lineHeight: 18,
|
|
269
|
+
marginLeft: 10
|
|
270
|
+
},
|
|
213
271
|
inviteChip: {
|
|
214
272
|
borderWidth: 1,
|
|
215
273
|
borderRadius: 14,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContactsScreen.js","sources":["../../../src/features/messaging/ContactsScreen.tsx"],"sourcesContent":["/**\n * \"Find people you know\" - the contacts sync and invite screen.\n */\nimport * as React from 'react';\nimport { ActivityIndicator, Pressable, SectionList, StyleSheet, Text, TextInput, View } from 'react-native';\nimport { SafeAreaView } from 'react-native-safe-area-context';\nimport { MaterialCommunityIcons } from '@expo/vector-icons';\nimport { Avatar, EmptyState, InlineBanner, type ChatTheme } from './ChatComponents';\nimport type { MatchedContact } from './useContactMatches';\nimport type { ChatPermissionStatus } from './chatHost';\n\nfunction ContactsHeader({ theme, onBack }: { theme: ChatTheme; onBack: () => void }) {\n return (\n <SafeAreaView edges={['top']} style={{ backgroundColor: theme.headerBg }}>\n <View\n style={{\n flexDirection: 'row',\n alignItems: 'center',\n paddingHorizontal: 8,\n paddingBottom: 8,\n borderBottomWidth: StyleSheet.hairlineWidth,\n borderBottomColor: theme.border,\n backgroundColor: theme.headerBg,\n }}\n >\n <Pressable onPress={onBack} hitSlop={10} accessibilityRole=\"button\" accessibilityLabel=\"Back\">\n <MaterialCommunityIcons name=\"chevron-left\" size={28} color={theme.accent} />\n </Pressable>\n <Text\n style={{\n flex: 1,\n fontSize: 20,\n fontWeight: '700',\n color: theme.text,\n marginLeft: 8,\n }}\n >\n Select contact\n </Text>\n </View>\n </SafeAreaView>\n );\n}\n\nexport function ContactsScreen({\n permission,\n loading,\n error,\n onYantra,\n invitable,\n theme,\n busyContactId,\n onBack,\n onRequestAccess,\n onOpenSettings,\n onStartChat,\n onInvite,\n onDismissError,\n}: {\n permission: ChatPermissionStatus | 'unsupported';\n loading: boolean;\n error: string | null;\n onYantra: MatchedContact[];\n invitable: MatchedContact[];\n theme: ChatTheme;\n busyContactId: string | null;\n onBack: () => void;\n onRequestAccess: () => void;\n onOpenSettings: () => void;\n onStartChat: (contact: MatchedContact) => void;\n onInvite: (contact: MatchedContact) => void;\n onDismissError: () => void;\n}) {\n const [query, setQuery] = React.useState('');\n\n const filterContact = React.useCallback(\n (c: MatchedContact) => {\n const q = query.trim().toLowerCase();\n if (!q) return true;\n const name = (c.match?.displayName || c.displayName).toLowerCase();\n const qDigits = q.replace(/\\D/g, '');\n return (\n name.includes(q) ||\n (qDigits.length > 0 &&\n [...c.e164, ...(c.rawNumbers ?? [])].some((n) => n.replace(/\\D/g, '').includes(qDigits)))\n );\n },\n [query],\n );\n\n const sections = React.useMemo(\n () =>\n [\n { key: 'here', title: 'On Yantra Chat', data: onYantra.filter(filterContact) },\n { key: 'invite', title: 'Invite to Yantra Chat', data: invitable.filter(filterContact) },\n ].filter((s) => s.data.length > 0),\n [onYantra, invitable, filterContact],\n );\n\n const hasPeople = onYantra.length > 0 || invitable.length > 0;\n\n const shell = (body: React.ReactNode) => (\n <View style={[styles.fill, { backgroundColor: theme.bg }]}>\n <ContactsHeader theme={theme} onBack={onBack} />\n {permission === 'granted' ? (\n <View style={[styles.searchWrap, { backgroundColor: theme.surface }]}>\n <MaterialCommunityIcons name=\"magnify\" size={20} color={theme.textMuted} />\n <TextInput\n value={query}\n onChangeText={setQuery}\n placeholder=\"Search name or number\"\n placeholderTextColor={theme.textMuted}\n style={[styles.searchInput, { color: theme.text }]}\n />\n </View>\n ) : null}\n {body}\n </View>\n );\n\n if (permission === 'unsupported' && !hasPeople) {\n return shell(\n <View style={styles.centerFill}>\n <EmptyState\n icon=\"cellphone-off\"\n title=\"Not available in this build\"\n detail=\"Finding people from your contacts needs a newer version of the app.\"\n theme={theme}\n action={{ label: 'Back to chats', onPress: onBack }}\n />\n </View>,\n );\n }\n\n if (permission === 'denied' && !hasPeople) {\n return shell(\n <View style={styles.centerFill}>\n <EmptyState\n icon=\"lock-outline\"\n title=\"Contacts access is off\"\n detail=\"Allow contacts access in Settings to find people you know on Yantra Chat.\"\n theme={theme}\n action={{ label: 'Open Settings', onPress: onOpenSettings }}\n />\n </View>,\n );\n }\n\n if (permission === 'undetermined' && !hasPeople) {\n return shell(\n <View style={styles.centerFill}>\n <EmptyState\n icon=\"account-multiple-outline\"\n title={loading ? 'Looking for people' : 'No other members yet'}\n detail={\n loading\n ? 'Checking who is already on Yantra Chat…'\n : 'You are registered. Allow contacts to invite people, or start a chat when someone else registers.'\n }\n theme={theme}\n action={{ label: 'Find from contacts', onPress: onRequestAccess }}\n />\n </View>,\n );\n }\n\n if (loading && !sections.length) {\n return shell(\n <View style={styles.centerFill}>\n <ActivityIndicator color={theme.accent} />\n <Text style={[styles.loadingText, { color: theme.textMuted }]}>Checking your contacts…</Text>\n </View>,\n );\n }\n\n return shell(\n <>\n <InlineBanner message={error} theme={theme} onDismiss={onDismissError} />\n <SectionList\n sections={sections}\n keyExtractor={(item) => item.id}\n stickySectionHeadersEnabled={false}\n renderSectionHeader={({ section }) => (\n <Text style={[styles.sectionTitle, { color: theme.textMuted, backgroundColor: theme.bg }]}>\n {section.title}\n </Text>\n )}\n renderItem={({ item, section }) => (\n <ContactRow\n contact={item}\n theme={theme}\n busy={busyContactId === item.id}\n mode={section.key === 'here' ? 'chat' : 'invite'}\n onPress={() => (section.key === 'here' ? onStartChat(item) : onInvite(item))}\n />\n )}\n ListEmptyComponent={\n <View style={styles.centerFill}>\n <EmptyState\n icon=\"account-search-outline\"\n title=\"No contacts found\"\n detail=\"Add phone numbers to your contacts, or search with a different name.\"\n theme={theme}\n action={{ label: 'Back to chats', onPress: onBack }}\n />\n </View>\n }\n contentContainerStyle={sections.length === 0 ? styles.listEmptyGrow : undefined}\n testID=\"chat-contacts-list\"\n />\n </>,\n );\n}\n\nfunction ContactRow({\n contact,\n theme,\n mode,\n busy,\n onPress,\n}: {\n contact: MatchedContact;\n theme: ChatTheme;\n mode: 'chat' | 'invite';\n busy: boolean;\n onPress: () => void;\n}) {\n const name = contact.match?.displayName || contact.displayName;\n return (\n <Pressable\n onPress={onPress}\n disabled={busy}\n accessibilityRole=\"button\"\n accessibilityLabel={mode === 'chat' ? `Message ${name}` : `Invite ${name}`}\n style={({ pressed }) => ({\n width: '100%',\n backgroundColor: pressed ? theme.surface : theme.bg,\n })}\n testID={`chat-contact-${contact.id}`}\n >\n {/* Inner wrapper: NativeWind can strip flexDirection from Pressable styles. */}\n <View style={styles.row}>\n <Avatar name={name} uri={contact.match?.picture ?? contact.imageUri} theme={theme} size={44} />\n <View style={[styles.rowBody, { borderBottomColor: theme.border }]}>\n <View style={styles.rowText}>\n <Text numberOfLines={1} style={[styles.rowName, { color: theme.text }]}>\n {name}\n </Text>\n <Text numberOfLines={1} style={[styles.rowPhone, { color: theme.textMuted }]}>\n {contact.rawNumbers?.[0] ?? contact.e164[0]}\n </Text>\n </View>\n {busy ? (\n <ActivityIndicator size=\"small\" color={theme.accent} />\n ) : mode === 'invite' ? (\n <View style={[styles.inviteChip, { borderColor: theme.accent }]}>\n <Text style={[styles.inviteText, { color: theme.accent }]}>Invite</Text>\n </View>\n ) : null}\n </View>\n </View>\n </Pressable>\n );\n}\n\nconst styles = StyleSheet.create({\n fill: { flex: 1 },\n centerFill: {\n flex: 1,\n alignItems: 'center',\n justifyContent: 'center',\n paddingHorizontal: 28,\n minHeight: 360,\n },\n listEmptyGrow: { flexGrow: 1 },\n loadingText: { fontSize: 14, marginTop: 12 },\n searchWrap: {\n flexDirection: 'row',\n alignItems: 'center',\n marginHorizontal: 16,\n marginVertical: 8,\n borderRadius: 10,\n paddingHorizontal: 12,\n height: 36,\n },\n searchInput: { flex: 1, marginLeft: 8, fontSize: 16, paddingVertical: 0 },\n row: { flexDirection: 'row', alignItems: 'center', width: '100%', paddingLeft: 16 },\n rowBody: {\n flex: 1,\n flexDirection: 'row',\n alignItems: 'center',\n marginLeft: 12,\n paddingVertical: 12,\n paddingRight: 16,\n minHeight: 64,\n borderBottomWidth: StyleSheet.hairlineWidth,\n },\n rowText: { flex: 1, minWidth: 0, marginRight: 12 },\n rowName: { fontSize: 16, fontWeight: '600' },\n rowPhone: { fontSize: 13, marginTop: 2 },\n inviteChip: { borderWidth: 1, borderRadius: 14, paddingHorizontal: 12, paddingVertical: 4 },\n inviteText: { fontSize: 13, fontWeight: '600' },\n sectionTitle: {\n fontSize: 13,\n fontWeight: '600',\n paddingHorizontal: 16,\n paddingTop: 14,\n paddingBottom: 6,\n },\n});\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ContactsScreen.js","sources":["../../../src/features/messaging/ContactsScreen.tsx"],"sourcesContent":["/**\n * \"Find people you know\" - the contacts sync and invite screen.\n */\nimport * as React from 'react';\nimport { ActivityIndicator, Pressable, SectionList, StyleSheet, Text, TextInput, View } from 'react-native';\nimport { SafeAreaView } from 'react-native-safe-area-context';\nimport { MaterialCommunityIcons } from '@expo/vector-icons';\nimport { Avatar, EmptyState, InlineBanner, type ChatTheme } from './ChatComponents';\nimport type { MatchedContact } from './useContactMatches';\nimport type { ChatPermissionStatus } from './chatHost';\n\nfunction ContactsHeader({ theme, onBack }: { theme: ChatTheme; onBack: () => void }) {\n return (\n <SafeAreaView edges={['top']} style={{ backgroundColor: theme.headerBg }}>\n <View\n style={{\n flexDirection: 'row',\n alignItems: 'center',\n paddingHorizontal: 8,\n paddingBottom: 8,\n borderBottomWidth: StyleSheet.hairlineWidth,\n borderBottomColor: theme.border,\n backgroundColor: theme.headerBg,\n }}\n >\n <Pressable onPress={onBack} hitSlop={10} accessibilityRole=\"button\" accessibilityLabel=\"Back\">\n <MaterialCommunityIcons name=\"chevron-left\" size={28} color={theme.accent} />\n </Pressable>\n <Text\n style={{\n flex: 1,\n fontSize: 20,\n fontWeight: '700',\n color: theme.text,\n marginLeft: 8,\n }}\n >\n Select contact\n </Text>\n </View>\n </SafeAreaView>\n );\n}\n\nexport function ContactsScreen({\n permission,\n loading,\n error,\n onYantra,\n invitable,\n theme,\n busyContactId,\n onBack,\n onRequestAccess,\n onOpenSettings,\n onStartChat,\n onInvite,\n onDismissError,\n limited = false,\n onChooseMoreContacts,\n}: {\n permission: ChatPermissionStatus | 'unsupported';\n loading: boolean;\n error: string | null;\n onYantra: MatchedContact[];\n invitable: MatchedContact[];\n theme: ChatTheme;\n busyContactId: string | null;\n onBack: () => void;\n onRequestAccess: () => void;\n onOpenSettings: () => void;\n onStartChat: (contact: MatchedContact) => void;\n onInvite: (contact: MatchedContact) => void;\n onDismissError: () => void;\n /** Only some contacts are shared (iOS 18 Limited Access). */\n limited?: boolean;\n onChooseMoreContacts?: () => void;\n}) {\n const [query, setQuery] = React.useState('');\n\n const filterContact = React.useCallback(\n (c: MatchedContact) => {\n const q = query.trim().toLowerCase();\n if (!q) return true;\n const name = (c.match?.displayName || c.displayName).toLowerCase();\n const qDigits = q.replace(/\\D/g, '');\n return (\n name.includes(q) ||\n (qDigits.length > 0 &&\n [...c.e164, ...(c.rawNumbers ?? [])].some((n) => n.replace(/\\D/g, '').includes(qDigits)))\n );\n },\n [query],\n );\n\n const sections = React.useMemo(\n () =>\n [\n { key: 'here', title: 'On Yantra Chat', data: onYantra.filter(filterContact) },\n { key: 'invite', title: 'Invite to Yantra Chat', data: invitable.filter(filterContact) },\n ].filter((s) => s.data.length > 0),\n [onYantra, invitable, filterContact],\n );\n\n const hasPeople = onYantra.length > 0 || invitable.length > 0;\n\n const shell = (body: React.ReactNode) => (\n <View style={[styles.fill, { backgroundColor: theme.bg }]}>\n <ContactsHeader theme={theme} onBack={onBack} />\n {permission === 'granted' ? (\n <View style={[styles.searchWrap, { backgroundColor: theme.surface }]}>\n <MaterialCommunityIcons name=\"magnify\" size={20} color={theme.textMuted} />\n <TextInput\n value={query}\n onChangeText={setQuery}\n placeholder=\"Search name or number\"\n placeholderTextColor={theme.textMuted}\n style={[styles.searchInput, { color: theme.text }]}\n />\n </View>\n ) : null}\n {body}\n </View>\n );\n\n if (permission === 'unsupported' && !hasPeople) {\n return shell(\n <View style={styles.centerFill}>\n <EmptyState\n icon=\"cellphone-off\"\n title=\"Not available in this build\"\n detail=\"Finding people from your contacts needs a newer version of the app.\"\n theme={theme}\n action={{ label: 'Back to chats', onPress: onBack }}\n />\n </View>,\n );\n }\n\n if (permission === 'denied' && !hasPeople) {\n return shell(\n <View style={styles.centerFill}>\n <EmptyState\n icon=\"lock-outline\"\n title=\"Contacts access is off\"\n detail=\"Allow contacts access in Settings to find people you know on Yantra Chat.\"\n theme={theme}\n action={{ label: 'Open Settings', onPress: onOpenSettings }}\n />\n </View>,\n );\n }\n\n if (permission === 'undetermined' && !hasPeople) {\n return shell(\n <View style={styles.centerFill}>\n <EmptyState\n icon=\"account-multiple-outline\"\n title={loading ? 'Looking for people' : 'No other members yet'}\n detail={\n loading\n ? 'Checking who is already on Yantra Chat…'\n : 'You are registered. Allow contacts to invite people, or start a chat when someone else registers.'\n }\n theme={theme}\n action={{ label: 'Find from contacts', onPress: onRequestAccess }}\n />\n </View>,\n );\n }\n\n if (loading && !sections.length) {\n return shell(\n <View style={styles.centerFill}>\n <ActivityIndicator color={theme.accent} />\n <Text style={[styles.loadingText, { color: theme.textMuted }]}>Checking your contacts…</Text>\n </View>,\n );\n }\n\n // A limited selection is never a dead end: the user can share more contacts from here.\n const listHeader =\n permission === 'granted' && limited ? (\n <View>\n <View style={[styles.limitedNote, { backgroundColor: theme.surface }]}>\n <MaterialCommunityIcons name=\"account-lock-outline\" size={20} color={theme.textMuted} />\n <Text style={[styles.limitedText, { color: theme.textMuted }]}>\n You shared only some contacts with Yantra. People you did not share will not show here.\n </Text>\n </View>\n {onChooseMoreContacts ? (\n <ActionRow\n icon=\"account-multiple-plus-outline\"\n label=\"Choose more contacts\"\n theme={theme}\n onPress={onChooseMoreContacts}\n testID=\"chat-contacts-choose-more\"\n />\n ) : null}\n </View>\n ) : null;\n\n return shell(\n <>\n <InlineBanner message={error} theme={theme} onDismiss={onDismissError} />\n <SectionList\n ListHeaderComponent={listHeader}\n sections={sections}\n keyExtractor={(item) => item.id}\n stickySectionHeadersEnabled={false}\n renderSectionHeader={({ section }) => (\n <Text style={[styles.sectionTitle, { color: theme.textMuted, backgroundColor: theme.bg }]}>\n {section.title}\n </Text>\n )}\n renderItem={({ item, section }) => (\n <ContactRow\n contact={item}\n theme={theme}\n busy={busyContactId === item.id}\n mode={section.key === 'here' ? 'chat' : 'invite'}\n onPress={() => (section.key === 'here' ? onStartChat(item) : onInvite(item))}\n />\n )}\n ListEmptyComponent={\n <View style={styles.centerFill}>\n <EmptyState\n icon=\"account-search-outline\"\n title=\"No contacts found\"\n detail={\n limited && !query.trim()\n ? 'Choose more contacts to share with Yantra.'\n : 'Add phone numbers to your contacts, or search with a different name.'\n }\n theme={theme}\n action={{ label: 'Back to chats', onPress: onBack }}\n />\n </View>\n }\n contentContainerStyle={sections.length === 0 ? styles.listEmptyGrow : undefined}\n testID=\"chat-contacts-list\"\n />\n </>,\n );\n}\n\nfunction ActionRow({\n icon,\n label,\n theme,\n onPress,\n testID,\n}: {\n icon: React.ComponentProps<typeof MaterialCommunityIcons>['name'];\n label: string;\n theme: ChatTheme;\n onPress: () => void;\n testID: string;\n}) {\n return (\n <Pressable\n onPress={onPress}\n accessibilityRole=\"button\"\n accessibilityLabel={label}\n style={({ pressed }) => ({ width: '100%', backgroundColor: pressed ? theme.surface : theme.bg })}\n testID={testID}\n >\n {/* Inner wrapper: NativeWind can strip flexDirection from Pressable styles. */}\n <View style={styles.row}>\n <View style={[styles.actionIcon, { backgroundColor: theme.surface }]}>\n <MaterialCommunityIcons name={icon} size={22} color={theme.accent} />\n </View>\n <View style={[styles.rowBody, { borderBottomColor: theme.border }]}>\n <Text style={[styles.rowName, { color: theme.accent }]}>{label}</Text>\n </View>\n </View>\n </Pressable>\n );\n}\n\nfunction ContactRow({\n contact,\n theme,\n mode,\n busy,\n onPress,\n}: {\n contact: MatchedContact;\n theme: ChatTheme;\n mode: 'chat' | 'invite';\n busy: boolean;\n onPress: () => void;\n}) {\n const name = contact.match?.displayName || contact.displayName;\n return (\n <Pressable\n onPress={onPress}\n disabled={busy}\n accessibilityRole=\"button\"\n accessibilityLabel={mode === 'chat' ? `Message ${name}` : `Invite ${name}`}\n style={({ pressed }) => ({\n width: '100%',\n backgroundColor: pressed ? theme.surface : theme.bg,\n })}\n testID={`chat-contact-${contact.id}`}\n >\n {/* Inner wrapper: NativeWind can strip flexDirection from Pressable styles. */}\n <View style={styles.row}>\n <Avatar name={name} uri={contact.match?.picture ?? contact.imageUri} theme={theme} size={44} />\n <View style={[styles.rowBody, { borderBottomColor: theme.border }]}>\n <View style={styles.rowText}>\n <Text numberOfLines={1} style={[styles.rowName, { color: theme.text }]}>\n {name}\n </Text>\n <Text numberOfLines={1} style={[styles.rowPhone, { color: theme.textMuted }]}>\n {contact.rawNumbers?.[0] ?? contact.e164[0]}\n </Text>\n </View>\n {busy ? (\n <ActivityIndicator size=\"small\" color={theme.accent} />\n ) : mode === 'invite' ? (\n <View style={[styles.inviteChip, { borderColor: theme.accent }]}>\n <Text style={[styles.inviteText, { color: theme.accent }]}>Invite</Text>\n </View>\n ) : null}\n </View>\n </View>\n </Pressable>\n );\n}\n\nconst styles = StyleSheet.create({\n fill: { flex: 1 },\n centerFill: {\n flex: 1,\n alignItems: 'center',\n justifyContent: 'center',\n paddingHorizontal: 28,\n minHeight: 360,\n },\n listEmptyGrow: { flexGrow: 1 },\n loadingText: { fontSize: 14, marginTop: 12 },\n searchWrap: {\n flexDirection: 'row',\n alignItems: 'center',\n marginHorizontal: 16,\n marginVertical: 8,\n borderRadius: 10,\n paddingHorizontal: 12,\n height: 36,\n },\n searchInput: { flex: 1, marginLeft: 8, fontSize: 16, paddingVertical: 0 },\n row: { flexDirection: 'row', alignItems: 'center', width: '100%', paddingLeft: 16 },\n rowBody: {\n flex: 1,\n flexDirection: 'row',\n alignItems: 'center',\n marginLeft: 12,\n paddingVertical: 12,\n paddingRight: 16,\n minHeight: 64,\n borderBottomWidth: StyleSheet.hairlineWidth,\n },\n rowText: { flex: 1, minWidth: 0, marginRight: 12 },\n rowName: { fontSize: 16, fontWeight: '600' },\n rowPhone: { fontSize: 13, marginTop: 2 },\n actionIcon: { width: 44, height: 44, borderRadius: 22, alignItems: 'center', justifyContent: 'center' },\n limitedNote: {\n flexDirection: 'row',\n alignItems: 'flex-start',\n marginHorizontal: 16,\n marginTop: 4,\n marginBottom: 4,\n padding: 12,\n borderRadius: 10,\n },\n limitedText: { flex: 1, fontSize: 13, lineHeight: 18, marginLeft: 10 },\n inviteChip: { borderWidth: 1, borderRadius: 14, paddingHorizontal: 12, paddingVertical: 4 },\n inviteText: { fontSize: 13, fontWeight: '600' },\n sectionTitle: {\n fontSize: 13,\n fontWeight: '600',\n paddingHorizontal: 16,\n paddingTop: 14,\n paddingBottom: 6,\n },\n});\n"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {jsx,jsxs,Fragment}from'react/jsx-runtime';import*as React from'react';import {StyleSheet,Dimensions,Modal,Pressable,View,Text,KeyboardAvoidingView,TextInput,ScrollView,Platform}from'react-native';import {MaterialCommunityIcons}from'@expo/vector-icons';import {
|
|
1
|
+
import {jsx,jsxs,Fragment}from'react/jsx-runtime';import*as React from'react';import {StyleSheet,Dimensions,Modal,Pressable,View,Text,KeyboardAvoidingView,TextInput,ScrollView,Platform}from'react-native';import {MaterialCommunityIcons}from'@expo/vector-icons';import {searchEmoji,EMOJI_CATEGORIES,emojiFromInput}from'./emojiCatalog.js';var __defProp = Object.defineProperty;
|
|
2
2
|
var __defProps = Object.defineProperties;
|
|
3
3
|
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
4
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
@@ -117,6 +117,13 @@ function EmojiPickerSheet({
|
|
|
117
117
|
}) {
|
|
118
118
|
const [text, setText] = React.useState("");
|
|
119
119
|
const cell = Math.floor((Dimensions.get("window").width - 24) / 8);
|
|
120
|
+
const query = text.trim();
|
|
121
|
+
const results = React.useMemo(() => query ? searchEmoji(query) : null, [query]);
|
|
122
|
+
const sections = results ? [{
|
|
123
|
+
key: "results",
|
|
124
|
+
title: results.length ? "Results" : "No emoji found",
|
|
125
|
+
emojis: results
|
|
126
|
+
}] : EMOJI_CATEGORIES;
|
|
120
127
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
121
128
|
/* @__PURE__ */ jsx(Pressable, { style: styles.sheetBackdrop, onPress: onClose, accessibilityLabel: "Close emoji picker" }),
|
|
122
129
|
/* @__PURE__ */ jsx(KeyboardAvoidingView, { behavior: Platform.OS === "ios" ? "padding" : void 0, style: styles.sheetDock, children: /* @__PURE__ */ jsxs(View, { style: [styles.sheet, {
|
|
@@ -138,11 +145,11 @@ function EmojiPickerSheet({
|
|
|
138
145
|
return;
|
|
139
146
|
}
|
|
140
147
|
setText(value);
|
|
141
|
-
}, placeholder: "
|
|
148
|
+
}, placeholder: "Search emoji", placeholderTextColor: theme.textMuted, style: [styles.sheetInput, {
|
|
142
149
|
color: theme.text
|
|
143
|
-
}], autoCorrect: false, testID: "chat-emoji-input" })
|
|
150
|
+
}], autoCorrect: false, autoCapitalize: "none", returnKeyType: "search", clearButtonMode: "while-editing", testID: "chat-emoji-input" })
|
|
144
151
|
] }),
|
|
145
|
-
/* @__PURE__ */ jsx(ScrollView, { style: styles.sheetScroll, keyboardShouldPersistTaps: "handled", children:
|
|
152
|
+
/* @__PURE__ */ jsx(ScrollView, { style: styles.sheetScroll, keyboardShouldPersistTaps: "handled", children: sections.map((category) => /* @__PURE__ */ jsxs(View, { children: [
|
|
146
153
|
/* @__PURE__ */ jsx(Text, { style: [styles.sheetTitle, {
|
|
147
154
|
color: theme.textMuted
|
|
148
155
|
}], children: category.title }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageActions.js","sources":["../../../src/features/messaging/MessageActions.tsx"],"sourcesContent":["/**\n * iMessage-style long-press on a message: the Tapback bar above the bubble and\n * an action menu below it, over a dimmed transcript.\n *\n * Plain react-native `Modal` on purpose - the chat remote may only use what the\n * host shares, so no blur view or gesture library.\n */\nimport * as React from 'react';\nimport {\n Dimensions,\n KeyboardAvoidingView,\n Modal,\n Platform,\n Pressable,\n ScrollView,\n StyleSheet,\n Text,\n TextInput,\n View,\n} from 'react-native';\nimport { MaterialCommunityIcons } from '@expo/vector-icons';\nimport type { ChatTheme } from './ChatComponents';\nimport type { ChatMessage } from './chatModel';\nimport { EMOJI_CATEGORIES, emojiFromInput } from './emojiCatalog';\n\n/** The iMessage Tapback set. */\nexport const TAPBACKS = ['❤️', '👍', '👎', '😂', '‼️', '❓'] as const;\n\nexport interface BubbleFrame {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface MessageAction {\n key: string;\n label: string;\n icon: React.ComponentProps<typeof MaterialCommunityIcons>['name'];\n onPress: () => void;\n}\n\nconst BAR_HEIGHT = 52;\nconst GAP = 8;\nconst MENU_ROW = 46;\nconst EDGE = 12;\n\nexport function MessageActionsOverlay({\n message,\n frame,\n theme,\n actions,\n onReact,\n onClose,\n onDismissed,\n}: {\n message: ChatMessage | null;\n frame: BubbleFrame | null;\n theme: ChatTheme;\n actions: MessageAction[];\n onReact: (emoji: string) => void;\n onClose: () => void;\n /** Fires once the overlay is fully gone (iOS `onDismiss`), for work that needs to present UI. */\n onDismissed?: () => void;\n}) {\n const visible = Boolean(message && frame);\n const { height: screenH, width: screenW } = Dimensions.get('window');\n const mine = Boolean(message?.isMine);\n const current = message?.reactions.find((r) => r.mine)?.reaction ?? null;\n const [picking, setPicking] = React.useState(false);\n // A custom (non-Tapback) pick sits in the bar before \"+\", as in iMessage.\n const barEmojis = current && !(TAPBACKS as readonly string[]).includes(current) ? [...TAPBACKS, current] : TAPBACKS;\n\n React.useEffect(() => {\n if (!visible) setPicking(false);\n }, [visible]);\n\n if (picking && message) {\n // Same Modal instance as the overlay: presenting a second modal while the first dismisses fails on iOS.\n return (\n <Modal visible transparent animationType=\"fade\" onRequestClose={onClose} statusBarTranslucent>\n <EmojiPickerSheet\n theme={theme}\n current={current}\n onPick={(emoji) => {\n setPicking(false);\n onReact(emoji);\n }}\n onClose={() => {\n setPicking(false);\n onClose();\n }}\n />\n </Modal>\n );\n }\n\n // Keep bar + bubble + menu on screen: shift the whole stack when the bubble sits near an edge.\n const bubbleH = Math.min(frame?.height ?? 0, screenH * 0.4);\n const menuH = actions.length * MENU_ROW;\n const stackH = BAR_HEIGHT + GAP + bubbleH + GAP + menuH;\n const desiredTop = (frame?.y ?? 0) - BAR_HEIGHT - GAP;\n const top = Math.max(EDGE + 44, Math.min(desiredTop, screenH - stackH - EDGE - 24));\n const align = mine ? { right: EDGE } : { left: EDGE };\n const bubbleWidth = Math.min(frame?.width ?? 0, screenW - EDGE * 2);\n\n return (\n <Modal\n visible={visible}\n transparent\n animationType=\"fade\"\n onRequestClose={onClose}\n onDismiss={onDismissed}\n statusBarTranslucent\n >\n <Pressable style={styles.backdrop} onPress={onClose} accessibilityLabel=\"Close message actions\">\n {message && frame ? (\n <View style={[styles.stack, { top }, align]} pointerEvents=\"box-none\">\n <View\n style={[\n styles.bar,\n { backgroundColor: theme.bg, alignSelf: mine ? 'flex-end' : 'flex-start' },\n ]}\n >\n {barEmojis.map((emoji) => {\n const selected = emoji === current;\n return (\n <Pressable\n key={emoji}\n onPress={() => onReact(emoji)}\n accessibilityRole=\"button\"\n accessibilityLabel={selected ? `Remove ${emoji} reaction` : `React ${emoji}`}\n accessibilityState={{ selected }}\n style={[styles.tapback, selected && { backgroundColor: theme.accent }]}\n testID={`chat-tapback-${emoji}`}\n >\n <Text style={styles.tapbackEmoji}>{emoji}</Text>\n </Pressable>\n );\n })}\n <Pressable\n onPress={() => setPicking(true)}\n accessibilityRole=\"button\"\n accessibilityLabel=\"More emoji\"\n style={[styles.tapback, { backgroundColor: theme.surface }]}\n testID=\"chat-tapback-more\"\n >\n <MaterialCommunityIcons name=\"plus\" size={24} color={theme.textMuted} />\n </Pressable>\n </View>\n\n <View\n style={[\n styles.preview,\n {\n width: bubbleWidth,\n maxHeight: bubbleH,\n backgroundColor: mine ? theme.bubbleMine : theme.bubbleTheirs,\n alignSelf: mine ? 'flex-end' : 'flex-start',\n },\n ]}\n >\n <Text\n numberOfLines={12}\n style={[\n styles.previewText,\n { color: mine ? theme.bubbleMineText : theme.bubbleTheirsText },\n ]}\n >\n {message.body}\n </Text>\n </View>\n\n {actions.length ? (\n <View\n style={[\n styles.menu,\n { backgroundColor: theme.bg, alignSelf: mine ? 'flex-end' : 'flex-start' },\n ]}\n >\n {actions.map((action, i) => (\n <Pressable\n key={action.key}\n onPress={action.onPress}\n accessibilityRole=\"button\"\n style={({ pressed }) => [\n { backgroundColor: pressed ? theme.surface : 'transparent' },\n i > 0 && {\n borderTopWidth: StyleSheet.hairlineWidth,\n borderTopColor: theme.border,\n },\n ]}\n testID={`chat-message-action-${action.key}`}\n >\n {/* Inner wrapper: NativeWind can strip flexDirection from Pressable styles. */}\n <View style={styles.menuRow}>\n <Text style={[styles.menuLabel, { color: theme.text }]}>\n {action.label}\n </Text>\n <MaterialCommunityIcons name={action.icon} size={20} color={theme.text} />\n </View>\n </Pressable>\n ))}\n </View>\n ) : null}\n </View>\n ) : null}\n </Pressable>\n </Modal>\n );\n}\n\n/**\n * Any-emoji picker behind \"+\": a field for the system emoji keyboard plus a grid.\n * iOS cannot open the emoji keyboard programmatically without native code, so the\n * field opens the keyboard and the user switches to emoji with the globe key.\n */\nfunction EmojiPickerSheet({\n theme,\n current,\n onPick,\n onClose,\n}: {\n theme: ChatTheme;\n current: string | null;\n onPick: (emoji: string) => void;\n onClose: () => void;\n}) {\n const [text, setText] = React.useState('');\n const cell = Math.floor((Dimensions.get('window').width - 24) / 8);\n\n return (\n <>\n <Pressable style={styles.sheetBackdrop} onPress={onClose} accessibilityLabel=\"Close emoji picker\" />\n <KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'padding' : undefined} style={styles.sheetDock}>\n <View style={[styles.sheet, { backgroundColor: theme.bg }]}>\n <View style={[styles.sheetHandle, { backgroundColor: theme.border }]} />\n <View style={[styles.sheetSearch, { backgroundColor: theme.surface, flexDirection: 'row' }]}>\n <MaterialCommunityIcons name=\"emoticon-outline\" size={20} color={theme.textMuted} />\n <TextInput\n value={text}\n onChangeText={(value) => {\n const emoji = emojiFromInput(value);\n if (emoji) {\n setText('');\n onPick(emoji);\n return;\n }\n setText(value);\n }}\n placeholder=\"Type any emoji (🌐 for the emoji keyboard)\"\n placeholderTextColor={theme.textMuted}\n style={[styles.sheetInput, { color: theme.text }]}\n autoCorrect={false}\n testID=\"chat-emoji-input\"\n />\n </View>\n <ScrollView style={styles.sheetScroll} keyboardShouldPersistTaps=\"handled\">\n {EMOJI_CATEGORIES.map((category) => (\n <View key={category.key}>\n <Text style={[styles.sheetTitle, { color: theme.textMuted }]}>{category.title}</Text>\n <View style={[styles.sheetGrid, { flexDirection: 'row' }]}>\n {category.emojis.map((emoji) => (\n <Pressable\n key={`${category.key}-${emoji}`}\n onPress={() => onPick(emoji)}\n accessibilityRole=\"button\"\n accessibilityLabel={`React ${emoji}`}\n style={[\n {\n width: cell,\n height: cell,\n alignItems: 'center',\n justifyContent: 'center',\n },\n emoji === current && {\n backgroundColor: theme.surface,\n borderRadius: 10,\n },\n ]}\n >\n <Text style={styles.sheetEmoji}>{emoji}</Text>\n </Pressable>\n ))}\n </View>\n </View>\n ))}\n </ScrollView>\n </View>\n </KeyboardAvoidingView>\n </>\n );\n}\n\nconst styles = StyleSheet.create({\n sheetBackdrop: { ...StyleSheet.absoluteFillObject, backgroundColor: 'rgba(15, 23, 42, 0.45)' },\n sheetDock: { flex: 1, justifyContent: 'flex-end' },\n sheet: { borderTopLeftRadius: 16, borderTopRightRadius: 16, paddingTop: 8, paddingBottom: 24, maxHeight: '70%' },\n sheetHandle: { alignSelf: 'center', width: 36, height: 5, borderRadius: 3, marginBottom: 8 },\n sheetSearch: {\n alignItems: 'center',\n marginHorizontal: 12,\n marginBottom: 6,\n borderRadius: 10,\n paddingHorizontal: 10,\n height: 38,\n },\n sheetInput: { flex: 1, marginLeft: 8, fontSize: 16, paddingVertical: 0 },\n sheetScroll: { paddingHorizontal: 12 },\n sheetTitle: { fontSize: 13, fontWeight: '600', marginTop: 10, marginBottom: 4 },\n sheetGrid: { flexWrap: 'wrap' },\n sheetEmoji: { fontSize: 28 },\n backdrop: { flex: 1, backgroundColor: 'rgba(15, 23, 42, 0.45)' },\n stack: { position: 'absolute', maxWidth: '100%' },\n bar: {\n flexDirection: 'row',\n alignItems: 'center',\n height: BAR_HEIGHT,\n paddingHorizontal: 6,\n borderRadius: BAR_HEIGHT / 2,\n marginBottom: GAP,\n shadowColor: '#000',\n shadowOpacity: 0.18,\n shadowRadius: 12,\n shadowOffset: { width: 0, height: 4 },\n elevation: 6,\n },\n tapback: { width: 40, height: 40, borderRadius: 20, alignItems: 'center', justifyContent: 'center' },\n tapbackEmoji: { fontSize: 24 },\n preview: { borderRadius: 18, paddingHorizontal: 14, paddingVertical: 9, overflow: 'hidden' },\n previewText: { fontSize: 17, lineHeight: 22 },\n menu: {\n marginTop: GAP,\n width: 220,\n borderRadius: 14,\n overflow: 'hidden',\n shadowColor: '#000',\n shadowOpacity: 0.18,\n shadowRadius: 12,\n shadowOffset: { width: 0, height: 4 },\n elevation: 6,\n },\n menuRow: {\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'space-between',\n height: MENU_ROW,\n paddingHorizontal: 16,\n },\n menuLabel: { fontSize: 16 },\n});\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MessageActions.js","sources":["../../../src/features/messaging/MessageActions.tsx"],"sourcesContent":["/**\n * iMessage-style long-press on a message: the Tapback bar above the bubble and\n * an action menu below it, over a dimmed transcript.\n *\n * Plain react-native `Modal` on purpose - the chat remote may only use what the\n * host shares, so no blur view or gesture library.\n */\nimport * as React from 'react';\nimport {\n Dimensions,\n KeyboardAvoidingView,\n Modal,\n Platform,\n Pressable,\n ScrollView,\n StyleSheet,\n Text,\n TextInput,\n View,\n} from 'react-native';\nimport { MaterialCommunityIcons } from '@expo/vector-icons';\nimport type { ChatTheme } from './ChatComponents';\nimport type { ChatMessage } from './chatModel';\nimport { EMOJI_CATEGORIES, emojiFromInput, searchEmoji } from './emojiCatalog';\n\n/** The iMessage Tapback set. */\nexport const TAPBACKS = ['❤️', '👍', '👎', '😂', '‼️', '❓'] as const;\n\nexport interface BubbleFrame {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface MessageAction {\n key: string;\n label: string;\n icon: React.ComponentProps<typeof MaterialCommunityIcons>['name'];\n onPress: () => void;\n}\n\nconst BAR_HEIGHT = 52;\nconst GAP = 8;\nconst MENU_ROW = 46;\nconst EDGE = 12;\n\nexport function MessageActionsOverlay({\n message,\n frame,\n theme,\n actions,\n onReact,\n onClose,\n onDismissed,\n}: {\n message: ChatMessage | null;\n frame: BubbleFrame | null;\n theme: ChatTheme;\n actions: MessageAction[];\n onReact: (emoji: string) => void;\n onClose: () => void;\n /** Fires once the overlay is fully gone (iOS `onDismiss`), for work that needs to present UI. */\n onDismissed?: () => void;\n}) {\n const visible = Boolean(message && frame);\n const { height: screenH, width: screenW } = Dimensions.get('window');\n const mine = Boolean(message?.isMine);\n const current = message?.reactions.find((r) => r.mine)?.reaction ?? null;\n const [picking, setPicking] = React.useState(false);\n // A custom (non-Tapback) pick sits in the bar before \"+\", as in iMessage.\n const barEmojis = current && !(TAPBACKS as readonly string[]).includes(current) ? [...TAPBACKS, current] : TAPBACKS;\n\n React.useEffect(() => {\n if (!visible) setPicking(false);\n }, [visible]);\n\n if (picking && message) {\n // Same Modal instance as the overlay: presenting a second modal while the first dismisses fails on iOS.\n return (\n <Modal visible transparent animationType=\"fade\" onRequestClose={onClose} statusBarTranslucent>\n <EmojiPickerSheet\n theme={theme}\n current={current}\n onPick={(emoji) => {\n setPicking(false);\n onReact(emoji);\n }}\n onClose={() => {\n setPicking(false);\n onClose();\n }}\n />\n </Modal>\n );\n }\n\n // Keep bar + bubble + menu on screen: shift the whole stack when the bubble sits near an edge.\n const bubbleH = Math.min(frame?.height ?? 0, screenH * 0.4);\n const menuH = actions.length * MENU_ROW;\n const stackH = BAR_HEIGHT + GAP + bubbleH + GAP + menuH;\n const desiredTop = (frame?.y ?? 0) - BAR_HEIGHT - GAP;\n const top = Math.max(EDGE + 44, Math.min(desiredTop, screenH - stackH - EDGE - 24));\n const align = mine ? { right: EDGE } : { left: EDGE };\n const bubbleWidth = Math.min(frame?.width ?? 0, screenW - EDGE * 2);\n\n return (\n <Modal\n visible={visible}\n transparent\n animationType=\"fade\"\n onRequestClose={onClose}\n onDismiss={onDismissed}\n statusBarTranslucent\n >\n <Pressable style={styles.backdrop} onPress={onClose} accessibilityLabel=\"Close message actions\">\n {message && frame ? (\n <View style={[styles.stack, { top }, align]} pointerEvents=\"box-none\">\n <View\n style={[\n styles.bar,\n { backgroundColor: theme.bg, alignSelf: mine ? 'flex-end' : 'flex-start' },\n ]}\n >\n {barEmojis.map((emoji) => {\n const selected = emoji === current;\n return (\n <Pressable\n key={emoji}\n onPress={() => onReact(emoji)}\n accessibilityRole=\"button\"\n accessibilityLabel={selected ? `Remove ${emoji} reaction` : `React ${emoji}`}\n accessibilityState={{ selected }}\n style={[styles.tapback, selected && { backgroundColor: theme.accent }]}\n testID={`chat-tapback-${emoji}`}\n >\n <Text style={styles.tapbackEmoji}>{emoji}</Text>\n </Pressable>\n );\n })}\n <Pressable\n onPress={() => setPicking(true)}\n accessibilityRole=\"button\"\n accessibilityLabel=\"More emoji\"\n style={[styles.tapback, { backgroundColor: theme.surface }]}\n testID=\"chat-tapback-more\"\n >\n <MaterialCommunityIcons name=\"plus\" size={24} color={theme.textMuted} />\n </Pressable>\n </View>\n\n <View\n style={[\n styles.preview,\n {\n width: bubbleWidth,\n maxHeight: bubbleH,\n backgroundColor: mine ? theme.bubbleMine : theme.bubbleTheirs,\n alignSelf: mine ? 'flex-end' : 'flex-start',\n },\n ]}\n >\n <Text\n numberOfLines={12}\n style={[\n styles.previewText,\n { color: mine ? theme.bubbleMineText : theme.bubbleTheirsText },\n ]}\n >\n {message.body}\n </Text>\n </View>\n\n {actions.length ? (\n <View\n style={[\n styles.menu,\n { backgroundColor: theme.bg, alignSelf: mine ? 'flex-end' : 'flex-start' },\n ]}\n >\n {actions.map((action, i) => (\n <Pressable\n key={action.key}\n onPress={action.onPress}\n accessibilityRole=\"button\"\n style={({ pressed }) => [\n { backgroundColor: pressed ? theme.surface : 'transparent' },\n i > 0 && {\n borderTopWidth: StyleSheet.hairlineWidth,\n borderTopColor: theme.border,\n },\n ]}\n testID={`chat-message-action-${action.key}`}\n >\n {/* Inner wrapper: NativeWind can strip flexDirection from Pressable styles. */}\n <View style={styles.menuRow}>\n <Text style={[styles.menuLabel, { color: theme.text }]}>\n {action.label}\n </Text>\n <MaterialCommunityIcons name={action.icon} size={20} color={theme.text} />\n </View>\n </Pressable>\n ))}\n </View>\n ) : null}\n </View>\n ) : null}\n </Pressable>\n </Modal>\n );\n}\n\n/**\n * Any-emoji picker behind \"+\": a search field plus a grid. Typing a word searches\n * emoji by name (\"sleep\"); typing an emoji from the system keyboard (globe key)\n * reacts with it directly, so emoji missing from the grid are still reachable.\n */\nfunction EmojiPickerSheet({\n theme,\n current,\n onPick,\n onClose,\n}: {\n theme: ChatTheme;\n current: string | null;\n onPick: (emoji: string) => void;\n onClose: () => void;\n}) {\n const [text, setText] = React.useState('');\n const cell = Math.floor((Dimensions.get('window').width - 24) / 8);\n const query = text.trim();\n const results = React.useMemo(() => (query ? searchEmoji(query) : null), [query]);\n const sections = results\n ? [{ key: 'results', title: results.length ? 'Results' : 'No emoji found', emojis: results }]\n : EMOJI_CATEGORIES;\n\n return (\n <>\n <Pressable style={styles.sheetBackdrop} onPress={onClose} accessibilityLabel=\"Close emoji picker\" />\n <KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'padding' : undefined} style={styles.sheetDock}>\n <View style={[styles.sheet, { backgroundColor: theme.bg }]}>\n <View style={[styles.sheetHandle, { backgroundColor: theme.border }]} />\n <View style={[styles.sheetSearch, { backgroundColor: theme.surface, flexDirection: 'row' }]}>\n <MaterialCommunityIcons name=\"emoticon-outline\" size={20} color={theme.textMuted} />\n <TextInput\n value={text}\n onChangeText={(value) => {\n const emoji = emojiFromInput(value);\n if (emoji) {\n setText('');\n onPick(emoji);\n return;\n }\n setText(value);\n }}\n placeholder=\"Search emoji\"\n placeholderTextColor={theme.textMuted}\n style={[styles.sheetInput, { color: theme.text }]}\n autoCorrect={false}\n autoCapitalize=\"none\"\n returnKeyType=\"search\"\n clearButtonMode=\"while-editing\"\n testID=\"chat-emoji-input\"\n />\n </View>\n <ScrollView style={styles.sheetScroll} keyboardShouldPersistTaps=\"handled\">\n {sections.map((category) => (\n <View key={category.key}>\n <Text style={[styles.sheetTitle, { color: theme.textMuted }]}>{category.title}</Text>\n <View style={[styles.sheetGrid, { flexDirection: 'row' }]}>\n {category.emojis.map((emoji) => (\n <Pressable\n key={`${category.key}-${emoji}`}\n onPress={() => onPick(emoji)}\n accessibilityRole=\"button\"\n accessibilityLabel={`React ${emoji}`}\n style={[\n {\n width: cell,\n height: cell,\n alignItems: 'center',\n justifyContent: 'center',\n },\n emoji === current && {\n backgroundColor: theme.surface,\n borderRadius: 10,\n },\n ]}\n >\n <Text style={styles.sheetEmoji}>{emoji}</Text>\n </Pressable>\n ))}\n </View>\n </View>\n ))}\n </ScrollView>\n </View>\n </KeyboardAvoidingView>\n </>\n );\n}\n\nconst styles = StyleSheet.create({\n sheetBackdrop: { ...StyleSheet.absoluteFillObject, backgroundColor: 'rgba(15, 23, 42, 0.45)' },\n sheetDock: { flex: 1, justifyContent: 'flex-end' },\n sheet: { borderTopLeftRadius: 16, borderTopRightRadius: 16, paddingTop: 8, paddingBottom: 24, maxHeight: '70%' },\n sheetHandle: { alignSelf: 'center', width: 36, height: 5, borderRadius: 3, marginBottom: 8 },\n sheetSearch: {\n alignItems: 'center',\n marginHorizontal: 12,\n marginBottom: 6,\n borderRadius: 10,\n paddingHorizontal: 10,\n height: 38,\n },\n sheetInput: { flex: 1, marginLeft: 8, fontSize: 16, paddingVertical: 0 },\n sheetScroll: { paddingHorizontal: 12 },\n sheetTitle: { fontSize: 13, fontWeight: '600', marginTop: 10, marginBottom: 4 },\n sheetGrid: { flexWrap: 'wrap' },\n sheetEmoji: { fontSize: 28 },\n backdrop: { flex: 1, backgroundColor: 'rgba(15, 23, 42, 0.45)' },\n stack: { position: 'absolute', maxWidth: '100%' },\n bar: {\n flexDirection: 'row',\n alignItems: 'center',\n height: BAR_HEIGHT,\n paddingHorizontal: 6,\n borderRadius: BAR_HEIGHT / 2,\n marginBottom: GAP,\n shadowColor: '#000',\n shadowOpacity: 0.18,\n shadowRadius: 12,\n shadowOffset: { width: 0, height: 4 },\n elevation: 6,\n },\n tapback: { width: 40, height: 40, borderRadius: 20, alignItems: 'center', justifyContent: 'center' },\n tapbackEmoji: { fontSize: 24 },\n preview: { borderRadius: 18, paddingHorizontal: 14, paddingVertical: 9, overflow: 'hidden' },\n previewText: { fontSize: 17, lineHeight: 22 },\n menu: {\n marginTop: GAP,\n width: 220,\n borderRadius: 14,\n overflow: 'hidden',\n shadowColor: '#000',\n shadowOpacity: 0.18,\n shadowRadius: 12,\n shadowOffset: { width: 0, height: 4 },\n elevation: 6,\n },\n menuRow: {\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'space-between',\n height: MENU_ROW,\n paddingHorizontal: 16,\n },\n menuLabel: { fontSize: 16 },\n});\n"],"names":[],"mappings":"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"}
|
|
@@ -154,7 +154,10 @@ function NativeChatExperience({
|
|
|
154
154
|
} : void 0, onDismissError: () => setDismissedError(thread.error) }) });
|
|
155
155
|
}
|
|
156
156
|
if (route === "contacts") {
|
|
157
|
-
return /* @__PURE__ */ jsx(Shell, { theme, testID, children: /* @__PURE__ */ jsx(ContactsScreen, { permission: contactMatches.permission, loading: contactMatches.loading, error: startChatError != null ? startChatError : contactMatches.error, onYantra: contactMatches.onYantra, invitable: contactMatches.invitable, theme, busyContactId, onBack: () => setRoute("list"), onRequestAccess: () => void contactMatches.requestAccess(), onOpenSettings: () => void contactMatches.openSettings(), onStartChat: (c) => void startChatWith(c), onInvite: (c) => void inviteContact(c),
|
|
157
|
+
return /* @__PURE__ */ jsx(Shell, { theme, testID, children: /* @__PURE__ */ jsx(ContactsScreen, { permission: contactMatches.permission, loading: contactMatches.loading, error: startChatError != null ? startChatError : contactMatches.error, onYantra: contactMatches.onYantra, invitable: contactMatches.invitable, theme, busyContactId, onBack: () => setRoute("list"), onRequestAccess: () => void contactMatches.requestAccess(), onOpenSettings: () => void contactMatches.openSettings(), onStartChat: (c) => void startChatWith(c), onInvite: (c) => void inviteContact(c), limited: contactMatches.limited, onChooseMoreContacts: contactMatches.chooseMoreContacts ? () => {
|
|
158
|
+
var _a;
|
|
159
|
+
return void ((_a = contactMatches.chooseMoreContacts) == null ? void 0 : _a.call(contactMatches));
|
|
160
|
+
} : void 0, onDismissError: () => {
|
|
158
161
|
if (startChatError) setStartChatError(null);
|
|
159
162
|
else void contactMatches.refresh();
|
|
160
163
|
} }) });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NativeChatExperience.js","sources":["../../../src/features/messaging/NativeChatExperience.tsx"],"sourcesContent":["/**\n * The chat remote's exposed root (`chat_native/Chat`).\n *\n * Owns its own navigation as local state - the remote must not import\n * @react-navigation, because a second copy of the navigator would not share the\n * host's container. Android hardware back is handled with RN's own BackHandler.\n *\n * Everything here is plain react-native (plus expo-image and\n * @expo/vector-icons, both host-shared singletons) - no gluestack, no\n * NativeWind, no Apollo - because a federated remote may only use what the host\n * shares, and every extra dependency is another way to fail to load on a device\n * in a way the simulator will not reproduce.\n */\nimport * as React from 'react';\nimport { ActivityIndicator, AppState, BackHandler, Platform, StyleSheet, Text, View } from 'react-native';\nimport type { NativeChatExperienceProps } from './chatHost';\nimport { chatThemes, EmptyState } from './ChatComponents';\nimport { useAppTheme } from '../../theme/AppThemeProvider';\nimport { ConversationListScreen } from './ConversationListScreen';\nimport { ContactsScreen } from './ContactsScreen';\nimport { ThreadScreen } from './ThreadScreen';\nimport { useConversations, useMarkRead, useThread } from './useChatData';\nimport { VIEW_CHANNEL_MUTATION, YANTRA_CHAT_INVITER_QUERY } from './chatOperations';\nimport { buildChatInviteUrl, chatInviteMessage } from './chatInviteLink';\n\n/** Used only when the host predates `chatInviteUrl`; the host supplies the per-environment domain. */\nconst DEFAULT_INVITE_WEB_BASE = 'https://yantra.app';\nimport { useContactMatches, type MatchedContact } from './useContactMatches';\nimport type { WirePost } from './chatModel';\n\n/** The screens the remote routes between internally. */\nexport type ChatRoute = 'list' | 'thread' | 'contacts' | 'newGroup' | 'groupInfo';\n\nexport function NativeChatExperience({\n viewer,\n sessionError,\n services,\n initialChannelId,\n initialPeer,\n onActiveChannelChange,\n onInternalRouteChange,\n testID,\n}: NativeChatExperienceProps) {\n const { isDark } = useAppTheme();\n const theme = isDark ? chatThemes.dark : chatThemes.light;\n\n const [route, setRoute] = React.useState<ChatRoute>(initialChannelId ? 'thread' : 'list');\n const [activeChannelId, setActiveChannelId] = React.useState<string | null>(initialChannelId ?? null);\n const [dismissedError, setDismissedError] = React.useState<string | null>(null);\n\n const conversations = useConversations(services, viewer, { syncListRealtime: route === 'list' });\n const contactMatches = useContactMatches(services.gql, services.contacts, viewer, services.phone);\n const [busyContactId, setBusyContactId] = React.useState<string | null>(null);\n const [startChatError, setStartChatError] = React.useState<string | null>(null);\n const onThreadListUpdate = React.useCallback(\n (post: WirePost) => {\n if (!activeChannelId) return;\n conversations.applyConversationMessage(activeChannelId, post);\n // The viewer is looking at this thread, so a message arriving here is already read.\n conversations.markRead(activeChannelId);\n },\n [activeChannelId, conversations.applyConversationMessage, conversations.markRead],\n );\n const thread = useThread(services.gql, route === 'thread' ? activeChannelId : null, viewer, {\n onConversationMessage: onThreadListUpdate,\n });\n\n // Clearing the badge is local-first; the mutation is fire-and-forget.\n useMarkRead(services.gql, route === 'thread' ? activeChannelId : null, conversations.markRead);\n\n // Mark read again on LEAVING a thread, so messages that arrived while it was open\n // are not counted as unread by the server on the next list load.\n const threadOpenRef = React.useRef<string | null>(null);\n React.useEffect(() => {\n const open = route === 'thread' ? activeChannelId : null;\n const left = threadOpenRef.current;\n if (left && left !== open) void services.gql.mutate(VIEW_CHANNEL_MUTATION, { id: left });\n threadOpenRef.current = open;\n }, [route, activeChannelId, services.gql]);\n\n // The host suppresses a foreground push banner for the conversation already\n // on screen, so it has to be told every time that changes.\n React.useEffect(() => {\n onActiveChannelChange?.(route === 'thread' ? activeChannelId : null);\n }, [route, activeChannelId, onActiveChannelChange]);\n\n React.useEffect(() => {\n const internal: 'list' | 'thread' | 'contacts' | 'register' =\n route === 'thread' ? 'thread' : route === 'contacts' ? 'contacts' : 'list';\n onInternalRouteChange?.(internal);\n }, [route, onInternalRouteChange]);\n\n // The address book changes outside the app, so re-read it whenever the picker opens\n // and whenever the app returns to the foreground while the picker is showing.\n const refreshContactsRef = React.useRef(contactMatches.refresh);\n refreshContactsRef.current = contactMatches.refresh;\n const contactsGranted = contactMatches.permission === 'granted';\n React.useEffect(() => {\n if (route !== 'contacts' || !contactsGranted) return undefined;\n void refreshContactsRef.current();\n const sub = AppState.addEventListener('change', (state) => {\n if (state === 'active') void refreshContactsRef.current();\n });\n return () => sub.remove();\n }, [route, contactsGranted]);\n\n const activeConversation = React.useMemo(\n () => conversations.conversations.find((c) => c.id === activeChannelId) ?? null,\n [conversations.conversations, activeChannelId],\n );\n\n // Android back pops the internal stack before the host's navigator sees it.\n React.useEffect(() => {\n if (Platform.OS !== 'android') return undefined;\n const sub = BackHandler.addEventListener('hardwareBackPress', () => {\n if (route === 'list') return false;\n setRoute('list');\n return true;\n });\n return () => sub.remove();\n }, [route]);\n\n const openThread = React.useCallback((id: string) => {\n setActiveChannelId(id);\n setRoute('thread');\n }, []);\n\n /**\n * Open (or reuse) the conversation with a matched contact. createDirectChannel\n * is idempotent server-side, so tapping someone you already talk to lands in\n * the existing thread rather than making a second one.\n */\n const startChatWith = React.useCallback(\n async (contact: MatchedContact) => {\n if (!contact.match || busyContactId) return;\n setBusyContactId(contact.id);\n setStartChatError(null);\n try {\n const id = await conversations.openDirect(contact.match.userId, contact.match.displayName);\n if (id) openThread(id);\n else setStartChatError('Could not start this conversation. Please try again.');\n } finally {\n setBusyContactId(null);\n }\n },\n [conversations, openThread, busyContactId],\n );\n\n // Opened from a chat invite link: go straight into the conversation with the inviter\n // (createDirectChannel is idempotent, so reopening an old invite lands in the same thread).\n // The link is editable by anyone, so the server has to vouch for the inviter first, and\n // its name is used - never the one in the link.\n // Tracks the invite object, not the user: every tap on a link arrives as a new object,\n // so reopening a link retries, while re-renders of the same one do not.\n const openedPeer = React.useRef<typeof initialPeer | null>(null);\n const [inviteNotice, setInviteNotice] = React.useState<string | null>(null);\n const { openDirect } = conversations;\n React.useEffect(() => {\n const peer = initialPeer;\n if (!peer?.userId || !viewer?.userId || peer.userId === viewer.userId) return;\n if (openedPeer.current === peer) return;\n openedPeer.current = peer;\n void (async () => {\n const res = await services.gql.query<{\n yantraChatInviter?: { userId: string; displayName: string } | null;\n }>(YANTRA_CHAT_INVITER_QUERY, { userId: peer.userId }, { fetchPolicy: 'network-only' });\n const inviter = res.data?.yantraChatInviter;\n if (!inviter) {\n setInviteNotice(\n res.errors?.length\n ? 'Could not open this invite. Check your connection and open the link again.'\n : 'This invite link is not valid anymore. Ask for a new one.',\n );\n return;\n }\n const id = await openDirect(inviter.userId, inviter.displayName);\n if (id) openThread(id);\n else setInviteNotice(`Could not start a conversation with ${inviter.displayName}. Please try again.`);\n })();\n }, [initialPeer, viewer?.userId, services.gql, openDirect, openThread]);\n\n /**\n * Invite someone who is not here yet.\n *\n * With phone-based membership (issue #1312):\n * 1. Call the connector to generate a deep link\n * 2. Open SMS composer with the deep link (user approves before sending)\n * 3. Fall back to share sheet where SMS is unavailable (simulators)\n *\n * The deep link includes the invitee's phone and the inviter's phone,\n * enabling install-then-attribute and direct link-back to the inviter\n * after the app is installed.\n */\n const inviteContact = React.useCallback(\n async (contact: MatchedContact) => {\n const link = services.link;\n\n if (!link || !viewer?.userId) return;\n const smsTo = contact.rawNumbers?.length ? contact.rawNumbers.slice(0, 1) : contact.e164.slice(0, 1);\n if (!smsTo.length) return;\n\n // The link names the inviter (so the app can open this conversation on arrival) and\n // carries no phone numbers.\n const invite = { inviterId: viewer.userId, inviterName: viewer.displayName ?? null };\n const url = services.phone?.chatInviteUrl?.(invite) ?? buildChatInviteUrl(DEFAULT_INVITE_WEB_BASE, invite);\n const body = chatInviteMessage(viewer.displayName, url);\n\n // Saved numbers, not our E.164 guesses: the phone dials them in its own region.\n const result = await link.composeSms(smsTo, body);\n if (result === 'unavailable') await link.share(body);\n },\n [services.link, services.phone, viewer],\n );\n\n const activeTitle = React.useMemo(\n () => conversations.conversations.find((c) => c.id === activeChannelId)?.title ?? 'Chat',\n [conversations.conversations, activeChannelId],\n );\n\n if (sessionError) {\n return (\n <Shell theme={theme} testID={testID}>\n <EmptyState\n icon=\"alert-circle-outline\"\n title=\"Chat is unavailable\"\n detail={sessionError}\n theme={theme}\n />\n </Shell>\n );\n }\n\n // Session still resolving: a spinner, never an error. org/user arrive a beat\n // after mount and a flash of failure reads as a broken feature.\n if (!viewer) {\n return (\n <Shell theme={theme} testID={testID}>\n <View style={styles.center}>\n <ActivityIndicator />\n </View>\n </Shell>\n );\n }\n\n if (route === 'thread' && activeChannelId) {\n const threadError = thread.error === dismissedError ? null : thread.error;\n return (\n <Shell theme={theme} testID={testID}>\n <ThreadScreen\n title={activeTitle}\n avatarUrl={activeConversation?.avatarUrl}\n messages={thread.messages}\n loading={thread.loading}\n loadingMore={thread.loadingMore}\n error={threadError}\n theme={theme}\n connectionState={services.gql.connectionState}\n haptics={services.haptics}\n onBack={() => setRoute('list')}\n onSend={(body) => void thread.send(body)}\n onLoadMore={thread.loadMore}\n onRetry={thread.retry}\n onReact={(messageId, emoji) => {\n const current = thread.messages\n .find((m) => m.id === messageId)\n ?.reactions.find((r) => r.mine)?.reaction;\n // Tapping your current Tapback removes it, as in iMessage.\n thread.react(messageId, current === emoji ? null : emoji);\n }}\n onCopyText={services.clipboard ? (text) => void services.clipboard?.copy(text) : undefined}\n onShareText={services.link ? (text) => void services.link?.share(text) : undefined}\n onDismissError={() => setDismissedError(thread.error)}\n />\n </Shell>\n );\n }\n\n if (route === 'contacts') {\n return (\n <Shell theme={theme} testID={testID}>\n <ContactsScreen\n permission={contactMatches.permission}\n loading={contactMatches.loading}\n error={startChatError ?? contactMatches.error}\n onYantra={contactMatches.onYantra}\n invitable={contactMatches.invitable}\n theme={theme}\n busyContactId={busyContactId}\n onBack={() => setRoute('list')}\n onRequestAccess={() => void contactMatches.requestAccess()}\n onOpenSettings={() => void contactMatches.openSettings()}\n onStartChat={(c) => void startChatWith(c)}\n onInvite={(c) => void inviteContact(c)}\n onDismissError={() => {\n if (startChatError) setStartChatError(null);\n else void contactMatches.refresh();\n }}\n />\n </Shell>\n );\n }\n\n const listError = conversations.error === dismissedError ? null : conversations.error;\n return (\n <Shell theme={theme} testID={testID}>\n <ConversationListScreen\n conversations={conversations.conversations}\n loading={conversations.loading}\n refreshing={conversations.refreshing}\n error={listError}\n theme={theme}\n connectionState={services.gql.connectionState}\n canInvite\n onRefresh={() => {\n conversations.refresh();\n void contactMatches.refresh();\n }}\n onRetry={conversations.refresh}\n loadingMore={conversations.loadingMore}\n onLoadMore={conversations.loadMore}\n onOpen={openThread}\n onNewChat={() => setRoute('contacts')}\n onDismissError={() => setDismissedError(conversations.error)}\n notice={inviteNotice}\n onDismissNotice={() => setInviteNotice(null)}\n />\n </Shell>\n );\n}\n\nfunction Shell({ theme, testID, children }: { theme: { bg: string }; testID?: string; children: React.ReactNode }) {\n return (\n <View testID={testID ?? 'chat-native'} style={[styles.fill, { backgroundColor: theme.bg }]}>\n {children}\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n fill: { flex: 1 },\n center: { flex: 1, alignItems: 'center', justifyContent: 'center' },\n});\n\nexport default NativeChatExperience;\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"NativeChatExperience.js","sources":["../../../src/features/messaging/NativeChatExperience.tsx"],"sourcesContent":["/**\n * The chat remote's exposed root (`chat_native/Chat`).\n *\n * Owns its own navigation as local state - the remote must not import\n * @react-navigation, because a second copy of the navigator would not share the\n * host's container. Android hardware back is handled with RN's own BackHandler.\n *\n * Everything here is plain react-native (plus expo-image and\n * @expo/vector-icons, both host-shared singletons) - no gluestack, no\n * NativeWind, no Apollo - because a federated remote may only use what the host\n * shares, and every extra dependency is another way to fail to load on a device\n * in a way the simulator will not reproduce.\n */\nimport * as React from 'react';\nimport { ActivityIndicator, AppState, BackHandler, Platform, StyleSheet, Text, View } from 'react-native';\nimport type { NativeChatExperienceProps } from './chatHost';\nimport { chatThemes, EmptyState } from './ChatComponents';\nimport { useAppTheme } from '../../theme/AppThemeProvider';\nimport { ConversationListScreen } from './ConversationListScreen';\nimport { ContactsScreen } from './ContactsScreen';\nimport { ThreadScreen } from './ThreadScreen';\nimport { useConversations, useMarkRead, useThread } from './useChatData';\nimport { VIEW_CHANNEL_MUTATION, YANTRA_CHAT_INVITER_QUERY } from './chatOperations';\nimport { buildChatInviteUrl, chatInviteMessage } from './chatInviteLink';\n\n/** Used only when the host predates `chatInviteUrl`; the host supplies the per-environment domain. */\nconst DEFAULT_INVITE_WEB_BASE = 'https://yantra.app';\nimport { useContactMatches, type MatchedContact } from './useContactMatches';\nimport type { WirePost } from './chatModel';\n\n/** The screens the remote routes between internally. */\nexport type ChatRoute = 'list' | 'thread' | 'contacts' | 'newGroup' | 'groupInfo';\n\nexport function NativeChatExperience({\n viewer,\n sessionError,\n services,\n initialChannelId,\n initialPeer,\n onActiveChannelChange,\n onInternalRouteChange,\n testID,\n}: NativeChatExperienceProps) {\n const { isDark } = useAppTheme();\n const theme = isDark ? chatThemes.dark : chatThemes.light;\n\n const [route, setRoute] = React.useState<ChatRoute>(initialChannelId ? 'thread' : 'list');\n const [activeChannelId, setActiveChannelId] = React.useState<string | null>(initialChannelId ?? null);\n const [dismissedError, setDismissedError] = React.useState<string | null>(null);\n\n const conversations = useConversations(services, viewer, { syncListRealtime: route === 'list' });\n const contactMatches = useContactMatches(services.gql, services.contacts, viewer, services.phone);\n const [busyContactId, setBusyContactId] = React.useState<string | null>(null);\n const [startChatError, setStartChatError] = React.useState<string | null>(null);\n const onThreadListUpdate = React.useCallback(\n (post: WirePost) => {\n if (!activeChannelId) return;\n conversations.applyConversationMessage(activeChannelId, post);\n // The viewer is looking at this thread, so a message arriving here is already read.\n conversations.markRead(activeChannelId);\n },\n [activeChannelId, conversations.applyConversationMessage, conversations.markRead],\n );\n const thread = useThread(services.gql, route === 'thread' ? activeChannelId : null, viewer, {\n onConversationMessage: onThreadListUpdate,\n });\n\n // Clearing the badge is local-first; the mutation is fire-and-forget.\n useMarkRead(services.gql, route === 'thread' ? activeChannelId : null, conversations.markRead);\n\n // Mark read again on LEAVING a thread, so messages that arrived while it was open\n // are not counted as unread by the server on the next list load.\n const threadOpenRef = React.useRef<string | null>(null);\n React.useEffect(() => {\n const open = route === 'thread' ? activeChannelId : null;\n const left = threadOpenRef.current;\n if (left && left !== open) void services.gql.mutate(VIEW_CHANNEL_MUTATION, { id: left });\n threadOpenRef.current = open;\n }, [route, activeChannelId, services.gql]);\n\n // The host suppresses a foreground push banner for the conversation already\n // on screen, so it has to be told every time that changes.\n React.useEffect(() => {\n onActiveChannelChange?.(route === 'thread' ? activeChannelId : null);\n }, [route, activeChannelId, onActiveChannelChange]);\n\n React.useEffect(() => {\n const internal: 'list' | 'thread' | 'contacts' | 'register' =\n route === 'thread' ? 'thread' : route === 'contacts' ? 'contacts' : 'list';\n onInternalRouteChange?.(internal);\n }, [route, onInternalRouteChange]);\n\n // The address book changes outside the app, so re-read it whenever the picker opens\n // and whenever the app returns to the foreground while the picker is showing.\n const refreshContactsRef = React.useRef(contactMatches.refresh);\n refreshContactsRef.current = contactMatches.refresh;\n const contactsGranted = contactMatches.permission === 'granted';\n React.useEffect(() => {\n if (route !== 'contacts' || !contactsGranted) return undefined;\n void refreshContactsRef.current();\n const sub = AppState.addEventListener('change', (state) => {\n if (state === 'active') void refreshContactsRef.current();\n });\n return () => sub.remove();\n }, [route, contactsGranted]);\n\n const activeConversation = React.useMemo(\n () => conversations.conversations.find((c) => c.id === activeChannelId) ?? null,\n [conversations.conversations, activeChannelId],\n );\n\n // Android back pops the internal stack before the host's navigator sees it.\n React.useEffect(() => {\n if (Platform.OS !== 'android') return undefined;\n const sub = BackHandler.addEventListener('hardwareBackPress', () => {\n if (route === 'list') return false;\n setRoute('list');\n return true;\n });\n return () => sub.remove();\n }, [route]);\n\n const openThread = React.useCallback((id: string) => {\n setActiveChannelId(id);\n setRoute('thread');\n }, []);\n\n /**\n * Open (or reuse) the conversation with a matched contact. createDirectChannel\n * is idempotent server-side, so tapping someone you already talk to lands in\n * the existing thread rather than making a second one.\n */\n const startChatWith = React.useCallback(\n async (contact: MatchedContact) => {\n if (!contact.match || busyContactId) return;\n setBusyContactId(contact.id);\n setStartChatError(null);\n try {\n const id = await conversations.openDirect(contact.match.userId, contact.match.displayName);\n if (id) openThread(id);\n else setStartChatError('Could not start this conversation. Please try again.');\n } finally {\n setBusyContactId(null);\n }\n },\n [conversations, openThread, busyContactId],\n );\n\n // Opened from a chat invite link: go straight into the conversation with the inviter\n // (createDirectChannel is idempotent, so reopening an old invite lands in the same thread).\n // The link is editable by anyone, so the server has to vouch for the inviter first, and\n // its name is used - never the one in the link.\n // Tracks the invite object, not the user: every tap on a link arrives as a new object,\n // so reopening a link retries, while re-renders of the same one do not.\n const openedPeer = React.useRef<typeof initialPeer | null>(null);\n const [inviteNotice, setInviteNotice] = React.useState<string | null>(null);\n const { openDirect } = conversations;\n React.useEffect(() => {\n const peer = initialPeer;\n if (!peer?.userId || !viewer?.userId || peer.userId === viewer.userId) return;\n if (openedPeer.current === peer) return;\n openedPeer.current = peer;\n void (async () => {\n const res = await services.gql.query<{\n yantraChatInviter?: { userId: string; displayName: string } | null;\n }>(YANTRA_CHAT_INVITER_QUERY, { userId: peer.userId }, { fetchPolicy: 'network-only' });\n const inviter = res.data?.yantraChatInviter;\n if (!inviter) {\n setInviteNotice(\n res.errors?.length\n ? 'Could not open this invite. Check your connection and open the link again.'\n : 'This invite link is not valid anymore. Ask for a new one.',\n );\n return;\n }\n const id = await openDirect(inviter.userId, inviter.displayName);\n if (id) openThread(id);\n else setInviteNotice(`Could not start a conversation with ${inviter.displayName}. Please try again.`);\n })();\n }, [initialPeer, viewer?.userId, services.gql, openDirect, openThread]);\n\n /**\n * Invite someone who is not here yet.\n *\n * With phone-based membership (issue #1312):\n * 1. Call the connector to generate a deep link\n * 2. Open SMS composer with the deep link (user approves before sending)\n * 3. Fall back to share sheet where SMS is unavailable (simulators)\n *\n * The deep link includes the invitee's phone and the inviter's phone,\n * enabling install-then-attribute and direct link-back to the inviter\n * after the app is installed.\n */\n const inviteContact = React.useCallback(\n async (contact: MatchedContact) => {\n const link = services.link;\n\n if (!link || !viewer?.userId) return;\n const smsTo = contact.rawNumbers?.length ? contact.rawNumbers.slice(0, 1) : contact.e164.slice(0, 1);\n if (!smsTo.length) return;\n\n // The link names the inviter (so the app can open this conversation on arrival) and\n // carries no phone numbers.\n const invite = { inviterId: viewer.userId, inviterName: viewer.displayName ?? null };\n const url = services.phone?.chatInviteUrl?.(invite) ?? buildChatInviteUrl(DEFAULT_INVITE_WEB_BASE, invite);\n const body = chatInviteMessage(viewer.displayName, url);\n\n // Saved numbers, not our E.164 guesses: the phone dials them in its own region.\n const result = await link.composeSms(smsTo, body);\n if (result === 'unavailable') await link.share(body);\n },\n [services.link, services.phone, viewer],\n );\n\n const activeTitle = React.useMemo(\n () => conversations.conversations.find((c) => c.id === activeChannelId)?.title ?? 'Chat',\n [conversations.conversations, activeChannelId],\n );\n\n if (sessionError) {\n return (\n <Shell theme={theme} testID={testID}>\n <EmptyState\n icon=\"alert-circle-outline\"\n title=\"Chat is unavailable\"\n detail={sessionError}\n theme={theme}\n />\n </Shell>\n );\n }\n\n // Session still resolving: a spinner, never an error. org/user arrive a beat\n // after mount and a flash of failure reads as a broken feature.\n if (!viewer) {\n return (\n <Shell theme={theme} testID={testID}>\n <View style={styles.center}>\n <ActivityIndicator />\n </View>\n </Shell>\n );\n }\n\n if (route === 'thread' && activeChannelId) {\n const threadError = thread.error === dismissedError ? null : thread.error;\n return (\n <Shell theme={theme} testID={testID}>\n <ThreadScreen\n title={activeTitle}\n avatarUrl={activeConversation?.avatarUrl}\n messages={thread.messages}\n loading={thread.loading}\n loadingMore={thread.loadingMore}\n error={threadError}\n theme={theme}\n connectionState={services.gql.connectionState}\n haptics={services.haptics}\n onBack={() => setRoute('list')}\n onSend={(body) => void thread.send(body)}\n onLoadMore={thread.loadMore}\n onRetry={thread.retry}\n onReact={(messageId, emoji) => {\n const current = thread.messages\n .find((m) => m.id === messageId)\n ?.reactions.find((r) => r.mine)?.reaction;\n // Tapping your current Tapback removes it, as in iMessage.\n thread.react(messageId, current === emoji ? null : emoji);\n }}\n onCopyText={services.clipboard ? (text) => void services.clipboard?.copy(text) : undefined}\n onShareText={services.link ? (text) => void services.link?.share(text) : undefined}\n onDismissError={() => setDismissedError(thread.error)}\n />\n </Shell>\n );\n }\n\n if (route === 'contacts') {\n return (\n <Shell theme={theme} testID={testID}>\n <ContactsScreen\n permission={contactMatches.permission}\n loading={contactMatches.loading}\n error={startChatError ?? contactMatches.error}\n onYantra={contactMatches.onYantra}\n invitable={contactMatches.invitable}\n theme={theme}\n busyContactId={busyContactId}\n onBack={() => setRoute('list')}\n onRequestAccess={() => void contactMatches.requestAccess()}\n onOpenSettings={() => void contactMatches.openSettings()}\n onStartChat={(c) => void startChatWith(c)}\n onInvite={(c) => void inviteContact(c)}\n limited={contactMatches.limited}\n onChooseMoreContacts={\n contactMatches.chooseMoreContacts ? () => void contactMatches.chooseMoreContacts?.() : undefined\n }\n onDismissError={() => {\n if (startChatError) setStartChatError(null);\n else void contactMatches.refresh();\n }}\n />\n </Shell>\n );\n }\n\n const listError = conversations.error === dismissedError ? null : conversations.error;\n return (\n <Shell theme={theme} testID={testID}>\n <ConversationListScreen\n conversations={conversations.conversations}\n loading={conversations.loading}\n refreshing={conversations.refreshing}\n error={listError}\n theme={theme}\n connectionState={services.gql.connectionState}\n canInvite\n onRefresh={() => {\n conversations.refresh();\n void contactMatches.refresh();\n }}\n onRetry={conversations.refresh}\n loadingMore={conversations.loadingMore}\n onLoadMore={conversations.loadMore}\n onOpen={openThread}\n onNewChat={() => setRoute('contacts')}\n onDismissError={() => setDismissedError(conversations.error)}\n notice={inviteNotice}\n onDismissNotice={() => setInviteNotice(null)}\n />\n </Shell>\n );\n}\n\nfunction Shell({ theme, testID, children }: { theme: { bg: string }; testID?: string; children: React.ReactNode }) {\n return (\n <View testID={testID ?? 'chat-native'} style={[styles.fill, { backgroundColor: theme.bg }]}>\n {children}\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n fill: { flex: 1 },\n center: { flex: 1, alignItems: 'center', justifyContent: 'center' },\n});\n\nexport default NativeChatExperience;\n"],"names":[],"mappings":"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"}
|
|
@@ -46,7 +46,7 @@ function parseChatInviteUrl(url) {
|
|
|
46
46
|
}
|
|
47
47
|
function chatInviteMessage(inviterName, url) {
|
|
48
48
|
const who = (inviterName == null ? void 0 : inviterName.trim()) || "A friend";
|
|
49
|
-
return `${who} invited you to chat on Yantra.
|
|
49
|
+
return `${who} invited you to chat on Yantra. Tap the link below to join.
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
${url}`;
|
|
52
52
|
}export{CHAT_INVITE_PATH,buildChatInviteUrl,chatInviteMessage,parseChatInviteUrl};//# sourceMappingURL=chatInviteLink.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chatInviteLink.js","sources":["../../../src/features/messaging/chatInviteLink.ts"],"sourcesContent":["/**\n * Yantra Chat invite links (issue #1312).\n *\n * https://<web>/chat/invite?by=<inviterAccountId>&n=<inviterName>\n *\n * The same link must work as a universal/app link (app installed), as a web page\n * (app not installed - the landing page offers the stores and an \"Open in Yantra\"\n * button), and through the app's custom schemes. It carries the inviter's account\n * id so the app can open the conversation straight away - and deliberately NO phone\n * numbers, which the previous `?phone=&inviter=` form leaked into every forwarded SMS.\n *\n * Hand-parsed rather than `new URL(...)`: React Native's URL has no `searchParams`\n * without a polyfill and throws on custom schemes.\n */\n\nexport const CHAT_INVITE_PATH = '/chat/invite';\nexport const CHAT_INVITE_SCHEME = 'yantraapp';\n\nexport interface ChatInvite {\n inviterId: string;\n inviterName: string | null;\n}\n\nconst ACCOUNT_ID = /^[a-f0-9]{24}$/i;\n/** `chat/invite` as whole path segments, after any scheme/host/`--` prefix. */\nconst CHAT_INVITE_SEGMENTS = /(?:^|[/:])chat\\/invite(?:$|[/?#])/i;\n\nfunction query(url: string): Record<string, string> {\n const withoutFragment = url.split('#')[0];\n const at = withoutFragment.indexOf('?');\n const out: Record<string, string> = {};\n if (at === -1) return out;\n for (const pair of withoutFragment.slice(at + 1).split('&')) {\n const eq = pair.indexOf('=');\n if (eq <= 0) continue;\n try {\n const key = decodeURIComponent(pair.slice(0, eq)).trim();\n const value = decodeURIComponent(pair.slice(eq + 1).replace(/\\+/g, ' ')).trim();\n if (key && value && !(key in out)) out[key] = value;\n } catch {\n // A malformed escape in one pair must not discard the others.\n }\n }\n return out;\n}\n\nfunction inviteQuery(invite: ChatInvite): string {\n const params = [`by=${encodeURIComponent(invite.inviterId)}`];\n const name = invite.inviterName?.trim();\n if (name) params.push(`n=${encodeURIComponent(name.slice(0, 60))}`);\n return params.join('&');\n}\n\nexport function buildChatInviteUrl(baseUrl: string, invite: ChatInvite): string {\n const base = baseUrl.trim().replace(/\\/+$/, '');\n return `${base}${CHAT_INVITE_PATH}?${inviteQuery(invite)}`;\n}\n\n/** The app's own scheme for the same invite - what the web page's \"Open in Yantra\" uses. */\nexport function chatInviteAppUrl(invite: ChatInvite): string {\n return `${CHAT_INVITE_SCHEME}://chat/invite?${inviteQuery(invite)}`;\n}\n\nexport function parseChatInviteUrl(url: string | null | undefined): ChatInvite | null {\n if (!url) return null;\n const value = String(url);\n const queryAt = value.indexOf('?');\n const path = queryAt === -1 ? value : value.slice(0, queryAt);\n if (!CHAT_INVITE_SEGMENTS.test(path)) return null;\n const params = query(value);\n const inviterId = params.by ?? '';\n if (!ACCOUNT_ID.test(inviterId)) return null;\n return { inviterId, inviterName: params.n || null };\n}\n\nexport function chatInviteMessage(inviterName: string | null | undefined, url: string): string {\n const who = inviterName?.trim() || 'A friend';\n return `${who} invited you to chat on Yantra
|
|
1
|
+
{"version":3,"file":"chatInviteLink.js","sources":["../../../src/features/messaging/chatInviteLink.ts"],"sourcesContent":["/**\n * Yantra Chat invite links (issue #1312).\n *\n * https://<web>/chat/invite?by=<inviterAccountId>&n=<inviterName>\n *\n * The same link must work as a universal/app link (app installed), as a web page\n * (app not installed - the landing page offers the stores and an \"Open in Yantra\"\n * button), and through the app's custom schemes. It carries the inviter's account\n * id so the app can open the conversation straight away - and deliberately NO phone\n * numbers, which the previous `?phone=&inviter=` form leaked into every forwarded SMS.\n *\n * Hand-parsed rather than `new URL(...)`: React Native's URL has no `searchParams`\n * without a polyfill and throws on custom schemes.\n */\n\nexport const CHAT_INVITE_PATH = '/chat/invite';\nexport const CHAT_INVITE_SCHEME = 'yantraapp';\n\nexport interface ChatInvite {\n inviterId: string;\n inviterName: string | null;\n}\n\nconst ACCOUNT_ID = /^[a-f0-9]{24}$/i;\n/** `chat/invite` as whole path segments, after any scheme/host/`--` prefix. */\nconst CHAT_INVITE_SEGMENTS = /(?:^|[/:])chat\\/invite(?:$|[/?#])/i;\n\nfunction query(url: string): Record<string, string> {\n const withoutFragment = url.split('#')[0];\n const at = withoutFragment.indexOf('?');\n const out: Record<string, string> = {};\n if (at === -1) return out;\n for (const pair of withoutFragment.slice(at + 1).split('&')) {\n const eq = pair.indexOf('=');\n if (eq <= 0) continue;\n try {\n const key = decodeURIComponent(pair.slice(0, eq)).trim();\n const value = decodeURIComponent(pair.slice(eq + 1).replace(/\\+/g, ' ')).trim();\n if (key && value && !(key in out)) out[key] = value;\n } catch {\n // A malformed escape in one pair must not discard the others.\n }\n }\n return out;\n}\n\nfunction inviteQuery(invite: ChatInvite): string {\n const params = [`by=${encodeURIComponent(invite.inviterId)}`];\n const name = invite.inviterName?.trim();\n if (name) params.push(`n=${encodeURIComponent(name.slice(0, 60))}`);\n return params.join('&');\n}\n\nexport function buildChatInviteUrl(baseUrl: string, invite: ChatInvite): string {\n const base = baseUrl.trim().replace(/\\/+$/, '');\n return `${base}${CHAT_INVITE_PATH}?${inviteQuery(invite)}`;\n}\n\n/** The app's own scheme for the same invite - what the web page's \"Open in Yantra\" uses. */\nexport function chatInviteAppUrl(invite: ChatInvite): string {\n return `${CHAT_INVITE_SCHEME}://chat/invite?${inviteQuery(invite)}`;\n}\n\nexport function parseChatInviteUrl(url: string | null | undefined): ChatInvite | null {\n if (!url) return null;\n const value = String(url);\n const queryAt = value.indexOf('?');\n const path = queryAt === -1 ? value : value.slice(0, queryAt);\n if (!CHAT_INVITE_SEGMENTS.test(path)) return null;\n const params = query(value);\n const inviterId = params.by ?? '';\n if (!ACCOUNT_ID.test(inviterId)) return null;\n return { inviterId, inviterName: params.n || null };\n}\n\n/**\n * The invite SMS/iMessage text. The link goes last and on its own line: iMessage turns a\n * trailing link into a preview card (the text above must read fine without it), and plain\n * SMS shows it as a tappable link.\n */\nexport function chatInviteMessage(inviterName: string | null | undefined, url: string): string {\n const who = inviterName?.trim() || 'A friend';\n return `${who} invited you to chat on Yantra. Tap the link below to join.\\n\\n${url}`;\n}\n"],"names":[],"mappings":"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"}
|