@planningcenter/chat-react-native 3.13.0-rc.5 → 3.13.0-rc.6

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.
@@ -1,10 +1,11 @@
1
1
  import React, { ReactNode } from 'react';
2
2
  import { StyleProp, ViewStyle } from 'react-native';
3
3
  import { ConversationResource } from '../../types';
4
- export declare function ConversationActions({ children, conversation, style, onPress, }: {
4
+ export declare function ConversationActions({ children, conversation, style, onPress, accessibilityLabel, }: {
5
5
  children: ReactNode;
6
6
  conversation: ConversationResource;
7
7
  onPress: () => void;
8
8
  style?: StyleProp<ViewStyle>;
9
+ accessibilityLabel?: string;
9
10
  }): React.JSX.Element;
10
11
  //# sourceMappingURL=conversation_actions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"conversation_actions.d.ts","sourceRoot":"","sources":["../../../src/components/conversations/conversation_actions.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAA4C,MAAM,OAAO,CAAA;AAClF,OAAO,EAAuB,SAAS,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAA;AAU1F,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAKlD,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,YAAY,EACZ,KAAK,EACL,OAAO,GACR,EAAE;IACD,QAAQ,EAAE,SAAS,CAAA;IACnB,YAAY,EAAE,oBAAoB,CAAA;IAClC,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAA;CAC7B,qBAuDA"}
1
+ {"version":3,"file":"conversation_actions.d.ts","sourceRoot":"","sources":["../../../src/components/conversations/conversation_actions.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAA4C,MAAM,OAAO,CAAA;AAClF,OAAO,EAIL,SAAS,EAGT,SAAS,EACV,MAAM,cAAc,CAAA;AAUrB,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAKlD,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,YAAY,EACZ,KAAK,EACL,OAAO,EACP,kBAAkB,GACnB,EAAE;IACD,QAAQ,EAAE,SAAS,CAAA;IACnB,YAAY,EAAE,oBAAoB,CAAA;IAClC,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAA;IAC5B,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B,qBA4HA"}
@@ -1,5 +1,5 @@
1
1
  import React, { useCallback, useEffect, useRef, useState } from 'react';
2
- import { Platform, Pressable, StyleSheet, View } from 'react-native';
2
+ import { Platform, Pressable, StyleSheet, View, } from 'react-native';
3
3
  import ReanimatedSwipeable from 'react-native-gesture-handler/ReanimatedSwipeable';
4
4
  import { useConversationsContext } from '../../contexts/conversations_context';
5
5
  import { useTheme, useCreateAndroidRippleColor } from '../../hooks';
@@ -7,7 +7,7 @@ import { useConversationsMarkRead, useConversationsMute, } from '../../hooks/use
7
7
  import { platformPressedOpacityStyle } from '../../utils';
8
8
  import { tokens } from '../../vendor/tapestry/tokens';
9
9
  import { ActionToggleButton } from './action_toggle_button';
10
- export function ConversationActions({ children, conversation, style, onPress, }) {
10
+ export function ConversationActions({ children, conversation, style, onPress, accessibilityLabel, }) {
11
11
  const swipeableRef = useRef(null);
12
12
  const styles = useStyles();
13
13
  const { activeConversationId, setActiveConversationId } = useConversationsContext();
@@ -16,10 +16,15 @@ export function ConversationActions({ children, conversation, style, onPress, })
16
16
  const androidRippleColor = useCreateAndroidRippleColor({
17
17
  color: styles.swipeableChildContainer.backgroundColor,
18
18
  });
19
- const handleSwipeableClose = () => {
19
+ const { muted, setMuted, isPending: isMutedPending } = useConversationsMute({ conversation });
20
+ const { read, markRead, isPending: isMarkReadPending, } = useConversationsMarkRead({
21
+ conversation,
22
+ });
23
+ const isConversationEmpty = conversation.lastMessageCreatedAt === null;
24
+ const handleSwipeableClose = useCallback(() => {
20
25
  setDisabled(false);
21
26
  swipeableRef.current?.close();
22
- };
27
+ }, [setDisabled]);
23
28
  const handleSwipeableOpen = () => {
24
29
  setDisabled(true);
25
30
  setActiveConversationId(conversation.id);
@@ -29,20 +34,58 @@ export function ConversationActions({ children, conversation, style, onPress, })
29
34
  return;
30
35
  onPress();
31
36
  }, [disabled, onPress]);
37
+ const handleMute = useCallback(() => {
38
+ setMuted(!muted);
39
+ handleSwipeableClose();
40
+ }, [muted, handleSwipeableClose, setMuted]);
41
+ const handleLatestMessageUnread = useCallback(() => {
42
+ markRead(!read);
43
+ handleSwipeableClose();
44
+ }, [read, handleSwipeableClose, markRead]);
45
+ const readAccessibilityAction = !isConversationEmpty
46
+ ? [
47
+ {
48
+ name: read ? 'markUnread' : 'markRead',
49
+ label: read ? 'Mark unread' : 'Mark read',
50
+ },
51
+ ]
52
+ : [];
53
+ const accessibilityActions = [
54
+ {
55
+ name: muted ? 'unmute' : 'mute',
56
+ label: muted ? 'Unmute' : 'Mute',
57
+ },
58
+ ...readAccessibilityAction,
59
+ ];
60
+ const handleAccessibilityAction = useCallback((event) => {
61
+ switch (event.nativeEvent.actionName) {
62
+ case 'markRead':
63
+ handleLatestMessageUnread();
64
+ break;
65
+ case 'markUnread':
66
+ handleLatestMessageUnread();
67
+ break;
68
+ case 'mute':
69
+ handleMute();
70
+ break;
71
+ case 'unmute':
72
+ handleMute();
73
+ break;
74
+ }
75
+ }, [handleLatestMessageUnread, handleMute]);
32
76
  useEffect(() => {
33
77
  if (activeConversationId === conversation.id)
34
78
  return;
35
79
  handleSwipeableClose();
36
- }, [activeConversationId, conversation.id]);
37
- return (<ReanimatedSwipeable ref={swipeableRef} childrenContainerStyle={styles.swipeableChildContainer} containerStyle={styles.swipeableContainer} overshootFriction={8} overshootLeft={overshootLeft} overshootRight={false} onSwipeableOpenStartDrag={handleSwipeableOpen} onSwipeableClose={() => setDisabled(false)} renderRightActions={() => <></>} renderLeftActions={() => (<LeftActions conversation={conversation} onClose={handleSwipeableClose}/>)}>
38
- <Pressable onPress={handlePress} style={({ pressed }) => [style, pressed && platformPressedOpacityStyle]} android_ripple={{ color: androidRippleColor, borderless: false, foreground: true }}>
80
+ }, [activeConversationId, conversation.id, handleSwipeableClose]);
81
+ return (<ReanimatedSwipeable ref={swipeableRef} childrenContainerStyle={styles.swipeableChildContainer} containerStyle={styles.swipeableContainer} overshootFriction={8} overshootLeft={overshootLeft} overshootRight={false} onSwipeableOpenStartDrag={handleSwipeableOpen} onSwipeableClose={() => setDisabled(false)} renderRightActions={() => <></>} renderLeftActions={() => (<LeftActions isConversationEmpty={isConversationEmpty} handleMute={handleMute} handleLatestMessageUnread={handleLatestMessageUnread} muted={muted} read={read} isMutedPending={isMutedPending} isMarkReadPending={isMarkReadPending}/>)}>
82
+ <Pressable onPress={handlePress} style={({ pressed }) => [style, pressed && platformPressedOpacityStyle]} android_ripple={{ color: androidRippleColor, borderless: false, foreground: true }} accessibilityLabel={accessibilityLabel} accessibilityRole="button" accessibilityActions={accessibilityActions} onAccessibilityAction={handleAccessibilityAction}>
39
83
  {children}
40
84
  </Pressable>
41
85
  </ReanimatedSwipeable>);
42
86
  }
43
- function LeftActions({ conversation, onClose }) {
87
+ function LeftActions({ isConversationEmpty, handleMute, handleLatestMessageUnread, muted, read, isMutedPending, isMarkReadPending, }) {
44
88
  const styles = useStyles();
45
- const emptyConversation = conversation.lastMessageCreatedAt === null;
46
89
  const muteToggleContent = {
47
90
  true: { iconName: 'general.bell', label: 'Unmute' },
48
91
  false: { iconName: 'general.bellMuted', label: 'Mute' },
@@ -51,19 +94,9 @@ function LeftActions({ conversation, onClose }) {
51
94
  true: { iconName: 'general.outlinedTextMessage', label: 'Mark read' },
52
95
  false: { iconName: 'general.textMessageNotifications', label: 'Mark unread' },
53
96
  };
54
- const { muted, setMuted, isPending } = useConversationsMute({ conversation });
55
- const { read, markRead, isPending: markReadPending } = useConversationsMarkRead({ conversation });
56
- const handleMute = useCallback(() => {
57
- setMuted(!muted);
58
- onClose();
59
- }, [muted, onClose, setMuted]);
60
- const handleLatestMessageUnread = useCallback(() => {
61
- markRead(!read);
62
- onClose();
63
- }, [read, onClose, markRead]);
64
97
  return (<View style={styles.actionButtonContainer}>
65
- <ActionToggleButton loading={markReadPending} disabled={emptyConversation} toggled={!read} onPress={handleLatestMessageUnread} toggleContent={latestMessageUnreadToggleContent} backgroundColor={tokens.fillColorInteractionSwipeDefault}/>
66
- <ActionToggleButton loading={isPending} toggled={muted} onPress={handleMute} toggleContent={muteToggleContent} backgroundColor={tokens.fillColorInteractionSwipeSecondary}/>
98
+ <ActionToggleButton loading={isMarkReadPending} disabled={isConversationEmpty} toggled={!read} onPress={handleLatestMessageUnread} toggleContent={latestMessageUnreadToggleContent} backgroundColor={tokens.fillColorInteractionSwipeDefault}/>
99
+ <ActionToggleButton loading={isMutedPending} toggled={muted} onPress={handleMute} toggleContent={muteToggleContent} backgroundColor={tokens.fillColorInteractionSwipeSecondary}/>
67
100
  </View>);
68
101
  }
69
102
  const ACTION_BUTTON_WIDTH = 120;
@@ -1 +1 @@
1
- {"version":3,"file":"conversation_actions.js","sourceRoot":"","sources":["../../../src/components/conversations/conversation_actions.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { ReactNode, useCallback, useEffect, useRef, useState } from 'react'\nimport { Platform, Pressable, StyleProp, StyleSheet, View, ViewStyle } from 'react-native'\nimport ReanimatedSwipeable, {\n SwipeableMethods,\n} from 'react-native-gesture-handler/ReanimatedSwipeable'\nimport { useConversationsContext } from '../../contexts/conversations_context'\nimport { useTheme, useCreateAndroidRippleColor } from '../../hooks'\nimport {\n useConversationsMarkRead,\n useConversationsMute,\n} from '../../hooks/use_conversations_actions'\nimport { ConversationResource } from '../../types'\nimport { platformPressedOpacityStyle } from '../../utils'\nimport { tokens } from '../../vendor/tapestry/tokens'\nimport { ActionToggleButton } from './action_toggle_button'\n\nexport function ConversationActions({\n children,\n conversation,\n style,\n onPress,\n}: {\n children: ReactNode\n conversation: ConversationResource\n onPress: () => void\n style?: StyleProp<ViewStyle>\n}) {\n const swipeableRef = useRef<SwipeableMethods>(null)\n const styles = useStyles()\n const { activeConversationId, setActiveConversationId } = useConversationsContext()\n const [disabled, setDisabled] = useState(false)\n const overshootLeft = Platform.OS === 'ios'\n const androidRippleColor = useCreateAndroidRippleColor({\n color: styles.swipeableChildContainer.backgroundColor,\n })\n\n const handleSwipeableClose = () => {\n setDisabled(false)\n swipeableRef.current?.close()\n }\n\n const handleSwipeableOpen = () => {\n setDisabled(true)\n setActiveConversationId(conversation.id)\n }\n\n const handlePress = useCallback(() => {\n if (disabled || !onPress) return\n onPress()\n }, [disabled, onPress])\n\n useEffect(() => {\n if (activeConversationId === conversation.id) return\n\n handleSwipeableClose()\n }, [activeConversationId, conversation.id])\n\n return (\n <ReanimatedSwipeable\n ref={swipeableRef}\n childrenContainerStyle={styles.swipeableChildContainer}\n containerStyle={styles.swipeableContainer}\n overshootFriction={8}\n overshootLeft={overshootLeft}\n overshootRight={false}\n onSwipeableOpenStartDrag={handleSwipeableOpen}\n onSwipeableClose={() => setDisabled(false)}\n renderRightActions={() => <></>}\n renderLeftActions={() => (\n <LeftActions conversation={conversation} onClose={handleSwipeableClose} />\n )}\n >\n <Pressable\n onPress={handlePress}\n style={({ pressed }) => [style, pressed && platformPressedOpacityStyle]}\n android_ripple={{ color: androidRippleColor, borderless: false, foreground: true }}\n >\n {children}\n </Pressable>\n </ReanimatedSwipeable>\n )\n}\n\ninterface LeftActionsProps {\n conversation: ConversationResource\n onClose: () => void\n}\n\nfunction LeftActions({ conversation, onClose }: LeftActionsProps) {\n const styles = useStyles()\n const emptyConversation = conversation.lastMessageCreatedAt === null\n\n const muteToggleContent = {\n true: { iconName: 'general.bell', label: 'Unmute' },\n false: { iconName: 'general.bellMuted', label: 'Mute' },\n } as const\n\n const latestMessageUnreadToggleContent = {\n true: { iconName: 'general.outlinedTextMessage', label: 'Mark read' },\n false: { iconName: 'general.textMessageNotifications', label: 'Mark unread' },\n } as const\n const { muted, setMuted, isPending } = useConversationsMute({ conversation })\n const { read, markRead, isPending: markReadPending } = useConversationsMarkRead({ conversation })\n\n const handleMute = useCallback(() => {\n setMuted(!muted)\n onClose()\n }, [muted, onClose, setMuted])\n\n const handleLatestMessageUnread = useCallback(() => {\n markRead(!read)\n onClose()\n }, [read, onClose, markRead])\n\n return (\n <View style={styles.actionButtonContainer}>\n <ActionToggleButton\n loading={markReadPending}\n disabled={emptyConversation}\n toggled={!read}\n onPress={handleLatestMessageUnread}\n toggleContent={latestMessageUnreadToggleContent}\n backgroundColor={tokens.fillColorInteractionSwipeDefault}\n />\n <ActionToggleButton\n loading={isPending}\n toggled={muted}\n onPress={handleMute}\n toggleContent={muteToggleContent}\n backgroundColor={tokens.fillColorInteractionSwipeSecondary}\n />\n </View>\n )\n}\n\nconst ACTION_BUTTON_WIDTH = 120\nconst ACTION_BUTTON_COUNT = 2\n\nconst useStyles = () => {\n const { colors } = useTheme()\n return StyleSheet.create({\n swipeableChildContainer: {\n backgroundColor: colors.surfaceColor100,\n },\n actionButtonContainer: {\n flexDirection: 'row',\n alignItems: 'center',\n width: ACTION_BUTTON_WIDTH * ACTION_BUTTON_COUNT,\n alignContent: 'stretch',\n },\n swipeableContainer: {\n backgroundColor: colors.surfaceColor090,\n },\n })\n}\n"]}
1
+ {"version":3,"file":"conversation_actions.js","sourceRoot":"","sources":["../../../src/components/conversations/conversation_actions.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAa,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAClF,OAAO,EAEL,QAAQ,EACR,SAAS,EAET,UAAU,EACV,IAAI,GAEL,MAAM,cAAc,CAAA;AACrB,OAAO,mBAEN,MAAM,kDAAkD,CAAA;AACzD,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAA;AAC9E,OAAO,EAAE,QAAQ,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAA;AACnE,OAAO,EACL,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,uCAAuC,CAAA;AAE9C,OAAO,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAA;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAA;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAE3D,MAAM,UAAU,mBAAmB,CAAC,EAClC,QAAQ,EACR,YAAY,EACZ,KAAK,EACL,OAAO,EACP,kBAAkB,GAOnB;IACC,MAAM,YAAY,GAAG,MAAM,CAAmB,IAAI,CAAC,CAAA;IACnD,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAC1B,MAAM,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,GAAG,uBAAuB,EAAE,CAAA;IACnF,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC/C,MAAM,aAAa,GAAG,QAAQ,CAAC,EAAE,KAAK,KAAK,CAAA;IAC3C,MAAM,kBAAkB,GAAG,2BAA2B,CAAC;QACrD,KAAK,EAAE,MAAM,CAAC,uBAAuB,CAAC,eAAe;KACtD,CAAC,CAAA;IACF,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,cAAc,EAAE,GAAG,oBAAoB,CAAC,EAAE,YAAY,EAAE,CAAC,CAAA;IAC7F,MAAM,EACJ,IAAI,EACJ,QAAQ,EACR,SAAS,EAAE,iBAAiB,GAC7B,GAAG,wBAAwB,CAAC;QAC3B,YAAY;KACb,CAAC,CAAA;IAEF,MAAM,mBAAmB,GAAG,YAAY,CAAC,oBAAoB,KAAK,IAAI,CAAA;IAEtE,MAAM,oBAAoB,GAAG,WAAW,CAAC,GAAG,EAAE;QAC5C,WAAW,CAAC,KAAK,CAAC,CAAA;QAClB,YAAY,CAAC,OAAO,EAAE,KAAK,EAAE,CAAA;IAC/B,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAA;IAEjB,MAAM,mBAAmB,GAAG,GAAG,EAAE;QAC/B,WAAW,CAAC,IAAI,CAAC,CAAA;QACjB,uBAAuB,CAAC,YAAY,CAAC,EAAE,CAAC,CAAA;IAC1C,CAAC,CAAA;IAED,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE;QACnC,IAAI,QAAQ,IAAI,CAAC,OAAO;YAAE,OAAM;QAChC,OAAO,EAAE,CAAA;IACX,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAA;IAEvB,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE;QAClC,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAA;QAChB,oBAAoB,EAAE,CAAA;IACxB,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,EAAE,QAAQ,CAAC,CAAC,CAAA;IAE3C,MAAM,yBAAyB,GAAG,WAAW,CAAC,GAAG,EAAE;QACjD,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAA;QACf,oBAAoB,EAAE,CAAA;IACxB,CAAC,EAAE,CAAC,IAAI,EAAE,oBAAoB,EAAE,QAAQ,CAAC,CAAC,CAAA;IAE1C,MAAM,uBAAuB,GAAG,CAAC,mBAAmB;QAClD,CAAC,CAAC;YACE;gBACE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,UAAU;gBACtC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW;aAC1C;SACF;QACH,CAAC,CAAC,EAAE,CAAA;IAEN,MAAM,oBAAoB,GAAG;QAC3B;YACE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM;YAC/B,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM;SACjC;QACD,GAAG,uBAAuB;KAC3B,CAAA;IAED,MAAM,yBAAyB,GAAG,WAAW,CAC3C,CAAC,KAA+B,EAAE,EAAE;QAClC,QAAQ,KAAK,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC;YACrC,KAAK,UAAU;gBACb,yBAAyB,EAAE,CAAA;gBAC3B,MAAK;YACP,KAAK,YAAY;gBACf,yBAAyB,EAAE,CAAA;gBAC3B,MAAK;YACP,KAAK,MAAM;gBACT,UAAU,EAAE,CAAA;gBACZ,MAAK;YACP,KAAK,QAAQ;gBACX,UAAU,EAAE,CAAA;gBACZ,MAAK;QACT,CAAC;IACH,CAAC,EACD,CAAC,yBAAyB,EAAE,UAAU,CAAC,CACxC,CAAA;IAED,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,oBAAoB,KAAK,YAAY,CAAC,EAAE;YAAE,OAAM;QAEpD,oBAAoB,EAAE,CAAA;IACxB,CAAC,EAAE,CAAC,oBAAoB,EAAE,YAAY,CAAC,EAAE,EAAE,oBAAoB,CAAC,CAAC,CAAA;IAEjE,OAAO,CACL,CAAC,mBAAmB,CAClB,GAAG,CAAC,CAAC,YAAY,CAAC,CAClB,sBAAsB,CAAC,CAAC,MAAM,CAAC,uBAAuB,CAAC,CACvD,cAAc,CAAC,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAC1C,iBAAiB,CAAC,CAAC,CAAC,CAAC,CACrB,aAAa,CAAC,CAAC,aAAa,CAAC,CAC7B,cAAc,CAAC,CAAC,KAAK,CAAC,CACtB,wBAAwB,CAAC,CAAC,mBAAmB,CAAC,CAC9C,gBAAgB,CAAC,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAC3C,kBAAkB,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,CAAC,CAChC,iBAAiB,CAAC,CAAC,GAAG,EAAE,CAAC,CACvB,CAAC,WAAW,CACV,mBAAmB,CAAC,CAAC,mBAAmB,CAAC,CACzC,UAAU,CAAC,CAAC,UAAU,CAAC,CACvB,yBAAyB,CAAC,CAAC,yBAAyB,CAAC,CACrD,KAAK,CAAC,CAAC,KAAK,CAAC,CACb,IAAI,CAAC,CAAC,IAAI,CAAC,CACX,cAAc,CAAC,CAAC,cAAc,CAAC,CAC/B,iBAAiB,CAAC,CAAC,iBAAiB,CAAC,EACrC,CACH,CAAC,CAEF;MAAA,CAAC,SAAS,CACR,OAAO,CAAC,CAAC,WAAW,CAAC,CACrB,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,IAAI,2BAA2B,CAAC,CAAC,CACxE,cAAc,CAAC,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CACnF,kBAAkB,CAAC,CAAC,kBAAkB,CAAC,CACvC,iBAAiB,CAAC,QAAQ,CAC1B,oBAAoB,CAAC,CAAC,oBAAoB,CAAC,CAC3C,qBAAqB,CAAC,CAAC,yBAAyB,CAAC,CAEjD;QAAA,CAAC,QAAQ,CACX;MAAA,EAAE,SAAS,CACb;IAAA,EAAE,mBAAmB,CAAC,CACvB,CAAA;AACH,CAAC;AAYD,SAAS,WAAW,CAAC,EACnB,mBAAmB,EACnB,UAAU,EACV,yBAAyB,EACzB,KAAK,EACL,IAAI,EACJ,cAAc,EACd,iBAAiB,GACA;IACjB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAE1B,MAAM,iBAAiB,GAAG;QACxB,IAAI,EAAE,EAAE,QAAQ,EAAE,cAAc,EAAE,KAAK,EAAE,QAAQ,EAAE;QACnD,KAAK,EAAE,EAAE,QAAQ,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,EAAE;KAC/C,CAAA;IAEV,MAAM,gCAAgC,GAAG;QACvC,IAAI,EAAE,EAAE,QAAQ,EAAE,6BAA6B,EAAE,KAAK,EAAE,WAAW,EAAE;QACrE,KAAK,EAAE,EAAE,QAAQ,EAAE,kCAAkC,EAAE,KAAK,EAAE,aAAa,EAAE;KACrE,CAAA;IAEV,OAAO,CACL,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,qBAAqB,CAAC,CACxC;MAAA,CAAC,kBAAkB,CACjB,OAAO,CAAC,CAAC,iBAAiB,CAAC,CAC3B,QAAQ,CAAC,CAAC,mBAAmB,CAAC,CAC9B,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CACf,OAAO,CAAC,CAAC,yBAAyB,CAAC,CACnC,aAAa,CAAC,CAAC,gCAAgC,CAAC,CAChD,eAAe,CAAC,CAAC,MAAM,CAAC,gCAAgC,CAAC,EAE3D;MAAA,CAAC,kBAAkB,CACjB,OAAO,CAAC,CAAC,cAAc,CAAC,CACxB,OAAO,CAAC,CAAC,KAAK,CAAC,CACf,OAAO,CAAC,CAAC,UAAU,CAAC,CACpB,aAAa,CAAC,CAAC,iBAAiB,CAAC,CACjC,eAAe,CAAC,CAAC,MAAM,CAAC,kCAAkC,CAAC,EAE/D;IAAA,EAAE,IAAI,CAAC,CACR,CAAA;AACH,CAAC;AAED,MAAM,mBAAmB,GAAG,GAAG,CAAA;AAC/B,MAAM,mBAAmB,GAAG,CAAC,CAAA;AAE7B,MAAM,SAAS,GAAG,GAAG,EAAE;IACrB,MAAM,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,CAAA;IAC7B,OAAO,UAAU,CAAC,MAAM,CAAC;QACvB,uBAAuB,EAAE;YACvB,eAAe,EAAE,MAAM,CAAC,eAAe;SACxC;QACD,qBAAqB,EAAE;YACrB,aAAa,EAAE,KAAK;YACpB,UAAU,EAAE,QAAQ;YACpB,KAAK,EAAE,mBAAmB,GAAG,mBAAmB;YAChD,YAAY,EAAE,SAAS;SACxB;QACD,kBAAkB,EAAE;YAClB,eAAe,EAAE,MAAM,CAAC,eAAe;SACxC;KACF,CAAC,CAAA;AACJ,CAAC,CAAA","sourcesContent":["import React, { ReactNode, useCallback, useEffect, useRef, useState } from 'react'\nimport {\n AccessibilityActionEvent,\n Platform,\n Pressable,\n StyleProp,\n StyleSheet,\n View,\n ViewStyle,\n} from 'react-native'\nimport ReanimatedSwipeable, {\n SwipeableMethods,\n} from 'react-native-gesture-handler/ReanimatedSwipeable'\nimport { useConversationsContext } from '../../contexts/conversations_context'\nimport { useTheme, useCreateAndroidRippleColor } from '../../hooks'\nimport {\n useConversationsMarkRead,\n useConversationsMute,\n} from '../../hooks/use_conversations_actions'\nimport { ConversationResource } from '../../types'\nimport { platformPressedOpacityStyle } from '../../utils'\nimport { tokens } from '../../vendor/tapestry/tokens'\nimport { ActionToggleButton } from './action_toggle_button'\n\nexport function ConversationActions({\n children,\n conversation,\n style,\n onPress,\n accessibilityLabel,\n}: {\n children: ReactNode\n conversation: ConversationResource\n onPress: () => void\n style?: StyleProp<ViewStyle>\n accessibilityLabel?: string\n}) {\n const swipeableRef = useRef<SwipeableMethods>(null)\n const styles = useStyles()\n const { activeConversationId, setActiveConversationId } = useConversationsContext()\n const [disabled, setDisabled] = useState(false)\n const overshootLeft = Platform.OS === 'ios'\n const androidRippleColor = useCreateAndroidRippleColor({\n color: styles.swipeableChildContainer.backgroundColor,\n })\n const { muted, setMuted, isPending: isMutedPending } = useConversationsMute({ conversation })\n const {\n read,\n markRead,\n isPending: isMarkReadPending,\n } = useConversationsMarkRead({\n conversation,\n })\n\n const isConversationEmpty = conversation.lastMessageCreatedAt === null\n\n const handleSwipeableClose = useCallback(() => {\n setDisabled(false)\n swipeableRef.current?.close()\n }, [setDisabled])\n\n const handleSwipeableOpen = () => {\n setDisabled(true)\n setActiveConversationId(conversation.id)\n }\n\n const handlePress = useCallback(() => {\n if (disabled || !onPress) return\n onPress()\n }, [disabled, onPress])\n\n const handleMute = useCallback(() => {\n setMuted(!muted)\n handleSwipeableClose()\n }, [muted, handleSwipeableClose, setMuted])\n\n const handleLatestMessageUnread = useCallback(() => {\n markRead(!read)\n handleSwipeableClose()\n }, [read, handleSwipeableClose, markRead])\n\n const readAccessibilityAction = !isConversationEmpty\n ? [\n {\n name: read ? 'markUnread' : 'markRead',\n label: read ? 'Mark unread' : 'Mark read',\n },\n ]\n : []\n\n const accessibilityActions = [\n {\n name: muted ? 'unmute' : 'mute',\n label: muted ? 'Unmute' : 'Mute',\n },\n ...readAccessibilityAction,\n ]\n\n const handleAccessibilityAction = useCallback(\n (event: AccessibilityActionEvent) => {\n switch (event.nativeEvent.actionName) {\n case 'markRead':\n handleLatestMessageUnread()\n break\n case 'markUnread':\n handleLatestMessageUnread()\n break\n case 'mute':\n handleMute()\n break\n case 'unmute':\n handleMute()\n break\n }\n },\n [handleLatestMessageUnread, handleMute]\n )\n\n useEffect(() => {\n if (activeConversationId === conversation.id) return\n\n handleSwipeableClose()\n }, [activeConversationId, conversation.id, handleSwipeableClose])\n\n return (\n <ReanimatedSwipeable\n ref={swipeableRef}\n childrenContainerStyle={styles.swipeableChildContainer}\n containerStyle={styles.swipeableContainer}\n overshootFriction={8}\n overshootLeft={overshootLeft}\n overshootRight={false}\n onSwipeableOpenStartDrag={handleSwipeableOpen}\n onSwipeableClose={() => setDisabled(false)}\n renderRightActions={() => <></>}\n renderLeftActions={() => (\n <LeftActions\n isConversationEmpty={isConversationEmpty}\n handleMute={handleMute}\n handleLatestMessageUnread={handleLatestMessageUnread}\n muted={muted}\n read={read}\n isMutedPending={isMutedPending}\n isMarkReadPending={isMarkReadPending}\n />\n )}\n >\n <Pressable\n onPress={handlePress}\n style={({ pressed }) => [style, pressed && platformPressedOpacityStyle]}\n android_ripple={{ color: androidRippleColor, borderless: false, foreground: true }}\n accessibilityLabel={accessibilityLabel}\n accessibilityRole=\"button\"\n accessibilityActions={accessibilityActions}\n onAccessibilityAction={handleAccessibilityAction}\n >\n {children}\n </Pressable>\n </ReanimatedSwipeable>\n )\n}\n\ninterface LeftActionsProps {\n isConversationEmpty: boolean\n handleMute: () => void\n handleLatestMessageUnread: () => void\n muted: boolean\n read: boolean\n isMutedPending: boolean\n isMarkReadPending: boolean\n}\n\nfunction LeftActions({\n isConversationEmpty,\n handleMute,\n handleLatestMessageUnread,\n muted,\n read,\n isMutedPending,\n isMarkReadPending,\n}: LeftActionsProps) {\n const styles = useStyles()\n\n const muteToggleContent = {\n true: { iconName: 'general.bell', label: 'Unmute' },\n false: { iconName: 'general.bellMuted', label: 'Mute' },\n } as const\n\n const latestMessageUnreadToggleContent = {\n true: { iconName: 'general.outlinedTextMessage', label: 'Mark read' },\n false: { iconName: 'general.textMessageNotifications', label: 'Mark unread' },\n } as const\n\n return (\n <View style={styles.actionButtonContainer}>\n <ActionToggleButton\n loading={isMarkReadPending}\n disabled={isConversationEmpty}\n toggled={!read}\n onPress={handleLatestMessageUnread}\n toggleContent={latestMessageUnreadToggleContent}\n backgroundColor={tokens.fillColorInteractionSwipeDefault}\n />\n <ActionToggleButton\n loading={isMutedPending}\n toggled={muted}\n onPress={handleMute}\n toggleContent={muteToggleContent}\n backgroundColor={tokens.fillColorInteractionSwipeSecondary}\n />\n </View>\n )\n}\n\nconst ACTION_BUTTON_WIDTH = 120\nconst ACTION_BUTTON_COUNT = 2\n\nconst useStyles = () => {\n const { colors } = useTheme()\n return StyleSheet.create({\n swipeableChildContainer: {\n backgroundColor: colors.surfaceColor100,\n },\n actionButtonContainer: {\n flexDirection: 'row',\n alignItems: 'center',\n width: ACTION_BUTTON_WIDTH * ACTION_BUTTON_COUNT,\n alignContent: 'stretch',\n },\n swipeableContainer: {\n backgroundColor: colors.surfaceColor090,\n },\n })\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"conversation_preview.d.ts","sourceRoot":"","sources":["../../../src/components/conversations/conversation_preview.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAA;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAQlD,UAAU,wBAAwB;IAChC,YAAY,EAAE,oBAAoB,CAAA;IAClC,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,KAAK,CAAC,EAAE,SAAS,CAAA;CAClB;AAED,eAAO,MAAM,mBAAmB,kDAK7B,wBAAwB,sBAwD1B,CAAA;AAmCD,eAAO,MAAM,2BAA2B,yBAqBvC,CAAA"}
1
+ {"version":3,"file":"conversation_preview.d.ts","sourceRoot":"","sources":["../../../src/components/conversations/conversation_preview.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAA;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAUlD,UAAU,wBAAwB;IAChC,YAAY,EAAE,oBAAoB,CAAA;IAClC,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,KAAK,CAAC,EAAE,SAAS,CAAA;CAClB;AAED,eAAO,MAAM,mBAAmB,kDAK7B,wBAAwB,sBA8D1B,CAAA;AAmCD,eAAO,MAAM,2BAA2B,yBAqBvC,CAAA"}
@@ -6,6 +6,7 @@ import { useTheme } from '../../hooks';
6
6
  import { UnreadCountBadge } from './unread_count_badge';
7
7
  import { ConversationActions } from './conversation_actions';
8
8
  import { MuteIndicator } from './mute_indicator';
9
+ const EMPTY_CONVERSATION_PREVIEW_TEXT = 'Send your first message';
9
10
  export const ConversationPreview = ({ conversation, onPress, showBadges = true, style, }) => {
10
11
  const styles = useStyles();
11
12
  const { lastMessageAuthorName, lastMessageCreatedAt, lastMessageTextPreview, previewAvatarUrls, title, unreadCount, badges, muted, } = conversation;
@@ -15,8 +16,12 @@ export const ConversationPreview = ({ conversation, onPress, showBadges = true,
15
16
  const fallbackIconName = emptyConversation ? 'people.noTextMessage' : 'general.person';
16
17
  const conversationPreviewText = lastMessageTextPreview
17
18
  ? `${lastMessageAuthorName}: ${lastMessageTextPreview}`
18
- : 'Send your first message';
19
- return (<ConversationActions conversation={conversation} style={[styles.previewRow, style]} onPress={onPress}>
19
+ : EMPTY_CONVERSATION_PREVIEW_TEXT;
20
+ const conversationAccessibilityLabel = getConversationAccessibilityLabel({
21
+ conversation,
22
+ showBadges,
23
+ });
24
+ return (<ConversationActions conversation={conversation} style={[styles.previewRow, style]} onPress={onPress} accessibilityLabel={conversationAccessibilityLabel}>
20
25
  <AvatarGroup size="lg" sourceUris={previewAvatarUrls || []} showFallback={shouldShowFallback} fallbackIconName={fallbackIconName}/>
21
26
  <View style={styles.conversationBody}>
22
27
  <Heading numberOfLines={1} variant="h3" style={styles.title}>
@@ -65,6 +70,22 @@ export const ConversationPreviewSkeleton = () => {
65
70
  </View>
66
71
  </View>);
67
72
  };
73
+ const getConversationAccessibilityLabel = ({ conversation, showBadges, }) => {
74
+ const { badges, title, lastMessageAuthorName, lastMessageTextPreview, muted, unreadCount, lastMessageCreatedAt, } = conversation;
75
+ const hasBadges = badges && showBadges;
76
+ const hasUnreadMessages = unreadCount > 0;
77
+ const hasLastMessage = lastMessageCreatedAt;
78
+ const label = {
79
+ title,
80
+ badge: hasBadges ? `for ${badges[0]?.pcoResourceType} ${badges[0]?.text || ''}` : '',
81
+ muted: muted ? 'Muted' : '',
82
+ readStatus: hasUnreadMessages ? `${unreadCount} unread messages` : '',
83
+ lastMessage: hasLastMessage
84
+ ? `Last message sent ${formatDatePreview(lastMessageCreatedAt)} from ${lastMessageAuthorName} and says ${lastMessageTextPreview}`
85
+ : EMPTY_CONVERSATION_PREVIEW_TEXT,
86
+ };
87
+ return `Select ${label.title}, ${label.badge}, ${label.muted}, ${label.readStatus}, ${label.lastMessage}`.trim();
88
+ };
68
89
  const useStyles = () => {
69
90
  const { colors } = useTheme();
70
91
  return StyleSheet.create({
@@ -1 +1 @@
1
- {"version":3,"file":"conversation_preview.js","sourceRoot":"","sources":["../../../src/components/conversations/conversation_preview.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from 'react'\nimport { StyleSheet, View, ViewStyle } from 'react-native'\nimport { ConversationResource } from '../../types'\nimport { AvatarGroup, Heading, Text, Badge, type BadgeProps } from '../display'\nimport { formatDatePreview } from '../../utils/date'\nimport { useTheme } from '../../hooks'\nimport { UnreadCountBadge } from './unread_count_badge'\nimport { ConversationActions } from './conversation_actions'\nimport { MuteIndicator } from './mute_indicator'\n\ninterface ConversationPreviewProps {\n conversation: ConversationResource\n onPress: () => void\n showBadges?: boolean\n style?: ViewStyle\n}\n\nexport const ConversationPreview = ({\n conversation,\n onPress,\n showBadges = true,\n style,\n}: ConversationPreviewProps) => {\n const styles = useStyles()\n const {\n lastMessageAuthorName,\n lastMessageCreatedAt,\n lastMessageTextPreview,\n previewAvatarUrls,\n title,\n unreadCount,\n badges,\n muted,\n } = conversation\n\n const emptyConversation = !lastMessageCreatedAt\n const hasAvatarUrls = previewAvatarUrls && previewAvatarUrls.length > 0\n const shouldShowFallback = emptyConversation || !hasAvatarUrls\n const fallbackIconName = emptyConversation ? 'people.noTextMessage' : 'general.person'\n\n const conversationPreviewText = lastMessageTextPreview\n ? `${lastMessageAuthorName}: ${lastMessageTextPreview}`\n : 'Send your first message'\n\n return (\n <ConversationActions\n conversation={conversation}\n style={[styles.previewRow, style]}\n onPress={onPress}\n >\n <AvatarGroup\n size=\"lg\"\n sourceUris={previewAvatarUrls || []}\n showFallback={shouldShowFallback}\n fallbackIconName={fallbackIconName}\n />\n <View style={styles.conversationBody}>\n <Heading numberOfLines={1} variant=\"h3\" style={styles.title}>\n {title}\n </Heading>\n <Text\n variant=\"tertiary\"\n numberOfLines={2}\n style={emptyConversation && styles.emptyConversationPreviewText}\n >\n {conversationPreviewText}\n </Text>\n <ConversationBadges visible={showBadges} badges={badges} />\n </View>\n <View style={styles.metaContainer}>\n <Text variant=\"tertiary\">{formatDatePreview(lastMessageCreatedAt)}</Text>\n <View style={styles.statusContainer}>\n <UnreadCountBadge count={unreadCount} showDot={muted} />\n <MuteIndicator muted={muted} />\n </View>\n </View>\n </ConversationActions>\n )\n}\n\ninterface BadgeShape {\n appName?: BadgeProps['productLogoName']\n pcoResourceType: string\n text: string | null\n}\nconst ConversationBadges = ({ visible, badges }: { visible: boolean; badges?: BadgeShape[] }) => {\n const styles = useStyles()\n\n if (!visible || !badges || badges.length === 0) {\n return null\n }\n\n return (\n <View style={styles.badges}>\n {badges.map(badge => (\n <Badge\n key={badge.text}\n variant=\"meta\"\n productLogoName={badge.appName}\n label={badge.pcoResourceType}\n metaLabel={badge.text || ''}\n />\n ))}\n </View>\n )\n}\n\nconst skeletonBadges: BadgeShape[] = [\n {\n pcoResourceType: ' ',\n text: ' ',\n },\n]\nexport const ConversationPreviewSkeleton = () => {\n const styles = useStyles()\n\n return (\n <View style={styles.previewRow}>\n <AvatarGroup\n size=\"lg\"\n sourceUris={[]}\n showFallback={true}\n fallbackIconName={'general.person'}\n />\n <View style={styles.conversationBody}>\n <View style={styles.titleLoading} />\n <View style={styles.subtitleLoading} />\n <ConversationBadges visible={true} badges={skeletonBadges} />\n </View>\n <View style={styles.metaContainer}>\n <View style={styles.dateLoading} />\n </View>\n </View>\n )\n}\n\nconst useStyles = () => {\n const { colors } = useTheme()\n\n return StyleSheet.create({\n previewRow: {\n flexDirection: 'row',\n gap: 8,\n borderBottomWidth: 1,\n borderBottomColor: colors.borderColorDefaultDim,\n paddingTop: 12,\n paddingBottom: 12,\n paddingHorizontal: 16,\n },\n title: {\n fontSize: 16,\n color: colors.textColorDefaultPrimary,\n },\n titleLoading: {\n width: 120,\n height: 20,\n backgroundColor: colors.fillColorNeutral080,\n borderRadius: 4,\n },\n conversationBody: {\n flex: 1,\n rowGap: 2,\n },\n emptyConversationPreviewText: {\n color: colors.interaction,\n },\n subtitleLoading: {\n width: 80,\n height: 16,\n backgroundColor: colors.fillColorNeutral090,\n borderRadius: 4,\n },\n metaContainer: {\n rowGap: 4,\n alignItems: 'flex-end',\n },\n dateLoading: {\n width: 60,\n height: 16,\n backgroundColor: colors.fillColorNeutral090,\n borderRadius: 4,\n },\n badges: {\n marginTop: 4,\n alignItems: 'flex-start',\n },\n statusContainer: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: 4,\n },\n })\n}\n"]}
1
+ {"version":3,"file":"conversation_preview.js","sourceRoot":"","sources":["../../../src/components/conversations/conversation_preview.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from 'react'\nimport { StyleSheet, View, ViewStyle } from 'react-native'\nimport { ConversationResource } from '../../types'\nimport { AvatarGroup, Heading, Text, Badge, type BadgeProps } from '../display'\nimport { formatDatePreview } from '../../utils/date'\nimport { useTheme } from '../../hooks'\nimport { UnreadCountBadge } from './unread_count_badge'\nimport { ConversationActions } from './conversation_actions'\nimport { MuteIndicator } from './mute_indicator'\n\nconst EMPTY_CONVERSATION_PREVIEW_TEXT = 'Send your first message'\n\ninterface ConversationPreviewProps {\n conversation: ConversationResource\n onPress: () => void\n showBadges?: boolean\n style?: ViewStyle\n}\n\nexport const ConversationPreview = ({\n conversation,\n onPress,\n showBadges = true,\n style,\n}: ConversationPreviewProps) => {\n const styles = useStyles()\n const {\n lastMessageAuthorName,\n lastMessageCreatedAt,\n lastMessageTextPreview,\n previewAvatarUrls,\n title,\n unreadCount,\n badges,\n muted,\n } = conversation\n\n const emptyConversation = !lastMessageCreatedAt\n const hasAvatarUrls = previewAvatarUrls && previewAvatarUrls.length > 0\n const shouldShowFallback = emptyConversation || !hasAvatarUrls\n const fallbackIconName = emptyConversation ? 'people.noTextMessage' : 'general.person'\n\n const conversationPreviewText = lastMessageTextPreview\n ? `${lastMessageAuthorName}: ${lastMessageTextPreview}`\n : EMPTY_CONVERSATION_PREVIEW_TEXT\n\n const conversationAccessibilityLabel = getConversationAccessibilityLabel({\n conversation,\n showBadges,\n })\n\n return (\n <ConversationActions\n conversation={conversation}\n style={[styles.previewRow, style]}\n onPress={onPress}\n accessibilityLabel={conversationAccessibilityLabel}\n >\n <AvatarGroup\n size=\"lg\"\n sourceUris={previewAvatarUrls || []}\n showFallback={shouldShowFallback}\n fallbackIconName={fallbackIconName}\n />\n <View style={styles.conversationBody}>\n <Heading numberOfLines={1} variant=\"h3\" style={styles.title}>\n {title}\n </Heading>\n <Text\n variant=\"tertiary\"\n numberOfLines={2}\n style={emptyConversation && styles.emptyConversationPreviewText}\n >\n {conversationPreviewText}\n </Text>\n <ConversationBadges visible={showBadges} badges={badges} />\n </View>\n <View style={styles.metaContainer}>\n <Text variant=\"tertiary\">{formatDatePreview(lastMessageCreatedAt)}</Text>\n <View style={styles.statusContainer}>\n <UnreadCountBadge count={unreadCount} showDot={muted} />\n <MuteIndicator muted={muted} />\n </View>\n </View>\n </ConversationActions>\n )\n}\n\ninterface BadgeShape {\n appName?: BadgeProps['productLogoName']\n pcoResourceType: string\n text: string | null\n}\nconst ConversationBadges = ({ visible, badges }: { visible: boolean; badges?: BadgeShape[] }) => {\n const styles = useStyles()\n\n if (!visible || !badges || badges.length === 0) {\n return null\n }\n\n return (\n <View style={styles.badges}>\n {badges.map(badge => (\n <Badge\n key={badge.text}\n variant=\"meta\"\n productLogoName={badge.appName}\n label={badge.pcoResourceType}\n metaLabel={badge.text || ''}\n />\n ))}\n </View>\n )\n}\n\nconst skeletonBadges: BadgeShape[] = [\n {\n pcoResourceType: ' ',\n text: ' ',\n },\n]\nexport const ConversationPreviewSkeleton = () => {\n const styles = useStyles()\n\n return (\n <View style={styles.previewRow}>\n <AvatarGroup\n size=\"lg\"\n sourceUris={[]}\n showFallback={true}\n fallbackIconName={'general.person'}\n />\n <View style={styles.conversationBody}>\n <View style={styles.titleLoading} />\n <View style={styles.subtitleLoading} />\n <ConversationBadges visible={true} badges={skeletonBadges} />\n </View>\n <View style={styles.metaContainer}>\n <View style={styles.dateLoading} />\n </View>\n </View>\n )\n}\n\ninterface GetConversationAccessibilityLabelProps {\n conversation: ConversationResource\n showBadges: boolean\n}\nconst getConversationAccessibilityLabel = ({\n conversation,\n showBadges,\n}: GetConversationAccessibilityLabelProps) => {\n const {\n badges,\n title,\n lastMessageAuthorName,\n lastMessageTextPreview,\n muted,\n unreadCount,\n lastMessageCreatedAt,\n } = conversation\n\n const hasBadges = badges && showBadges\n const hasUnreadMessages = unreadCount > 0\n const hasLastMessage = lastMessageCreatedAt\n\n const label = {\n title,\n badge: hasBadges ? `for ${badges[0]?.pcoResourceType} ${badges[0]?.text || ''}` : '',\n muted: muted ? 'Muted' : '',\n readStatus: hasUnreadMessages ? `${unreadCount} unread messages` : '',\n lastMessage: hasLastMessage\n ? `Last message sent ${formatDatePreview(lastMessageCreatedAt)} from ${lastMessageAuthorName} and says ${lastMessageTextPreview}`\n : EMPTY_CONVERSATION_PREVIEW_TEXT,\n }\n\n return `Select ${label.title}, ${label.badge}, ${label.muted}, ${label.readStatus}, ${label.lastMessage}`.trim()\n}\n\nconst useStyles = () => {\n const { colors } = useTheme()\n\n return StyleSheet.create({\n previewRow: {\n flexDirection: 'row',\n gap: 8,\n borderBottomWidth: 1,\n borderBottomColor: colors.borderColorDefaultDim,\n paddingTop: 12,\n paddingBottom: 12,\n paddingHorizontal: 16,\n },\n title: {\n fontSize: 16,\n color: colors.textColorDefaultPrimary,\n },\n titleLoading: {\n width: 120,\n height: 20,\n backgroundColor: colors.fillColorNeutral080,\n borderRadius: 4,\n },\n conversationBody: {\n flex: 1,\n rowGap: 2,\n },\n emptyConversationPreviewText: {\n color: colors.interaction,\n },\n subtitleLoading: {\n width: 80,\n height: 16,\n backgroundColor: colors.fillColorNeutral090,\n borderRadius: 4,\n },\n metaContainer: {\n rowGap: 4,\n alignItems: 'flex-end',\n },\n dateLoading: {\n width: 60,\n height: 16,\n backgroundColor: colors.fillColorNeutral090,\n borderRadius: 4,\n },\n badges: {\n marginTop: 4,\n alignItems: 'flex-start',\n },\n statusContainer: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: 4,\n },\n })\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planningcenter/chat-react-native",
3
- "version": "3.13.0-rc.5",
3
+ "version": "3.13.0-rc.6",
4
4
  "description": "",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -55,5 +55,5 @@
55
55
  "react-native-url-polyfill": "^2.0.0",
56
56
  "typescript": "<5.6.0"
57
57
  },
58
- "gitHead": "1665332ab6af74c29d9630be662f0f9920a8ea2d"
58
+ "gitHead": "2769972916f0f7ca8434bf88f9d3acf94ab6594b"
59
59
  }
@@ -1,5 +1,13 @@
1
1
  import React, { ReactNode, useCallback, useEffect, useRef, useState } from 'react'
2
- import { Platform, Pressable, StyleProp, StyleSheet, View, ViewStyle } from 'react-native'
2
+ import {
3
+ AccessibilityActionEvent,
4
+ Platform,
5
+ Pressable,
6
+ StyleProp,
7
+ StyleSheet,
8
+ View,
9
+ ViewStyle,
10
+ } from 'react-native'
3
11
  import ReanimatedSwipeable, {
4
12
  SwipeableMethods,
5
13
  } from 'react-native-gesture-handler/ReanimatedSwipeable'
@@ -19,11 +27,13 @@ export function ConversationActions({
19
27
  conversation,
20
28
  style,
21
29
  onPress,
30
+ accessibilityLabel,
22
31
  }: {
23
32
  children: ReactNode
24
33
  conversation: ConversationResource
25
34
  onPress: () => void
26
35
  style?: StyleProp<ViewStyle>
36
+ accessibilityLabel?: string
27
37
  }) {
28
38
  const swipeableRef = useRef<SwipeableMethods>(null)
29
39
  const styles = useStyles()
@@ -33,11 +43,21 @@ export function ConversationActions({
33
43
  const androidRippleColor = useCreateAndroidRippleColor({
34
44
  color: styles.swipeableChildContainer.backgroundColor,
35
45
  })
46
+ const { muted, setMuted, isPending: isMutedPending } = useConversationsMute({ conversation })
47
+ const {
48
+ read,
49
+ markRead,
50
+ isPending: isMarkReadPending,
51
+ } = useConversationsMarkRead({
52
+ conversation,
53
+ })
54
+
55
+ const isConversationEmpty = conversation.lastMessageCreatedAt === null
36
56
 
37
- const handleSwipeableClose = () => {
57
+ const handleSwipeableClose = useCallback(() => {
38
58
  setDisabled(false)
39
59
  swipeableRef.current?.close()
40
- }
60
+ }, [setDisabled])
41
61
 
42
62
  const handleSwipeableOpen = () => {
43
63
  setDisabled(true)
@@ -49,11 +69,58 @@ export function ConversationActions({
49
69
  onPress()
50
70
  }, [disabled, onPress])
51
71
 
72
+ const handleMute = useCallback(() => {
73
+ setMuted(!muted)
74
+ handleSwipeableClose()
75
+ }, [muted, handleSwipeableClose, setMuted])
76
+
77
+ const handleLatestMessageUnread = useCallback(() => {
78
+ markRead(!read)
79
+ handleSwipeableClose()
80
+ }, [read, handleSwipeableClose, markRead])
81
+
82
+ const readAccessibilityAction = !isConversationEmpty
83
+ ? [
84
+ {
85
+ name: read ? 'markUnread' : 'markRead',
86
+ label: read ? 'Mark unread' : 'Mark read',
87
+ },
88
+ ]
89
+ : []
90
+
91
+ const accessibilityActions = [
92
+ {
93
+ name: muted ? 'unmute' : 'mute',
94
+ label: muted ? 'Unmute' : 'Mute',
95
+ },
96
+ ...readAccessibilityAction,
97
+ ]
98
+
99
+ const handleAccessibilityAction = useCallback(
100
+ (event: AccessibilityActionEvent) => {
101
+ switch (event.nativeEvent.actionName) {
102
+ case 'markRead':
103
+ handleLatestMessageUnread()
104
+ break
105
+ case 'markUnread':
106
+ handleLatestMessageUnread()
107
+ break
108
+ case 'mute':
109
+ handleMute()
110
+ break
111
+ case 'unmute':
112
+ handleMute()
113
+ break
114
+ }
115
+ },
116
+ [handleLatestMessageUnread, handleMute]
117
+ )
118
+
52
119
  useEffect(() => {
53
120
  if (activeConversationId === conversation.id) return
54
121
 
55
122
  handleSwipeableClose()
56
- }, [activeConversationId, conversation.id])
123
+ }, [activeConversationId, conversation.id, handleSwipeableClose])
57
124
 
58
125
  return (
59
126
  <ReanimatedSwipeable
@@ -67,13 +134,25 @@ export function ConversationActions({
67
134
  onSwipeableClose={() => setDisabled(false)}
68
135
  renderRightActions={() => <></>}
69
136
  renderLeftActions={() => (
70
- <LeftActions conversation={conversation} onClose={handleSwipeableClose} />
137
+ <LeftActions
138
+ isConversationEmpty={isConversationEmpty}
139
+ handleMute={handleMute}
140
+ handleLatestMessageUnread={handleLatestMessageUnread}
141
+ muted={muted}
142
+ read={read}
143
+ isMutedPending={isMutedPending}
144
+ isMarkReadPending={isMarkReadPending}
145
+ />
71
146
  )}
72
147
  >
73
148
  <Pressable
74
149
  onPress={handlePress}
75
150
  style={({ pressed }) => [style, pressed && platformPressedOpacityStyle]}
76
151
  android_ripple={{ color: androidRippleColor, borderless: false, foreground: true }}
152
+ accessibilityLabel={accessibilityLabel}
153
+ accessibilityRole="button"
154
+ accessibilityActions={accessibilityActions}
155
+ onAccessibilityAction={handleAccessibilityAction}
77
156
  >
78
157
  {children}
79
158
  </Pressable>
@@ -82,13 +161,25 @@ export function ConversationActions({
82
161
  }
83
162
 
84
163
  interface LeftActionsProps {
85
- conversation: ConversationResource
86
- onClose: () => void
164
+ isConversationEmpty: boolean
165
+ handleMute: () => void
166
+ handleLatestMessageUnread: () => void
167
+ muted: boolean
168
+ read: boolean
169
+ isMutedPending: boolean
170
+ isMarkReadPending: boolean
87
171
  }
88
172
 
89
- function LeftActions({ conversation, onClose }: LeftActionsProps) {
173
+ function LeftActions({
174
+ isConversationEmpty,
175
+ handleMute,
176
+ handleLatestMessageUnread,
177
+ muted,
178
+ read,
179
+ isMutedPending,
180
+ isMarkReadPending,
181
+ }: LeftActionsProps) {
90
182
  const styles = useStyles()
91
- const emptyConversation = conversation.lastMessageCreatedAt === null
92
183
 
93
184
  const muteToggleContent = {
94
185
  true: { iconName: 'general.bell', label: 'Unmute' },
@@ -99,31 +190,19 @@ function LeftActions({ conversation, onClose }: LeftActionsProps) {
99
190
  true: { iconName: 'general.outlinedTextMessage', label: 'Mark read' },
100
191
  false: { iconName: 'general.textMessageNotifications', label: 'Mark unread' },
101
192
  } as const
102
- const { muted, setMuted, isPending } = useConversationsMute({ conversation })
103
- const { read, markRead, isPending: markReadPending } = useConversationsMarkRead({ conversation })
104
-
105
- const handleMute = useCallback(() => {
106
- setMuted(!muted)
107
- onClose()
108
- }, [muted, onClose, setMuted])
109
-
110
- const handleLatestMessageUnread = useCallback(() => {
111
- markRead(!read)
112
- onClose()
113
- }, [read, onClose, markRead])
114
193
 
115
194
  return (
116
195
  <View style={styles.actionButtonContainer}>
117
196
  <ActionToggleButton
118
- loading={markReadPending}
119
- disabled={emptyConversation}
197
+ loading={isMarkReadPending}
198
+ disabled={isConversationEmpty}
120
199
  toggled={!read}
121
200
  onPress={handleLatestMessageUnread}
122
201
  toggleContent={latestMessageUnreadToggleContent}
123
202
  backgroundColor={tokens.fillColorInteractionSwipeDefault}
124
203
  />
125
204
  <ActionToggleButton
126
- loading={isPending}
205
+ loading={isMutedPending}
127
206
  toggled={muted}
128
207
  onPress={handleMute}
129
208
  toggleContent={muteToggleContent}
@@ -8,6 +8,8 @@ import { UnreadCountBadge } from './unread_count_badge'
8
8
  import { ConversationActions } from './conversation_actions'
9
9
  import { MuteIndicator } from './mute_indicator'
10
10
 
11
+ const EMPTY_CONVERSATION_PREVIEW_TEXT = 'Send your first message'
12
+
11
13
  interface ConversationPreviewProps {
12
14
  conversation: ConversationResource
13
15
  onPress: () => void
@@ -40,13 +42,19 @@ export const ConversationPreview = ({
40
42
 
41
43
  const conversationPreviewText = lastMessageTextPreview
42
44
  ? `${lastMessageAuthorName}: ${lastMessageTextPreview}`
43
- : 'Send your first message'
45
+ : EMPTY_CONVERSATION_PREVIEW_TEXT
46
+
47
+ const conversationAccessibilityLabel = getConversationAccessibilityLabel({
48
+ conversation,
49
+ showBadges,
50
+ })
44
51
 
45
52
  return (
46
53
  <ConversationActions
47
54
  conversation={conversation}
48
55
  style={[styles.previewRow, style]}
49
56
  onPress={onPress}
57
+ accessibilityLabel={conversationAccessibilityLabel}
50
58
  >
51
59
  <AvatarGroup
52
60
  size="lg"
@@ -134,6 +142,41 @@ export const ConversationPreviewSkeleton = () => {
134
142
  )
135
143
  }
136
144
 
145
+ interface GetConversationAccessibilityLabelProps {
146
+ conversation: ConversationResource
147
+ showBadges: boolean
148
+ }
149
+ const getConversationAccessibilityLabel = ({
150
+ conversation,
151
+ showBadges,
152
+ }: GetConversationAccessibilityLabelProps) => {
153
+ const {
154
+ badges,
155
+ title,
156
+ lastMessageAuthorName,
157
+ lastMessageTextPreview,
158
+ muted,
159
+ unreadCount,
160
+ lastMessageCreatedAt,
161
+ } = conversation
162
+
163
+ const hasBadges = badges && showBadges
164
+ const hasUnreadMessages = unreadCount > 0
165
+ const hasLastMessage = lastMessageCreatedAt
166
+
167
+ const label = {
168
+ title,
169
+ badge: hasBadges ? `for ${badges[0]?.pcoResourceType} ${badges[0]?.text || ''}` : '',
170
+ muted: muted ? 'Muted' : '',
171
+ readStatus: hasUnreadMessages ? `${unreadCount} unread messages` : '',
172
+ lastMessage: hasLastMessage
173
+ ? `Last message sent ${formatDatePreview(lastMessageCreatedAt)} from ${lastMessageAuthorName} and says ${lastMessageTextPreview}`
174
+ : EMPTY_CONVERSATION_PREVIEW_TEXT,
175
+ }
176
+
177
+ return `Select ${label.title}, ${label.badge}, ${label.muted}, ${label.readStatus}, ${label.lastMessage}`.trim()
178
+ }
179
+
137
180
  const useStyles = () => {
138
181
  const { colors } = useTheme()
139
182