@planningcenter/chat-react-native 3.44.1-qa-staging.1 → 3.44.1-qa-staging.3
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/hooks/use_rest_away_from_dismiss_edge.d.ts +22 -0
- package/build/hooks/use_rest_away_from_dismiss_edge.d.ts.map +1 -0
- package/build/hooks/use_rest_away_from_dismiss_edge.js +48 -0
- package/build/hooks/use_rest_away_from_dismiss_edge.js.map +1 -0
- package/build/screens/conversation_screen.d.ts.map +1 -1
- package/build/screens/conversation_screen.js +8 -3
- package/build/screens/conversation_screen.js.map +1 -1
- package/package.json +3 -3
- package/src/hooks/use_rest_away_from_dismiss_edge.ts +63 -0
- package/src/screens/conversation_screen.tsx +12 -2
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
import { FlatList } from 'react-native';
|
|
3
|
+
/**
|
|
4
|
+
* iOS starts a sheet's interactive dismissal once the content scroll view sits at its
|
|
5
|
+
* top content edge — for an inverted list, wherever the newest message rests. Resting
|
|
6
|
+
* the list 1pt in from that edge keeps a drag at the bottom of a conversation scrolling
|
|
7
|
+
* history instead of swiping the modal away.
|
|
8
|
+
*
|
|
9
|
+
* Returns the offset the list rests at plus the scroll listeners that nudge it back
|
|
10
|
+
* there: once on first layout, then after each gesture settles. On Android there is no
|
|
11
|
+
* sheet gesture to avoid, so the offset is 0 and the listeners are undefined.
|
|
12
|
+
*/
|
|
13
|
+
export declare const useRestAwayFromDismissEdge: ({ listRef, enabled, }: {
|
|
14
|
+
listRef: RefObject<FlatList | null>;
|
|
15
|
+
enabled?: boolean;
|
|
16
|
+
}) => {
|
|
17
|
+
restOffset: number;
|
|
18
|
+
onContentSizeChange: (...args: any[]) => void;
|
|
19
|
+
onScrollEndDrag: (...args: any[]) => void;
|
|
20
|
+
onMomentumScrollEnd: (...args: any[]) => void;
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=use_rest_away_from_dismiss_edge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use_rest_away_from_dismiss_edge.d.ts","sourceRoot":"","sources":["../../src/hooks/use_rest_away_from_dismiss_edge.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAuB,MAAM,OAAO,CAAA;AACtD,OAAO,EAAE,QAAQ,EAAqD,MAAM,cAAc,CAAA;AAI1F;;;;;;;;;GASG;AACH,eAAO,MAAM,0BAA0B,GAAI,uBAGxC;IACD,OAAO,EAAE,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAA;IACnC,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;;;;;CAwCA,CAAA"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { noop } from 'lodash';
|
|
2
|
+
import { useCallback, useRef } from 'react';
|
|
3
|
+
import { Platform } from 'react-native';
|
|
4
|
+
const REST_OFFSET = 1;
|
|
5
|
+
/**
|
|
6
|
+
* iOS starts a sheet's interactive dismissal once the content scroll view sits at its
|
|
7
|
+
* top content edge — for an inverted list, wherever the newest message rests. Resting
|
|
8
|
+
* the list 1pt in from that edge keeps a drag at the bottom of a conversation scrolling
|
|
9
|
+
* history instead of swiping the modal away.
|
|
10
|
+
*
|
|
11
|
+
* Returns the offset the list rests at plus the scroll listeners that nudge it back
|
|
12
|
+
* there: once on first layout, then after each gesture settles. On Android there is no
|
|
13
|
+
* sheet gesture to avoid, so the offset is 0 and the listeners are undefined.
|
|
14
|
+
*/
|
|
15
|
+
export const useRestAwayFromDismissEdge = ({ listRef, enabled = Platform.OS === 'ios', }) => {
|
|
16
|
+
const restAwayFromDismissEdge = useCallback(() => {
|
|
17
|
+
listRef.current?.scrollToOffset({ animated: false, offset: REST_OFFSET });
|
|
18
|
+
}, [listRef]);
|
|
19
|
+
const didSetInitialOffset = useRef(false);
|
|
20
|
+
const handleInitialOffset = useCallback(() => {
|
|
21
|
+
if (!didSetInitialOffset.current) {
|
|
22
|
+
didSetInitialOffset.current = true;
|
|
23
|
+
restAwayFromDismissEdge();
|
|
24
|
+
}
|
|
25
|
+
}, [restAwayFromDismissEdge]);
|
|
26
|
+
// A negative offset means the bounce is still animating; let it settle first.
|
|
27
|
+
const handleScrollSettled = useCallback((event) => {
|
|
28
|
+
const offsetY = event.nativeEvent.contentOffset.y;
|
|
29
|
+
if (offsetY >= 0 && offsetY < REST_OFFSET) {
|
|
30
|
+
restAwayFromDismissEdge();
|
|
31
|
+
}
|
|
32
|
+
}, [restAwayFromDismissEdge]);
|
|
33
|
+
if (!enabled) {
|
|
34
|
+
return {
|
|
35
|
+
restOffset: 0,
|
|
36
|
+
onContentSizeChange: noop,
|
|
37
|
+
onScrollEndDrag: noop,
|
|
38
|
+
onMomentumScrollEnd: noop,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
restOffset: REST_OFFSET,
|
|
43
|
+
onContentSizeChange: handleInitialOffset,
|
|
44
|
+
onScrollEndDrag: handleScrollSettled,
|
|
45
|
+
onMomentumScrollEnd: handleScrollSettled,
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=use_rest_away_from_dismiss_edge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use_rest_away_from_dismiss_edge.js","sourceRoot":"","sources":["../../src/hooks/use_rest_away_from_dismiss_edge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAC7B,OAAO,EAAa,WAAW,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AACtD,OAAO,EAAqD,QAAQ,EAAE,MAAM,cAAc,CAAA;AAE1F,MAAM,WAAW,GAAG,CAAC,CAAA;AAErB;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,EACzC,OAAO,EACP,OAAO,GAAG,QAAQ,CAAC,EAAE,KAAK,KAAK,GAIhC,EAAE,EAAE;IACH,MAAM,uBAAuB,GAAG,WAAW,CAAC,GAAG,EAAE;QAC/C,OAAO,CAAC,OAAO,EAAE,cAAc,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAA;IAC3E,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAA;IAEb,MAAM,mBAAmB,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;IACzC,MAAM,mBAAmB,GAAG,WAAW,CAAC,GAAG,EAAE;QAC3C,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE,CAAC;YACjC,mBAAmB,CAAC,OAAO,GAAG,IAAI,CAAA;YAClC,uBAAuB,EAAE,CAAA;QAC3B,CAAC;IACH,CAAC,EAAE,CAAC,uBAAuB,CAAC,CAAC,CAAA;IAE7B,8EAA8E;IAC9E,MAAM,mBAAmB,GAAG,WAAW,CACrC,CAAC,KAA8C,EAAE,EAAE;QACjD,MAAM,OAAO,GAAG,KAAK,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,CAAA;QAEjD,IAAI,OAAO,IAAI,CAAC,IAAI,OAAO,GAAG,WAAW,EAAE,CAAC;YAC1C,uBAAuB,EAAE,CAAA;QAC3B,CAAC;IACH,CAAC,EACD,CAAC,uBAAuB,CAAC,CAC1B,CAAA;IAED,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO;YACL,UAAU,EAAE,CAAC;YACb,mBAAmB,EAAE,IAAI;YACzB,eAAe,EAAE,IAAI;YACrB,mBAAmB,EAAE,IAAI;SAC1B,CAAA;IACH,CAAC;IAED,OAAO;QACL,UAAU,EAAE,WAAW;QACvB,mBAAmB,EAAE,mBAAmB;QACxC,eAAe,EAAE,mBAAmB;QACpC,mBAAmB,EAAE,mBAAmB;KACzC,CAAA;AACH,CAAC,CAAA","sourcesContent":["import { noop } from 'lodash'\nimport { RefObject, useCallback, useRef } from 'react'\nimport { FlatList, NativeScrollEvent, NativeSyntheticEvent, Platform } from 'react-native'\n\nconst REST_OFFSET = 1\n\n/**\n * iOS starts a sheet's interactive dismissal once the content scroll view sits at its\n * top content edge — for an inverted list, wherever the newest message rests. Resting\n * the list 1pt in from that edge keeps a drag at the bottom of a conversation scrolling\n * history instead of swiping the modal away.\n *\n * Returns the offset the list rests at plus the scroll listeners that nudge it back\n * there: once on first layout, then after each gesture settles. On Android there is no\n * sheet gesture to avoid, so the offset is 0 and the listeners are undefined.\n */\nexport const useRestAwayFromDismissEdge = ({\n listRef,\n enabled = Platform.OS === 'ios',\n}: {\n listRef: RefObject<FlatList | null>\n enabled?: boolean\n}) => {\n const restAwayFromDismissEdge = useCallback(() => {\n listRef.current?.scrollToOffset({ animated: false, offset: REST_OFFSET })\n }, [listRef])\n\n const didSetInitialOffset = useRef(false)\n const handleInitialOffset = useCallback(() => {\n if (!didSetInitialOffset.current) {\n didSetInitialOffset.current = true\n restAwayFromDismissEdge()\n }\n }, [restAwayFromDismissEdge])\n\n // A negative offset means the bounce is still animating; let it settle first.\n const handleScrollSettled = useCallback(\n (event: NativeSyntheticEvent<NativeScrollEvent>) => {\n const offsetY = event.nativeEvent.contentOffset.y\n\n if (offsetY >= 0 && offsetY < REST_OFFSET) {\n restAwayFromDismissEdge()\n }\n },\n [restAwayFromDismissEdge]\n )\n\n if (!enabled) {\n return {\n restOffset: 0,\n onContentSizeChange: noop,\n onScrollEndDrag: noop,\n onMomentumScrollEnd: noop,\n }\n }\n\n return {\n restOffset: REST_OFFSET,\n onContentSizeChange: handleInitialOffset,\n onScrollEndDrag: handleScrollSettled,\n onMomentumScrollEnd: handleScrollSettled,\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation_screen.d.ts","sourceRoot":"","sources":["../../src/screens/conversation_screen.tsx"],"names":[],"mappings":"AACA,OAAO,EAAe,gBAAgB,EAAqB,MAAM,4BAA4B,CAAA;AAC7F,OAAO,EAGL,iBAAiB,EAIlB,MAAM,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"conversation_screen.d.ts","sourceRoot":"","sources":["../../src/screens/conversation_screen.tsx"],"names":[],"mappings":"AACA,OAAO,EAAe,gBAAgB,EAAqB,MAAM,4BAA4B,CAAA;AAC7F,OAAO,EAGL,iBAAiB,EAIlB,MAAM,0BAA0B,CAAA;AAoCjC,OAAO,EAAE,yBAAyB,EAAE,MAAM,uCAAuC,CAAA;AAOjF,MAAM,MAAM,sBAAsB,GAAG;IACnC,eAAe,EAAE,MAAM,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,yBAAyB,CAAA;IACjC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG,iBAAiB,CAAC,sBAAsB,CAAC,CAAA;AAE/E,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,EAAE,uBAAuB,2CAkBpE;AAwPD,UAAU,4BAA6B,SAAQ,gBAAgB;IAC7D,eAAe,EAAE,MAAM,CAAA;IACvB,KAAK,CAAC,EAAE,yBAAyB,CAAA;IACjC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,eAAO,MAAM,uBAAuB,GAAI,8DAOrC,4BAA4B,4CAoC9B,CAAA"}
|
|
@@ -26,6 +26,7 @@ import { useConversationMessages } from '../hooks/use_conversation_messages';
|
|
|
26
26
|
import { useConversationMessagesJoltEvents } from '../hooks/use_conversation_messages_jolt_events';
|
|
27
27
|
import { useMarkLatestMessageRead } from '../hooks/use_mark_latest_message_read';
|
|
28
28
|
import { analyticsEvents, normalizeAnalyticsMetadata, usePublishProductAnalyticsEvent, } from '../hooks/use_product_analytics';
|
|
29
|
+
import { useRestAwayFromDismissEdge } from '../hooks/use_rest_away_from_dismiss_edge';
|
|
29
30
|
import { useVideoModerationJoltEvents } from '../hooks/use_video_moderation_jolt_events';
|
|
30
31
|
import { getRelativeDateStatus } from '../utils/date';
|
|
31
32
|
import { groupMessages } from '../utils/group_messages';
|
|
@@ -76,11 +77,15 @@ function ConversationScreenContent({ route }) {
|
|
|
76
77
|
const offsetY = event.nativeEvent.contentOffset.y;
|
|
77
78
|
setShowJumpToBottomButton(offsetY > 200);
|
|
78
79
|
};
|
|
80
|
+
const { restOffset, onContentSizeChange, onMomentumScrollEnd, onScrollEndDrag } = useRestAwayFromDismissEdge({
|
|
81
|
+
listRef,
|
|
82
|
+
enabled: Platform.OS === 'ios',
|
|
83
|
+
});
|
|
79
84
|
const handleReturnToBottom = useCallback(() => {
|
|
80
85
|
listRef.current?.scrollToOffset({
|
|
81
|
-
offset:
|
|
86
|
+
offset: restOffset,
|
|
82
87
|
});
|
|
83
|
-
}, []);
|
|
88
|
+
}, [restOffset]);
|
|
84
89
|
useEffect(() => {
|
|
85
90
|
if (reply_root_id) {
|
|
86
91
|
navigation.setParams({
|
|
@@ -99,7 +104,7 @@ function ConversationScreenContent({ route }) {
|
|
|
99
104
|
if (!conversation || conversation.deleted) {
|
|
100
105
|
return (_jsx(View, { style: styles.container, children: _jsxs(BlankState.Root, { children: [_jsx(BlankState.Imagery, { name: "general.outlinedTextMessage" }), _jsx(BlankState.Content, { children: _jsx(BlankState.Heading, { children: "This conversation has been deleted" }) }), _jsx(BlankState.Button, { onPress: navigation.goBack, title: "Back to conversations", accessibilityHint: "Navigates back to the conversations list", accessibilityRole: "link" })] }) }));
|
|
101
106
|
}
|
|
102
|
-
return (_jsx(View, { style: styles.container, children: _jsxs(KeyboardView, { children: [noMessages ? (_jsx(View, { style: styles.emptyState, children: _jsx(ConversationStartBanner, { conversation: conversation, hasMessages: false }) })) : (_jsx(FlatList, { inverted: true, ref: listRef, contentContainerStyle: styles.listContainer, data: messagesWithSeparators, keyExtractor: item => item.id, onScroll: trackScroll, scrollEventThrottle: 10, ListFooterComponent: hasNextPage ? null : (_jsx(ConversationStartBanner, { conversation: conversation, hasMessages: true })), renderItem: ({ item }) => {
|
|
107
|
+
return (_jsx(View, { style: styles.container, children: _jsxs(KeyboardView, { children: [noMessages ? (_jsx(View, { style: styles.emptyState, children: _jsx(ConversationStartBanner, { conversation: conversation, hasMessages: false }) })) : (_jsx(FlatList, { inverted: true, ref: listRef, contentContainerStyle: styles.listContainer, data: messagesWithSeparators, keyExtractor: item => item.id, onScroll: trackScroll, onContentSizeChange: onContentSizeChange, onScrollEndDrag: onScrollEndDrag, onMomentumScrollEnd: onMomentumScrollEnd, scrollEventThrottle: 10, ListFooterComponent: hasNextPage ? null : (_jsx(ConversationStartBanner, { conversation: conversation, hasMessages: true })), renderItem: ({ item }) => {
|
|
103
108
|
if (item.type === 'DateSeparator') {
|
|
104
109
|
return _jsx(InlineDateSeparator, { ...item });
|
|
105
110
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation_screen.js","sourceRoot":"","sources":["../../src/screens/conversation_screen.tsx"],"names":[],"mappings":"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","sourcesContent":["import { date as formatDate } from '@planningcenter/datetime-fmt'\nimport { HeaderTitle, HeaderTitleProps, PlatformPressable } from '@react-navigation/elements'\nimport {\n CommonActions,\n RouteProp,\n StaticScreenProps,\n useNavigation,\n useTheme as useNavigationTheme,\n useRoute,\n} from '@react-navigation/native'\nimport React, { useCallback, useEffect, useRef, useState } from 'react'\nimport { FlatList, Platform, StyleSheet, useWindowDimensions, View } from 'react-native'\nimport { useSafeAreaInsets } from 'react-native-safe-area-context'\nimport { Badge, Icon, Text } from '../components'\nimport { ConversationStartBanner } from '../components/conversation/conversation_start_banner'\nimport { JumpToBottomButton } from '../components/conversation/jump_to_bottom_button'\nimport { Message } from '../components/conversation/message'\nimport { MessageForm } from '../components/conversation/message_form'\nimport {\n ConversationDisabledBanner,\n LeaderMessagesDisabledBanner,\n MemberMessagesDisabledBanner,\n} from '../components/conversation/messages_disabled_banners'\nimport { ReplyShadowMessage } from '../components/conversation/reply_shadow_message'\nimport { SystemMessage } from '../components/conversation/system_message'\nimport { TypingIndicator } from '../components/conversation/typing_indicator'\nimport { KeyboardView } from '../components/display/keyboard_view'\nimport { useHeaderSlotReserve } from '../components/display/platform_modal_header_buttons'\nimport BlankState from '../components/primitive/blank_state_primitive'\nimport { ConversationContextProvider } from '../contexts/conversation_context'\nimport { useTheme } from '../hooks'\nimport { useMessageAvailableMembersAction } from '../hooks/direct_messages/use_message_available_members_action'\nimport { useConversation } from '../hooks/use_conversation'\nimport { useConversationJoltEvents } from '../hooks/use_conversation_jolt_events'\nimport { useConversationMessages } from '../hooks/use_conversation_messages'\nimport { useConversationMessagesJoltEvents } from '../hooks/use_conversation_messages_jolt_events'\nimport { useMarkLatestMessageRead } from '../hooks/use_mark_latest_message_read'\nimport {\n analyticsEvents,\n normalizeAnalyticsMetadata,\n usePublishProductAnalyticsEvent,\n} from '../hooks/use_product_analytics'\nimport { useVideoModerationJoltEvents } from '../hooks/use_video_moderation_jolt_events'\nimport { ConversationResource } from '../types/resources/conversation'\nimport { ConversationBadgeResource } from '../types/resources/conversation_badge'\nimport { MessageResource } from '../types/resources/message'\nimport { getRelativeDateStatus } from '../utils/date'\nimport { groupMessages, type DateSeparator } from '../utils/group_messages'\nimport { CONVERSATION_MESSAGE_LIST_PADDING_HORIZONTAL } from '../utils/styles'\nimport { isSystemMessage } from '../utils/system_messages'\n\nexport type ConversationRouteProps = {\n conversation_id: number\n reply_root_id?: string | null\n reply_root_author_name?: string\n chat_group_graph_id?: string\n clear_input?: boolean\n editing_message_id?: number | null\n title?: string\n subtitle?: string\n badge?: ConversationBadgeResource\n deleted?: boolean\n muted?: boolean\n}\n\nexport type ConversationScreenProps = StaticScreenProps<ConversationRouteProps>\n\nexport function ConversationScreen({ route }: ConversationScreenProps) {\n const { conversation_id, reply_root_id } = route.params\n\n const { data: conversation } = useConversation({ conversation_id })\n\n usePublishProductAnalyticsEvent(analyticsEvents.conversation_show_opened, {\n reply_root_id,\n ...normalizeAnalyticsMetadata(conversation),\n })\n\n return (\n <ConversationContextProvider\n conversationId={conversation_id}\n currentPageReplyRootId={reply_root_id ?? null}\n >\n <ConversationScreenContent route={route} />\n </ConversationContextProvider>\n )\n}\n\nfunction ConversationScreenContent({ route }: ConversationScreenProps) {\n const styles = useStyles()\n const navigation = useNavigation()\n const { conversation_id, editing_message_id, reply_root_id, reply_root_author_name } =\n route.params\n const { data: conversation } = useConversation(route.params)\n const { messages, fetchNextPage, hasNextPage } = useConversationMessages({\n conversation_id,\n reply_root_id,\n })\n useConversationJoltEvents({ conversationId: conversation_id })\n useConversationMessagesJoltEvents({ conversationId: conversation_id })\n const { markHandledByMessageForm } = useVideoModerationJoltEvents()\n useEnsureConversationsRouteExists()\n useMarkLatestMessageRead({ conversation, messages })\n const messagesWithSeparators = groupMessages({\n ms: messages,\n inReplyScreen: !!reply_root_id,\n })\n const noMessages = messagesWithSeparators.length === 0\n\n const { repliesDisabled, memberAbility, badges, title } = conversation\n const canReply = memberAbility?.canReply\n const showLeaderDisabledReplyBanner = canReply && repliesDisabled\n const canDeleteNonAuthoredMessages = memberAbility?.canDeleteNonAuthoredMessages ?? false\n const currentlyEditingMessage = messages.find(m => String(m.id) === String(editing_message_id))\n const replyRootAuthorFirstName = reply_root_author_name?.split(' ')[0]\n const replyHeaderTitle = replyRootAuthorFirstName\n ? `Reply to ${replyRootAuthorFirstName}`\n : 'Reply'\n // Prefer the membership for optimistic updates.\n const muted = conversation.conversationMembership?.muted ?? conversation.muted\n\n const listRef = useRef<FlatList>(null)\n const [showJumpToBottomButton, setShowJumpToBottomButton] = useState(false)\n\n const trackScroll = (event: any) => {\n const offsetY = event.nativeEvent.contentOffset.y\n setShowJumpToBottomButton(offsetY > 200)\n }\n\n const handleReturnToBottom = useCallback(() => {\n listRef.current?.scrollToOffset({\n offset: 0,\n })\n }, [])\n\n useEffect(() => {\n if (reply_root_id) {\n navigation.setParams({\n title: replyHeaderTitle,\n })\n } else {\n navigation.setParams({\n title: title,\n badge: badges?.[0],\n deleted: conversation?.deleted,\n muted,\n })\n }\n }, [navigation, title, badges, conversation?.deleted, reply_root_id, replyHeaderTitle, muted])\n\n if (!conversation || conversation.deleted) {\n return (\n <View style={styles.container}>\n <BlankState.Root>\n <BlankState.Imagery name=\"general.outlinedTextMessage\" />\n <BlankState.Content>\n <BlankState.Heading>This conversation has been deleted</BlankState.Heading>\n </BlankState.Content>\n <BlankState.Button\n onPress={navigation.goBack}\n title=\"Back to conversations\"\n accessibilityHint=\"Navigates back to the conversations list\"\n accessibilityRole=\"link\"\n />\n </BlankState.Root>\n </View>\n )\n }\n\n return (\n <View style={styles.container}>\n <KeyboardView>\n {noMessages ? (\n <View style={styles.emptyState}>\n <ConversationStartBanner conversation={conversation} hasMessages={false} />\n </View>\n ) : (\n <FlatList\n inverted\n ref={listRef}\n contentContainerStyle={styles.listContainer}\n data={messagesWithSeparators}\n keyExtractor={item => item.id}\n onScroll={trackScroll}\n scrollEventThrottle={10}\n ListFooterComponent={\n hasNextPage ? null : (\n <ConversationStartBanner conversation={conversation} hasMessages={true} />\n )\n }\n renderItem={({ item }) => {\n if (item.type === 'DateSeparator') {\n return <InlineDateSeparator {...item} />\n }\n\n if (item.type === 'ReplyShadowMessage') {\n return (\n <ReplyShadowMessage\n {...(item as any)}\n conversation_id={conversation_id}\n inReplyScreen={!!reply_root_id}\n />\n )\n }\n\n if (isSystemMessage(item)) {\n return <SystemMessage message={item} conversationId={conversation_id} />\n }\n\n return (\n <Message\n {...item}\n canDeleteNonAuthoredMessages={canDeleteNonAuthoredMessages}\n conversation_id={conversation_id}\n latestReadMessageSortKey={conversation?.latestReadMessageSortKey}\n inReplyScreen={!!reply_root_id}\n />\n )\n }}\n onEndReached={() => fetchNextPage()}\n ListHeaderComponent={<View style={styles.listHeader} />}\n />\n )}\n <JumpToBottomButton onPress={handleReturnToBottom} visible={showJumpToBottomButton} />\n {!noMessages && <TypingIndicator />}\n {showLeaderDisabledReplyBanner && <LeaderMessagesDisabledBanner />}\n <ConversationBottomBar\n conversation={conversation}\n canReply={canReply}\n replyRootAuthorFirstName={replyRootAuthorFirstName}\n replyRootId={reply_root_id}\n currentlyEditingMessage={currentlyEditingMessage}\n onFlaggedInMessageForm={markHandledByMessageForm}\n />\n </KeyboardView>\n </View>\n )\n}\n\ninterface ConversationBottomBarProps {\n conversation: ConversationResource\n canReply: boolean | undefined\n replyRootAuthorFirstName: string | undefined\n replyRootId: string | null | undefined\n currentlyEditingMessage: MessageResource | undefined\n onFlaggedInMessageForm?: (id: string) => void\n}\n\nfunction ConversationBottomBar({\n conversation,\n canReply,\n replyRootAuthorFirstName,\n replyRootId,\n currentlyEditingMessage,\n onFlaggedInMessageForm,\n}: ConversationBottomBarProps) {\n const messageLink = useMessageAvailableMembersAction(conversation)\n\n if (conversation.disabled)\n return (\n <ConversationDisabledBanner\n message={conversation.disabledMessage ?? null}\n messageLink={messageLink}\n />\n )\n if (!canReply) return <MemberMessagesDisabledBanner />\n return (\n <MessageForm.Root\n replyRootAuthorFirstName={replyRootAuthorFirstName}\n conversation={conversation}\n replyRootId={replyRootId}\n currentlyEditingMessage={currentlyEditingMessage}\n onFlaggedInMessageForm={onFlaggedInMessageForm}\n key={\n currentlyEditingMessage\n ? `edit-message-form-${currentlyEditingMessage.id}`\n : 'new-message-form'\n }\n >\n <MessageForm.AttachmentPicker />\n <MessageForm.Commands />\n <MessageForm.TextInput />\n <MessageForm.SubmitButton />\n </MessageForm.Root>\n )\n}\n\nfunction InlineDateSeparator({ date }: DateSeparator) {\n const styles = useDateSeparatorStyles()\n const { isThisYear } = getRelativeDateStatus(date)\n const showYear = !isThisYear\n const dateStamp = formatDate(date, { style: 'long', year: showYear })\n\n return (\n <View style={styles.container}>\n <View style={styles.separator} />\n <Text variant=\"footnote\" style={styles.dateText}>\n {dateStamp}\n </Text>\n <View style={styles.separator} />\n </View>\n )\n}\n\nconst useDateSeparatorStyles = () => {\n const theme = useTheme()\n return StyleSheet.create({\n container: {\n alignItems: 'center',\n flexDirection: 'row',\n paddingHorizontal: CONVERSATION_MESSAGE_LIST_PADDING_HORIZONTAL,\n paddingVertical: 16,\n },\n separator: {\n flex: 1,\n height: 1,\n borderTopWidth: 1,\n borderTopColor: theme.colors.borderColorDefaultBase,\n },\n dateText: {\n paddingHorizontal: 8,\n },\n })\n}\n\ninterface ConversationScreenTitleProps extends HeaderTitleProps {\n conversation_id: number\n badge?: ConversationBadgeResource\n deleted?: boolean\n muted?: boolean\n}\n\nexport const ConversationScreenTitle = ({\n conversation_id,\n badge,\n children,\n style,\n deleted,\n muted,\n}: ConversationScreenTitleProps) => {\n const styles = usePressableHeaderStyle()\n const navigation = useNavigation()\n const resourceType = badge?.pcoResourceType || ''\n const productName = badge?.appName\n const name = badge?.text || undefined\n\n return (\n <PlatformPressable\n accessibilityHint=\"Opens details about members and settings\"\n style={styles.container}\n onPress={() => {\n if (deleted) return\n\n navigation.navigate('ConversationDetails', { conversation_id })\n }}\n >\n <View style={styles.titleWrapper}>\n <View style={styles.titleTextContainer}>\n <HeaderTitle maxFontSizeMultiplier={1} style={style}>\n {children}\n </HeaderTitle>\n </View>\n {muted && <Icon name=\"general.bellMuted\" size={12} />}\n {!deleted && <Icon name=\"general.downChevron\" size={12} />}\n </View>\n <Badge\n variant=\"metaSubtle\"\n productLogoName={productName}\n label={resourceType}\n metaLabel={name}\n style={styles.badge}\n maxFontSizeMultiplier={1}\n />\n </PlatformPressable>\n )\n}\n\nconst usePressableHeaderStyle = () => {\n const { width } = useWindowDimensions()\n const slotReserve = useHeaderSlotReserve()\n const headerContainerStyle = { maxWidth: width - slotReserve * 2 }\n\n return StyleSheet.create({\n container: {\n alignItems: Platform.select({ android: 'flex-start', default: 'center' }),\n flex: 1,\n ...headerContainerStyle,\n },\n titleWrapper: {\n alignItems: 'center',\n columnGap: 4,\n flexDirection: 'row',\n flexShrink: 1,\n },\n titleTextContainer: {\n flexShrink: 1,\n minWidth: 0,\n },\n badge: {\n alignSelf: Platform.select({ android: 'flex-start', default: 'center' }),\n marginTop: 2,\n },\n })\n}\n\nconst useStyles = () => {\n const navigationTheme = useNavigationTheme()\n const { bottom } = useSafeAreaInsets()\n\n return StyleSheet.create({\n container: {\n flex: 1,\n justifyContent: 'center',\n backgroundColor: navigationTheme.colors.card,\n paddingBottom: bottom,\n },\n emptyState: {\n flex: 1,\n justifyContent: 'center',\n },\n listContainer: {\n paddingVertical: 12,\n },\n listHeader: {\n // Just whitespace to provide space where the typing indicator can be\n height: 16,\n },\n })\n}\n\n/**\n * useEnsureConversationsRouteExists\n */\nconst useEnsureConversationsRouteExists = () => {\n const navigation = useNavigation()\n const { params } = useRoute<RouteProp<ConversationScreenProps['route']>>()\n\n useEffect(() => {\n const navigationState = navigation.getState()\n const routes = navigationState?.routes || []\n const conversationsRoute = routes.find(r => r.name === 'Conversations')\n\n if (conversationsRoute) return\n\n navigation.dispatch(state => {\n return CommonActions.reset({\n ...state,\n routes: [\n { name: 'Conversations', params: { chat_group_graph_id: params?.chat_group_graph_id } },\n ...routes,\n ],\n index: state.index + 1,\n })\n })\n }, [navigation, params?.chat_group_graph_id])\n}\n"]}
|
|
1
|
+
{"version":3,"file":"conversation_screen.js","sourceRoot":"","sources":["../../src/screens/conversation_screen.tsx"],"names":[],"mappings":"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","sourcesContent":["import { date as formatDate } from '@planningcenter/datetime-fmt'\nimport { HeaderTitle, HeaderTitleProps, PlatformPressable } from '@react-navigation/elements'\nimport {\n CommonActions,\n RouteProp,\n StaticScreenProps,\n useNavigation,\n useTheme as useNavigationTheme,\n useRoute,\n} from '@react-navigation/native'\nimport React, { useCallback, useEffect, useRef, useState } from 'react'\nimport { FlatList, Platform, StyleSheet, useWindowDimensions, View } from 'react-native'\nimport { useSafeAreaInsets } from 'react-native-safe-area-context'\nimport { Badge, Icon, Text } from '../components'\nimport { ConversationStartBanner } from '../components/conversation/conversation_start_banner'\nimport { JumpToBottomButton } from '../components/conversation/jump_to_bottom_button'\nimport { Message } from '../components/conversation/message'\nimport { MessageForm } from '../components/conversation/message_form'\nimport {\n ConversationDisabledBanner,\n LeaderMessagesDisabledBanner,\n MemberMessagesDisabledBanner,\n} from '../components/conversation/messages_disabled_banners'\nimport { ReplyShadowMessage } from '../components/conversation/reply_shadow_message'\nimport { SystemMessage } from '../components/conversation/system_message'\nimport { TypingIndicator } from '../components/conversation/typing_indicator'\nimport { KeyboardView } from '../components/display/keyboard_view'\nimport { useHeaderSlotReserve } from '../components/display/platform_modal_header_buttons'\nimport BlankState from '../components/primitive/blank_state_primitive'\nimport { ConversationContextProvider } from '../contexts/conversation_context'\nimport { useTheme } from '../hooks'\nimport { useMessageAvailableMembersAction } from '../hooks/direct_messages/use_message_available_members_action'\nimport { useConversation } from '../hooks/use_conversation'\nimport { useConversationJoltEvents } from '../hooks/use_conversation_jolt_events'\nimport { useConversationMessages } from '../hooks/use_conversation_messages'\nimport { useConversationMessagesJoltEvents } from '../hooks/use_conversation_messages_jolt_events'\nimport { useMarkLatestMessageRead } from '../hooks/use_mark_latest_message_read'\nimport {\n analyticsEvents,\n normalizeAnalyticsMetadata,\n usePublishProductAnalyticsEvent,\n} from '../hooks/use_product_analytics'\nimport { useRestAwayFromDismissEdge } from '../hooks/use_rest_away_from_dismiss_edge'\nimport { useVideoModerationJoltEvents } from '../hooks/use_video_moderation_jolt_events'\nimport { ConversationResource } from '../types/resources/conversation'\nimport { ConversationBadgeResource } from '../types/resources/conversation_badge'\nimport { MessageResource } from '../types/resources/message'\nimport { getRelativeDateStatus } from '../utils/date'\nimport { groupMessages, type DateSeparator } from '../utils/group_messages'\nimport { CONVERSATION_MESSAGE_LIST_PADDING_HORIZONTAL } from '../utils/styles'\nimport { isSystemMessage } from '../utils/system_messages'\n\nexport type ConversationRouteProps = {\n conversation_id: number\n reply_root_id?: string | null\n reply_root_author_name?: string\n chat_group_graph_id?: string\n clear_input?: boolean\n editing_message_id?: number | null\n title?: string\n subtitle?: string\n badge?: ConversationBadgeResource\n deleted?: boolean\n muted?: boolean\n}\n\nexport type ConversationScreenProps = StaticScreenProps<ConversationRouteProps>\n\nexport function ConversationScreen({ route }: ConversationScreenProps) {\n const { conversation_id, reply_root_id } = route.params\n\n const { data: conversation } = useConversation({ conversation_id })\n\n usePublishProductAnalyticsEvent(analyticsEvents.conversation_show_opened, {\n reply_root_id,\n ...normalizeAnalyticsMetadata(conversation),\n })\n\n return (\n <ConversationContextProvider\n conversationId={conversation_id}\n currentPageReplyRootId={reply_root_id ?? null}\n >\n <ConversationScreenContent route={route} />\n </ConversationContextProvider>\n )\n}\n\nfunction ConversationScreenContent({ route }: ConversationScreenProps) {\n const styles = useStyles()\n const navigation = useNavigation()\n const { conversation_id, editing_message_id, reply_root_id, reply_root_author_name } =\n route.params\n const { data: conversation } = useConversation(route.params)\n const { messages, fetchNextPage, hasNextPage } = useConversationMessages({\n conversation_id,\n reply_root_id,\n })\n useConversationJoltEvents({ conversationId: conversation_id })\n useConversationMessagesJoltEvents({ conversationId: conversation_id })\n const { markHandledByMessageForm } = useVideoModerationJoltEvents()\n useEnsureConversationsRouteExists()\n useMarkLatestMessageRead({ conversation, messages })\n const messagesWithSeparators = groupMessages({\n ms: messages,\n inReplyScreen: !!reply_root_id,\n })\n const noMessages = messagesWithSeparators.length === 0\n\n const { repliesDisabled, memberAbility, badges, title } = conversation\n const canReply = memberAbility?.canReply\n const showLeaderDisabledReplyBanner = canReply && repliesDisabled\n const canDeleteNonAuthoredMessages = memberAbility?.canDeleteNonAuthoredMessages ?? false\n const currentlyEditingMessage = messages.find(m => String(m.id) === String(editing_message_id))\n const replyRootAuthorFirstName = reply_root_author_name?.split(' ')[0]\n const replyHeaderTitle = replyRootAuthorFirstName\n ? `Reply to ${replyRootAuthorFirstName}`\n : 'Reply'\n // Prefer the membership for optimistic updates.\n const muted = conversation.conversationMembership?.muted ?? conversation.muted\n\n const listRef = useRef<FlatList>(null)\n const [showJumpToBottomButton, setShowJumpToBottomButton] = useState(false)\n\n const trackScroll = (event: any) => {\n const offsetY = event.nativeEvent.contentOffset.y\n setShowJumpToBottomButton(offsetY > 200)\n }\n\n const { restOffset, onContentSizeChange, onMomentumScrollEnd, onScrollEndDrag } =\n useRestAwayFromDismissEdge({\n listRef,\n enabled: Platform.OS === 'ios',\n })\n\n const handleReturnToBottom = useCallback(() => {\n listRef.current?.scrollToOffset({\n offset: restOffset,\n })\n }, [restOffset])\n\n useEffect(() => {\n if (reply_root_id) {\n navigation.setParams({\n title: replyHeaderTitle,\n })\n } else {\n navigation.setParams({\n title: title,\n badge: badges?.[0],\n deleted: conversation?.deleted,\n muted,\n })\n }\n }, [navigation, title, badges, conversation?.deleted, reply_root_id, replyHeaderTitle, muted])\n\n if (!conversation || conversation.deleted) {\n return (\n <View style={styles.container}>\n <BlankState.Root>\n <BlankState.Imagery name=\"general.outlinedTextMessage\" />\n <BlankState.Content>\n <BlankState.Heading>This conversation has been deleted</BlankState.Heading>\n </BlankState.Content>\n <BlankState.Button\n onPress={navigation.goBack}\n title=\"Back to conversations\"\n accessibilityHint=\"Navigates back to the conversations list\"\n accessibilityRole=\"link\"\n />\n </BlankState.Root>\n </View>\n )\n }\n\n return (\n <View style={styles.container}>\n <KeyboardView>\n {noMessages ? (\n <View style={styles.emptyState}>\n <ConversationStartBanner conversation={conversation} hasMessages={false} />\n </View>\n ) : (\n <FlatList\n inverted\n ref={listRef}\n contentContainerStyle={styles.listContainer}\n data={messagesWithSeparators}\n keyExtractor={item => item.id}\n onScroll={trackScroll}\n onContentSizeChange={onContentSizeChange}\n onScrollEndDrag={onScrollEndDrag}\n onMomentumScrollEnd={onMomentumScrollEnd}\n scrollEventThrottle={10}\n ListFooterComponent={\n hasNextPage ? null : (\n <ConversationStartBanner conversation={conversation} hasMessages={true} />\n )\n }\n renderItem={({ item }) => {\n if (item.type === 'DateSeparator') {\n return <InlineDateSeparator {...item} />\n }\n\n if (item.type === 'ReplyShadowMessage') {\n return (\n <ReplyShadowMessage\n {...(item as any)}\n conversation_id={conversation_id}\n inReplyScreen={!!reply_root_id}\n />\n )\n }\n\n if (isSystemMessage(item)) {\n return <SystemMessage message={item} conversationId={conversation_id} />\n }\n\n return (\n <Message\n {...item}\n canDeleteNonAuthoredMessages={canDeleteNonAuthoredMessages}\n conversation_id={conversation_id}\n latestReadMessageSortKey={conversation?.latestReadMessageSortKey}\n inReplyScreen={!!reply_root_id}\n />\n )\n }}\n onEndReached={() => fetchNextPage()}\n ListHeaderComponent={<View style={styles.listHeader} />}\n />\n )}\n <JumpToBottomButton onPress={handleReturnToBottom} visible={showJumpToBottomButton} />\n {!noMessages && <TypingIndicator />}\n {showLeaderDisabledReplyBanner && <LeaderMessagesDisabledBanner />}\n <ConversationBottomBar\n conversation={conversation}\n canReply={canReply}\n replyRootAuthorFirstName={replyRootAuthorFirstName}\n replyRootId={reply_root_id}\n currentlyEditingMessage={currentlyEditingMessage}\n onFlaggedInMessageForm={markHandledByMessageForm}\n />\n </KeyboardView>\n </View>\n )\n}\n\ninterface ConversationBottomBarProps {\n conversation: ConversationResource\n canReply: boolean | undefined\n replyRootAuthorFirstName: string | undefined\n replyRootId: string | null | undefined\n currentlyEditingMessage: MessageResource | undefined\n onFlaggedInMessageForm?: (id: string) => void\n}\n\nfunction ConversationBottomBar({\n conversation,\n canReply,\n replyRootAuthorFirstName,\n replyRootId,\n currentlyEditingMessage,\n onFlaggedInMessageForm,\n}: ConversationBottomBarProps) {\n const messageLink = useMessageAvailableMembersAction(conversation)\n\n if (conversation.disabled)\n return (\n <ConversationDisabledBanner\n message={conversation.disabledMessage ?? null}\n messageLink={messageLink}\n />\n )\n if (!canReply) return <MemberMessagesDisabledBanner />\n return (\n <MessageForm.Root\n replyRootAuthorFirstName={replyRootAuthorFirstName}\n conversation={conversation}\n replyRootId={replyRootId}\n currentlyEditingMessage={currentlyEditingMessage}\n onFlaggedInMessageForm={onFlaggedInMessageForm}\n key={\n currentlyEditingMessage\n ? `edit-message-form-${currentlyEditingMessage.id}`\n : 'new-message-form'\n }\n >\n <MessageForm.AttachmentPicker />\n <MessageForm.Commands />\n <MessageForm.TextInput />\n <MessageForm.SubmitButton />\n </MessageForm.Root>\n )\n}\n\nfunction InlineDateSeparator({ date }: DateSeparator) {\n const styles = useDateSeparatorStyles()\n const { isThisYear } = getRelativeDateStatus(date)\n const showYear = !isThisYear\n const dateStamp = formatDate(date, { style: 'long', year: showYear })\n\n return (\n <View style={styles.container}>\n <View style={styles.separator} />\n <Text variant=\"footnote\" style={styles.dateText}>\n {dateStamp}\n </Text>\n <View style={styles.separator} />\n </View>\n )\n}\n\nconst useDateSeparatorStyles = () => {\n const theme = useTheme()\n return StyleSheet.create({\n container: {\n alignItems: 'center',\n flexDirection: 'row',\n paddingHorizontal: CONVERSATION_MESSAGE_LIST_PADDING_HORIZONTAL,\n paddingVertical: 16,\n },\n separator: {\n flex: 1,\n height: 1,\n borderTopWidth: 1,\n borderTopColor: theme.colors.borderColorDefaultBase,\n },\n dateText: {\n paddingHorizontal: 8,\n },\n })\n}\n\ninterface ConversationScreenTitleProps extends HeaderTitleProps {\n conversation_id: number\n badge?: ConversationBadgeResource\n deleted?: boolean\n muted?: boolean\n}\n\nexport const ConversationScreenTitle = ({\n conversation_id,\n badge,\n children,\n style,\n deleted,\n muted,\n}: ConversationScreenTitleProps) => {\n const styles = usePressableHeaderStyle()\n const navigation = useNavigation()\n const resourceType = badge?.pcoResourceType || ''\n const productName = badge?.appName\n const name = badge?.text || undefined\n\n return (\n <PlatformPressable\n accessibilityHint=\"Opens details about members and settings\"\n style={styles.container}\n onPress={() => {\n if (deleted) return\n\n navigation.navigate('ConversationDetails', { conversation_id })\n }}\n >\n <View style={styles.titleWrapper}>\n <View style={styles.titleTextContainer}>\n <HeaderTitle maxFontSizeMultiplier={1} style={style}>\n {children}\n </HeaderTitle>\n </View>\n {muted && <Icon name=\"general.bellMuted\" size={12} />}\n {!deleted && <Icon name=\"general.downChevron\" size={12} />}\n </View>\n <Badge\n variant=\"metaSubtle\"\n productLogoName={productName}\n label={resourceType}\n metaLabel={name}\n style={styles.badge}\n maxFontSizeMultiplier={1}\n />\n </PlatformPressable>\n )\n}\n\nconst usePressableHeaderStyle = () => {\n const { width } = useWindowDimensions()\n const slotReserve = useHeaderSlotReserve()\n const headerContainerStyle = { maxWidth: width - slotReserve * 2 }\n\n return StyleSheet.create({\n container: {\n alignItems: Platform.select({ android: 'flex-start', default: 'center' }),\n flex: 1,\n ...headerContainerStyle,\n },\n titleWrapper: {\n alignItems: 'center',\n columnGap: 4,\n flexDirection: 'row',\n flexShrink: 1,\n },\n titleTextContainer: {\n flexShrink: 1,\n minWidth: 0,\n },\n badge: {\n alignSelf: Platform.select({ android: 'flex-start', default: 'center' }),\n marginTop: 2,\n },\n })\n}\n\nconst useStyles = () => {\n const navigationTheme = useNavigationTheme()\n const { bottom } = useSafeAreaInsets()\n\n return StyleSheet.create({\n container: {\n flex: 1,\n justifyContent: 'center',\n backgroundColor: navigationTheme.colors.card,\n paddingBottom: bottom,\n },\n emptyState: {\n flex: 1,\n justifyContent: 'center',\n },\n listContainer: {\n paddingVertical: 12,\n },\n listHeader: {\n // Just whitespace to provide space where the typing indicator can be\n height: 16,\n },\n })\n}\n\n/**\n * useEnsureConversationsRouteExists\n */\nconst useEnsureConversationsRouteExists = () => {\n const navigation = useNavigation()\n const { params } = useRoute<RouteProp<ConversationScreenProps['route']>>()\n\n useEffect(() => {\n const navigationState = navigation.getState()\n const routes = navigationState?.routes || []\n const conversationsRoute = routes.find(r => r.name === 'Conversations')\n\n if (conversationsRoute) return\n\n navigation.dispatch(state => {\n return CommonActions.reset({\n ...state,\n routes: [\n { name: 'Conversations', params: { chat_group_graph_id: params?.chat_group_graph_id } },\n ...routes,\n ],\n index: state.index + 1,\n })\n })\n }, [navigation, params?.chat_group_graph_id])\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@planningcenter/chat-react-native",
|
|
3
|
-
"version": "3.44.1-qa-staging.
|
|
3
|
+
"version": "3.44.1-qa-staging.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"react-native": "./src/index.tsx",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@fortawesome/fontawesome-svg-core": "^7.2.0",
|
|
28
28
|
"@fortawesome/react-native-fontawesome": "^0.3.2",
|
|
29
|
-
"@planningcenter/emoji-keyboard": "3.44.1-qa-staging.
|
|
29
|
+
"@planningcenter/emoji-keyboard": "3.44.1-qa-staging.3",
|
|
30
30
|
"lodash-inflection": "^1.5.0",
|
|
31
31
|
"react-compiler-runtime": "^1.0.0"
|
|
32
32
|
},
|
|
@@ -73,5 +73,5 @@
|
|
|
73
73
|
"sf-symbols-typescript": "^2.2.0",
|
|
74
74
|
"typescript": "~6.0.3"
|
|
75
75
|
},
|
|
76
|
-
"gitHead": "
|
|
76
|
+
"gitHead": "e50958a24e18ab98d2378166273d21a10ed66f6c"
|
|
77
77
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { noop } from 'lodash'
|
|
2
|
+
import { RefObject, useCallback, useRef } from 'react'
|
|
3
|
+
import { FlatList, NativeScrollEvent, NativeSyntheticEvent, Platform } from 'react-native'
|
|
4
|
+
|
|
5
|
+
const REST_OFFSET = 1
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* iOS starts a sheet's interactive dismissal once the content scroll view sits at its
|
|
9
|
+
* top content edge — for an inverted list, wherever the newest message rests. Resting
|
|
10
|
+
* the list 1pt in from that edge keeps a drag at the bottom of a conversation scrolling
|
|
11
|
+
* history instead of swiping the modal away.
|
|
12
|
+
*
|
|
13
|
+
* Returns the offset the list rests at plus the scroll listeners that nudge it back
|
|
14
|
+
* there: once on first layout, then after each gesture settles. On Android there is no
|
|
15
|
+
* sheet gesture to avoid, so the offset is 0 and the listeners are undefined.
|
|
16
|
+
*/
|
|
17
|
+
export const useRestAwayFromDismissEdge = ({
|
|
18
|
+
listRef,
|
|
19
|
+
enabled = Platform.OS === 'ios',
|
|
20
|
+
}: {
|
|
21
|
+
listRef: RefObject<FlatList | null>
|
|
22
|
+
enabled?: boolean
|
|
23
|
+
}) => {
|
|
24
|
+
const restAwayFromDismissEdge = useCallback(() => {
|
|
25
|
+
listRef.current?.scrollToOffset({ animated: false, offset: REST_OFFSET })
|
|
26
|
+
}, [listRef])
|
|
27
|
+
|
|
28
|
+
const didSetInitialOffset = useRef(false)
|
|
29
|
+
const handleInitialOffset = useCallback(() => {
|
|
30
|
+
if (!didSetInitialOffset.current) {
|
|
31
|
+
didSetInitialOffset.current = true
|
|
32
|
+
restAwayFromDismissEdge()
|
|
33
|
+
}
|
|
34
|
+
}, [restAwayFromDismissEdge])
|
|
35
|
+
|
|
36
|
+
// A negative offset means the bounce is still animating; let it settle first.
|
|
37
|
+
const handleScrollSettled = useCallback(
|
|
38
|
+
(event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
39
|
+
const offsetY = event.nativeEvent.contentOffset.y
|
|
40
|
+
|
|
41
|
+
if (offsetY >= 0 && offsetY < REST_OFFSET) {
|
|
42
|
+
restAwayFromDismissEdge()
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
[restAwayFromDismissEdge]
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
if (!enabled) {
|
|
49
|
+
return {
|
|
50
|
+
restOffset: 0,
|
|
51
|
+
onContentSizeChange: noop,
|
|
52
|
+
onScrollEndDrag: noop,
|
|
53
|
+
onMomentumScrollEnd: noop,
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
restOffset: REST_OFFSET,
|
|
59
|
+
onContentSizeChange: handleInitialOffset,
|
|
60
|
+
onScrollEndDrag: handleScrollSettled,
|
|
61
|
+
onMomentumScrollEnd: handleScrollSettled,
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
normalizeAnalyticsMetadata,
|
|
41
41
|
usePublishProductAnalyticsEvent,
|
|
42
42
|
} from '../hooks/use_product_analytics'
|
|
43
|
+
import { useRestAwayFromDismissEdge } from '../hooks/use_rest_away_from_dismiss_edge'
|
|
43
44
|
import { useVideoModerationJoltEvents } from '../hooks/use_video_moderation_jolt_events'
|
|
44
45
|
import { ConversationResource } from '../types/resources/conversation'
|
|
45
46
|
import { ConversationBadgeResource } from '../types/resources/conversation_badge'
|
|
@@ -126,11 +127,17 @@ function ConversationScreenContent({ route }: ConversationScreenProps) {
|
|
|
126
127
|
setShowJumpToBottomButton(offsetY > 200)
|
|
127
128
|
}
|
|
128
129
|
|
|
130
|
+
const { restOffset, onContentSizeChange, onMomentumScrollEnd, onScrollEndDrag } =
|
|
131
|
+
useRestAwayFromDismissEdge({
|
|
132
|
+
listRef,
|
|
133
|
+
enabled: Platform.OS === 'ios',
|
|
134
|
+
})
|
|
135
|
+
|
|
129
136
|
const handleReturnToBottom = useCallback(() => {
|
|
130
137
|
listRef.current?.scrollToOffset({
|
|
131
|
-
offset:
|
|
138
|
+
offset: restOffset,
|
|
132
139
|
})
|
|
133
|
-
}, [])
|
|
140
|
+
}, [restOffset])
|
|
134
141
|
|
|
135
142
|
useEffect(() => {
|
|
136
143
|
if (reply_root_id) {
|
|
@@ -181,6 +188,9 @@ function ConversationScreenContent({ route }: ConversationScreenProps) {
|
|
|
181
188
|
data={messagesWithSeparators}
|
|
182
189
|
keyExtractor={item => item.id}
|
|
183
190
|
onScroll={trackScroll}
|
|
191
|
+
onContentSizeChange={onContentSizeChange}
|
|
192
|
+
onScrollEndDrag={onScrollEndDrag}
|
|
193
|
+
onMomentumScrollEnd={onMomentumScrollEnd}
|
|
184
194
|
scrollEventThrottle={10}
|
|
185
195
|
ListFooterComponent={
|
|
186
196
|
hasNextPage ? null : (
|