@planningcenter/chat-react-native 4.4.1-qa-staging.13 → 4.4.1-qa-staging.14

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 (104) hide show
  1. package/build/components/conversation/message.d.ts +2 -1
  2. package/build/components/conversation/message.d.ts.map +1 -1
  3. package/build/components/conversation/message.js +3 -1
  4. package/build/components/conversation/message.js.map +1 -1
  5. package/build/components/conversation/message_list.d.ts.map +1 -1
  6. package/build/components/conversation/message_list.js +35 -31
  7. package/build/components/conversation/message_list.js.map +1 -1
  8. package/build/components/conversation/message_reaction.d.ts +2 -1
  9. package/build/components/conversation/message_reaction.d.ts.map +1 -1
  10. package/build/components/conversation/message_reaction.js +67 -60
  11. package/build/components/conversation/message_reaction.js.map +1 -1
  12. package/build/components/conversation/messages_disabled_banners.d.ts +7 -5
  13. package/build/components/conversation/messages_disabled_banners.d.ts.map +1 -1
  14. package/build/components/conversation/messages_disabled_banners.js +15 -14
  15. package/build/components/conversation/messages_disabled_banners.js.map +1 -1
  16. package/build/components/conversation/system_message.d.ts +2 -1
  17. package/build/components/conversation/system_message.d.ts.map +1 -1
  18. package/build/components/conversation/system_message.js +87 -65
  19. package/build/components/conversation/system_message.js.map +1 -1
  20. package/build/components/display/filter_dropdown.d.ts +13 -0
  21. package/build/components/display/filter_dropdown.d.ts.map +1 -0
  22. package/build/components/display/filter_dropdown.js +188 -0
  23. package/build/components/display/filter_dropdown.js.map +1 -0
  24. package/build/components/display/index.d.ts +1 -0
  25. package/build/components/display/index.d.ts.map +1 -1
  26. package/build/components/display/index.js +1 -0
  27. package/build/components/display/index.js.map +1 -1
  28. package/build/hooks/use_conversation.d.ts +73 -1
  29. package/build/hooks/use_conversation.d.ts.map +1 -1
  30. package/build/hooks/use_conversation.js +122 -3
  31. package/build/hooks/use_conversation.js.map +1 -1
  32. package/build/hooks/use_unarchive_conversation_action.d.ts +7 -0
  33. package/build/hooks/use_unarchive_conversation_action.d.ts.map +1 -0
  34. package/build/hooks/use_unarchive_conversation_action.js +52 -0
  35. package/build/hooks/use_unarchive_conversation_action.js.map +1 -0
  36. package/build/screens/attachment_actions/attachment_actions_screen.d.ts.map +1 -1
  37. package/build/screens/attachment_actions/attachment_actions_screen.js +84 -61
  38. package/build/screens/attachment_actions/attachment_actions_screen.js.map +1 -1
  39. package/build/screens/conversation_details_screen.d.ts.map +1 -1
  40. package/build/screens/conversation_details_screen.js +487 -393
  41. package/build/screens/conversation_details_screen.js.map +1 -1
  42. package/build/screens/conversation_screen.d.ts.map +1 -1
  43. package/build/screens/conversation_screen.js +12 -9
  44. package/build/screens/conversation_screen.js.map +1 -1
  45. package/build/screens/design_system_screen.d.ts.map +1 -1
  46. package/build/screens/design_system_screen.js +275 -215
  47. package/build/screens/design_system_screen.js.map +1 -1
  48. package/build/screens/message_actions_screen.d.ts.map +1 -1
  49. package/build/screens/message_actions_screen.js +112 -92
  50. package/build/screens/message_actions_screen.js.map +1 -1
  51. package/build/types/resources/member_ability.d.ts +2 -0
  52. package/build/types/resources/member_ability.d.ts.map +1 -1
  53. package/build/types/resources/member_ability.js.map +1 -1
  54. package/build/utils/archived_conversation.d.ts +5 -0
  55. package/build/utils/archived_conversation.d.ts.map +1 -0
  56. package/build/utils/archived_conversation.js +5 -0
  57. package/build/utils/archived_conversation.js.map +1 -0
  58. package/build/utils/index.d.ts +1 -0
  59. package/build/utils/index.d.ts.map +1 -1
  60. package/build/utils/index.js +1 -0
  61. package/build/utils/index.js.map +1 -1
  62. package/build/utils/native_adapters/configuration.d.ts +3 -0
  63. package/build/utils/native_adapters/configuration.d.ts.map +1 -1
  64. package/build/utils/native_adapters/configuration.js +3 -0
  65. package/build/utils/native_adapters/configuration.js.map +1 -1
  66. package/build/utils/native_adapters/index.d.ts +1 -0
  67. package/build/utils/native_adapters/index.d.ts.map +1 -1
  68. package/build/utils/native_adapters/index.js +1 -0
  69. package/build/utils/native_adapters/index.js.map +1 -1
  70. package/build/utils/native_adapters/menu.d.ts +40 -0
  71. package/build/utils/native_adapters/menu.d.ts.map +1 -0
  72. package/build/utils/native_adapters/menu.js +55 -0
  73. package/build/utils/native_adapters/menu.js.map +1 -0
  74. package/package.json +3 -3
  75. package/src/__tests__/screens/message_actions_screen.test.tsx +65 -2
  76. package/src/components/conversation/__tests__/message_list.test.tsx +68 -2
  77. package/src/components/conversation/__tests__/system_message.test.tsx +3 -1
  78. package/src/components/conversation/message.tsx +3 -0
  79. package/src/components/conversation/message_list.tsx +9 -1
  80. package/src/components/conversation/message_reaction.tsx +7 -2
  81. package/src/components/conversation/messages_disabled_banners.tsx +17 -18
  82. package/src/components/conversation/system_message.tsx +24 -10
  83. package/src/components/display/__tests__/filter_dropdown.test.tsx +106 -0
  84. package/src/components/display/filter_dropdown.tsx +123 -0
  85. package/src/components/display/index.ts +1 -0
  86. package/src/hooks/__tests__/use_conversation_archive.test.tsx +130 -0
  87. package/src/hooks/__tests__/use_conversation_jolt_events.test.tsx +74 -0
  88. package/src/hooks/__tests__/use_unarchive_conversation_action.test.tsx +67 -0
  89. package/src/hooks/use_conversation.ts +41 -3
  90. package/src/hooks/use_unarchive_conversation_action.ts +24 -0
  91. package/src/screens/__tests__/conversation_details_screen.test.tsx +289 -0
  92. package/src/screens/attachment_actions/__tests__/attachment_actions_screen.test.tsx +72 -0
  93. package/src/screens/attachment_actions/attachment_actions_screen.tsx +3 -1
  94. package/src/screens/conversation_details_screen.tsx +78 -14
  95. package/src/screens/conversation_screen.tsx +3 -1
  96. package/src/screens/design_system_screen.tsx +26 -0
  97. package/src/screens/message_actions_screen.tsx +29 -20
  98. package/src/types/resources/member_ability.ts +2 -0
  99. package/src/utils/archived_conversation.ts +13 -0
  100. package/src/utils/index.ts +1 -0
  101. package/src/utils/native_adapters/__tests__/menu.test.tsx +42 -0
  102. package/src/utils/native_adapters/configuration.ts +5 -0
  103. package/src/utils/native_adapters/index.ts +1 -0
  104. package/src/utils/native_adapters/menu.tsx +68 -0
@@ -9,6 +9,7 @@ import { useReactionStyles } from '../components/conversation/message_reaction'
9
9
  import FormSheet, { getFormSheetScreenOptions } from '../components/primitive/form_sheet'
10
10
  import { useCreateAndroidRippleColor, useFontScale, useTheme } from '../hooks'
11
11
  import { useApiClient } from '../hooks/use_api_client'
12
+ import { useConversation } from '../hooks/use_conversation'
12
13
  import { useConversationMessages } from '../hooks/use_conversation_messages'
13
14
  import { availableFeatures, useFeatures } from '../hooks/use_features'
14
15
  import { useMessageReactionToggle } from '../hooks/use_message_reaction_toggle'
@@ -49,6 +50,7 @@ export function MessageActionsScreen({ route }: MessageActionsScreenProps) {
49
50
  { conversation_id },
50
51
  { refetchOnMount: false }
51
52
  )
53
+ const { data: conversation } = useConversation({ conversation_id }, { refetchOnMount: false })
52
54
  const message = messages.find(m => m.id === message_id)
53
55
 
54
56
  if (!message) return null
@@ -57,6 +59,7 @@ export function MessageActionsScreen({ route }: MessageActionsScreenProps) {
57
59
  <MessageActionsScreenContent
58
60
  message={message}
59
61
  conversation_id={conversation_id}
62
+ conversationDisabled={!!conversation.disabled}
60
63
  canDeleteNonAuthoredMessages={canDeleteNonAuthoredMessages || false}
61
64
  refetchMessages={refetch}
62
65
  inReplyScreen={inReplyScreen}
@@ -69,6 +72,7 @@ export function MessageActionsScreen({ route }: MessageActionsScreenProps) {
69
72
  function MessageActionsScreenContent({
70
73
  message,
71
74
  conversation_id,
75
+ conversationDisabled,
72
76
  canDeleteNonAuthoredMessages,
73
77
  refetchMessages,
74
78
  inReplyScreen,
@@ -77,6 +81,7 @@ function MessageActionsScreenContent({
77
81
  }: {
78
82
  message: MessageResource
79
83
  conversation_id: number
84
+ conversationDisabled: boolean
80
85
  canDeleteNonAuthoredMessages: boolean
81
86
  refetchMessages: () => void
82
87
  inReplyScreen?: boolean
@@ -241,28 +246,32 @@ function MessageActionsScreenContent({
241
246
 
242
247
  const showReportMessageAction =
243
248
  !message?.mine && featureEnabled(availableFeatures.message_reporting)
249
+ const showDeleteMessageAction =
250
+ !conversationDisabled && (message?.mine || canDeleteNonAuthoredMessages)
244
251
 
245
252
  return (
246
253
  <FormSheet.Root style={styles.formSheetContent}>
247
- <View style={styles.reactionList}>
248
- {availableReactions.map((reaction, index) => (
249
- <Reaction
250
- key={index}
251
- reaction={reaction}
252
- onPress={() => {
253
- Haptic.impactLight()
254
- handleReactionToggle({
255
- value: reaction.value,
256
- mine: reaction.mine,
257
- })
258
- navigation.goBack()
259
- }}
260
- />
261
- ))}
262
- </View>
254
+ {!conversationDisabled && (
255
+ <View style={styles.reactionList}>
256
+ {availableReactions.map((reaction, index) => (
257
+ <Reaction
258
+ key={index}
259
+ reaction={reaction}
260
+ onPress={() => {
261
+ Haptic.impactLight()
262
+ handleReactionToggle({
263
+ value: reaction.value,
264
+ mine: reaction.mine,
265
+ })
266
+ navigation.goBack()
267
+ }}
268
+ />
269
+ ))}
270
+ </View>
271
+ )}
263
272
  {!isSystemMessage && (
264
273
  <View style={styles.actions}>
265
- {!inReplyScreen && (
274
+ {!inReplyScreen && !conversationDisabled && (
266
275
  <FormSheet.Action
267
276
  onPress={handleReplyPress}
268
277
  title="Reply to message"
@@ -285,7 +294,7 @@ function MessageActionsScreenContent({
285
294
  accessibilityHint="Opens a form to report this message"
286
295
  />
287
296
  )}
288
- {message?.mine && (
297
+ {message?.mine && !conversationDisabled && (
289
298
  <FormSheet.Action
290
299
  onPress={() => handleEditPress()}
291
300
  title="Edit message"
@@ -301,7 +310,7 @@ function MessageActionsScreenContent({
301
310
  accessibilityHint="Opens a modal with a list of people who read your message."
302
311
  />
303
312
  )}
304
- {message?.mine && hasExpandedLink && (
313
+ {message?.mine && hasExpandedLink && !conversationDisabled && (
305
314
  <FormSheet.Action
306
315
  onPress={() => handleRemoveLinkPreviewConfirm()}
307
316
  title="Remove link preview"
@@ -309,7 +318,7 @@ function MessageActionsScreenContent({
309
318
  accessibilityHint="Removes an expanded link preview from the message."
310
319
  />
311
320
  )}
312
- {(message?.mine || canDeleteNonAuthoredMessages) && (
321
+ {showDeleteMessageAction && (
313
322
  <FormSheet.Action
314
323
  onPress={() => handleDeleteConfirm()}
315
324
  title="Delete message"
@@ -6,4 +6,6 @@ export interface MemberAbilityResource {
6
6
  leader: boolean
7
7
  canReply: boolean
8
8
  canMessageAvailableMembers: boolean
9
+ canArchive: boolean
10
+ canUnarchive: boolean
9
11
  }
@@ -0,0 +1,13 @@
1
+ import type { ConversationResource } from '../types/resources/conversation'
2
+
3
+ export const ARCHIVED_DISABLED_REASON = 'archived'
4
+
5
+ const DEFAULT_ARCHIVED_MESSAGE = 'This conversation is archived.'
6
+
7
+ export const isArchivedConversation = (
8
+ conversation: Pick<ConversationResource, 'disabledReason'>
9
+ ) => conversation.disabledReason === ARCHIVED_DISABLED_REASON
10
+
11
+ export const archivedConversationMessage = (
12
+ conversation: Pick<ConversationResource, 'disabledMessage'>
13
+ ) => conversation.disabledMessage || DEFAULT_ARCHIVED_MESSAGE
@@ -1,3 +1,4 @@
1
+ export * from './archived_conversation'
1
2
  export * from './assert_keys_are_numbers'
2
3
  export * from './attachment_kind'
3
4
  export * from './chat_type_label'
@@ -0,0 +1,42 @@
1
+ import { render, screen } from '@testing-library/react-native'
2
+ import React from 'react'
3
+ import { Text } from 'react-native'
4
+ import { MenuAdapter, MenuViewProps } from '../menu'
5
+
6
+ const actions = [{ id: 'all', title: 'All' }]
7
+
8
+ describe('MenuAdapter', () => {
9
+ it('renders the trigger and warns when the host configured no menu', () => {
10
+ const warn = jest.spyOn(console, 'warn').mockImplementation(() => {})
11
+ const { View } = new MenuAdapter()
12
+
13
+ render(
14
+ <View actions={actions}>
15
+ <Text>Trigger</Text>
16
+ </View>
17
+ )
18
+
19
+ expect(screen.getByText('Trigger')).toBeTruthy()
20
+ expect(warn).toHaveBeenCalledTimes(1)
21
+ warn.mockRestore()
22
+ })
23
+
24
+ it('renders through the view the host supplies', () => {
25
+ const HostMenuView = ({ actions: hostActions, children }: MenuViewProps) => (
26
+ <>
27
+ {children}
28
+ <Text>{hostActions.map(action => action.title).join(',')}</Text>
29
+ </>
30
+ )
31
+ const { View } = new MenuAdapter({ View: HostMenuView })
32
+
33
+ render(
34
+ <View actions={actions}>
35
+ <Text>Trigger</Text>
36
+ </View>
37
+ )
38
+
39
+ expect(screen.getByText('Trigger')).toBeTruthy()
40
+ expect(screen.getByText('All')).toBeTruthy()
41
+ })
42
+ })
@@ -7,6 +7,7 @@ import { HapticAdapter } from './haptic'
7
7
  import { ImagePickerAdapter } from './image_picker'
8
8
  import { LinkingAdapter } from './linking'
9
9
  import { LogAdapter } from './log'
10
+ import { MenuAdapter } from './menu'
10
11
  import { StorageAdapter } from './storage_adapter'
11
12
  import { VideoAdapter } from './video'
12
13
 
@@ -19,6 +20,7 @@ type ChatConfigurations = {
19
20
  log?: LogAdapter
20
21
  linking?: LinkingAdapter
21
22
  haptic?: HapticAdapter
23
+ menu?: MenuAdapter
22
24
  }
23
25
 
24
26
  export class ChatAdapters {
@@ -31,6 +33,7 @@ export class ChatAdapters {
31
33
  Log = configurations.log || new LogAdapter()
32
34
  Linking = configurations.linking || new LinkingAdapter(RNLinking)
33
35
  Haptic = configurations.haptic || new HapticAdapter()
36
+ Menu = configurations.menu || new MenuAdapter()
34
37
  }
35
38
  }
36
39
 
@@ -82,6 +85,8 @@ export let Linking: LinkingAdapter = new LinkingAdapter(RNLinking)
82
85
 
83
86
  export let Haptic: HapticAdapter = new HapticAdapter()
84
87
 
88
+ export let Menu: MenuAdapter = new MenuAdapter()
89
+
85
90
  export let Storage = new StorageAdapter({
86
91
  getItem: AsyncStorage.getItem,
87
92
  setItem: AsyncStorage.setItem,
@@ -5,6 +5,7 @@ export * from './document_picker'
5
5
  export * from './image_picker'
6
6
  export * from './linking'
7
7
  export * from './log'
8
+ export * from './menu'
8
9
  export * from './video'
9
10
  export * from './haptic'
10
11
  export * from './storage_adapter'
@@ -0,0 +1,68 @@
1
+ import React, { useEffect } from 'react'
2
+ import { StyleProp, View, ViewStyle } from 'react-native'
3
+
4
+ // =================================
5
+ // ====== Types ====================
6
+ // =================================
7
+
8
+ // Mirrors the props shared by `@expo/ui/community/menu` and
9
+ // `@react-native-menu/menu`, so a host can pass either `MenuView` straight in.
10
+
11
+ export type MenuActionState = 'on' | 'off'
12
+
13
+ export interface MenuActionAttributes {
14
+ destructive?: boolean
15
+ disabled?: boolean
16
+ hidden?: boolean
17
+ }
18
+
19
+ export interface MenuAction {
20
+ id?: string
21
+ title: string
22
+ state?: MenuActionState
23
+ attributes?: MenuActionAttributes
24
+ subactions?: MenuAction[]
25
+ displayInline?: boolean
26
+ }
27
+
28
+ export interface MenuActionEvent {
29
+ nativeEvent: { event: string }
30
+ }
31
+
32
+ export interface MenuViewProps {
33
+ actions: MenuAction[]
34
+ onPressAction?: (event: MenuActionEvent) => void
35
+ onOpenMenu?: () => void
36
+ onCloseMenu?: () => void
37
+ shouldOpenOnLongPress?: boolean
38
+ title?: string
39
+ style?: StyleProp<ViewStyle>
40
+ testID?: string
41
+ children?: React.ReactNode
42
+ }
43
+
44
+ export type MenuView = React.ComponentType<MenuViewProps>
45
+
46
+ // =================================
47
+ // ====== Adapter ==================
48
+ // =================================
49
+
50
+ function UnconfiguredMenuView({ children, style, testID }: MenuViewProps) {
51
+ useEffect(() => {
52
+ console.warn('ChatAdapters.configure() must include a menu adapter before rendering a menu')
53
+ }, [])
54
+
55
+ return (
56
+ <View style={style} testID={testID}>
57
+ {children}
58
+ </View>
59
+ )
60
+ }
61
+
62
+ export class MenuAdapter {
63
+ View: MenuView
64
+
65
+ constructor(methods?: { View: MenuView }) {
66
+ this.View = methods?.View ?? UnconfiguredMenuView
67
+ }
68
+ }