@planningcenter/chat-react-native 3.23.0 → 3.23.1-qa-538.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/build/components/display/toggle_button.d.ts +11 -4
  2. package/build/components/display/toggle_button.d.ts.map +1 -1
  3. package/build/components/display/toggle_button.js +6 -5
  4. package/build/components/display/toggle_button.js.map +1 -1
  5. package/build/components/primitive/form_sheet.d.ts +1 -1
  6. package/build/components/primitive/form_sheet.d.ts.map +1 -1
  7. package/build/components/primitive/form_sheet.js +1 -1
  8. package/build/components/primitive/form_sheet.js.map +1 -1
  9. package/build/hooks/index.d.ts +1 -0
  10. package/build/hooks/index.d.ts.map +1 -1
  11. package/build/hooks/index.js +1 -0
  12. package/build/hooks/index.js.map +1 -1
  13. package/build/hooks/use_conversation.d.ts.map +1 -1
  14. package/build/hooks/use_conversation.js +6 -1
  15. package/build/hooks/use_conversation.js.map +1 -1
  16. package/build/hooks/use_conversation_membership.d.ts +5 -0
  17. package/build/hooks/use_conversation_membership.d.ts.map +1 -0
  18. package/build/hooks/use_conversation_membership.js +54 -0
  19. package/build/hooks/use_conversation_membership.js.map +1 -0
  20. package/build/hooks/use_features.d.ts +1 -0
  21. package/build/hooks/use_features.d.ts.map +1 -1
  22. package/build/hooks/use_features.js +1 -0
  23. package/build/hooks/use_features.js.map +1 -1
  24. package/build/hooks/use_new_conversation_entry.d.ts +3 -0
  25. package/build/hooks/use_new_conversation_entry.d.ts.map +1 -0
  26. package/build/hooks/use_new_conversation_entry.js +20 -0
  27. package/build/hooks/use_new_conversation_entry.js.map +1 -0
  28. package/build/hooks/use_report_message.d.ts +6 -0
  29. package/build/hooks/use_report_message.d.ts.map +1 -0
  30. package/build/hooks/use_report_message.js +28 -0
  31. package/build/hooks/use_report_message.js.map +1 -0
  32. package/build/navigation/index.d.ts +18 -8
  33. package/build/navigation/index.d.ts.map +1 -1
  34. package/build/navigation/index.js +16 -6
  35. package/build/navigation/index.js.map +1 -1
  36. package/build/screens/conversation_details_screen.d.ts.map +1 -1
  37. package/build/screens/conversation_details_screen.js +18 -10
  38. package/build/screens/conversation_details_screen.js.map +1 -1
  39. package/build/screens/conversation_filter_recipients/conversation_filter_recipients_screen.js +5 -3
  40. package/build/screens/conversation_filter_recipients/conversation_filter_recipients_screen.js.map +1 -1
  41. package/build/screens/conversation_select_recipients/conversation_new_entry_screen.d.ts +4 -0
  42. package/build/screens/conversation_select_recipients/conversation_new_entry_screen.d.ts.map +1 -0
  43. package/build/screens/conversation_select_recipients/conversation_new_entry_screen.js +67 -0
  44. package/build/screens/conversation_select_recipients/conversation_new_entry_screen.js.map +1 -0
  45. package/build/screens/conversation_select_recipients/conversation_select_teams_i_lead_recipients_screen.d.ts.map +1 -1
  46. package/build/screens/conversation_select_recipients/conversation_select_teams_i_lead_recipients_screen.js +17 -2
  47. package/build/screens/conversation_select_recipients/conversation_select_teams_i_lead_recipients_screen.js.map +1 -1
  48. package/build/screens/conversation_select_type_screen.d.ts +11 -0
  49. package/build/screens/conversation_select_type_screen.d.ts.map +1 -0
  50. package/build/screens/conversation_select_type_screen.js +32 -0
  51. package/build/screens/conversation_select_type_screen.js.map +1 -0
  52. package/build/screens/conversations/components/list_header_component.d.ts.map +1 -1
  53. package/build/screens/conversations/components/list_header_component.js +54 -25
  54. package/build/screens/conversations/components/list_header_component.js.map +1 -1
  55. package/build/screens/index.d.ts +1 -1
  56. package/build/screens/index.d.ts.map +1 -1
  57. package/build/screens/index.js +1 -1
  58. package/build/screens/index.js.map +1 -1
  59. package/build/screens/message_actions_screen.js +14 -2
  60. package/build/screens/message_actions_screen.js.map +1 -1
  61. package/build/screens/message_report/components/message_preview.d.ts +10 -0
  62. package/build/screens/message_report/components/message_preview.d.ts.map +1 -0
  63. package/build/screens/message_report/components/message_preview.js +81 -0
  64. package/build/screens/message_report/components/message_preview.js.map +1 -0
  65. package/build/screens/message_report/components/report_reason_list.d.ts +9 -0
  66. package/build/screens/message_report/components/report_reason_list.d.ts.map +1 -0
  67. package/build/screens/message_report/components/report_reason_list.js +67 -0
  68. package/build/screens/message_report/components/report_reason_list.js.map +1 -0
  69. package/build/screens/message_report_screen.d.ts +10 -0
  70. package/build/screens/message_report_screen.d.ts.map +1 -0
  71. package/build/screens/message_report_screen.js +214 -0
  72. package/build/screens/message_report_screen.js.map +1 -0
  73. package/build/types/resources/conversation.d.ts +2 -3
  74. package/build/types/resources/conversation.d.ts.map +1 -1
  75. package/build/types/resources/conversation.js.map +1 -1
  76. package/build/types/resources/conversation_membership.d.ts +14 -0
  77. package/build/types/resources/conversation_membership.d.ts.map +1 -0
  78. package/build/types/resources/conversation_membership.js +2 -0
  79. package/build/types/resources/conversation_membership.js.map +1 -0
  80. package/build/types/resources/index.d.ts +1 -0
  81. package/build/types/resources/index.d.ts.map +1 -1
  82. package/build/types/resources/index.js +1 -0
  83. package/build/types/resources/index.js.map +1 -1
  84. package/build/types/resources/message_report.d.ts +19 -0
  85. package/build/types/resources/message_report.d.ts.map +1 -0
  86. package/build/types/resources/message_report.js +9 -0
  87. package/build/types/resources/message_report.js.map +1 -0
  88. package/build/utils/deep_snake_case_keys.d.ts +4 -0
  89. package/build/utils/deep_snake_case_keys.d.ts.map +1 -0
  90. package/build/utils/deep_snake_case_keys.js +13 -0
  91. package/build/utils/deep_snake_case_keys.js.map +1 -0
  92. package/package.json +2 -2
  93. package/src/components/display/toggle_button.tsx +27 -15
  94. package/src/components/primitive/form_sheet.tsx +4 -2
  95. package/src/hooks/index.ts +1 -0
  96. package/src/hooks/use_conversation.ts +6 -1
  97. package/src/hooks/use_conversation_membership.ts +82 -0
  98. package/src/hooks/use_features.ts +1 -0
  99. package/src/hooks/use_new_conversation_entry.ts +31 -0
  100. package/src/hooks/use_report_message.ts +37 -0
  101. package/src/navigation/index.tsx +19 -6
  102. package/src/screens/conversation_details_screen.tsx +34 -16
  103. package/src/screens/conversation_filter_recipients/conversation_filter_recipients_screen.tsx +10 -3
  104. package/src/screens/conversation_select_recipients/conversation_new_entry_screen.tsx +100 -0
  105. package/src/screens/conversation_select_recipients/conversation_select_teams_i_lead_recipients_screen.tsx +23 -3
  106. package/src/screens/conversation_select_type_screen.tsx +53 -0
  107. package/src/screens/conversations/components/list_header_component.tsx +103 -67
  108. package/src/screens/index.ts +1 -1
  109. package/src/screens/message_actions_screen.tsx +24 -2
  110. package/src/screens/message_report/components/message_preview.tsx +99 -0
  111. package/src/screens/message_report/components/report_reason_list.tsx +106 -0
  112. package/src/screens/message_report_screen.tsx +278 -0
  113. package/src/types/resources/conversation.ts +2 -3
  114. package/src/types/resources/conversation_membership.ts +15 -0
  115. package/src/types/resources/index.ts +1 -0
  116. package/src/types/resources/message_report.ts +20 -0
  117. package/src/utils/deep_snake_case_keys.ts +16 -0
  118. package/build/screens/conversation_select_recipients/conversation_select_recipients_screen.d.ts +0 -4
  119. package/build/screens/conversation_select_recipients/conversation_select_recipients_screen.d.ts.map +0 -1
  120. package/build/screens/conversation_select_recipients/conversation_select_recipients_screen.js +0 -146
  121. package/build/screens/conversation_select_recipients/conversation_select_recipients_screen.js.map +0 -1
  122. package/src/screens/conversation_select_recipients/conversation_select_recipients_screen.tsx +0 -215
@@ -0,0 +1,99 @@
1
+ import React from 'react'
2
+ import { View, StyleSheet, ViewStyle } from 'react-native'
3
+ import { Avatar, Text, Icon } from '../../../components'
4
+ import { useTheme } from '../../../hooks'
5
+ import { MessageResource } from '../../../types'
6
+
7
+ interface MessagePreviewProps {
8
+ message: MessageResource
9
+ style?: ViewStyle
10
+ }
11
+
12
+ export function MessagePreview({ message, style }: MessagePreviewProps) {
13
+ const styles = useStyles()
14
+
15
+ const hasImages = message.attachments.some(
16
+ att => att.type === 'MessageAttachment' && att.attributes?.contentType?.startsWith('image/')
17
+ )
18
+
19
+ return (
20
+ <View style={[styles.previewContainer, style]}>
21
+ <View style={styles.previewHeader}>
22
+ <Avatar sourceUri={message.author.avatar} size="md" />
23
+ <Text style={styles.authorName}>{message.author.name}</Text>
24
+ </View>
25
+
26
+ <View style={styles.messageBubbleContainer}>
27
+ {message.text && (
28
+ <View style={styles.messageBubble}>
29
+ <Text style={styles.messageText} numberOfLines={5}>
30
+ {message.text}
31
+ </Text>
32
+ </View>
33
+ )}
34
+
35
+ {hasImages && (
36
+ <View style={styles.imagePlaceholder}>
37
+ <Icon name="general.image" style={styles.placeholderIcon} />
38
+ <Text variant="secondary" style={styles.placeholderText}>
39
+ Image hidden
40
+ </Text>
41
+ </View>
42
+ )}
43
+ </View>
44
+ </View>
45
+ )
46
+ }
47
+
48
+ const useStyles = () => {
49
+ const { colors } = useTheme()
50
+
51
+ return StyleSheet.create({
52
+ previewContainer: {
53
+ gap: 12,
54
+ },
55
+ previewHeader: {
56
+ flexDirection: 'row',
57
+ alignItems: 'center',
58
+ gap: 12,
59
+ },
60
+ authorName: {
61
+ fontSize: 14,
62
+ fontWeight: '600',
63
+ color: colors.textColorDefaultPrimary,
64
+ },
65
+ messageBubbleContainer: {
66
+ gap: 8,
67
+ },
68
+ messageBubble: {
69
+ backgroundColor: colors.fillColorNeutral060,
70
+ borderRadius: 16,
71
+ paddingHorizontal: 16,
72
+ paddingVertical: 12,
73
+ alignSelf: 'flex-start',
74
+ maxWidth: '80%',
75
+ },
76
+ messageText: {
77
+ fontSize: 14,
78
+ color: colors.textColorDefaultPrimary,
79
+ lineHeight: 20,
80
+ },
81
+ imagePlaceholder: {
82
+ height: 120,
83
+ backgroundColor: colors.fillColorNeutral060,
84
+ borderRadius: 16,
85
+ justifyContent: 'center',
86
+ alignItems: 'center',
87
+ gap: 8,
88
+ maxWidth: '80%',
89
+ alignSelf: 'flex-start',
90
+ },
91
+ placeholderIcon: {
92
+ fontSize: 32,
93
+ color: colors.iconColorDefaultSecondary,
94
+ },
95
+ placeholderText: {
96
+ fontSize: 14,
97
+ },
98
+ })
99
+ }
@@ -0,0 +1,106 @@
1
+ import React from 'react'
2
+ import { View, StyleSheet } from 'react-native'
3
+ import { PlatformPressable } from '@react-navigation/elements'
4
+ import { Text } from '../../../components'
5
+ import { useTheme, useCreateAndroidRippleColor } from '../../../hooks'
6
+ import { Haptic } from '../../../utils/native_adapters'
7
+ import {
8
+ MESSAGE_REPORT_REASONS,
9
+ MessageReportReason,
10
+ } from '../../../types/resources/message_report'
11
+
12
+ const MESSAGE_REPORT_REASONS_LIST = Object.entries(MESSAGE_REPORT_REASONS).map(([key, label]) => ({
13
+ key: key as MessageReportReason,
14
+ label,
15
+ }))
16
+
17
+ interface ReportReasonListProps {
18
+ selectedReason: MessageReportReason | null
19
+ onReasonChange: (reason: MessageReportReason) => void
20
+ }
21
+
22
+ export function ReportReasonList({ selectedReason, onReasonChange }: ReportReasonListProps) {
23
+ return (
24
+ <View>
25
+ {MESSAGE_REPORT_REASONS_LIST.map(({ key, label }) => (
26
+ <ReportReasonButton
27
+ key={key}
28
+ reason={key}
29
+ label={label}
30
+ isSelected={selectedReason === key}
31
+ onPress={() => {
32
+ Haptic.impactLight()
33
+ onReasonChange(key)
34
+ }}
35
+ />
36
+ ))}
37
+ </View>
38
+ )
39
+ }
40
+
41
+ interface ReportReasonButtonProps {
42
+ reason: MessageReportReason
43
+ label: string
44
+ isSelected: boolean
45
+ onPress: () => void
46
+ }
47
+
48
+ function ReportReasonButton({ label, isSelected, onPress }: ReportReasonButtonProps) {
49
+ const styles = useStyles({ isSelected })
50
+ const { colors } = useTheme()
51
+ const androidRippleColor = useCreateAndroidRippleColor({ color: colors.fillColorNeutral060 })
52
+
53
+ return (
54
+ <PlatformPressable
55
+ onPress={onPress}
56
+ style={styles.pressable}
57
+ android_ripple={{ color: androidRippleColor }}
58
+ accessibilityRole="radio"
59
+ accessibilityState={{ selected: isSelected }}
60
+ accessibilityLabel={label}
61
+ >
62
+ <View style={styles.row}>
63
+ <View style={styles.radioButton}>
64
+ {isSelected && <View style={styles.radioButtonInner} />}
65
+ </View>
66
+ <Text style={styles.label}>{label}</Text>
67
+ </View>
68
+ </PlatformPressable>
69
+ )
70
+ }
71
+
72
+ const useStyles = ({ isSelected }: { isSelected: boolean }) => {
73
+ const { colors } = useTheme()
74
+
75
+ return StyleSheet.create({
76
+ pressable: {
77
+ paddingVertical: 16,
78
+ paddingHorizontal: 16,
79
+ },
80
+ row: {
81
+ flexDirection: 'row',
82
+ alignItems: 'center',
83
+ gap: 12,
84
+ },
85
+ radioButton: {
86
+ width: 20,
87
+ height: 20,
88
+ borderRadius: 10,
89
+ borderWidth: 2,
90
+ borderColor: isSelected ? colors.interaction : colors.borderColorDefaultBase,
91
+ justifyContent: 'center',
92
+ alignItems: 'center',
93
+ },
94
+ radioButtonInner: {
95
+ width: 10,
96
+ height: 10,
97
+ borderRadius: 5,
98
+ backgroundColor: colors.interaction,
99
+ },
100
+ label: {
101
+ fontSize: 16,
102
+ color: colors.textColorDefaultPrimary,
103
+ flex: 1,
104
+ },
105
+ })
106
+ }
@@ -0,0 +1,278 @@
1
+ import React, { useCallback, useLayoutEffect, useMemo, useState } from 'react'
2
+ import { View, StyleSheet, TextInput, ScrollView } from 'react-native'
3
+ import type {
4
+ NativeStackNavigationOptions,
5
+ NativeStackScreenProps,
6
+ } from '@react-navigation/native-stack'
7
+ import { StaticScreenProps, useNavigation, StackActions } from '@react-navigation/native'
8
+ import { Spinner, Text, KeyboardView } from '../components'
9
+ import BlankState from '../components/primitive/blank_state_primitive'
10
+ import { useTheme } from '../hooks'
11
+ import { useReportMessage } from '../hooks/use_report_message'
12
+ import {
13
+ HeaderDismissButton,
14
+ HeaderTextButton,
15
+ } from '../components/display/platform_modal_header_buttons'
16
+ import { useSafeAreaInsets } from 'react-native-safe-area-context'
17
+ import { useConversationMessages } from '../hooks/use_conversation_messages'
18
+ import { MessageResource } from '../types'
19
+ import { MessageReportReason } from '../types/resources/message_report'
20
+ import { MessagePreview } from './message_report/components/message_preview'
21
+ import { ReportReasonList } from './message_report/components/report_reason_list'
22
+ import { platformFontWeightBold } from '../utils'
23
+
24
+ const MAX_DETAIL_LENGTH = 500
25
+
26
+ export const MessageReportScreenOptions = ({
27
+ navigation,
28
+ }: NativeStackScreenProps<any>): NativeStackNavigationOptions => ({
29
+ presentation: 'modal',
30
+ title: 'Report message',
31
+ headerLeft: () => <HeaderDismissButton title="Cancel" onPress={() => navigation.goBack()} />,
32
+ })
33
+
34
+ export type MessageReportScreenProps = StaticScreenProps<{
35
+ conversation_id: number
36
+ message_id: string
37
+ }>
38
+
39
+ export function MessageReportScreen({ route }: MessageReportScreenProps) {
40
+ const { conversation_id, message_id } = route.params
41
+
42
+ const { messages } = useConversationMessages({ conversation_id }, { refetchOnMount: false })
43
+ const message = messages.find(m => m.id === message_id)
44
+
45
+ if (!message) return null
46
+
47
+ return <MessageReportScreenContent message={message} conversation_id={conversation_id} />
48
+ }
49
+
50
+ function MessageReportScreenContent({
51
+ message,
52
+ conversation_id,
53
+ }: {
54
+ message: MessageResource
55
+ conversation_id: number
56
+ }) {
57
+ const styles = useStyles()
58
+ const navigation = useNavigation()
59
+ const [selectedReason, setSelectedReason] = useState<MessageReportReason | null>(null)
60
+ const [reasonDetail, setReasonDetail] = useState('')
61
+ const { mutate: submitReport, status } = useReportMessage(conversation_id, message.id)
62
+
63
+ const handleReasonChange = useCallback((reason: MessageReportReason) => {
64
+ setSelectedReason(reason)
65
+
66
+ if (reason !== 'other') {
67
+ setReasonDetail('')
68
+ }
69
+ }, [])
70
+
71
+ const isFormValid = useMemo(() => {
72
+ if (selectedReason === null) return false
73
+
74
+ if (selectedReason === 'other' && reasonDetail.trim().length === 0) {
75
+ return false
76
+ }
77
+
78
+ if (status === 'pending') return false
79
+
80
+ return true
81
+ }, [selectedReason, reasonDetail, status])
82
+
83
+ const handleSubmit = useCallback(() => {
84
+ if (!isFormValid || !selectedReason) return
85
+
86
+ submitReport({
87
+ reason: selectedReason,
88
+ reasonDetail: selectedReason === 'other' ? reasonDetail : undefined,
89
+ })
90
+ }, [isFormValid, selectedReason, reasonDetail, submitReport])
91
+
92
+ const handleClose = useCallback(() => {
93
+ navigation.dispatch(StackActions.popTo('Conversation', { conversation_id }))
94
+ }, [navigation, conversation_id])
95
+
96
+ const HeaderRight = useCallback(
97
+ () => <HeaderTextButton title="Submit" onPress={handleSubmit} disabled={!isFormValid} />,
98
+ [handleSubmit, isFormValid]
99
+ )
100
+
101
+ useLayoutEffect(() => {
102
+ if (status === 'pending' || status === 'success') {
103
+ navigation.setOptions({
104
+ headerRight: () => null,
105
+ })
106
+ } else {
107
+ navigation.setOptions({
108
+ headerTitle: 'Report message',
109
+ headerRight: HeaderRight,
110
+ })
111
+ }
112
+ }, [HeaderRight, navigation, status])
113
+
114
+ if (status === 'pending') {
115
+ return (
116
+ <View style={styles.fullScreenContainer}>
117
+ <Spinner />
118
+ <Text style={styles.loadingText}>Submitting report...</Text>
119
+ </View>
120
+ )
121
+ }
122
+
123
+ if (status === 'success') {
124
+ return (
125
+ <View style={styles.fullScreenContainer}>
126
+ <BlankState.Root>
127
+ <BlankState.Imagery name="general.checkCircle" style={styles.successIcon} />
128
+ <BlankState.Content>
129
+ <BlankState.Heading style={styles.successTitle}>Message reported</BlankState.Heading>
130
+ <BlankState.Text style={styles.successSubtitle}>
131
+ The message has been reported and a church leader will be contacted.
132
+ </BlankState.Text>
133
+ </BlankState.Content>
134
+ <BlankState.Button title="Close" onPress={handleClose} size="lg" variant="fill" />
135
+ </BlankState.Root>
136
+ </View>
137
+ )
138
+ }
139
+
140
+ if (status === 'error') {
141
+ return (
142
+ <BlankState.Root>
143
+ <BlankState.Imagery name="general.exclamationTriangle" />
144
+ <BlankState.Content>
145
+ <BlankState.Heading>Unable to submit report</BlankState.Heading>
146
+ <BlankState.Text>
147
+ We were unable to report this message. Please try again.
148
+ </BlankState.Text>
149
+ </BlankState.Content>
150
+ <BlankState.Button title="Close" onPress={handleClose} size="lg" variant="outline" />
151
+ </BlankState.Root>
152
+ )
153
+ }
154
+
155
+ return (
156
+ <KeyboardView>
157
+ <ScrollView contentContainerStyle={styles.container}>
158
+ <Text style={styles.warningText}>
159
+ Reporting this message will notify a church leader. The person you are reporting will not
160
+ receive a notification from Church Center. If someone is in immediate danger, call local
161
+ emergency services. Don&apos;t wait.
162
+ </Text>
163
+
164
+ <MessagePreview message={message} />
165
+
166
+ <View style={styles.reasonSection}>
167
+ <Text style={styles.fieldLabel}>
168
+ What best describes the problem? <Text style={styles.required}>*</Text>
169
+ </Text>
170
+
171
+ <View style={styles.reasonListContainer}>
172
+ <ReportReasonList selectedReason={selectedReason} onReasonChange={handleReasonChange} />
173
+ </View>
174
+ </View>
175
+
176
+ {selectedReason === 'other' && (
177
+ <View style={styles.detailSection}>
178
+ <Text style={styles.fieldLabel}>
179
+ Please provide more details <Text style={styles.required}>*</Text>
180
+ </Text>
181
+ <TextInput
182
+ style={styles.textInput}
183
+ multiline
184
+ placeholder="Describe the issue..."
185
+ value={reasonDetail}
186
+ onChangeText={setReasonDetail}
187
+ maxLength={MAX_DETAIL_LENGTH}
188
+ accessibilityLabel="Additional details"
189
+ accessibilityHint="Provide more information about why you're reporting this message"
190
+ />
191
+ {reasonDetail.length >= MAX_DETAIL_LENGTH - 100 && (
192
+ <Text variant="footnote">
193
+ {reasonDetail.length}/{MAX_DETAIL_LENGTH}
194
+ </Text>
195
+ )}
196
+ </View>
197
+ )}
198
+ </ScrollView>
199
+ </KeyboardView>
200
+ )
201
+ }
202
+
203
+ const useStyles = () => {
204
+ const { bottom } = useSafeAreaInsets()
205
+ const { colors } = useTheme()
206
+
207
+ return StyleSheet.create({
208
+ container: {
209
+ padding: 16,
210
+ paddingBottom: 16 + bottom,
211
+ gap: 24,
212
+ },
213
+ fullHeight: {
214
+ flex: 1,
215
+ justifyContent: 'center',
216
+ alignItems: 'center',
217
+ },
218
+ fullScreenContainer: {
219
+ flex: 1,
220
+ justifyContent: 'center',
221
+ alignItems: 'center',
222
+ backgroundColor: colors.surfaceColor100,
223
+ },
224
+ loadingText: {
225
+ marginTop: 16,
226
+ fontSize: 16,
227
+ fontWeight: '600',
228
+ color: colors.textColorDefaultPrimary,
229
+ },
230
+ warningText: {
231
+ fontSize: 14,
232
+ lineHeight: 20,
233
+ color: colors.textColorDefaultSecondary,
234
+ },
235
+ reasonSection: {
236
+ gap: 8,
237
+ },
238
+ fieldLabel: {
239
+ fontSize: 16,
240
+ fontWeight: platformFontWeightBold,
241
+ color: colors.textColorDefaultPrimary,
242
+ },
243
+ required: {
244
+ color: colors.statusErrorText,
245
+ },
246
+ reasonListContainer: {
247
+ backgroundColor: colors.surfaceColor100,
248
+ },
249
+ detailSection: {
250
+ gap: 8,
251
+ },
252
+ textInput: {
253
+ color: colors.textColorDefaultPrimary,
254
+ fontSize: 16,
255
+ textAlignVertical: 'top',
256
+ minHeight: 120,
257
+ maxHeight: 200,
258
+ borderWidth: 1,
259
+ borderColor: colors.borderColorDefaultBase,
260
+ borderRadius: 8,
261
+ paddingHorizontal: 16,
262
+ paddingVertical: 12,
263
+ backgroundColor: colors.surfaceColor100,
264
+ },
265
+ successIcon: {
266
+ color: colors.statusSuccessIcon,
267
+ fontSize: 48,
268
+ },
269
+ successTitle: {
270
+ fontSize: 24,
271
+ marginBottom: 4,
272
+ },
273
+ successSubtitle: {
274
+ fontSize: 16,
275
+ marginBottom: 4,
276
+ },
277
+ })
278
+ }
@@ -1,5 +1,6 @@
1
1
  import type { AnalyticsMetadataResource } from './analytics_metadata'
2
2
  import { ConversationBadgeResource } from './conversation_badge'
3
+ import { ConversationMembershipResource } from './conversation_membership'
3
4
  import { GroupResource } from './group_resource'
4
5
  import { MemberAbilityResource } from './member_ability'
5
6
 
@@ -8,9 +9,7 @@ export interface ConversationResource {
8
9
  id: number
9
10
  badges?: ConversationBadgeResource[]
10
11
  analyticsMetadata?: AnalyticsMetadataResource
11
- conversationMembership?: {
12
- lastReadMessageSortKey: string
13
- }
12
+ conversationMembership?: ConversationMembershipResource
14
13
  createdAt: string
15
14
  deleted?: boolean
16
15
  groups?: GroupResource[]
@@ -0,0 +1,15 @@
1
+ import { ResourceObject } from '../api_primitives'
2
+
3
+ export type NotificationLevelValue = 'everything' | 'nothing'
4
+ export type NotificationLevelDescription = string
5
+
6
+ export interface ConversationMembershipResource extends ResourceObject {
7
+ type: 'ConversationMembership'
8
+ lastReadMessageSortKey: string
9
+ notificationLevel: NotificationLevelValue
10
+ notificationLevelDescription: NotificationLevelDescription
11
+ notificationLevelOptions: Array<{
12
+ description: NotificationLevelDescription
13
+ value: NotificationLevelValue
14
+ }>
15
+ }
@@ -1,5 +1,6 @@
1
1
  export * from './analytics_metadata'
2
2
  export * from './conversation'
3
+ export * from './conversation_membership'
3
4
  export * from './member'
4
5
  export * from './message'
5
6
  export * from './oauth_token'
@@ -0,0 +1,20 @@
1
+ export const MESSAGE_REPORT_REASONS = {
2
+ scam_or_fraud: 'Scam or fraud',
3
+ inappropriate_content: 'Inappropriate or offensive content',
4
+ harassment_or_bullying: 'Harassment or bullying',
5
+ spam: 'Spam or irrelevant messages',
6
+ private_information: 'Private or sensitive information shared',
7
+ other: 'Other (please specify)',
8
+ } as const
9
+
10
+ export type MessageReportReason = keyof typeof MESSAGE_REPORT_REASONS
11
+
12
+ export interface MessageReportPayload {
13
+ data: {
14
+ type: 'MessageReport'
15
+ attributes: {
16
+ reason: MessageReportReason
17
+ reason_detail?: string
18
+ }
19
+ }
20
+ }
@@ -0,0 +1,16 @@
1
+ import { isArray, isObject, mapKeys, mapValues, snakeCase } from 'lodash'
2
+
3
+ type ObjType = Record<string, unknown> | unknown[] | unknown
4
+
5
+ export function deepSnakeCaseKeys<T extends ObjType>(obj: T): T {
6
+ if (isArray(obj)) {
7
+ return obj.map(deepSnakeCaseKeys) as T
8
+ } else if (isObject(obj)) {
9
+ return mapValues(
10
+ // @ts-ignore This mutates the object, but we don't care about the type here
11
+ mapKeys(obj, (_value: string, key: string) => snakeCase(key)),
12
+ deepSnakeCaseKeys
13
+ ) as T
14
+ }
15
+ return obj
16
+ }
@@ -1,4 +0,0 @@
1
- import React from 'react';
2
- import { ConversationSelectRecipientsScreenProps } from './types/screen_props';
3
- export declare const ConversationSelectRecipientsScreen: ({ route, }: ConversationSelectRecipientsScreenProps) => React.JSX.Element;
4
- //# sourceMappingURL=conversation_select_recipients_screen.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"conversation_select_recipients_screen.d.ts","sourceRoot":"","sources":["../../../src/screens/conversation_select_recipients/conversation_select_recipients_screen.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAA;AAOzB,OAAO,EAAE,uCAAuC,EAAE,MAAM,sBAAsB,CAAA;AAa9E,eAAO,MAAM,kCAAkC,eAE5C,uCAAuC,sBAwJzC,CAAA"}