@planningcenter/chat-react-native 3.30.0 → 3.30.1-qa-618.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/conversation/system_message.d.ts +9 -0
- package/build/components/conversation/system_message.d.ts.map +1 -0
- package/build/components/conversation/system_message.js +93 -0
- package/build/components/conversation/system_message.js.map +1 -0
- package/build/hooks/index.d.ts +13 -14
- package/build/hooks/index.d.ts.map +1 -1
- package/build/hooks/index.js +13 -14
- package/build/hooks/index.js.map +1 -1
- package/build/hooks/services/use_find_or_create_services_conversation.js +2 -2
- package/build/hooks/services/use_find_or_create_services_conversation.js.map +1 -1
- package/build/hooks/use_teams.d.ts +0 -1
- package/build/hooks/use_teams.d.ts.map +1 -1
- package/build/hooks/use_teams.js +0 -4
- package/build/hooks/use_teams.js.map +1 -1
- package/build/navigation/index.d.ts +5 -0
- package/build/navigation/index.d.ts.map +1 -1
- package/build/navigation/index.js +5 -0
- package/build/navigation/index.js.map +1 -1
- package/build/screens/conversation_screen.d.ts.map +1 -1
- package/build/screens/conversation_screen.js +20 -1
- package/build/screens/conversation_screen.js.map +1 -1
- package/build/screens/design_system_screen.d.ts.map +1 -1
- package/build/screens/design_system_screen.js +0 -51
- package/build/screens/design_system_screen.js.map +1 -1
- package/build/screens/index.d.ts +1 -0
- package/build/screens/index.d.ts.map +1 -1
- package/build/screens/index.js +1 -0
- package/build/screens/index.js.map +1 -1
- package/build/screens/message_actions_screen.d.ts +1 -0
- package/build/screens/message_actions_screen.d.ts.map +1 -1
- package/build/screens/message_actions_screen.js +12 -12
- package/build/screens/message_actions_screen.js.map +1 -1
- package/build/screens/system_message_people_screen.d.ts +9 -0
- package/build/screens/system_message_people_screen.d.ts.map +1 -0
- package/build/screens/system_message_people_screen.js +68 -0
- package/build/screens/system_message_people_screen.js.map +1 -0
- package/build/types/jolt_events/message_events.d.ts +7 -0
- package/build/types/jolt_events/message_events.d.ts.map +1 -1
- package/build/types/jolt_events/message_events.js.map +1 -1
- package/build/types/resources/message.d.ts +7 -0
- package/build/types/resources/message.d.ts.map +1 -1
- package/build/types/resources/message.js.map +1 -1
- package/build/utils/cache/optimistically_create_message.d.ts.map +1 -1
- package/build/utils/cache/optimistically_create_message.js +1 -0
- package/build/utils/cache/optimistically_create_message.js.map +1 -1
- package/build/utils/index.d.ts +1 -0
- package/build/utils/index.d.ts.map +1 -1
- package/build/utils/index.js +1 -0
- package/build/utils/index.js.map +1 -1
- package/build/utils/jolt/transform_message_event_data_to_message_resource.d.ts.map +1 -1
- package/build/utils/jolt/transform_message_event_data_to_message_resource.js +9 -0
- package/build/utils/jolt/transform_message_event_data_to_message_resource.js.map +1 -1
- package/build/utils/request/messages_data_options.d.ts.map +1 -1
- package/build/utils/request/messages_data_options.js +3 -0
- package/build/utils/request/messages_data_options.js.map +1 -1
- package/build/utils/system_messages.d.ts +15 -0
- package/build/utils/system_messages.d.ts.map +1 -0
- package/build/utils/system_messages.js +5 -0
- package/build/utils/system_messages.js.map +1 -0
- package/package.json +2 -2
- package/src/__tests__/utils/system_messages.ts +79 -0
- package/src/components/conversation/system_message.tsx +130 -0
- package/src/hooks/index.ts +13 -14
- package/src/hooks/services/use_find_or_create_services_conversation.ts +2 -2
- package/src/hooks/use_teams.ts +0 -6
- package/src/navigation/index.tsx +8 -0
- package/src/screens/conversation_screen.tsx +25 -2
- package/src/screens/design_system_screen.tsx +0 -52
- package/src/screens/index.ts +1 -0
- package/src/screens/message_actions_screen.tsx +64 -57
- package/src/screens/system_message_people_screen.tsx +94 -0
- package/src/types/jolt_events/message_events.ts +3 -0
- package/src/types/resources/message.ts +3 -0
- package/src/utils/cache/optimistically_create_message.ts +1 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/jolt/transform_message_event_data_to_message_resource.ts +9 -0
- package/src/utils/request/messages_data_options.ts +3 -0
- package/src/utils/system_messages.ts +16 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { useNavigation } from '@react-navigation/native'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { Pressable, StyleSheet, View } from 'react-native'
|
|
4
|
+
import Animated from 'react-native-reanimated'
|
|
5
|
+
import { useAnimatedMessageBackgroundColor, useTheme } from '../../hooks'
|
|
6
|
+
import { ReactionCountResource } from '../../types/resources/reaction'
|
|
7
|
+
import { Haptic } from '../../utils/native_adapters'
|
|
8
|
+
import { SystemMessageResource } from '../../utils/system_messages'
|
|
9
|
+
import { Text } from '../display'
|
|
10
|
+
import { MessageReaction } from './message_reaction'
|
|
11
|
+
|
|
12
|
+
interface SystemMessageProps {
|
|
13
|
+
message: SystemMessageResource
|
|
14
|
+
conversationId: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function SystemMessage({ message, conversationId }: SystemMessageProps) {
|
|
18
|
+
const { reactionCounts, systemTextParts, personIdsForSystemEvent } = message
|
|
19
|
+
const { names, overflowCount, action } = systemTextParts
|
|
20
|
+
const styles = useStyles()
|
|
21
|
+
const navigation = useNavigation()
|
|
22
|
+
const { colors } = useTheme()
|
|
23
|
+
const text = systemMessageText(names, overflowCount, action)
|
|
24
|
+
const hasPeople = personIdsForSystemEvent.length > 0
|
|
25
|
+
const hasReactions = reactionCounts.length > 0
|
|
26
|
+
const { animatedBackgroundColor, handleMessagePressIn, handleMessagePressOut } =
|
|
27
|
+
useAnimatedMessageBackgroundColor()
|
|
28
|
+
|
|
29
|
+
const handlePress = () => {
|
|
30
|
+
navigation.navigate('SystemMessagePeople', {
|
|
31
|
+
conversation_id: conversationId,
|
|
32
|
+
person_ids: personIdsForSystemEvent,
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const handleLongPress = () => {
|
|
37
|
+
Haptic.impactLight()
|
|
38
|
+
navigation.navigate('MessageActions', {
|
|
39
|
+
message_id: message.id,
|
|
40
|
+
conversation_id: conversationId,
|
|
41
|
+
isSystemMessage: true,
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const handleReactionLongPress = (reaction: ReactionCountResource) => {
|
|
46
|
+
Haptic.impactLight()
|
|
47
|
+
navigation.navigate('Reactions', {
|
|
48
|
+
message_id: message.id,
|
|
49
|
+
conversation_id: conversationId,
|
|
50
|
+
reaction_value: reaction.value,
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<Pressable
|
|
56
|
+
onPress={hasPeople ? handlePress : undefined}
|
|
57
|
+
onLongPress={handleLongPress}
|
|
58
|
+
onPressIn={handleMessagePressIn}
|
|
59
|
+
onPressOut={handleMessagePressOut}
|
|
60
|
+
android_ripple={{ color: colors.androidRippleNeutral, borderless: false, foreground: true }}
|
|
61
|
+
accessibilityRole="button"
|
|
62
|
+
accessibilityHint={
|
|
63
|
+
hasPeople
|
|
64
|
+
? 'Tap to see people. Long press for message actions.'
|
|
65
|
+
: 'Long press for message actions.'
|
|
66
|
+
}
|
|
67
|
+
>
|
|
68
|
+
<Animated.View style={[styles.container, animatedBackgroundColor]}>
|
|
69
|
+
<Text variant="tertiary" style={styles.text}>
|
|
70
|
+
{text}
|
|
71
|
+
</Text>
|
|
72
|
+
{hasReactions && (
|
|
73
|
+
<View style={styles.reactions}>
|
|
74
|
+
{message.reactionCounts.map(reaction => (
|
|
75
|
+
<MessageReaction
|
|
76
|
+
key={reaction.value}
|
|
77
|
+
reaction={reaction}
|
|
78
|
+
onLongPress={handleReactionLongPress}
|
|
79
|
+
message={message}
|
|
80
|
+
conversationId={conversationId}
|
|
81
|
+
/>
|
|
82
|
+
))}
|
|
83
|
+
</View>
|
|
84
|
+
)}
|
|
85
|
+
</Animated.View>
|
|
86
|
+
</Pressable>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function systemMessageText(names: string[], overflowCount: number, action: string): string {
|
|
91
|
+
if (overflowCount > 0) {
|
|
92
|
+
const overflowLabel = `${overflowCount} ${overflowCount === 1 ? 'other' : 'others'}`
|
|
93
|
+
return `${names.join(', ')}, and ${overflowLabel} ${action}`
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
switch (names.length) {
|
|
97
|
+
case 0:
|
|
98
|
+
return `Someone ${action}`
|
|
99
|
+
case 1:
|
|
100
|
+
return `${names[0]} ${action}`
|
|
101
|
+
case 2:
|
|
102
|
+
return `${names[0]} and ${names[1]} ${action}`
|
|
103
|
+
// Backend guarantees names.length <= 3 when overflowCount is 0;
|
|
104
|
+
// overflow branch handles 4+ people.
|
|
105
|
+
default:
|
|
106
|
+
return `${names[0]}, ${names[1]}, and ${names[2]} ${action}`
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const useStyles = () => {
|
|
111
|
+
const { colors } = useTheme()
|
|
112
|
+
return StyleSheet.create({
|
|
113
|
+
container: {
|
|
114
|
+
paddingVertical: 12,
|
|
115
|
+
paddingHorizontal: 16,
|
|
116
|
+
alignItems: 'center',
|
|
117
|
+
},
|
|
118
|
+
text: {
|
|
119
|
+
color: colors.textColorDefaultSecondary,
|
|
120
|
+
textAlign: 'center',
|
|
121
|
+
},
|
|
122
|
+
reactions: {
|
|
123
|
+
flexDirection: 'row',
|
|
124
|
+
flexWrap: 'wrap',
|
|
125
|
+
justifyContent: 'center',
|
|
126
|
+
gap: 4,
|
|
127
|
+
marginTop: 8,
|
|
128
|
+
},
|
|
129
|
+
})
|
|
130
|
+
}
|
package/src/hooks/index.ts
CHANGED
|
@@ -1,24 +1,23 @@
|
|
|
1
|
-
export * from './use_async_storage'
|
|
2
1
|
export * from './use_animated_message_background_color'
|
|
3
|
-
export * from './
|
|
4
|
-
export * from './
|
|
2
|
+
export * from './use_api_client'
|
|
3
|
+
export * from './use_api'
|
|
4
|
+
export * from './use_async_storage'
|
|
5
|
+
export * from './use_at_font_scale_breakpoint'
|
|
6
|
+
export * from './use_chat_permissions'
|
|
7
|
+
export * from './use_create_android_ripple_color'
|
|
5
8
|
export * from './use_current_person'
|
|
6
9
|
export * from './use_deleting_ids'
|
|
7
10
|
export * from './use_font_scale'
|
|
8
|
-
export * from './use_create_android_ripple_color'
|
|
9
|
-
export * from './use_chat_permissions'
|
|
10
|
-
export * from './use_api_client'
|
|
11
11
|
export * from './use_groups_groups'
|
|
12
12
|
export * from './use_groups'
|
|
13
|
-
export * from './use_api'
|
|
14
|
-
export * from './use_api_client'
|
|
15
|
-
export * from './use_message_reaction_toggle'
|
|
16
13
|
export * from './use_interaction_ghost_color'
|
|
17
|
-
export * from './
|
|
14
|
+
export * from './use_message_reaction_toggle'
|
|
15
|
+
export * from './use_new_conversation_entry'
|
|
16
|
+
export * from './use_organization'
|
|
17
|
+
export * from './use_people_person'
|
|
18
|
+
export * from './use_product_analytics'
|
|
18
19
|
export * from './use_qualified_by_age'
|
|
19
20
|
export * from './use_scalable_number_of_lines'
|
|
20
21
|
export * from './use_submit_age_check'
|
|
21
|
-
export * from './
|
|
22
|
-
export * from './
|
|
23
|
-
export * from './use_people_person'
|
|
24
|
-
export * from './use_new_conversation_entry'
|
|
22
|
+
export * from './use_suspense_api'
|
|
23
|
+
export * from './use_theme'
|
|
@@ -114,7 +114,7 @@ export const findConversationWithExactTeams = (
|
|
|
114
114
|
url: '/me/conversations',
|
|
115
115
|
data: {
|
|
116
116
|
fields: {
|
|
117
|
-
Conversation: ['
|
|
117
|
+
Conversation: ['title'],
|
|
118
118
|
},
|
|
119
119
|
filter: 'with_exact_groups',
|
|
120
120
|
gids: groupIdentifiers.join(','),
|
|
@@ -136,7 +136,7 @@ export const findExactTeamConversation = (apiClient: ApiClient, groupIdentifiers
|
|
|
136
136
|
url: '/me/conversations',
|
|
137
137
|
data: {
|
|
138
138
|
fields: {
|
|
139
|
-
Conversation: ['
|
|
139
|
+
Conversation: ['title'],
|
|
140
140
|
},
|
|
141
141
|
filter: 'with_exact_groups',
|
|
142
142
|
gids: groupIdentifiers.join(','),
|
package/src/hooks/use_teams.ts
CHANGED
package/src/navigation/index.tsx
CHANGED
|
@@ -71,6 +71,10 @@ import { NotificationSettingsScreen } from '../screens/notification_settings_scr
|
|
|
71
71
|
import { PreferredAppSelectionScreen } from '../screens/preferred_app_selection_screen'
|
|
72
72
|
import { ReactionsScreen, ReactionsScreenOptions } from '../screens/reactions_screen'
|
|
73
73
|
import { SendGiphyScreen, SendGiphyScreenOptions } from '../screens/send_giphy_screen'
|
|
74
|
+
import {
|
|
75
|
+
SystemMessagePeopleScreen,
|
|
76
|
+
SystemMessagePeopleScreenOptions,
|
|
77
|
+
} from '../screens/system_message_people_screen'
|
|
74
78
|
import { TeamConversationScreen } from '../screens/team_conversation_screen'
|
|
75
79
|
import { ScreenLayoutWithChatAccessGate } from './screenLayout'
|
|
76
80
|
|
|
@@ -333,6 +337,10 @@ export const ChatStack = createNativeStackNavigator({
|
|
|
333
337
|
screen: ReactionsScreen,
|
|
334
338
|
options: ReactionsScreenOptions,
|
|
335
339
|
},
|
|
340
|
+
SystemMessagePeople: {
|
|
341
|
+
screen: SystemMessagePeopleScreen,
|
|
342
|
+
options: SystemMessagePeopleScreenOptions,
|
|
343
|
+
},
|
|
336
344
|
MessageReadReceipts: {
|
|
337
345
|
screen: MessageReadReceiptsScreen,
|
|
338
346
|
options: MessageReadReceiptsScreenOptions,
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
MemberMessagesDisabledBanner,
|
|
22
22
|
} from '../components/conversation/messages_disabled_banners'
|
|
23
23
|
import { ReplyShadowMessage } from '../components/conversation/reply_shadow_message'
|
|
24
|
+
import { SystemMessage } from '../components/conversation/system_message'
|
|
24
25
|
import { TypingIndicator } from '../components/conversation/typing_indicator'
|
|
25
26
|
import { KeyboardView } from '../components/display/keyboard_view'
|
|
26
27
|
import BlankState from '../components/primitive/blank_state_primitive'
|
|
@@ -40,6 +41,7 @@ import { ConversationBadgeResource } from '../types/resources/conversation_badge
|
|
|
40
41
|
import { getRelativeDateStatus } from '../utils/date'
|
|
41
42
|
import dayjs from '../utils/dayjs'
|
|
42
43
|
import { CONVERSATION_MESSAGE_LIST_PADDING_HORIZONTAL } from '../utils/styles'
|
|
44
|
+
import { isSystemMessage } from '../utils/system_messages'
|
|
43
45
|
|
|
44
46
|
export type ConversationRouteProps = {
|
|
45
47
|
conversation_id: number
|
|
@@ -188,6 +190,10 @@ function ConversationScreenContent({ route }: ConversationScreenProps) {
|
|
|
188
190
|
)
|
|
189
191
|
}
|
|
190
192
|
|
|
193
|
+
if (isSystemMessage(item)) {
|
|
194
|
+
return <SystemMessage message={item} conversationId={conversation_id} />
|
|
195
|
+
}
|
|
196
|
+
|
|
191
197
|
return (
|
|
192
198
|
<Message
|
|
193
199
|
{...item}
|
|
@@ -293,6 +299,25 @@ export const groupMessages = ({ ms, inReplyScreen }: GroupMessagesProps) => {
|
|
|
293
299
|
const prevMessage = ms[i + 1]
|
|
294
300
|
const nextMessage = ms[i - 1]
|
|
295
301
|
const date = dayjs(message.createdAt).format('YYYY-MM-DD')
|
|
302
|
+
|
|
303
|
+
const prevMessageIsDateSeparator =
|
|
304
|
+
prevMessage && date !== dayjs(prevMessage.createdAt).format('YYYY-MM-DD')
|
|
305
|
+
|
|
306
|
+
if (isSystemMessage(message)) {
|
|
307
|
+
message.myLatestInConversation = false
|
|
308
|
+
message.lastInGroup = true
|
|
309
|
+
message.renderAuthor = false
|
|
310
|
+
message.nextRendersAuthor = nextMessage?.renderAuthor
|
|
311
|
+
message.isReplyShadowMessage = false
|
|
312
|
+
message.nextIsReplyShadowMessage = false
|
|
313
|
+
message.threadPosition = null
|
|
314
|
+
enrichedMessages.push(message)
|
|
315
|
+
if (prevMessageIsDateSeparator) {
|
|
316
|
+
enrichedMessages.push({ type: 'DateSeparator', id: `day-divider-${message.id}`, date })
|
|
317
|
+
}
|
|
318
|
+
return
|
|
319
|
+
}
|
|
320
|
+
|
|
296
321
|
const inThread = message.replyRootId !== null
|
|
297
322
|
const nextMessageInThread = nextMessage?.replyRootId !== null
|
|
298
323
|
const threadRoot = message.replyRootId === message.id
|
|
@@ -307,8 +332,6 @@ export const groupMessages = ({ ms, inReplyScreen }: GroupMessagesProps) => {
|
|
|
307
332
|
const nextMessageMoreThan5Minutes =
|
|
308
333
|
nextMessage &&
|
|
309
334
|
new Date(nextMessage.createdAt).getTime() - new Date(message.createdAt).getTime() > 60000 * 5
|
|
310
|
-
const prevMessageIsDateSeparator =
|
|
311
|
-
prevMessage && date !== dayjs(prevMessage.createdAt).format('YYYY-MM-DD')
|
|
312
335
|
const nextMessageIsDateSeparator =
|
|
313
336
|
nextMessage && date !== dayjs(nextMessage.createdAt).format('YYYY-MM-DD')
|
|
314
337
|
const insertReplyShadowMessage =
|
|
@@ -89,8 +89,6 @@ export function DesignSystemScreen() {
|
|
|
89
89
|
return (
|
|
90
90
|
<ScrollView contentContainerStyle={styles.container} style={styles.scrollView}>
|
|
91
91
|
<ThemeSection />
|
|
92
|
-
{/* TODO: Enable & update when we install @planningcenter/tapestry */}
|
|
93
|
-
{/* <TokensSection /> */}
|
|
94
92
|
<IndicatorsSection />
|
|
95
93
|
<HeadingTextSection />
|
|
96
94
|
<PressablesSection />
|
|
@@ -166,56 +164,6 @@ function ThemeSection({ isLast }: SectionProps) {
|
|
|
166
164
|
)
|
|
167
165
|
}
|
|
168
166
|
|
|
169
|
-
// function TokensSection({ isLast }: SectionProps) {
|
|
170
|
-
// return (
|
|
171
|
-
// <CollapsableSection title="Tokens" isLast={isLast}>
|
|
172
|
-
// <TextGroup>
|
|
173
|
-
// <TextRow>
|
|
174
|
-
// <Heading variant="h3">What are they?</Heading>
|
|
175
|
-
// <Text>
|
|
176
|
-
// Tokens are UX approved CSS values that we can use to style our UI in a consistent way.
|
|
177
|
-
// (e.g. colors, spacing amounts, and font weights.)
|
|
178
|
-
// </Text>
|
|
179
|
-
// </TextRow>
|
|
180
|
-
// <TextRow>
|
|
181
|
-
// <Heading variant="h3">Where do they come from?</Heading>
|
|
182
|
-
// <Text>
|
|
183
|
-
// Tokens primarily come from our internal `@planningcenter/tapestry` package. However, at
|
|
184
|
-
// this time the package only support light mode colors, so Chat uses a workaround.
|
|
185
|
-
// </Text>
|
|
186
|
-
// <Text>Color-based tokens are infused into our theming system with two local files…</Text>
|
|
187
|
-
// <TextListItem label="1. `src/vendor/tapestry/tokens`:">
|
|
188
|
-
// Primitive color values are stored* here. Primitives capture light or dark mode values,
|
|
189
|
-
// but don't take into account the devices's colors scheme.
|
|
190
|
-
// </TextListItem>
|
|
191
|
-
// <TextListItem label="2. `src/vendor/tapestry/alias_tokens_color_map`:">
|
|
192
|
-
// Alias tokens reference the primitive color values token file* in light and dark mode
|
|
193
|
-
// specfic objects. Our theming system then selects the right color to use based on the
|
|
194
|
-
// device's color scheme.
|
|
195
|
-
// </TextListItem>
|
|
196
|
-
// <Text variant="footnote">
|
|
197
|
-
// *If available, reference the color from the Tapestry package instead.
|
|
198
|
-
// </Text>
|
|
199
|
-
// </TextRow>
|
|
200
|
-
// <TextRow>
|
|
201
|
-
// <Heading variant="h3">How do we use them?</Heading>
|
|
202
|
-
// <Text>There are two places to reference tokens at this time…</Text>
|
|
203
|
-
// <TextListItem label="• Color tokens:">
|
|
204
|
-
// Reference them from our internal `useTheme` hook to ensure the correct light or dark
|
|
205
|
-
// mode color token is used.
|
|
206
|
-
// </TextListItem>
|
|
207
|
-
// <TextListItem label="• All other tokens:">
|
|
208
|
-
// Use the `computedToken` function from `@planningcenter/tapestry` by passing it a string
|
|
209
|
-
// with the token's name. This function provides Typescript support and maps to the token's
|
|
210
|
-
// raw CSS value. There is another function called `token`, but it maps to CSS custom
|
|
211
|
-
// properties which React Native doesn't support.
|
|
212
|
-
// </TextListItem>
|
|
213
|
-
// </TextRow>
|
|
214
|
-
// </TextGroup>
|
|
215
|
-
// </CollapsableSection>
|
|
216
|
-
// )
|
|
217
|
-
// }
|
|
218
|
-
|
|
219
167
|
function IndicatorsSection({ isLast }: SectionProps) {
|
|
220
168
|
return (
|
|
221
169
|
<CollapsableSection title="Indicators" isLast={isLast}>
|
package/src/screens/index.ts
CHANGED
|
@@ -20,3 +20,4 @@ export * from './conversation_select_recipients/conversation_select_group_recipi
|
|
|
20
20
|
export * from './conversation_select_recipients/conversation_select_teams_i_lead_recipients_screen'
|
|
21
21
|
export * from './attachment_actions/attachment_actions_screen'
|
|
22
22
|
export * from './conversations/conversations_screen'
|
|
23
|
+
export * from './system_message_people_screen'
|
|
@@ -28,6 +28,7 @@ export type MessageActionsScreenProps = StaticScreenProps<{
|
|
|
28
28
|
conversation_id: number
|
|
29
29
|
canDeleteNonAuthoredMessages?: boolean
|
|
30
30
|
inReplyScreen?: boolean
|
|
31
|
+
isSystemMessage?: boolean
|
|
31
32
|
}>
|
|
32
33
|
|
|
33
34
|
export function MessageActionsScreen({ route }: MessageActionsScreenProps) {
|
|
@@ -37,6 +38,7 @@ export function MessageActionsScreen({ route }: MessageActionsScreenProps) {
|
|
|
37
38
|
canDeleteNonAuthoredMessages,
|
|
38
39
|
inReplyScreen,
|
|
39
40
|
reply_root_author_name,
|
|
41
|
+
isSystemMessage,
|
|
40
42
|
} = route.params
|
|
41
43
|
|
|
42
44
|
const { messages, refetch } = useConversationMessages(
|
|
@@ -55,6 +57,7 @@ export function MessageActionsScreen({ route }: MessageActionsScreenProps) {
|
|
|
55
57
|
refetchMessages={refetch}
|
|
56
58
|
inReplyScreen={inReplyScreen}
|
|
57
59
|
replyRootAuthorName={reply_root_author_name}
|
|
60
|
+
isSystemMessage={isSystemMessage}
|
|
58
61
|
/>
|
|
59
62
|
)
|
|
60
63
|
}
|
|
@@ -66,6 +69,7 @@ function MessageActionsScreenContent({
|
|
|
66
69
|
refetchMessages,
|
|
67
70
|
inReplyScreen,
|
|
68
71
|
replyRootAuthorName,
|
|
72
|
+
isSystemMessage,
|
|
69
73
|
}: {
|
|
70
74
|
message: MessageResource
|
|
71
75
|
conversation_id: number
|
|
@@ -73,6 +77,7 @@ function MessageActionsScreenContent({
|
|
|
73
77
|
refetchMessages: () => void
|
|
74
78
|
inReplyScreen?: boolean
|
|
75
79
|
replyRootAuthorName?: string
|
|
80
|
+
isSystemMessage?: boolean
|
|
76
81
|
}) {
|
|
77
82
|
const navigation = useNavigation()
|
|
78
83
|
const apiClient = useApiClient()
|
|
@@ -240,65 +245,67 @@ function MessageActionsScreenContent({
|
|
|
240
245
|
/>
|
|
241
246
|
))}
|
|
242
247
|
</View>
|
|
243
|
-
|
|
244
|
-
{
|
|
248
|
+
{!isSystemMessage && (
|
|
249
|
+
<View style={styles.actions}>
|
|
250
|
+
{!inReplyScreen && (
|
|
251
|
+
<FormSheet.Action
|
|
252
|
+
onPress={handleReplyPress}
|
|
253
|
+
title="Reply to message"
|
|
254
|
+
iconName="registrations.undo"
|
|
255
|
+
accessibilityHint="Navigates to the reply screen"
|
|
256
|
+
accessibilityRole="link"
|
|
257
|
+
/>
|
|
258
|
+
)}
|
|
245
259
|
<FormSheet.Action
|
|
246
|
-
onPress={
|
|
247
|
-
title="
|
|
248
|
-
iconName="
|
|
249
|
-
accessibilityHint="
|
|
250
|
-
accessibilityRole="link"
|
|
260
|
+
onPress={handleCopyPress}
|
|
261
|
+
title="Copy text"
|
|
262
|
+
iconName="services.fileCopy"
|
|
263
|
+
accessibilityHint="Copies text and links to clipboard"
|
|
251
264
|
/>
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
appearance="danger"
|
|
297
|
-
disabled={isPending}
|
|
298
|
-
accessibilityHint="Opens a confirmation alert to delete this message permanently."
|
|
299
|
-
/>
|
|
300
|
-
)}
|
|
301
|
-
</View>
|
|
265
|
+
{showReportMessageAction && (
|
|
266
|
+
<FormSheet.Action
|
|
267
|
+
onPress={handleReportPress}
|
|
268
|
+
title="Report message"
|
|
269
|
+
iconName="chat.reportMessageO"
|
|
270
|
+
accessibilityHint="Opens a form to report this message"
|
|
271
|
+
/>
|
|
272
|
+
)}
|
|
273
|
+
{message?.mine && (
|
|
274
|
+
<FormSheet.Action
|
|
275
|
+
onPress={() => handleEditPress()}
|
|
276
|
+
title="Edit message"
|
|
277
|
+
iconName="accounts.editor"
|
|
278
|
+
accessibilityHint="Opens existing text in the message form input."
|
|
279
|
+
/>
|
|
280
|
+
)}
|
|
281
|
+
{message?.mine && (
|
|
282
|
+
<FormSheet.Action
|
|
283
|
+
onPress={() => handleViewReadReceiptsPress()}
|
|
284
|
+
title="View read receipts"
|
|
285
|
+
iconName="general.checkPerson"
|
|
286
|
+
accessibilityHint="Opens a modal with a list of people who read your message."
|
|
287
|
+
/>
|
|
288
|
+
)}
|
|
289
|
+
{message?.mine && hasExpandedLink && (
|
|
290
|
+
<FormSheet.Action
|
|
291
|
+
onPress={() => handleRemoveLinkPreviewConfirm()}
|
|
292
|
+
title="Remove link preview"
|
|
293
|
+
iconName="general.brokenLink"
|
|
294
|
+
accessibilityHint="Removes an expanded link preview from the message."
|
|
295
|
+
/>
|
|
296
|
+
)}
|
|
297
|
+
{(message?.mine || canDeleteNonAuthoredMessages) && (
|
|
298
|
+
<FormSheet.Action
|
|
299
|
+
onPress={() => handleDeleteConfirm()}
|
|
300
|
+
title="Delete message"
|
|
301
|
+
iconName="publishing.trash"
|
|
302
|
+
appearance="danger"
|
|
303
|
+
disabled={isPending}
|
|
304
|
+
accessibilityHint="Opens a confirmation alert to delete this message permanently."
|
|
305
|
+
/>
|
|
306
|
+
)}
|
|
307
|
+
</View>
|
|
308
|
+
)}
|
|
302
309
|
</FormSheet.Root>
|
|
303
310
|
)
|
|
304
311
|
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { StaticScreenProps } from '@react-navigation/native'
|
|
2
|
+
import React, { memo } from 'react'
|
|
3
|
+
import { Platform, StyleSheet, View } from 'react-native'
|
|
4
|
+
import { FlatList } from 'react-native-gesture-handler'
|
|
5
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
6
|
+
import { Avatar, Text } from '../components'
|
|
7
|
+
import FormSheet, { getFormSheetScreenOptions } from '../components/primitive/form_sheet'
|
|
8
|
+
import { useSuspenseGet } from '../hooks'
|
|
9
|
+
import { MemberResource } from '../types'
|
|
10
|
+
|
|
11
|
+
export const SystemMessagePeopleScreenOptions = getFormSheetScreenOptions({
|
|
12
|
+
sheetAllowedDetents: Platform.select({
|
|
13
|
+
android: [0.5, 0.94],
|
|
14
|
+
default: [0.5, 1],
|
|
15
|
+
}),
|
|
16
|
+
headerTitle: 'People',
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
export type SystemMessagePeopleScreenProps = StaticScreenProps<{
|
|
20
|
+
conversation_id: number
|
|
21
|
+
person_ids: number[]
|
|
22
|
+
}>
|
|
23
|
+
|
|
24
|
+
export function SystemMessagePeopleScreen({ route }: SystemMessagePeopleScreenProps) {
|
|
25
|
+
const { conversation_id, person_ids } = route.params
|
|
26
|
+
const styles = useStyles()
|
|
27
|
+
|
|
28
|
+
const { data: members } = useSuspenseGet<MemberResource[]>({
|
|
29
|
+
url: `/me/conversations/${conversation_id}/members`,
|
|
30
|
+
data: {
|
|
31
|
+
fields: {
|
|
32
|
+
Member: ['id', 'name', 'avatar'],
|
|
33
|
+
},
|
|
34
|
+
where: {
|
|
35
|
+
id: person_ids,
|
|
36
|
+
},
|
|
37
|
+
limit: person_ids.length,
|
|
38
|
+
},
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const orderById = Object.fromEntries(person_ids.map((id, i) => [id, i]))
|
|
42
|
+
const sortedMembers = [...members].sort((a, b) => (orderById[a.id] ?? 0) - (orderById[b.id] ?? 0))
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<FormSheet.Root contentStyle={styles.formSheetContent}>
|
|
46
|
+
<FlatList
|
|
47
|
+
data={sortedMembers}
|
|
48
|
+
contentContainerStyle={styles.contentContainer}
|
|
49
|
+
keyExtractor={item => item.id.toString()}
|
|
50
|
+
renderItem={({ item }) => <Person person={item} />}
|
|
51
|
+
/>
|
|
52
|
+
</FormSheet.Root>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
type PersonProps = Pick<MemberResource, 'id' | 'name' | 'avatar'>
|
|
57
|
+
|
|
58
|
+
const Person = memo(({ person }: { person: PersonProps }) => {
|
|
59
|
+
const styles = useStyles()
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<View style={styles.personRow}>
|
|
63
|
+
<Avatar sourceUri={person.avatar} size="sm" />
|
|
64
|
+
<Text variant="tertiary" numberOfLines={2} style={styles.personName}>
|
|
65
|
+
{person.name}
|
|
66
|
+
</Text>
|
|
67
|
+
</View>
|
|
68
|
+
)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
const useStyles = () => {
|
|
72
|
+
const { bottom } = useSafeAreaInsets()
|
|
73
|
+
|
|
74
|
+
return StyleSheet.create({
|
|
75
|
+
formSheetContent: {
|
|
76
|
+
paddingTop: 16,
|
|
77
|
+
},
|
|
78
|
+
contentContainer: {
|
|
79
|
+
paddingTop: 8,
|
|
80
|
+
paddingBottom: bottom + 24,
|
|
81
|
+
},
|
|
82
|
+
personRow: {
|
|
83
|
+
flexDirection: 'row',
|
|
84
|
+
alignItems: 'center',
|
|
85
|
+
gap: 8,
|
|
86
|
+
paddingVertical: 8,
|
|
87
|
+
paddingHorizontal: 16,
|
|
88
|
+
flex: 1,
|
|
89
|
+
},
|
|
90
|
+
personName: {
|
|
91
|
+
flex: 1,
|
|
92
|
+
},
|
|
93
|
+
})
|
|
94
|
+
}
|
|
@@ -18,6 +18,9 @@ interface BaseMessageEventData extends Record<string, unknown> {
|
|
|
18
18
|
idempotent_key?: string | null
|
|
19
19
|
reply_count: number
|
|
20
20
|
reply_root_id: string | null
|
|
21
|
+
message_type: string
|
|
22
|
+
system_event_person_ids: number[] | null
|
|
23
|
+
system_text_parts: { names: string[]; overflow_count: number; action: string } | null
|
|
21
24
|
}
|
|
22
25
|
}
|
|
23
26
|
|
|
@@ -17,6 +17,9 @@ export interface MessageResource {
|
|
|
17
17
|
reactionCounts: ReactionCountResource[]
|
|
18
18
|
replyCount: number
|
|
19
19
|
replyRootId?: string | null
|
|
20
|
+
messageType: string
|
|
21
|
+
personIdsForSystemEvent?: number[] | null
|
|
22
|
+
systemTextParts?: { names: string[]; overflowCount: number; action: string } | null
|
|
20
23
|
|
|
21
24
|
// Custom Local Properties we set for rendering
|
|
22
25
|
renderAuthor?: boolean
|
package/src/utils/index.ts
CHANGED
|
@@ -29,5 +29,14 @@ export function transformMessageEventDataToMessageResource({
|
|
|
29
29
|
reactionCounts: [],
|
|
30
30
|
replyCount: data.reply_count || 0,
|
|
31
31
|
replyRootId: data.reply_root_id || null,
|
|
32
|
+
messageType: data.message_type,
|
|
33
|
+
personIdsForSystemEvent: data.system_event_person_ids,
|
|
34
|
+
systemTextParts: data.system_text_parts
|
|
35
|
+
? {
|
|
36
|
+
names: data.system_text_parts.names,
|
|
37
|
+
overflowCount: data.system_text_parts.overflow_count,
|
|
38
|
+
action: data.system_text_parts.action,
|
|
39
|
+
}
|
|
40
|
+
: null,
|
|
32
41
|
}
|
|
33
42
|
}
|