@planningcenter/chat-react-native 4.4.1-qa-staging.12 → 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.
- package/build/components/conversation/jump_to_bottom_button.d.ts.map +1 -1
- package/build/components/conversation/jump_to_bottom_button.js +42 -40
- package/build/components/conversation/jump_to_bottom_button.js.map +1 -1
- package/build/components/conversation/message.d.ts +2 -1
- package/build/components/conversation/message.d.ts.map +1 -1
- package/build/components/conversation/message.js +3 -1
- package/build/components/conversation/message.js.map +1 -1
- package/build/components/conversation/message_list.d.ts.map +1 -1
- package/build/components/conversation/message_list.js +35 -31
- package/build/components/conversation/message_list.js.map +1 -1
- package/build/components/conversation/message_reaction.d.ts +2 -1
- package/build/components/conversation/message_reaction.d.ts.map +1 -1
- package/build/components/conversation/message_reaction.js +67 -60
- package/build/components/conversation/message_reaction.js.map +1 -1
- package/build/components/conversation/messages_disabled_banners.d.ts +7 -5
- package/build/components/conversation/messages_disabled_banners.d.ts.map +1 -1
- package/build/components/conversation/messages_disabled_banners.js +15 -14
- package/build/components/conversation/messages_disabled_banners.js.map +1 -1
- package/build/components/conversation/system_message.d.ts +2 -1
- package/build/components/conversation/system_message.d.ts.map +1 -1
- package/build/components/conversation/system_message.js +87 -65
- package/build/components/conversation/system_message.js.map +1 -1
- package/build/components/display/action_button.d.ts.map +1 -1
- package/build/components/display/action_button.js +18 -16
- package/build/components/display/action_button.js.map +1 -1
- package/build/components/display/filter_dropdown.d.ts +13 -0
- package/build/components/display/filter_dropdown.d.ts.map +1 -0
- package/build/components/display/filter_dropdown.js +188 -0
- package/build/components/display/filter_dropdown.js.map +1 -0
- package/build/components/display/index.d.ts +1 -0
- package/build/components/display/index.d.ts.map +1 -1
- package/build/components/display/index.js +1 -0
- package/build/components/display/index.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_animated_message_background_color.d.ts.map +1 -1
- package/build/hooks/use_animated_message_background_color.js +5 -3
- package/build/hooks/use_animated_message_background_color.js.map +1 -1
- package/build/hooks/use_conversation.d.ts +73 -1
- package/build/hooks/use_conversation.d.ts.map +1 -1
- package/build/hooks/use_conversation.js +122 -3
- package/build/hooks/use_conversation.js.map +1 -1
- package/build/hooks/use_reduce_motion.d.ts +2 -0
- package/build/hooks/use_reduce_motion.d.ts.map +1 -0
- package/build/hooks/use_reduce_motion.js +27 -0
- package/build/hooks/use_reduce_motion.js.map +1 -0
- package/build/hooks/use_unarchive_conversation_action.d.ts +7 -0
- package/build/hooks/use_unarchive_conversation_action.d.ts.map +1 -0
- package/build/hooks/use_unarchive_conversation_action.js +52 -0
- package/build/hooks/use_unarchive_conversation_action.js.map +1 -0
- package/build/screens/attachment_actions/attachment_actions_screen.d.ts.map +1 -1
- package/build/screens/attachment_actions/attachment_actions_screen.js +84 -61
- package/build/screens/attachment_actions/attachment_actions_screen.js.map +1 -1
- package/build/screens/conversation_details_screen.d.ts.map +1 -1
- package/build/screens/conversation_details_screen.js +487 -393
- package/build/screens/conversation_details_screen.js.map +1 -1
- package/build/screens/conversation_screen.d.ts.map +1 -1
- package/build/screens/conversation_screen.js +12 -9
- 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 +275 -215
- package/build/screens/design_system_screen.js.map +1 -1
- package/build/screens/message_actions_screen.d.ts.map +1 -1
- package/build/screens/message_actions_screen.js +112 -92
- package/build/screens/message_actions_screen.js.map +1 -1
- package/build/types/resources/member_ability.d.ts +2 -0
- package/build/types/resources/member_ability.d.ts.map +1 -1
- package/build/types/resources/member_ability.js.map +1 -1
- package/build/utils/archived_conversation.d.ts +5 -0
- package/build/utils/archived_conversation.d.ts.map +1 -0
- package/build/utils/archived_conversation.js +5 -0
- package/build/utils/archived_conversation.js.map +1 -0
- 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/native_adapters/configuration.d.ts +3 -0
- package/build/utils/native_adapters/configuration.d.ts.map +1 -1
- package/build/utils/native_adapters/configuration.js +3 -0
- package/build/utils/native_adapters/configuration.js.map +1 -1
- package/build/utils/native_adapters/index.d.ts +1 -0
- package/build/utils/native_adapters/index.d.ts.map +1 -1
- package/build/utils/native_adapters/index.js +1 -0
- package/build/utils/native_adapters/index.js.map +1 -1
- package/build/utils/native_adapters/menu.d.ts +40 -0
- package/build/utils/native_adapters/menu.d.ts.map +1 -0
- package/build/utils/native_adapters/menu.js +55 -0
- package/build/utils/native_adapters/menu.js.map +1 -0
- package/package.json +3 -3
- package/src/__tests__/screens/message_actions_screen.test.tsx +65 -2
- package/src/components/conversation/__tests__/message_list.test.tsx +68 -2
- package/src/components/conversation/__tests__/system_message.test.tsx +3 -1
- package/src/components/conversation/jump_to_bottom_button.tsx +4 -3
- package/src/components/conversation/message.tsx +3 -0
- package/src/components/conversation/message_list.tsx +9 -1
- package/src/components/conversation/message_reaction.tsx +7 -2
- package/src/components/conversation/messages_disabled_banners.tsx +17 -18
- package/src/components/conversation/system_message.tsx +24 -10
- package/src/components/display/__tests__/filter_dropdown.test.tsx +106 -0
- package/src/components/display/action_button.tsx +4 -3
- package/src/components/display/filter_dropdown.tsx +123 -0
- package/src/components/display/index.ts +1 -0
- package/src/hooks/__tests__/use_conversation_archive.test.tsx +130 -0
- package/src/hooks/__tests__/use_conversation_jolt_events.test.tsx +74 -0
- package/src/hooks/__tests__/use_reduce_motion.test.tsx +49 -0
- package/src/hooks/__tests__/use_unarchive_conversation_action.test.tsx +67 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use_animated_message_background_color.ts +6 -3
- package/src/hooks/use_conversation.ts +41 -3
- package/src/hooks/use_reduce_motion.ts +18 -0
- package/src/hooks/use_unarchive_conversation_action.ts +24 -0
- package/src/screens/__tests__/conversation_details_screen.test.tsx +289 -0
- package/src/screens/attachment_actions/__tests__/attachment_actions_screen.test.tsx +72 -0
- package/src/screens/attachment_actions/attachment_actions_screen.tsx +3 -1
- package/src/screens/conversation_details_screen.tsx +78 -14
- package/src/screens/conversation_screen.tsx +3 -1
- package/src/screens/design_system_screen.tsx +26 -0
- package/src/screens/message_actions_screen.tsx +29 -20
- package/src/types/resources/member_ability.ts +2 -0
- package/src/utils/archived_conversation.ts +13 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/native_adapters/__tests__/menu.test.tsx +42 -0
- package/src/utils/native_adapters/configuration.ts +5 -0
- package/src/utils/native_adapters/index.ts +1 -0
- package/src/utils/native_adapters/menu.tsx +68 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configuration.js","names":["AsyncStorage","Linking","RNLinking","AudioAdapter","ClipboardAdapter","DocumentPickerAdapter","HapticAdapter","ImagePickerAdapter","LinkingAdapter","LogAdapter","StorageAdapter","VideoAdapter","ChatAdapters","configure","configurations","Clipboard","clipboard","Audio","audio","Video","video","ImagePicker","imagePicker","DocumentPicker","documentPicker","Log","log","linking","Haptic","haptic","methodMissing","console","warn","getStringAsync","setStringAsync","_","useAudio","Player","Object","assign","$$typeof","Symbol","for","openCameraAsync","canceled","assets","openImageLibraryAsync","Storage","getItem","setItem","removeItem"],"sources":["../../../src/utils/native_adapters/configuration.ts"],"sourcesContent":["import AsyncStorage from '@react-native-async-storage/async-storage'\nimport { Linking as RNLinking } from 'react-native'\nimport { AudioAdapter } from './audio'\nimport { ClipboardAdapter } from './clipboard'\nimport { DocumentPickerAdapter } from './document_picker'\nimport { HapticAdapter } from './haptic'\nimport { ImagePickerAdapter } from './image_picker'\nimport { LinkingAdapter } from './linking'\nimport { LogAdapter } from './log'\nimport { StorageAdapter } from './storage_adapter'\nimport { VideoAdapter } from './video'\n\ntype ChatConfigurations = {\n clipboard: ClipboardAdapter\n audio: AudioAdapter\n video: VideoAdapter\n imagePicker: ImagePickerAdapter\n documentPicker?: DocumentPickerAdapter\n log?: LogAdapter\n linking?: LinkingAdapter\n haptic?: HapticAdapter\n}\n\nexport class ChatAdapters {\n static configure(configurations: ChatConfigurations) {\n Clipboard = configurations.clipboard\n Audio = configurations.audio\n Video = configurations.video\n ImagePicker = configurations.imagePicker\n DocumentPicker = configurations.documentPicker || new DocumentPickerAdapter()\n Log = configurations.log || new LogAdapter()\n Linking = configurations.linking || new LinkingAdapter(RNLinking)\n Haptic = configurations.haptic || new HapticAdapter()\n }\n}\n\nconst methodMissing = () => {\n console.warn('ChatAdapters.configure() must be called before using any adapters')\n}\n\nexport let Clipboard: ClipboardAdapter = new ClipboardAdapter({\n getStringAsync: async () => {\n methodMissing()\n return ''\n },\n setStringAsync: async (_: string) => methodMissing(),\n})\n\nexport let Audio: AudioAdapter = new AudioAdapter({\n useAudio: (_: string) => {\n methodMissing()\n return {} as any\n },\n})\n\nexport let Video: VideoAdapter = new VideoAdapter({\n Player: Object.assign(\n () => {\n methodMissing()\n return null\n },\n { $$typeof: Symbol.for('react.forward_ref') }\n ),\n})\n\nexport let ImagePicker: ImagePickerAdapter = new ImagePickerAdapter({\n openCameraAsync: async () => {\n methodMissing()\n return { canceled: true, assets: null }\n },\n openImageLibraryAsync: async () => {\n methodMissing()\n return { canceled: true, assets: null }\n },\n})\n\nexport let DocumentPicker: DocumentPickerAdapter = new DocumentPickerAdapter()\n\nexport let Log: LogAdapter = new LogAdapter()\n\nexport let Linking: LinkingAdapter = new LinkingAdapter(RNLinking)\n\nexport let Haptic: HapticAdapter = new HapticAdapter()\n\nexport let Storage = new StorageAdapter({\n getItem: AsyncStorage.getItem,\n setItem: AsyncStorage.setItem,\n removeItem: AsyncStorage.removeItem,\n})\n"],"mappings":"AAAA,OAAOA,YAAY,MAAM,2CAA2C;AACpE,SAASC,OAAO,IAAIC,SAAS,QAAQ,cAAc;AACnD,SAASC,YAAY,QAAQ,SAAS;AACtC,SAASC,gBAAgB,QAAQ,aAAa;AAC9C,SAASC,qBAAqB,QAAQ,mBAAmB;AACzD,SAASC,aAAa,QAAQ,UAAU;AACxC,SAASC,kBAAkB,QAAQ,gBAAgB;AACnD,SAASC,cAAc,QAAQ,WAAW;AAC1C,SAASC,UAAU,QAAQ,OAAO;AAClC,SAASC,cAAc,QAAQ,mBAAmB;AAClD,SAASC,YAAY,QAAQ,SAAS;
|
|
1
|
+
{"version":3,"file":"configuration.js","names":["AsyncStorage","Linking","RNLinking","AudioAdapter","ClipboardAdapter","DocumentPickerAdapter","HapticAdapter","ImagePickerAdapter","LinkingAdapter","LogAdapter","MenuAdapter","StorageAdapter","VideoAdapter","ChatAdapters","configure","configurations","Clipboard","clipboard","Audio","audio","Video","video","ImagePicker","imagePicker","DocumentPicker","documentPicker","Log","log","linking","Haptic","haptic","Menu","menu","methodMissing","console","warn","getStringAsync","setStringAsync","_","useAudio","Player","Object","assign","$$typeof","Symbol","for","openCameraAsync","canceled","assets","openImageLibraryAsync","Storage","getItem","setItem","removeItem"],"sources":["../../../src/utils/native_adapters/configuration.ts"],"sourcesContent":["import AsyncStorage from '@react-native-async-storage/async-storage'\nimport { Linking as RNLinking } from 'react-native'\nimport { AudioAdapter } from './audio'\nimport { ClipboardAdapter } from './clipboard'\nimport { DocumentPickerAdapter } from './document_picker'\nimport { HapticAdapter } from './haptic'\nimport { ImagePickerAdapter } from './image_picker'\nimport { LinkingAdapter } from './linking'\nimport { LogAdapter } from './log'\nimport { MenuAdapter } from './menu'\nimport { StorageAdapter } from './storage_adapter'\nimport { VideoAdapter } from './video'\n\ntype ChatConfigurations = {\n clipboard: ClipboardAdapter\n audio: AudioAdapter\n video: VideoAdapter\n imagePicker: ImagePickerAdapter\n documentPicker?: DocumentPickerAdapter\n log?: LogAdapter\n linking?: LinkingAdapter\n haptic?: HapticAdapter\n menu?: MenuAdapter\n}\n\nexport class ChatAdapters {\n static configure(configurations: ChatConfigurations) {\n Clipboard = configurations.clipboard\n Audio = configurations.audio\n Video = configurations.video\n ImagePicker = configurations.imagePicker\n DocumentPicker = configurations.documentPicker || new DocumentPickerAdapter()\n Log = configurations.log || new LogAdapter()\n Linking = configurations.linking || new LinkingAdapter(RNLinking)\n Haptic = configurations.haptic || new HapticAdapter()\n Menu = configurations.menu || new MenuAdapter()\n }\n}\n\nconst methodMissing = () => {\n console.warn('ChatAdapters.configure() must be called before using any adapters')\n}\n\nexport let Clipboard: ClipboardAdapter = new ClipboardAdapter({\n getStringAsync: async () => {\n methodMissing()\n return ''\n },\n setStringAsync: async (_: string) => methodMissing(),\n})\n\nexport let Audio: AudioAdapter = new AudioAdapter({\n useAudio: (_: string) => {\n methodMissing()\n return {} as any\n },\n})\n\nexport let Video: VideoAdapter = new VideoAdapter({\n Player: Object.assign(\n () => {\n methodMissing()\n return null\n },\n { $$typeof: Symbol.for('react.forward_ref') }\n ),\n})\n\nexport let ImagePicker: ImagePickerAdapter = new ImagePickerAdapter({\n openCameraAsync: async () => {\n methodMissing()\n return { canceled: true, assets: null }\n },\n openImageLibraryAsync: async () => {\n methodMissing()\n return { canceled: true, assets: null }\n },\n})\n\nexport let DocumentPicker: DocumentPickerAdapter = new DocumentPickerAdapter()\n\nexport let Log: LogAdapter = new LogAdapter()\n\nexport let Linking: LinkingAdapter = new LinkingAdapter(RNLinking)\n\nexport let Haptic: HapticAdapter = new HapticAdapter()\n\nexport let Menu: MenuAdapter = new MenuAdapter()\n\nexport let Storage = new StorageAdapter({\n getItem: AsyncStorage.getItem,\n setItem: AsyncStorage.setItem,\n removeItem: AsyncStorage.removeItem,\n})\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/native_adapters/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAA;AACvB,cAAc,aAAa,CAAA;AAC3B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,mBAAmB,CAAA;AACjC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,WAAW,CAAA;AACzB,cAAc,OAAO,CAAA;AACrB,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,mBAAmB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/native_adapters/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAA;AACvB,cAAc,aAAa,CAAA;AAC3B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,mBAAmB,CAAA;AACjC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,WAAW,CAAA;AACzB,cAAc,OAAO,CAAA;AACrB,cAAc,QAAQ,CAAA;AACtB,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,mBAAmB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/utils/native_adapters/index.ts"],"sourcesContent":["export * from './audio'\nexport * from './clipboard'\nexport * from './configuration'\nexport * from './document_picker'\nexport * from './image_picker'\nexport * from './linking'\nexport * from './log'\nexport * from './video'\nexport * from './haptic'\nexport * from './storage_adapter'\n"],"mappings":"AAAA,cAAc,SAAS;AACvB,cAAc,aAAa;AAC3B,cAAc,iBAAiB;AAC/B,cAAc,mBAAmB;AACjC,cAAc,gBAAgB;AAC9B,cAAc,WAAW;AACzB,cAAc,OAAO;AACrB,cAAc,SAAS;AACvB,cAAc,UAAU;AACxB,cAAc,mBAAmB","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/utils/native_adapters/index.ts"],"sourcesContent":["export * from './audio'\nexport * from './clipboard'\nexport * from './configuration'\nexport * from './document_picker'\nexport * from './image_picker'\nexport * from './linking'\nexport * from './log'\nexport * from './menu'\nexport * from './video'\nexport * from './haptic'\nexport * from './storage_adapter'\n"],"mappings":"AAAA,cAAc,SAAS;AACvB,cAAc,aAAa;AAC3B,cAAc,iBAAiB;AAC/B,cAAc,mBAAmB;AACjC,cAAc,gBAAgB;AAC9B,cAAc,WAAW;AACzB,cAAc,OAAO;AACrB,cAAc,QAAQ;AACtB,cAAc,SAAS;AACvB,cAAc,UAAU;AACxB,cAAc,mBAAmB","ignoreList":[]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
export type MenuActionState = 'on' | 'off';
|
|
4
|
+
export interface MenuActionAttributes {
|
|
5
|
+
destructive?: boolean;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
hidden?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface MenuAction {
|
|
10
|
+
id?: string;
|
|
11
|
+
title: string;
|
|
12
|
+
state?: MenuActionState;
|
|
13
|
+
attributes?: MenuActionAttributes;
|
|
14
|
+
subactions?: MenuAction[];
|
|
15
|
+
displayInline?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface MenuActionEvent {
|
|
18
|
+
nativeEvent: {
|
|
19
|
+
event: string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export interface MenuViewProps {
|
|
23
|
+
actions: MenuAction[];
|
|
24
|
+
onPressAction?: (event: MenuActionEvent) => void;
|
|
25
|
+
onOpenMenu?: () => void;
|
|
26
|
+
onCloseMenu?: () => void;
|
|
27
|
+
shouldOpenOnLongPress?: boolean;
|
|
28
|
+
title?: string;
|
|
29
|
+
style?: StyleProp<ViewStyle>;
|
|
30
|
+
testID?: string;
|
|
31
|
+
children?: React.ReactNode;
|
|
32
|
+
}
|
|
33
|
+
export type MenuView = React.ComponentType<MenuViewProps>;
|
|
34
|
+
export declare class MenuAdapter {
|
|
35
|
+
View: MenuView;
|
|
36
|
+
constructor(methods?: {
|
|
37
|
+
View: MenuView;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=menu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu.d.ts","sourceRoot":"","sources":["../../../src/utils/native_adapters/menu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoB,MAAM,OAAO,CAAA;AACxC,OAAO,EAAE,SAAS,EAAQ,SAAS,EAAE,MAAM,cAAc,CAAA;AASzD,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,KAAK,CAAA;AAE1C,MAAM,WAAW,oBAAoB;IACnC,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,eAAe,CAAA;IACvB,UAAU,CAAC,EAAE,oBAAoB,CAAA;IACjC,UAAU,CAAC,EAAE,UAAU,EAAE,CAAA;IACzB,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CAC/B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,UAAU,EAAE,CAAA;IACrB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAA;IAChD,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAA;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC3B;AAED,MAAM,MAAM,QAAQ,GAAG,KAAK,CAAC,aAAa,CAAC,aAAa,CAAC,CAAA;AAkBzD,qBAAa,WAAW;IACtB,IAAI,EAAE,QAAQ,CAAA;gBAEF,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAA;KAAE;CAGzC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { c as _c } from "react/compiler-runtime";
|
|
2
|
+
import React, { useEffect } from 'react';
|
|
3
|
+
import { View } from 'react-native';
|
|
4
|
+
|
|
5
|
+
// =================================
|
|
6
|
+
// ====== Types ====================
|
|
7
|
+
// =================================
|
|
8
|
+
|
|
9
|
+
// Mirrors the props shared by `@expo/ui/community/menu` and
|
|
10
|
+
// `@react-native-menu/menu`, so a host can pass either `MenuView` straight in.
|
|
11
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
12
|
+
// =================================
|
|
13
|
+
// ====== Adapter ==================
|
|
14
|
+
// =================================
|
|
15
|
+
|
|
16
|
+
function UnconfiguredMenuView(t0) {
|
|
17
|
+
const $ = _c(5);
|
|
18
|
+
const {
|
|
19
|
+
children,
|
|
20
|
+
style,
|
|
21
|
+
testID
|
|
22
|
+
} = t0;
|
|
23
|
+
let t1;
|
|
24
|
+
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
|
25
|
+
t1 = [];
|
|
26
|
+
$[0] = t1;
|
|
27
|
+
} else {
|
|
28
|
+
t1 = $[0];
|
|
29
|
+
}
|
|
30
|
+
useEffect(_temp, t1);
|
|
31
|
+
let t2;
|
|
32
|
+
if ($[1] !== children || $[2] !== style || $[3] !== testID) {
|
|
33
|
+
t2 = /*#__PURE__*/_jsx(View, {
|
|
34
|
+
style: style,
|
|
35
|
+
testID: testID,
|
|
36
|
+
children: children
|
|
37
|
+
});
|
|
38
|
+
$[1] = children;
|
|
39
|
+
$[2] = style;
|
|
40
|
+
$[3] = testID;
|
|
41
|
+
$[4] = t2;
|
|
42
|
+
} else {
|
|
43
|
+
t2 = $[4];
|
|
44
|
+
}
|
|
45
|
+
return t2;
|
|
46
|
+
}
|
|
47
|
+
function _temp() {
|
|
48
|
+
console.warn("ChatAdapters.configure() must include a menu adapter before rendering a menu");
|
|
49
|
+
}
|
|
50
|
+
export class MenuAdapter {
|
|
51
|
+
constructor(methods) {
|
|
52
|
+
this.View = methods?.View ?? UnconfiguredMenuView;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=menu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu.js","names":["React","useEffect","View","jsx","_jsx","UnconfiguredMenuView","t0","$","_c","children","style","testID","t1","Symbol","for","_temp","t2","console","warn","MenuAdapter","constructor","methods"],"sources":["../../../src/utils/native_adapters/menu.tsx"],"sourcesContent":["import React, { useEffect } from 'react'\nimport { StyleProp, View, ViewStyle } from 'react-native'\n\n// =================================\n// ====== Types ====================\n// =================================\n\n// Mirrors the props shared by `@expo/ui/community/menu` and\n// `@react-native-menu/menu`, so a host can pass either `MenuView` straight in.\n\nexport type MenuActionState = 'on' | 'off'\n\nexport interface MenuActionAttributes {\n destructive?: boolean\n disabled?: boolean\n hidden?: boolean\n}\n\nexport interface MenuAction {\n id?: string\n title: string\n state?: MenuActionState\n attributes?: MenuActionAttributes\n subactions?: MenuAction[]\n displayInline?: boolean\n}\n\nexport interface MenuActionEvent {\n nativeEvent: { event: string }\n}\n\nexport interface MenuViewProps {\n actions: MenuAction[]\n onPressAction?: (event: MenuActionEvent) => void\n onOpenMenu?: () => void\n onCloseMenu?: () => void\n shouldOpenOnLongPress?: boolean\n title?: string\n style?: StyleProp<ViewStyle>\n testID?: string\n children?: React.ReactNode\n}\n\nexport type MenuView = React.ComponentType<MenuViewProps>\n\n// =================================\n// ====== Adapter ==================\n// =================================\n\nfunction UnconfiguredMenuView({ children, style, testID }: MenuViewProps) {\n useEffect(() => {\n console.warn('ChatAdapters.configure() must include a menu adapter before rendering a menu')\n }, [])\n\n return (\n <View style={style} testID={testID}>\n {children}\n </View>\n )\n}\n\nexport class MenuAdapter {\n View: MenuView\n\n constructor(methods?: { View: MenuView }) {\n this.View = methods?.View ?? UnconfiguredMenuView\n }\n}\n"],"mappings":";AAAA,OAAOA,KAAK,IAAIC,SAAS,QAAQ,OAAO;AACxC,SAAoBC,IAAI,QAAmB,cAAc;;AAEzD;AACA;AACA;;AAEA;AACA;AAAA,SAAAC,GAAA,IAAAC,IAAA;AAqCA;AACA;AACA;;AAEA,SAAAC,qBAAAC,EAAA;EAAA,MAAAC,CAAA,GAAAC,EAAA;EAA8B;IAAAC,QAAA;IAAAC,KAAA;IAAAC;EAAA,IAAAL,EAA0C;EAAA,IAAAM,EAAA;EAAA,IAAAL,CAAA,QAAAM,MAAA,CAAAC,GAAA;IAGnEF,EAAA,KAAE;IAAAL,CAAA,MAAAK,EAAA;EAAA;IAAAA,EAAA,GAAAL,CAAA;EAAA;EAFLN,SAAS,CAACc,KAET,EAAEH,EAAE,CAAC;EAAA,IAAAI,EAAA;EAAA,IAAAT,CAAA,QAAAE,QAAA,IAAAF,CAAA,QAAAG,KAAA,IAAAH,CAAA,QAAAI,MAAA;IAGJK,EAAA,gBAAAZ,IAAA,CAACF,IAAI;MAAQQ,KAAK,EAALA,KAAK;MAAUC,MAAM,EAANA,MAAM;MAAAF,QAAA,EAC/BA;IAAQ,CACL,CAAC;IAAAF,CAAA,MAAAE,QAAA;IAAAF,CAAA,MAAAG,KAAA;IAAAH,CAAA,MAAAI,MAAA;IAAAJ,CAAA,MAAAS,EAAA;EAAA;IAAAA,EAAA,GAAAT,CAAA;EAAA;EAAA,OAFPS,EAEO;AAAA;AARX,SAAAD,MAAA;EAEIE,OAAO,CAAAC,IAAK,CAAC,8EAA8E,CAAC;AAAA;AAUhG,OAAO,MAAMC,WAAW,CAAC;EAGvBC,WAAWA,CAACC,OAA4B,EAAE;IACxC,IAAI,CAACnB,IAAI,GAAGmB,OAAO,EAAEnB,IAAI,IAAIG,oBAAoB;EACnD;AACF","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@planningcenter/chat-react-native",
|
|
3
|
-
"version": "4.4.1-qa-staging.
|
|
3
|
+
"version": "4.4.1-qa-staging.14",
|
|
4
4
|
"description": "",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@fortawesome/fontawesome-svg-core": "^7.2.0",
|
|
35
35
|
"@fortawesome/react-native-fontawesome": "^0.3.2",
|
|
36
|
-
"@planningcenter/emoji-keyboard": "4.4.1-qa-staging.
|
|
36
|
+
"@planningcenter/emoji-keyboard": "4.4.1-qa-staging.14",
|
|
37
37
|
"lodash-inflection": "^1.5.0"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
@@ -84,5 +84,5 @@
|
|
|
84
84
|
"sf-symbols-typescript": "^2.2.0",
|
|
85
85
|
"typescript": "~6.0.3"
|
|
86
86
|
},
|
|
87
|
-
"gitHead": "
|
|
87
|
+
"gitHead": "cae9a5592e733dfe3eb5453dfe1252b75878eff4"
|
|
88
88
|
}
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
MessageActionsScreen,
|
|
10
10
|
MessageActionsScreenProps,
|
|
11
11
|
} from '../../screens/message_actions_screen'
|
|
12
|
-
import { MessageResource } from '../../types'
|
|
12
|
+
import { ConversationResource, MessageResource } from '../../types'
|
|
13
13
|
import {
|
|
14
14
|
AudioAdapter,
|
|
15
15
|
ChatAdapters,
|
|
@@ -36,7 +36,15 @@ const mockMessage: MessageResource = {
|
|
|
36
36
|
messageType: 'user',
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
const myMessageWithLinkPreview = {
|
|
40
|
+
...mockMessage,
|
|
41
|
+
mine: true,
|
|
42
|
+
attachments: [{ type: 'ExpandedLink', id: '9' }],
|
|
43
|
+
} as MessageResource
|
|
44
|
+
|
|
39
45
|
let mockCurrentMessage = mockMessage
|
|
46
|
+
let mockConversation = { id: 1, disabled: false } as ConversationResource
|
|
47
|
+
let mockEnabledFeatures: string[] = []
|
|
40
48
|
|
|
41
49
|
const mockGoBack = jest.fn()
|
|
42
50
|
jest.mock('@react-navigation/native', () => ({
|
|
@@ -44,9 +52,15 @@ jest.mock('@react-navigation/native', () => ({
|
|
|
44
52
|
useNavigation: () => ({ navigate: jest.fn(), dispatch: jest.fn(), goBack: mockGoBack }),
|
|
45
53
|
}))
|
|
46
54
|
jest.mock('../../hooks/use_api_client', () => ({ useApiClient: () => ({}) }))
|
|
55
|
+
jest.mock('../../hooks/use_conversation', () => ({
|
|
56
|
+
useConversation: () => ({ data: mockConversation }),
|
|
57
|
+
}))
|
|
47
58
|
jest.mock('../../hooks/use_features', () => ({
|
|
48
59
|
...jest.requireActual('../../hooks/use_features'),
|
|
49
|
-
useFeatures: () => ({
|
|
60
|
+
useFeatures: () => ({
|
|
61
|
+
features: [],
|
|
62
|
+
featureEnabled: (name: string) => mockEnabledFeatures.includes(name),
|
|
63
|
+
}),
|
|
50
64
|
}))
|
|
51
65
|
jest.mock('../../hooks/use_message_reaction_toggle', () => ({
|
|
52
66
|
useMessageReactionToggle: () => ({ handleReactionToggle: jest.fn(), isPending: false }),
|
|
@@ -108,6 +122,55 @@ describe('MessageActionsScreen', () => {
|
|
|
108
122
|
beforeEach(() => {
|
|
109
123
|
jest.clearAllMocks()
|
|
110
124
|
mockCurrentMessage = mockMessage
|
|
125
|
+
mockConversation = { id: 1, disabled: false } as ConversationResource
|
|
126
|
+
mockEnabledFeatures = []
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('offers Delete on my own message', () => {
|
|
130
|
+
mockCurrentMessage = { ...mockMessage, mine: true }
|
|
131
|
+
const { getByText } = renderScreen()
|
|
132
|
+
|
|
133
|
+
expect(getByText('Delete message')).toBeTruthy()
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('hides Delete when the conversation is disabled', () => {
|
|
137
|
+
mockCurrentMessage = { ...mockMessage, mine: true }
|
|
138
|
+
mockConversation = { id: 1, disabled: true, disabledReason: 'locked' } as ConversationResource
|
|
139
|
+
const { queryByText, getByText } = renderScreen()
|
|
140
|
+
|
|
141
|
+
expect(queryByText('Delete message')).toBeNull()
|
|
142
|
+
expect(getByText('Copy text')).toBeTruthy()
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('offers reactions, reply, edit, and link preview removal on my message', () => {
|
|
146
|
+
mockCurrentMessage = myMessageWithLinkPreview
|
|
147
|
+
const { getByText } = renderScreen()
|
|
148
|
+
|
|
149
|
+
expect(getByText('👍')).toBeTruthy()
|
|
150
|
+
expect(getByText('Reply to message')).toBeTruthy()
|
|
151
|
+
expect(getByText('Edit message')).toBeTruthy()
|
|
152
|
+
expect(getByText('Remove link preview')).toBeTruthy()
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
it('hides reactions, reply, edit, and link preview removal when the conversation is disabled', () => {
|
|
156
|
+
mockCurrentMessage = myMessageWithLinkPreview
|
|
157
|
+
mockConversation = { id: 1, disabled: true, disabledReason: 'archived' } as ConversationResource
|
|
158
|
+
const { getByText, queryByText } = renderScreen()
|
|
159
|
+
|
|
160
|
+
expect(queryByText('👍')).toBeNull()
|
|
161
|
+
expect(queryByText('Reply to message')).toBeNull()
|
|
162
|
+
expect(queryByText('Edit message')).toBeNull()
|
|
163
|
+
expect(queryByText('Remove link preview')).toBeNull()
|
|
164
|
+
expect(getByText('Copy text')).toBeTruthy()
|
|
165
|
+
expect(getByText('View read receipts')).toBeTruthy()
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it('keeps Report when the conversation is disabled', () => {
|
|
169
|
+
mockEnabledFeatures = ['ROLLOUT_MOBILE_message_reporting']
|
|
170
|
+
mockConversation = { id: 1, disabled: true, disabledReason: 'archived' } as ConversationResource
|
|
171
|
+
const { getByText } = renderScreen()
|
|
172
|
+
|
|
173
|
+
expect(getByText('Report message')).toBeTruthy()
|
|
111
174
|
})
|
|
112
175
|
|
|
113
176
|
it('copies the message text and gives success haptic feedback before closing', async () => {
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
QueryClient,
|
|
6
6
|
QueryClientProvider,
|
|
7
7
|
} from '@tanstack/react-query'
|
|
8
|
-
import { act, render, screen } from '@testing-library/react-native'
|
|
8
|
+
import { act, fireEvent, render, screen } from '@testing-library/react-native'
|
|
9
9
|
import React from 'react'
|
|
10
10
|
import { FlatList } from 'react-native'
|
|
11
11
|
import { buildTestQueryClient } from '../../../__utils__/query_client'
|
|
@@ -37,9 +37,18 @@ jest.mock('../../../hooks/use_animated_message_background_color', () => ({
|
|
|
37
37
|
handleMessagePressOut: jest.fn(),
|
|
38
38
|
}),
|
|
39
39
|
}))
|
|
40
|
+
const mockNavigate = jest.fn()
|
|
40
41
|
jest.mock('@react-navigation/native', () => ({
|
|
41
42
|
...jest.requireActual('@react-navigation/native'),
|
|
42
|
-
useNavigation: () => ({ navigate:
|
|
43
|
+
useNavigation: () => ({ navigate: mockNavigate }),
|
|
44
|
+
}))
|
|
45
|
+
const mockToggleReaction = jest.fn()
|
|
46
|
+
jest.mock('../../../hooks/use_message_reaction_toggle', () => ({
|
|
47
|
+
useMessageReactionToggle: () => ({ handleReactionToggle: mockToggleReaction, isPending: false }),
|
|
48
|
+
}))
|
|
49
|
+
jest.mock('../../../utils/native_adapters', () => ({
|
|
50
|
+
...jest.requireActual('../../../utils/native_adapters'),
|
|
51
|
+
Haptic: { impactLight: jest.fn() },
|
|
43
52
|
}))
|
|
44
53
|
// Every row calls this hook on each render, so its calls count row renders.
|
|
45
54
|
jest.mock('../../../hooks/use_live_relative_time', () => {
|
|
@@ -293,6 +302,63 @@ describe('MessageList', () => {
|
|
|
293
302
|
expect(renderedRowIds()).toEqual([])
|
|
294
303
|
})
|
|
295
304
|
|
|
305
|
+
describe('in a disabled conversation', () => {
|
|
306
|
+
const reacted = buildMessage(1, {
|
|
307
|
+
reactionCounts: [
|
|
308
|
+
{
|
|
309
|
+
type: 'ReactionCount',
|
|
310
|
+
id: 'heart',
|
|
311
|
+
messageId: messageId(1),
|
|
312
|
+
value: 'heart',
|
|
313
|
+
count: 1,
|
|
314
|
+
mine: 0,
|
|
315
|
+
authorIds: [OTHER],
|
|
316
|
+
},
|
|
317
|
+
],
|
|
318
|
+
})
|
|
319
|
+
const archived = buildMessage(2, {
|
|
320
|
+
text: 'This conversation was archived by Ann',
|
|
321
|
+
systemTextFormat: 'actor',
|
|
322
|
+
})
|
|
323
|
+
const disable = () => act(() => setHarnessConversation({ ...conversation, disabled: true }))
|
|
324
|
+
|
|
325
|
+
it('toggles a reaction pill while the conversation is active', () => {
|
|
326
|
+
renderList([reacted])
|
|
327
|
+
|
|
328
|
+
fireEvent.press(screen.getByTestId(`reaction-${reacted.id}-heart`))
|
|
329
|
+
|
|
330
|
+
expect(mockToggleReaction).toHaveBeenCalledWith({ value: 'heart', mine: false })
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
it('keeps reaction pills read-only but still shows who reacted', () => {
|
|
334
|
+
renderList([reacted])
|
|
335
|
+
disable()
|
|
336
|
+
const pill = screen.getByTestId(`reaction-${reacted.id}-heart`)
|
|
337
|
+
|
|
338
|
+
fireEvent.press(pill)
|
|
339
|
+
fireEvent(pill, 'longPress')
|
|
340
|
+
|
|
341
|
+
expect(mockToggleReaction).not.toHaveBeenCalled()
|
|
342
|
+
expect(mockNavigate).toHaveBeenCalledWith('Reactions', expect.anything())
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
it('opens message actions on a system message only while the conversation is active', () => {
|
|
346
|
+
queryClient.setQueryData<MessagesQueryData>(messagesQueryKey, {
|
|
347
|
+
pages: [{ data: [archived], links: {}, meta: { count: 1, totalCount: 1 } }],
|
|
348
|
+
pageParams: [{}],
|
|
349
|
+
})
|
|
350
|
+
render(<Harness />, { wrapper: Wrapper })
|
|
351
|
+
fireEvent(screen.getByText(/archived by Ann/), 'longPress')
|
|
352
|
+
expect(mockNavigate).toHaveBeenCalledWith('MessageActions', expect.anything())
|
|
353
|
+
|
|
354
|
+
mockNavigate.mockClear()
|
|
355
|
+
disable()
|
|
356
|
+
fireEvent(screen.getByText(/archived by Ann/), 'longPress')
|
|
357
|
+
|
|
358
|
+
expect(mockNavigate).not.toHaveBeenCalled()
|
|
359
|
+
})
|
|
360
|
+
})
|
|
361
|
+
|
|
296
362
|
describe('opened at an anchor message', () => {
|
|
297
363
|
const anchor = messages[4]
|
|
298
364
|
|
|
@@ -65,7 +65,9 @@ const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
|
65
65
|
)
|
|
66
66
|
|
|
67
67
|
const renderSystemMessage = (message: SystemMessageResource) =>
|
|
68
|
-
render(<SystemMessage message={message} conversationId={1} />, {
|
|
68
|
+
render(<SystemMessage message={message} conversationId={1} conversationDisabled={false} />, {
|
|
69
|
+
wrapper: Wrapper,
|
|
70
|
+
})
|
|
69
71
|
|
|
70
72
|
beforeEach(() => {
|
|
71
73
|
jest.useFakeTimers({ now: new Date(2026, 5, 15, 12, 0) })
|
|
@@ -8,7 +8,7 @@ import Animated, {
|
|
|
8
8
|
ReduceMotion,
|
|
9
9
|
withSpring,
|
|
10
10
|
} from 'react-native-reanimated'
|
|
11
|
-
import { useTheme } from '../../hooks'
|
|
11
|
+
import { useReduceMotion, useTheme } from '../../hooks'
|
|
12
12
|
import { platformFontWeightMedium } from '../../utils'
|
|
13
13
|
import { Icon, Text } from '../display'
|
|
14
14
|
|
|
@@ -20,6 +20,7 @@ interface JumpToBottomButtonProps {
|
|
|
20
20
|
export const JumpToBottomButton = ({ onPress, visible }: JumpToBottomButtonProps) => {
|
|
21
21
|
const styles = useStyles()
|
|
22
22
|
const progress = useSharedValue(0)
|
|
23
|
+
const reduceMotion = useReduceMotion()
|
|
23
24
|
|
|
24
25
|
useEffect(() => {
|
|
25
26
|
progress.value = withSpring(visible ? 1 : 0, {
|
|
@@ -27,9 +28,9 @@ export const JumpToBottomButton = ({ onPress, visible }: JumpToBottomButtonProps
|
|
|
27
28
|
damping: 16,
|
|
28
29
|
stiffness: 170,
|
|
29
30
|
overshootClamping: false,
|
|
30
|
-
reduceMotion: ReduceMotion.
|
|
31
|
+
reduceMotion: reduceMotion ? ReduceMotion.Always : ReduceMotion.Never,
|
|
31
32
|
})
|
|
32
|
-
}, [visible, progress])
|
|
33
|
+
}, [visible, progress, reduceMotion])
|
|
33
34
|
|
|
34
35
|
const animatedStyle = useAnimatedStyle(() => {
|
|
35
36
|
return {
|
|
@@ -44,6 +44,7 @@ import { TheirReplyConnector, MyReplyConnector, AVATAR_CONNECTOR_SPACING } from
|
|
|
44
44
|
*/
|
|
45
45
|
interface MessageProps extends MessageResource {
|
|
46
46
|
canDeleteNonAuthoredMessages: boolean
|
|
47
|
+
conversationDisabled: boolean
|
|
47
48
|
conversation_id: number
|
|
48
49
|
readByOthers?: boolean
|
|
49
50
|
inReplyScreen?: boolean
|
|
@@ -53,6 +54,7 @@ interface MessageProps extends MessageResource {
|
|
|
53
54
|
|
|
54
55
|
export const Message = memo(function MessageRow({
|
|
55
56
|
canDeleteNonAuthoredMessages,
|
|
57
|
+
conversationDisabled,
|
|
56
58
|
conversation_id,
|
|
57
59
|
readByOthers,
|
|
58
60
|
inReplyScreen,
|
|
@@ -304,6 +306,7 @@ export const Message = memo(function MessageRow({
|
|
|
304
306
|
onLongPress={handleReactionLongPress}
|
|
305
307
|
message={message}
|
|
306
308
|
conversationId={conversation_id}
|
|
309
|
+
disabled={conversationDisabled}
|
|
307
310
|
/>
|
|
308
311
|
))}
|
|
309
312
|
</View>
|
|
@@ -72,6 +72,7 @@ export function MessageList({
|
|
|
72
72
|
const inReplyScreen = !!replyRootId
|
|
73
73
|
const canDeleteNonAuthoredMessages =
|
|
74
74
|
conversation.memberAbility?.canDeleteNonAuthoredMessages ?? false
|
|
75
|
+
const conversationDisabled = conversation.disabled ?? false
|
|
75
76
|
const latestRead = conversation.latestReadMessageSortKey
|
|
76
77
|
const messagesWithSeparators = groupMessages({ ms: messages, inReplyScreen })
|
|
77
78
|
const anchorIndex = anchorMessageId
|
|
@@ -193,7 +194,13 @@ export function MessageList({
|
|
|
193
194
|
}
|
|
194
195
|
|
|
195
196
|
if (isSystemMessage(item)) {
|
|
196
|
-
return
|
|
197
|
+
return (
|
|
198
|
+
<SystemMessage
|
|
199
|
+
message={item}
|
|
200
|
+
conversationId={conversationId}
|
|
201
|
+
conversationDisabled={conversationDisabled}
|
|
202
|
+
/>
|
|
203
|
+
)
|
|
197
204
|
}
|
|
198
205
|
|
|
199
206
|
const readByOthers = item.mine && !!latestRead && item.id <= latestRead
|
|
@@ -201,6 +208,7 @@ export function MessageList({
|
|
|
201
208
|
<Message
|
|
202
209
|
{...item}
|
|
203
210
|
canDeleteNonAuthoredMessages={canDeleteNonAuthoredMessages}
|
|
211
|
+
conversationDisabled={conversationDisabled}
|
|
204
212
|
conversation_id={conversationId}
|
|
205
213
|
readByOthers={readByOthers}
|
|
206
214
|
inReplyScreen={inReplyScreen}
|
|
@@ -16,11 +16,13 @@ export function MessageReaction({
|
|
|
16
16
|
onLongPress,
|
|
17
17
|
message,
|
|
18
18
|
conversationId,
|
|
19
|
+
disabled = false,
|
|
19
20
|
}: {
|
|
20
21
|
reaction: ReactionCountResource
|
|
21
22
|
onLongPress: (_reaction: ReactionCountResource) => void
|
|
22
23
|
message: MessageResource
|
|
23
24
|
conversationId: number
|
|
25
|
+
disabled?: boolean
|
|
24
26
|
}) {
|
|
25
27
|
const styles = useReactionStyles(reaction)
|
|
26
28
|
const { handleReactionToggle } = useMessageReactionToggle({
|
|
@@ -46,8 +48,11 @@ export function MessageReaction({
|
|
|
46
48
|
testID={`reaction-${message.id}-${reaction.value}`}
|
|
47
49
|
style={styles.reaction}
|
|
48
50
|
onLongPress={() => onLongPress(reaction)}
|
|
49
|
-
onPress={handlePress}
|
|
50
|
-
|
|
51
|
+
onPress={disabled ? undefined : handlePress}
|
|
52
|
+
pressOpacity={disabled ? 1 : undefined}
|
|
53
|
+
android_ripple={
|
|
54
|
+
disabled ? undefined : { color: androidRippleColor, borderless: false, foreground: true }
|
|
55
|
+
}
|
|
51
56
|
>
|
|
52
57
|
<Text style={styles.reactionEmoji}>{REACTION_EMOJIS[reaction.value]}</Text>
|
|
53
58
|
<Text style={styles.reactionText}>{reaction.count}</Text>
|
|
@@ -22,38 +22,36 @@ export const MemberMessagesDisabledBanner = () => {
|
|
|
22
22
|
|
|
23
23
|
const DEFAULT_DISABLED_TEXT = 'This conversation has been disabled.'
|
|
24
24
|
|
|
25
|
+
interface BannerAction {
|
|
26
|
+
label: string
|
|
27
|
+
onPress: () => void
|
|
28
|
+
loading?: boolean
|
|
29
|
+
}
|
|
30
|
+
|
|
25
31
|
interface ConversationDisabledBannerProps {
|
|
26
32
|
message: string | null
|
|
27
|
-
|
|
33
|
+
action?: BannerAction
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
export const ConversationDisabledBanner = ({
|
|
31
37
|
message,
|
|
32
|
-
|
|
38
|
+
action,
|
|
33
39
|
}: ConversationDisabledBannerProps) => {
|
|
34
40
|
const styles = useStyles()
|
|
35
41
|
const description = message ?? DEFAULT_DISABLED_TEXT
|
|
36
42
|
|
|
37
43
|
return (
|
|
38
|
-
<MessagesDisabledBanner
|
|
39
|
-
description={description}
|
|
40
|
-
style={styles.memberBanner}
|
|
41
|
-
messageLink={messageLink}
|
|
42
|
-
/>
|
|
44
|
+
<MessagesDisabledBanner description={description} style={styles.memberBanner} action={action} />
|
|
43
45
|
)
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
interface MessagesDisabledBannerProps {
|
|
47
49
|
description: string
|
|
48
50
|
style?: ViewStyle
|
|
49
|
-
|
|
51
|
+
action?: BannerAction
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
const MessagesDisabledBanner = ({
|
|
53
|
-
description,
|
|
54
|
-
style,
|
|
55
|
-
messageLink,
|
|
56
|
-
}: MessagesDisabledBannerProps) => {
|
|
54
|
+
const MessagesDisabledBanner = ({ description, style, action }: MessagesDisabledBannerProps) => {
|
|
57
55
|
const styles = useStyles()
|
|
58
56
|
|
|
59
57
|
return (
|
|
@@ -61,13 +59,14 @@ const MessagesDisabledBanner = ({
|
|
|
61
59
|
<Text style={styles.text} variant="tertiary">
|
|
62
60
|
{description}
|
|
63
61
|
</Text>
|
|
64
|
-
{
|
|
62
|
+
{action && (
|
|
65
63
|
<Button
|
|
66
64
|
variant="fill"
|
|
67
65
|
size="lg"
|
|
68
|
-
title={
|
|
69
|
-
onPress={
|
|
70
|
-
|
|
66
|
+
title={action.label}
|
|
67
|
+
onPress={action.onPress}
|
|
68
|
+
loading={action.loading}
|
|
69
|
+
style={styles.actionButton}
|
|
71
70
|
/>
|
|
72
71
|
)}
|
|
73
72
|
</View>
|
|
@@ -94,7 +93,7 @@ const useStyles = () => {
|
|
|
94
93
|
paddingBottom: 16 + bottom,
|
|
95
94
|
marginBottom: -bottom,
|
|
96
95
|
},
|
|
97
|
-
|
|
96
|
+
actionButton: {
|
|
98
97
|
marginTop: 12,
|
|
99
98
|
},
|
|
100
99
|
})
|
|
@@ -13,9 +13,14 @@ import { MessageReaction } from './message_reaction'
|
|
|
13
13
|
interface SystemMessageProps {
|
|
14
14
|
message: SystemMessageResource
|
|
15
15
|
conversationId: number
|
|
16
|
+
conversationDisabled: boolean
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
export function SystemMessage({
|
|
19
|
+
export function SystemMessage({
|
|
20
|
+
message,
|
|
21
|
+
conversationId,
|
|
22
|
+
conversationDisabled,
|
|
23
|
+
}: SystemMessageProps) {
|
|
19
24
|
const { reactionCounts } = message
|
|
20
25
|
const styles = useStyles()
|
|
21
26
|
const navigation = useNavigation()
|
|
@@ -23,6 +28,7 @@ export function SystemMessage({ message, conversationId }: SystemMessageProps) {
|
|
|
23
28
|
const text = systemMessageText(message)
|
|
24
29
|
const isTruncated =
|
|
25
30
|
message.systemTextFormat === 'people' && message.systemTextParts.overflowCount > 0
|
|
31
|
+
const pressable = isTruncated || !conversationDisabled
|
|
26
32
|
const hasReactions = reactionCounts.length > 0
|
|
27
33
|
const { animatedBackgroundColor, handleMessagePressIn, handleMessagePressOut } =
|
|
28
34
|
useAnimatedMessageBackgroundColor()
|
|
@@ -57,16 +63,16 @@ export function SystemMessage({ message, conversationId }: SystemMessageProps) {
|
|
|
57
63
|
return (
|
|
58
64
|
<Pressable
|
|
59
65
|
onPress={isTruncated ? handlePress : undefined}
|
|
60
|
-
onLongPress={handleLongPress}
|
|
61
|
-
onPressIn={handleMessagePressIn}
|
|
62
|
-
onPressOut={handleMessagePressOut}
|
|
63
|
-
android_ripple={
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
? 'Tap to see people. Long press for message actions.'
|
|
68
|
-
: 'Long press for message actions.'
|
|
66
|
+
onLongPress={conversationDisabled ? undefined : handleLongPress}
|
|
67
|
+
onPressIn={pressable ? handleMessagePressIn : undefined}
|
|
68
|
+
onPressOut={pressable ? handleMessagePressOut : undefined}
|
|
69
|
+
android_ripple={
|
|
70
|
+
pressable
|
|
71
|
+
? { color: colors.androidRippleNeutral, borderless: false, foreground: true }
|
|
72
|
+
: undefined
|
|
69
73
|
}
|
|
74
|
+
accessibilityRole={pressable ? 'button' : undefined}
|
|
75
|
+
accessibilityHint={systemMessageHint(isTruncated, conversationDisabled)}
|
|
70
76
|
>
|
|
71
77
|
<Animated.View style={[styles.container, animatedBackgroundColor]}>
|
|
72
78
|
<Text variant="tertiary" style={styles.text}>
|
|
@@ -81,6 +87,7 @@ export function SystemMessage({ message, conversationId }: SystemMessageProps) {
|
|
|
81
87
|
onLongPress={handleReactionLongPress}
|
|
82
88
|
message={message}
|
|
83
89
|
conversationId={conversationId}
|
|
90
|
+
disabled={conversationDisabled}
|
|
84
91
|
/>
|
|
85
92
|
))}
|
|
86
93
|
</View>
|
|
@@ -90,6 +97,13 @@ export function SystemMessage({ message, conversationId }: SystemMessageProps) {
|
|
|
90
97
|
)
|
|
91
98
|
}
|
|
92
99
|
|
|
100
|
+
function systemMessageHint(isTruncated: boolean, conversationDisabled: boolean) {
|
|
101
|
+
if (conversationDisabled) return isTruncated ? 'Tap to see people.' : undefined
|
|
102
|
+
return isTruncated
|
|
103
|
+
? 'Tap to see people. Long press for message actions.'
|
|
104
|
+
: 'Long press for message actions.'
|
|
105
|
+
}
|
|
106
|
+
|
|
93
107
|
function systemMessageText(message: SystemMessageResource): string {
|
|
94
108
|
switch (message.systemTextFormat) {
|
|
95
109
|
case 'people': {
|