@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.
- package/build/components/display/toggle_button.d.ts +11 -4
- package/build/components/display/toggle_button.d.ts.map +1 -1
- package/build/components/display/toggle_button.js +6 -5
- package/build/components/display/toggle_button.js.map +1 -1
- package/build/components/primitive/form_sheet.d.ts +1 -1
- package/build/components/primitive/form_sheet.d.ts.map +1 -1
- package/build/components/primitive/form_sheet.js +1 -1
- package/build/components/primitive/form_sheet.js.map +1 -1
- package/build/hooks/index.d.ts +1 -0
- package/build/hooks/index.d.ts.map +1 -1
- package/build/hooks/index.js +1 -0
- package/build/hooks/index.js.map +1 -1
- package/build/hooks/use_conversation.d.ts.map +1 -1
- package/build/hooks/use_conversation.js +6 -1
- package/build/hooks/use_conversation.js.map +1 -1
- package/build/hooks/use_conversation_membership.d.ts +5 -0
- package/build/hooks/use_conversation_membership.d.ts.map +1 -0
- package/build/hooks/use_conversation_membership.js +54 -0
- package/build/hooks/use_conversation_membership.js.map +1 -0
- package/build/hooks/use_features.d.ts +1 -0
- package/build/hooks/use_features.d.ts.map +1 -1
- package/build/hooks/use_features.js +1 -0
- package/build/hooks/use_features.js.map +1 -1
- package/build/hooks/use_new_conversation_entry.d.ts +3 -0
- package/build/hooks/use_new_conversation_entry.d.ts.map +1 -0
- package/build/hooks/use_new_conversation_entry.js +20 -0
- package/build/hooks/use_new_conversation_entry.js.map +1 -0
- package/build/hooks/use_report_message.d.ts +6 -0
- package/build/hooks/use_report_message.d.ts.map +1 -0
- package/build/hooks/use_report_message.js +28 -0
- package/build/hooks/use_report_message.js.map +1 -0
- package/build/navigation/index.d.ts +18 -8
- package/build/navigation/index.d.ts.map +1 -1
- package/build/navigation/index.js +16 -6
- package/build/navigation/index.js.map +1 -1
- package/build/screens/conversation_details_screen.d.ts.map +1 -1
- package/build/screens/conversation_details_screen.js +18 -10
- package/build/screens/conversation_details_screen.js.map +1 -1
- package/build/screens/conversation_filter_recipients/conversation_filter_recipients_screen.js +5 -3
- package/build/screens/conversation_filter_recipients/conversation_filter_recipients_screen.js.map +1 -1
- package/build/screens/conversation_select_recipients/conversation_new_entry_screen.d.ts +4 -0
- package/build/screens/conversation_select_recipients/conversation_new_entry_screen.d.ts.map +1 -0
- package/build/screens/conversation_select_recipients/conversation_new_entry_screen.js +67 -0
- package/build/screens/conversation_select_recipients/conversation_new_entry_screen.js.map +1 -0
- package/build/screens/conversation_select_recipients/conversation_select_teams_i_lead_recipients_screen.d.ts.map +1 -1
- package/build/screens/conversation_select_recipients/conversation_select_teams_i_lead_recipients_screen.js +17 -2
- package/build/screens/conversation_select_recipients/conversation_select_teams_i_lead_recipients_screen.js.map +1 -1
- package/build/screens/conversation_select_type_screen.d.ts +11 -0
- package/build/screens/conversation_select_type_screen.d.ts.map +1 -0
- package/build/screens/conversation_select_type_screen.js +32 -0
- package/build/screens/conversation_select_type_screen.js.map +1 -0
- package/build/screens/conversations/components/list_header_component.d.ts.map +1 -1
- package/build/screens/conversations/components/list_header_component.js +54 -25
- package/build/screens/conversations/components/list_header_component.js.map +1 -1
- package/build/screens/index.d.ts +1 -1
- package/build/screens/index.d.ts.map +1 -1
- package/build/screens/index.js +1 -1
- package/build/screens/index.js.map +1 -1
- package/build/screens/message_actions_screen.js +14 -2
- package/build/screens/message_actions_screen.js.map +1 -1
- package/build/screens/message_report/components/message_preview.d.ts +10 -0
- package/build/screens/message_report/components/message_preview.d.ts.map +1 -0
- package/build/screens/message_report/components/message_preview.js +81 -0
- package/build/screens/message_report/components/message_preview.js.map +1 -0
- package/build/screens/message_report/components/report_reason_list.d.ts +9 -0
- package/build/screens/message_report/components/report_reason_list.d.ts.map +1 -0
- package/build/screens/message_report/components/report_reason_list.js +67 -0
- package/build/screens/message_report/components/report_reason_list.js.map +1 -0
- package/build/screens/message_report_screen.d.ts +10 -0
- package/build/screens/message_report_screen.d.ts.map +1 -0
- package/build/screens/message_report_screen.js +214 -0
- package/build/screens/message_report_screen.js.map +1 -0
- package/build/types/resources/conversation.d.ts +2 -3
- package/build/types/resources/conversation.d.ts.map +1 -1
- package/build/types/resources/conversation.js.map +1 -1
- package/build/types/resources/conversation_membership.d.ts +14 -0
- package/build/types/resources/conversation_membership.d.ts.map +1 -0
- package/build/types/resources/conversation_membership.js +2 -0
- package/build/types/resources/conversation_membership.js.map +1 -0
- package/build/types/resources/index.d.ts +1 -0
- package/build/types/resources/index.d.ts.map +1 -1
- package/build/types/resources/index.js +1 -0
- package/build/types/resources/index.js.map +1 -1
- package/build/types/resources/message_report.d.ts +19 -0
- package/build/types/resources/message_report.d.ts.map +1 -0
- package/build/types/resources/message_report.js +9 -0
- package/build/types/resources/message_report.js.map +1 -0
- package/build/utils/deep_snake_case_keys.d.ts +4 -0
- package/build/utils/deep_snake_case_keys.d.ts.map +1 -0
- package/build/utils/deep_snake_case_keys.js +13 -0
- package/build/utils/deep_snake_case_keys.js.map +1 -0
- package/package.json +2 -2
- package/src/components/display/toggle_button.tsx +27 -15
- package/src/components/primitive/form_sheet.tsx +4 -2
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use_conversation.ts +6 -1
- package/src/hooks/use_conversation_membership.ts +82 -0
- package/src/hooks/use_features.ts +1 -0
- package/src/hooks/use_new_conversation_entry.ts +31 -0
- package/src/hooks/use_report_message.ts +37 -0
- package/src/navigation/index.tsx +19 -6
- package/src/screens/conversation_details_screen.tsx +34 -16
- package/src/screens/conversation_filter_recipients/conversation_filter_recipients_screen.tsx +10 -3
- package/src/screens/conversation_select_recipients/conversation_new_entry_screen.tsx +100 -0
- package/src/screens/conversation_select_recipients/conversation_select_teams_i_lead_recipients_screen.tsx +23 -3
- package/src/screens/conversation_select_type_screen.tsx +53 -0
- package/src/screens/conversations/components/list_header_component.tsx +103 -67
- package/src/screens/index.ts +1 -1
- package/src/screens/message_actions_screen.tsx +24 -2
- package/src/screens/message_report/components/message_preview.tsx +99 -0
- package/src/screens/message_report/components/report_reason_list.tsx +106 -0
- package/src/screens/message_report_screen.tsx +278 -0
- package/src/types/resources/conversation.ts +2 -3
- package/src/types/resources/conversation_membership.ts +15 -0
- package/src/types/resources/index.ts +1 -0
- package/src/types/resources/message_report.ts +20 -0
- package/src/utils/deep_snake_case_keys.ts +16 -0
- package/build/screens/conversation_select_recipients/conversation_select_recipients_screen.d.ts +0 -4
- package/build/screens/conversation_select_recipients/conversation_select_recipients_screen.d.ts.map +0 -1
- package/build/screens/conversation_select_recipients/conversation_select_recipients_screen.js +0 -146
- package/build/screens/conversation_select_recipients/conversation_select_recipients_screen.js.map +0 -1
- 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'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
|
+
}
|
|
@@ -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
|
+
}
|
package/build/screens/conversation_select_recipients/conversation_select_recipients_screen.d.ts
DELETED
|
@@ -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
|
package/build/screens/conversation_select_recipients/conversation_select_recipients_screen.d.ts.map
DELETED
|
@@ -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"}
|