@planningcenter/chat-react-native 4.4.1-qa-staging.13 → 4.4.1-qa-staging.15
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/message.d.ts +2 -1
- package/build/components/conversation/message.d.ts.map +1 -1
- package/build/components/conversation/message.js +190 -161
- 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/reply_shadow_message.d.ts.map +1 -1
- package/build/components/conversation/reply_shadow_message.js +84 -83
- package/build/components/conversation/reply_shadow_message.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/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/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_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/message.tsx +11 -14
- 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/reply_shadow_message.tsx +9 -8
- package/src/components/conversation/system_message.tsx +24 -10
- package/src/components/display/__tests__/filter_dropdown.test.tsx +106 -0
- 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_unarchive_conversation_action.test.tsx +67 -0
- package/src/hooks/use_conversation.ts +41 -3
- 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.15",
|
|
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.15",
|
|
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": "810a097ac7d4e1d4eeb6ea64a0516e62dd8e0a2c"
|
|
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) })
|
|
@@ -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,
|
|
@@ -108,6 +110,7 @@ export const Message = memo(function MessageRow({
|
|
|
108
110
|
: hasReactions || showMessageMeta || showReplyCountButton
|
|
109
111
|
? 8
|
|
110
112
|
: 4
|
|
113
|
+
const spaceBelowPressable = messageBottomMargin - MESSAGE_BACKGROUND_OUTSET
|
|
111
114
|
|
|
112
115
|
const messageIsReplyLabel = replyToReplyRootMessage ? 'Reply' : ''
|
|
113
116
|
const attachmentLabel = some(attachments) ? pluralize(attachments.length, 'attachment') : ''
|
|
@@ -214,6 +217,7 @@ export const Message = memo(function MessageRow({
|
|
|
214
217
|
return (
|
|
215
218
|
<Pressable
|
|
216
219
|
testID={`message-${message.id}`}
|
|
220
|
+
style={[styles.pressable, { marginBottom: spaceBelowPressable }]}
|
|
217
221
|
onLongPress={isDeleting ? undefined : handleMessageLongPress}
|
|
218
222
|
onPress={isDeleting ? undefined : handleMessagePress}
|
|
219
223
|
onPressIn={isDeleting ? undefined : handleMessagePressIn}
|
|
@@ -226,14 +230,8 @@ export const Message = memo(function MessageRow({
|
|
|
226
230
|
onAccessibilityAction={isReplyRootMessage ? handleAccessibilityAction : undefined}
|
|
227
231
|
disabled={isDeletedReplyRootMessage || isDeleting}
|
|
228
232
|
>
|
|
229
|
-
<View style={
|
|
230
|
-
|
|
231
|
-
style={[
|
|
232
|
-
styles.background,
|
|
233
|
-
{ bottom: messageBottomMargin - MESSAGE_BACKGROUND_OUTSET },
|
|
234
|
-
animatedBackgroundColor,
|
|
235
|
-
]}
|
|
236
|
-
/>
|
|
233
|
+
<Animated.View style={[StyleSheet.absoluteFill, animatedBackgroundColor]} />
|
|
234
|
+
<View style={[styles.message, { marginBottom: -spaceBelowPressable }]}>
|
|
237
235
|
{!message.mine && (
|
|
238
236
|
<View>
|
|
239
237
|
{renderAuthor ? (
|
|
@@ -304,6 +302,7 @@ export const Message = memo(function MessageRow({
|
|
|
304
302
|
onLongPress={handleReactionLongPress}
|
|
305
303
|
message={message}
|
|
306
304
|
conversationId={conversation_id}
|
|
305
|
+
disabled={conversationDisabled}
|
|
307
306
|
/>
|
|
308
307
|
))}
|
|
309
308
|
</View>
|
|
@@ -370,18 +369,16 @@ const useMessageStyles = (mine: boolean, isDeleting: boolean) => {
|
|
|
370
369
|
const messageBubbleBackgroundColor = mine ? myMessageBackgroundColor : colors.fillColorNeutral070
|
|
371
370
|
|
|
372
371
|
return StyleSheet.create({
|
|
372
|
+
pressable: {
|
|
373
|
+
marginTop: -MESSAGE_BACKGROUND_OUTSET,
|
|
374
|
+
paddingTop: MESSAGE_BACKGROUND_OUTSET,
|
|
375
|
+
},
|
|
373
376
|
message: {
|
|
374
377
|
gap: 8,
|
|
375
378
|
flexDirection: mine ? 'row-reverse' : 'row',
|
|
376
379
|
paddingHorizontal: CONVERSATION_MESSAGE_LIST_PADDING_HORIZONTAL,
|
|
377
380
|
opacity: isDeleting ? 0.5 : 1,
|
|
378
381
|
},
|
|
379
|
-
background: {
|
|
380
|
-
position: 'absolute',
|
|
381
|
-
left: 0,
|
|
382
|
-
right: 0,
|
|
383
|
-
top: -MESSAGE_BACKGROUND_OUTSET,
|
|
384
|
-
},
|
|
385
382
|
messageContent: {
|
|
386
383
|
flex: 1,
|
|
387
384
|
gap: 4,
|
|
@@ -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
|
})
|