@planningcenter/chat-react-native 3.38.0-rc.1 → 3.38.0-rc.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/components/conversation/jump_to_bottom_button.d.ts +1 -2
- package/build/components/conversation/jump_to_bottom_button.d.ts.map +1 -1
- package/build/components/conversation/jump_to_bottom_button.js +7 -39
- package/build/components/conversation/jump_to_bottom_button.js.map +1 -1
- package/build/components/conversation/message_list.d.ts +10 -0
- package/build/components/conversation/message_list.d.ts.map +1 -0
- package/build/components/conversation/message_list.js +13 -0
- package/build/components/conversation/message_list.js.map +1 -0
- package/build/components/conversation/reply_shadow_message.d.ts +2 -1
- package/build/components/conversation/reply_shadow_message.d.ts.map +1 -1
- package/build/components/conversation/reply_shadow_message.js.map +1 -1
- package/build/components/display/conversation_avatar.d.ts +2 -1
- package/build/components/display/conversation_avatar.d.ts.map +1 -1
- package/build/components/display/conversation_avatar.js +6 -5
- package/build/components/display/conversation_avatar.js.map +1 -1
- package/build/components/display/emoji_avatar.d.ts +3 -1
- package/build/components/display/emoji_avatar.d.ts.map +1 -1
- package/build/components/display/emoji_avatar.js +2 -2
- package/build/components/display/emoji_avatar.js.map +1 -1
- package/build/components/display/icon_avatar.d.ts +3 -1
- package/build/components/display/icon_avatar.d.ts.map +1 -1
- package/build/components/display/icon_avatar.js +2 -2
- package/build/components/display/icon_avatar.js.map +1 -1
- package/build/contexts/conversation_context.d.ts +1 -8
- package/build/contexts/conversation_context.d.ts.map +1 -1
- package/build/contexts/conversation_context.js +3 -21
- package/build/contexts/conversation_context.js.map +1 -1
- package/build/hooks/groups/use_group_chat_conversation_payload.d.ts.map +1 -1
- package/build/hooks/groups/use_group_chat_conversation_payload.js +1 -0
- package/build/hooks/groups/use_group_chat_conversation_payload.js.map +1 -1
- package/build/hooks/index.d.ts +1 -0
- package/build/hooks/index.d.ts.map +1 -1
- package/build/hooks/index.js +1 -0
- package/build/hooks/index.js.map +1 -1
- package/build/hooks/use_conversation_messages.d.ts +6 -15
- package/build/hooks/use_conversation_messages.d.ts.map +1 -1
- package/build/hooks/use_conversation_messages.js +9 -62
- package/build/hooks/use_conversation_messages.js.map +1 -1
- package/build/hooks/use_conversation_messages_jolt_events.d.ts.map +1 -1
- package/build/hooks/use_conversation_messages_jolt_events.js +4 -4
- package/build/hooks/use_conversation_messages_jolt_events.js.map +1 -1
- package/build/hooks/use_conversations_actions.d.ts +0 -5
- package/build/hooks/use_conversations_actions.d.ts.map +1 -1
- package/build/hooks/use_conversations_actions.js +0 -12
- package/build/hooks/use_conversations_actions.js.map +1 -1
- package/build/hooks/use_features.d.ts +0 -1
- package/build/hooks/use_features.d.ts.map +1 -1
- package/build/hooks/use_features.js +0 -1
- package/build/hooks/use_features.js.map +1 -1
- package/build/hooks/use_mark_latest_message_read.d.ts +1 -1
- package/build/hooks/use_mark_latest_message_read.d.ts.map +1 -1
- package/build/hooks/use_mark_latest_message_read.js +1 -17
- package/build/hooks/use_mark_latest_message_read.js.map +1 -1
- package/build/hooks/use_preview_avatar_diameter.d.ts +2 -0
- package/build/hooks/use_preview_avatar_diameter.d.ts.map +1 -0
- package/build/hooks/use_preview_avatar_diameter.js +11 -0
- package/build/hooks/use_preview_avatar_diameter.js.map +1 -0
- package/build/hooks/use_suspense_api.d.ts +0 -1
- package/build/hooks/use_suspense_api.d.ts.map +1 -1
- package/build/hooks/use_suspense_api.js +1 -1
- package/build/hooks/use_suspense_api.js.map +1 -1
- package/build/jest.js +1 -1
- package/build/jest.js.map +1 -1
- package/build/screens/avatar_picker/avatar_picker_screen.d.ts.map +1 -1
- package/build/screens/avatar_picker/avatar_picker_screen.js +11 -9
- package/build/screens/avatar_picker/avatar_picker_screen.js.map +1 -1
- package/build/screens/avatar_picker/avatar_preview.d.ts.map +1 -1
- package/build/screens/avatar_picker/avatar_preview.js +13 -5
- package/build/screens/avatar_picker/avatar_preview.js.map +1 -1
- package/build/screens/avatar_picker/emoji_tab.d.ts.map +1 -1
- package/build/screens/avatar_picker/emoji_tab.js +3 -7
- package/build/screens/avatar_picker/emoji_tab.js.map +1 -1
- package/build/screens/avatar_picker/upload_tab.d.ts.map +1 -1
- package/build/screens/avatar_picker/upload_tab.js +2 -1
- package/build/screens/avatar_picker/upload_tab.js.map +1 -1
- package/build/screens/conversation_details_screen.d.ts.map +1 -1
- package/build/screens/conversation_details_screen.js +5 -2
- package/build/screens/conversation_details_screen.js.map +1 -1
- package/build/screens/conversation_filter_recipients/components/header_row.d.ts.map +1 -1
- package/build/screens/conversation_filter_recipients/components/header_row.js +3 -2
- package/build/screens/conversation_filter_recipients/components/header_row.js.map +1 -1
- package/build/screens/conversation_filter_recipients/hooks/use_flattened_array_of_service_types_with_teams.d.ts.map +1 -1
- package/build/screens/conversation_filter_recipients/hooks/use_flattened_array_of_service_types_with_teams.js +47 -18
- package/build/screens/conversation_filter_recipients/hooks/use_flattened_array_of_service_types_with_teams.js.map +1 -1
- package/build/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.d.ts +2 -1
- package/build/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.d.ts.map +1 -1
- package/build/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.js +23 -26
- package/build/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.js.map +1 -1
- package/build/screens/conversation_filter_recipients/types.d.ts +1 -1
- package/build/screens/conversation_filter_recipients/types.d.ts.map +1 -1
- package/build/screens/conversation_filter_recipients/types.js.map +1 -1
- package/build/screens/conversation_screen.d.ts +0 -1
- package/build/screens/conversation_screen.d.ts.map +1 -1
- package/build/screens/conversation_screen.js +45 -96
- package/build/screens/conversation_screen.js.map +1 -1
- package/build/screens/conversation_select_recipients/components/recipient_link_row.d.ts +1 -1
- package/build/screens/conversation_select_recipients/components/recipient_link_row.d.ts.map +1 -1
- package/build/screens/conversation_select_recipients/components/recipient_link_row.js +3 -3
- package/build/screens/conversation_select_recipients/components/recipient_link_row.js.map +1 -1
- package/build/screens/conversation_select_recipients/components/team_recipient_row.d.ts.map +1 -1
- package/build/screens/conversation_select_recipients/components/team_recipient_row.js +1 -1
- package/build/screens/conversation_select_recipients/components/team_recipient_row.js.map +1 -1
- package/build/screens/team_conversation_screen.d.ts.map +1 -1
- package/build/screens/team_conversation_screen.js +24 -1
- package/build/screens/team_conversation_screen.js.map +1 -1
- package/build/utils/cache/messages_cache.d.ts +0 -1
- package/build/utils/cache/messages_cache.d.ts.map +1 -1
- package/build/utils/cache/messages_cache.js +0 -4
- package/build/utils/cache/messages_cache.js.map +1 -1
- package/build/utils/client/client.d.ts +1 -1
- package/build/utils/client/client.d.ts.map +1 -1
- package/build/utils/client/client.js +7 -6
- package/build/utils/client/client.js.map +1 -1
- package/build/utils/client/instrumented_fetch.js +3 -5
- package/build/utils/client/instrumented_fetch.js.map +1 -1
- package/build/utils/group_messages.d.ts +2 -9
- package/build/utils/group_messages.d.ts.map +1 -1
- package/build/utils/group_messages.js +1 -20
- package/build/utils/group_messages.js.map +1 -1
- package/package.json +4 -4
- package/src/__tests__/hooks/use_group_chat_conversation_payload.test.tsx +50 -0
- package/src/__tests__/jest.ts +1 -1
- package/src/__tests__/utils/client.ts +32 -0
- package/src/components/conversation/__tests__/message_list.test.tsx +14 -0
- package/src/components/conversation/jump_to_bottom_button.tsx +8 -57
- package/src/components/conversation/message_list.tsx +42 -0
- package/src/components/conversation/reply_shadow_message.tsx +1 -1
- package/src/components/display/conversation_avatar.tsx +7 -5
- package/src/components/display/emoji_avatar.tsx +10 -2
- package/src/components/display/icon_avatar.tsx +10 -2
- package/src/contexts/conversation_context.tsx +2 -30
- package/src/hooks/groups/use_group_chat_conversation_payload.ts +1 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use_conversation_messages.ts +20 -120
- package/src/hooks/use_conversation_messages_jolt_events.ts +3 -4
- package/src/hooks/use_conversations_actions.ts +0 -15
- package/src/hooks/use_features.ts +0 -1
- package/src/hooks/use_mark_latest_message_read.ts +2 -16
- package/src/hooks/use_preview_avatar_diameter.ts +12 -0
- package/src/hooks/use_suspense_api.ts +1 -1
- package/src/jest.ts +1 -1
- package/src/screens/avatar_picker/avatar_picker_screen.tsx +25 -9
- package/src/screens/avatar_picker/avatar_preview.tsx +14 -5
- package/src/screens/avatar_picker/emoji_tab.tsx +3 -6
- package/src/screens/avatar_picker/upload_tab.tsx +2 -0
- package/src/screens/conversation_details_screen.tsx +10 -1
- package/src/screens/conversation_filter_recipients/components/header_row.tsx +3 -2
- package/src/screens/conversation_filter_recipients/hooks/__tests__/use_service_types_with_teams.test.ts +108 -0
- package/src/screens/conversation_filter_recipients/hooks/use_flattened_array_of_service_types_with_teams.tsx +46 -19
- package/src/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.ts +31 -29
- package/src/screens/conversation_filter_recipients/types.tsx +1 -1
- package/src/screens/conversation_screen.tsx +69 -186
- package/src/screens/conversation_select_recipients/components/recipient_link_row.tsx +6 -4
- package/src/screens/conversation_select_recipients/components/team_recipient_row.tsx +2 -1
- package/src/screens/team_conversation_screen.tsx +33 -1
- package/src/utils/__tests__/group_messages.test.ts +0 -71
- package/src/utils/cache/messages_cache.ts +0 -5
- package/src/utils/client/__tests__/instrumented_fetch.test.ts +9 -5
- package/src/utils/client/client.ts +9 -7
- package/src/utils/client/instrumented_fetch.ts +3 -6
- package/src/utils/group_messages.ts +2 -42
- package/build/components/conversation/unread_divider.d.ts +0 -6
- package/build/components/conversation/unread_divider.d.ts.map +0 -1
- package/build/components/conversation/unread_divider.js +0 -59
- package/build/components/conversation/unread_divider.js.map +0 -1
- package/build/hooks/use_flat_list_viewability.d.ts +0 -20
- package/build/hooks/use_flat_list_viewability.d.ts.map +0 -1
- package/build/hooks/use_flat_list_viewability.js +0 -30
- package/build/hooks/use_flat_list_viewability.js.map +0 -1
- package/build/hooks/use_jump_to_bottom_action.d.ts +0 -9
- package/build/hooks/use_jump_to_bottom_action.d.ts.map +0 -1
- package/build/hooks/use_jump_to_bottom_action.js +0 -62
- package/build/hooks/use_jump_to_bottom_action.js.map +0 -1
- package/build/hooks/use_jump_to_unread_anchor.d.ts +0 -20
- package/build/hooks/use_jump_to_unread_anchor.d.ts.map +0 -1
- package/build/hooks/use_jump_to_unread_anchor.js +0 -53
- package/build/hooks/use_jump_to_unread_anchor.js.map +0 -1
- package/build/hooks/use_jump_to_unread_gates.d.ts +0 -5
- package/build/hooks/use_jump_to_unread_gates.d.ts.map +0 -1
- package/build/hooks/use_jump_to_unread_gates.js +0 -10
- package/build/hooks/use_jump_to_unread_gates.js.map +0 -1
- package/build/hooks/use_scroll_tracking.d.ts +0 -13
- package/build/hooks/use_scroll_tracking.d.ts.map +0 -1
- package/build/hooks/use_scroll_tracking.js +0 -45
- package/build/hooks/use_scroll_tracking.js.map +0 -1
- package/build/hooks/use_track_highest_seen_message.d.ts +0 -4
- package/build/hooks/use_track_highest_seen_message.d.ts.map +0 -1
- package/build/hooks/use_track_highest_seen_message.js +0 -35
- package/build/hooks/use_track_highest_seen_message.js.map +0 -1
- package/build/utils/conversation_messages.d.ts +0 -10
- package/build/utils/conversation_messages.d.ts.map +0 -1
- package/build/utils/conversation_messages.js +0 -22
- package/build/utils/conversation_messages.js.map +0 -1
- package/build/utils/highest_seen_tracker.d.ts +0 -12
- package/build/utils/highest_seen_tracker.d.ts.map +0 -1
- package/build/utils/highest_seen_tracker.js +0 -37
- package/build/utils/highest_seen_tracker.js.map +0 -1
- package/build/utils/message_viewability.d.ts +0 -24
- package/build/utils/message_viewability.d.ts.map +0 -1
- package/build/utils/message_viewability.js +0 -29
- package/build/utils/message_viewability.js.map +0 -1
- package/build/utils/unread_divider_helpers.d.ts +0 -18
- package/build/utils/unread_divider_helpers.d.ts.map +0 -1
- package/build/utils/unread_divider_helpers.js +0 -13
- package/build/utils/unread_divider_helpers.js.map +0 -1
- package/src/__tests__/hooks/use_conversation_messages.test.tsx +0 -109
- package/src/__tests__/hooks/use_mark_latest_message_read.test.tsx +0 -154
- package/src/__tests__/utils/cache/messages_cache.test.ts +0 -54
- package/src/components/conversation/unread_divider.tsx +0 -90
- package/src/hooks/use_flat_list_viewability.ts +0 -50
- package/src/hooks/use_jump_to_bottom_action.ts +0 -75
- package/src/hooks/use_jump_to_unread_anchor.ts +0 -68
- package/src/hooks/use_jump_to_unread_gates.ts +0 -10
- package/src/hooks/use_scroll_tracking.ts +0 -64
- package/src/hooks/use_track_highest_seen_message.ts +0 -43
- package/src/utils/__tests__/conversation_messages.test.ts +0 -105
- package/src/utils/__tests__/highest_seen_tracker.test.ts +0 -82
- package/src/utils/__tests__/message_viewability.test.ts +0 -168
- package/src/utils/__tests__/unread_divider_helpers.test.ts +0 -85
- package/src/utils/conversation_messages.ts +0 -37
- package/src/utils/highest_seen_tracker.ts +0 -42
- package/src/utils/message_viewability.ts +0 -49
- package/src/utils/unread_divider_helpers.ts +0 -25
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import dayjs from './dayjs';
|
|
2
2
|
import { isSystemMessage } from './system_messages';
|
|
3
3
|
const FIVE_MINUTES_MS = 5 * 60 * 1000;
|
|
4
|
-
export
|
|
5
|
-
export function groupMessages({ ms, inReplyScreen, jumpToUnreadActive, initialMessageId, }) {
|
|
4
|
+
export function groupMessages({ ms, inReplyScreen }) {
|
|
6
5
|
const items = [];
|
|
7
6
|
let myLatestSeen = false;
|
|
8
7
|
let nextNeighborEnriched;
|
|
@@ -12,9 +11,6 @@ export function groupMessages({ ms, inReplyScreen, jumpToUnreadActive, initialMe
|
|
|
12
11
|
if (isSystemMessage(message)) {
|
|
13
12
|
const enriched = enrichSystemMessage(message, next);
|
|
14
13
|
items.push(enriched);
|
|
15
|
-
if (crossesUnreadBoundary(message, prev, jumpToUnreadActive, initialMessageId)) {
|
|
16
|
-
items.push(unreadDivider());
|
|
17
|
-
}
|
|
18
14
|
if (datesDifferBetween(message, prev))
|
|
19
15
|
items.push(dateSeparator(message));
|
|
20
16
|
nextNeighborEnriched = enriched;
|
|
@@ -25,9 +21,6 @@ export function groupMessages({ ms, inReplyScreen, jumpToUnreadActive, initialMe
|
|
|
25
21
|
myLatestSeen = true;
|
|
26
22
|
const enriched = enrichRegularMessage(message, prev, next, isMyLatest, !!inReplyScreen);
|
|
27
23
|
items.push(enriched);
|
|
28
|
-
if (crossesUnreadBoundary(message, prev, jumpToUnreadActive, initialMessageId)) {
|
|
29
|
-
items.push(unreadDivider());
|
|
30
|
-
}
|
|
31
24
|
const shadow = replyShadowFor(enriched, prev);
|
|
32
25
|
if (shadow)
|
|
33
26
|
items.push(shadow);
|
|
@@ -38,18 +31,6 @@ export function groupMessages({ ms, inReplyScreen, jumpToUnreadActive, initialMe
|
|
|
38
31
|
});
|
|
39
32
|
return items;
|
|
40
33
|
}
|
|
41
|
-
function crossesUnreadBoundary(message, prev, jumpToUnreadActive, initialMessageId) {
|
|
42
|
-
if (!jumpToUnreadActive)
|
|
43
|
-
return false;
|
|
44
|
-
if (!initialMessageId)
|
|
45
|
-
return false;
|
|
46
|
-
if (!prev)
|
|
47
|
-
return false;
|
|
48
|
-
return (prev.id.localeCompare(initialMessageId) <= 0 && message.id.localeCompare(initialMessageId) > 0);
|
|
49
|
-
}
|
|
50
|
-
function unreadDivider() {
|
|
51
|
-
return { type: 'UnreadDivider', id: UNREAD_DIVIDER_KEY };
|
|
52
|
-
}
|
|
53
34
|
function neighborsOf(arr, i) {
|
|
54
35
|
return { prev: arr[i + 1], next: arr[i - 1] };
|
|
55
36
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"group_messages.js","sourceRoot":"","sources":["../../src/utils/group_messages.ts"],"names":[],"mappings":"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","sourcesContent":["import type { MessageResource } from '../types/resources/message'\nimport dayjs from './dayjs'\nimport { isSystemMessage } from './system_messages'\n\nconst FIVE_MINUTES_MS = 5 * 60 * 1000\n\nexport const UNREAD_DIVIDER_KEY = 'unread-divider'\n\nexport type DateSeparator = { type: 'DateSeparator'; id: string; date: string }\n\nexport type UnreadDividerItem = { type: 'UnreadDivider'; id: typeof UNREAD_DIVIDER_KEY }\n\nexport type ReplyShadowMessage = {\n type: 'ReplyShadowMessage'\n id: string\n messageId: string\n isReplyShadowMessage: boolean\n nextRendersAuthor: boolean\n}\n\nexport type EnrichedMessage =\n | MessageResource\n | DateSeparator\n | UnreadDividerItem\n | ReplyShadowMessage\n\ninterface GroupMessagesProps {\n ms: MessageResource[]\n inReplyScreen?: boolean\n jumpToUnreadActive?: boolean\n initialMessageId?: string | null\n}\n\nexport function groupMessages({\n ms,\n inReplyScreen,\n jumpToUnreadActive,\n initialMessageId,\n}: GroupMessagesProps): EnrichedMessage[] {\n const items: EnrichedMessage[] = []\n let myLatestSeen = false\n let nextNeighborEnriched: MessageResource | undefined\n\n ms.forEach((message, i) => {\n const { prev } = neighborsOf(ms, i)\n const next = nextNeighborEnriched\n\n if (isSystemMessage(message)) {\n const enriched = enrichSystemMessage(message, next)\n items.push(enriched)\n if (crossesUnreadBoundary(message, prev, jumpToUnreadActive, initialMessageId)) {\n items.push(unreadDivider())\n }\n if (datesDifferBetween(message, prev)) items.push(dateSeparator(message))\n nextNeighborEnriched = enriched\n return\n }\n\n const isMyLatest = !myLatestSeen && message.mine\n if (isMyLatest) myLatestSeen = true\n\n const enriched = enrichRegularMessage(message, prev, next, isMyLatest, !!inReplyScreen)\n items.push(enriched)\n\n if (crossesUnreadBoundary(message, prev, jumpToUnreadActive, initialMessageId)) {\n items.push(unreadDivider())\n }\n\n const shadow = replyShadowFor(enriched, prev)\n if (shadow) items.push(shadow)\n\n if (!prev || datesDifferBetween(message, prev)) {\n items.push(dateSeparator(message))\n }\n\n nextNeighborEnriched = enriched\n })\n\n return items\n}\n\nfunction crossesUnreadBoundary(\n message: MessageResource,\n prev: MessageResource | undefined,\n jumpToUnreadActive: boolean | undefined,\n initialMessageId: string | null | undefined\n): boolean {\n if (!jumpToUnreadActive) return false\n if (!initialMessageId) return false\n if (!prev) return false\n return (\n prev.id.localeCompare(initialMessageId) <= 0 && message.id.localeCompare(initialMessageId) > 0\n )\n}\n\nfunction unreadDivider(): UnreadDividerItem {\n return { type: 'UnreadDivider', id: UNREAD_DIVIDER_KEY }\n}\n\nfunction neighborsOf<T>(arr: T[], i: number): { prev: T | undefined; next: T | undefined } {\n return { prev: arr[i + 1], next: arr[i - 1] }\n}\n\nfunction enrichSystemMessage(\n message: MessageResource,\n next: MessageResource | undefined\n): MessageResource {\n return {\n ...message,\n myLatestInConversation: false,\n lastInGroup: true,\n renderAuthor: false,\n nextRendersAuthor: next?.renderAuthor,\n isReplyShadowMessage: false,\n nextIsReplyShadowMessage: false,\n threadPosition: null,\n }\n}\n\nfunction enrichRegularMessage(\n message: MessageResource,\n prev: MessageResource | undefined,\n next: MessageResource | undefined,\n isMyLatest: boolean,\n inReplyScreen: boolean\n): MessageResource {\n const inThread = message.replyRootId !== null\n const showThreadDetails = !inReplyScreen && inThread\n\n return {\n ...message,\n myLatestInConversation: isMyLatest,\n lastInGroup: !next || startsNewGroup(next, message),\n renderAuthor: !message.mine && (!prev || startsNewGroup(message, prev)),\n nextRendersAuthor: next?.renderAuthor,\n isReplyShadowMessage: false,\n nextIsReplyShadowMessage: nextIntroducesReplyShadow(next, message),\n threadPosition: showThreadDetails ? threadPositionFor(message, next) : null,\n prevIsMyReply: showThreadDetails ? prev?.mine : undefined,\n nextIsMyReply: showThreadDetails ? next?.mine : undefined,\n }\n}\n\nfunction startsNewGroup(a: MessageResource, b: MessageResource): boolean {\n return (\n a.author?.id !== b.author?.id ||\n differsByMoreThan5Min(a, b) ||\n a.replyRootId !== b.replyRootId ||\n datesDifferBetween(a, b)\n )\n}\n\nfunction differsByMoreThan5Min(a: MessageResource, b: MessageResource): boolean {\n return Math.abs(toMillis(a.createdAt) - toMillis(b.createdAt)) > FIVE_MINUTES_MS\n}\n\nfunction toMillis(iso: string): number {\n return new Date(iso).getTime()\n}\n\nfunction datesDifferBetween(a: MessageResource, b: MessageResource | undefined): boolean {\n if (!b) return false\n return dateKey(a) !== dateKey(b)\n}\n\nfunction dateKey(message: MessageResource): string {\n return dayjs(message.createdAt).format('YYYY-MM-DD')\n}\n\nfunction dateSeparator(message: MessageResource): DateSeparator {\n return { type: 'DateSeparator', id: `day-divider-${message.id}`, date: dateKey(message) }\n}\n\nfunction threadPositionFor(\n message: MessageResource,\n next: MessageResource | undefined\n): 'first' | 'center' | 'last' | null {\n const isThreadRoot = message.replyRootId === message.id\n const isLast =\n !next || next.replyRootId !== message.replyRootId || datesDifferBetween(next, message)\n\n if (isThreadRoot && isLast) return null\n if (isThreadRoot) return 'first'\n if (isLast) return 'last'\n return 'center'\n}\n\nfunction nextIntroducesReplyShadow(\n next: MessageResource | undefined,\n current: MessageResource\n): boolean {\n if (!next) return false\n const nextInThread = next.replyRootId !== null\n const nextIsThreadRoot = next.replyRootId === next.id\n const differentThread = next.replyRootId !== current.replyRootId\n return nextInThread && !nextIsThreadRoot && (differentThread || datesDifferBetween(next, current))\n}\n\nfunction replyShadowFor(\n message: MessageResource,\n prev: MessageResource | undefined\n): ReplyShadowMessage | undefined {\n if (!message.replyRootId) return undefined\n if (message.replyRootId === message.id) return undefined\n\n const enteringNewThread =\n !prev || prev.replyRootId !== message.replyRootId || datesDifferBetween(prev, message)\n if (!enteringNewThread) return undefined\n\n return {\n type: 'ReplyShadowMessage',\n id: `${message.id}-${message.replyRootId}`,\n messageId: message.replyRootId,\n isReplyShadowMessage: true,\n nextRendersAuthor: message.renderAuthor ?? false,\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"group_messages.js","sourceRoot":"","sources":["../../src/utils/group_messages.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,SAAS,CAAA;AAC3B,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAEnD,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAA;AAmBrC,MAAM,UAAU,aAAa,CAAC,EAAE,EAAE,EAAE,aAAa,EAAsB;IACrE,MAAM,KAAK,GAAsB,EAAE,CAAA;IACnC,IAAI,YAAY,GAAG,KAAK,CAAA;IACxB,IAAI,oBAAiD,CAAA;IAErD,EAAE,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE;QACxB,MAAM,EAAE,IAAI,EAAE,GAAG,WAAW,CAAC,EAAE,EAAE,CAAC,CAAC,CAAA;QACnC,MAAM,IAAI,GAAG,oBAAoB,CAAA;QAEjC,IAAI,eAAe,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7B,MAAM,QAAQ,GAAG,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;YACnD,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;YACpB,IAAI,kBAAkB,CAAC,OAAO,EAAE,IAAI,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAA;YACzE,oBAAoB,GAAG,QAAQ,CAAA;YAC/B,OAAM;QACR,CAAC;QAED,MAAM,UAAU,GAAG,CAAC,YAAY,IAAI,OAAO,CAAC,IAAI,CAAA;QAChD,IAAI,UAAU;YAAE,YAAY,GAAG,IAAI,CAAA;QAEnC,MAAM,QAAQ,GAAG,oBAAoB,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,aAAa,CAAC,CAAA;QACvF,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QAEpB,MAAM,MAAM,GAAG,cAAc,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;QAC7C,IAAI,MAAM;YAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAE9B,IAAI,CAAC,IAAI,IAAI,kBAAkB,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;YAC/C,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAA;QACpC,CAAC;QAED,oBAAoB,GAAG,QAAQ,CAAA;IACjC,CAAC,CAAC,CAAA;IAEF,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,WAAW,CAAI,GAAQ,EAAE,CAAS;IACzC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAA;AAC/C,CAAC;AAED,SAAS,mBAAmB,CAC1B,OAAwB,EACxB,IAAiC;IAEjC,OAAO;QACL,GAAG,OAAO;QACV,sBAAsB,EAAE,KAAK;QAC7B,WAAW,EAAE,IAAI;QACjB,YAAY,EAAE,KAAK;QACnB,iBAAiB,EAAE,IAAI,EAAE,YAAY;QACrC,oBAAoB,EAAE,KAAK;QAC3B,wBAAwB,EAAE,KAAK;QAC/B,cAAc,EAAE,IAAI;KACrB,CAAA;AACH,CAAC;AAED,SAAS,oBAAoB,CAC3B,OAAwB,EACxB,IAAiC,EACjC,IAAiC,EACjC,UAAmB,EACnB,aAAsB;IAEtB,MAAM,QAAQ,GAAG,OAAO,CAAC,WAAW,KAAK,IAAI,CAAA;IAC7C,MAAM,iBAAiB,GAAG,CAAC,aAAa,IAAI,QAAQ,CAAA;IAEpD,OAAO;QACL,GAAG,OAAO;QACV,sBAAsB,EAAE,UAAU;QAClC,WAAW,EAAE,CAAC,IAAI,IAAI,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC;QACnD,YAAY,EAAE,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,IAAI,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACvE,iBAAiB,EAAE,IAAI,EAAE,YAAY;QACrC,oBAAoB,EAAE,KAAK;QAC3B,wBAAwB,EAAE,yBAAyB,CAAC,IAAI,EAAE,OAAO,CAAC;QAClE,cAAc,EAAE,iBAAiB,CAAC,CAAC,CAAC,iBAAiB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;QAC3E,aAAa,EAAE,iBAAiB,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,SAAS;QACzD,aAAa,EAAE,iBAAiB,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,SAAS;KAC1D,CAAA;AACH,CAAC;AAED,SAAS,cAAc,CAAC,CAAkB,EAAE,CAAkB;IAC5D,OAAO,CACL,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE;QAC7B,qBAAqB,CAAC,CAAC,EAAE,CAAC,CAAC;QAC3B,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,WAAW;QAC/B,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,CACzB,CAAA;AACH,CAAC;AAED,SAAS,qBAAqB,CAAC,CAAkB,EAAE,CAAkB;IACnE,OAAO,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,GAAG,eAAe,CAAA;AAClF,CAAC;AAED,SAAS,QAAQ,CAAC,GAAW;IAC3B,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAA;AAChC,CAAC;AAED,SAAS,kBAAkB,CAAC,CAAkB,EAAE,CAA8B;IAC5E,IAAI,CAAC,CAAC;QAAE,OAAO,KAAK,CAAA;IACpB,OAAO,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;AAClC,CAAC;AAED,SAAS,OAAO,CAAC,OAAwB;IACvC,OAAO,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;AACtD,CAAC;AAED,SAAS,aAAa,CAAC,OAAwB;IAC7C,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,EAAE,EAAE,eAAe,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE,CAAA;AAC3F,CAAC;AAED,SAAS,iBAAiB,CACxB,OAAwB,EACxB,IAAiC;IAEjC,MAAM,YAAY,GAAG,OAAO,CAAC,WAAW,KAAK,OAAO,CAAC,EAAE,CAAA;IACvD,MAAM,MAAM,GACV,CAAC,IAAI,IAAI,IAAI,CAAC,WAAW,KAAK,OAAO,CAAC,WAAW,IAAI,kBAAkB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAExF,IAAI,YAAY,IAAI,MAAM;QAAE,OAAO,IAAI,CAAA;IACvC,IAAI,YAAY;QAAE,OAAO,OAAO,CAAA;IAChC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAA;IACzB,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,SAAS,yBAAyB,CAChC,IAAiC,EACjC,OAAwB;IAExB,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAA;IACvB,MAAM,YAAY,GAAG,IAAI,CAAC,WAAW,KAAK,IAAI,CAAA;IAC9C,MAAM,gBAAgB,GAAG,IAAI,CAAC,WAAW,KAAK,IAAI,CAAC,EAAE,CAAA;IACrD,MAAM,eAAe,GAAG,IAAI,CAAC,WAAW,KAAK,OAAO,CAAC,WAAW,CAAA;IAChE,OAAO,YAAY,IAAI,CAAC,gBAAgB,IAAI,CAAC,eAAe,IAAI,kBAAkB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAA;AACpG,CAAC;AAED,SAAS,cAAc,CACrB,OAAwB,EACxB,IAAiC;IAEjC,IAAI,CAAC,OAAO,CAAC,WAAW;QAAE,OAAO,SAAS,CAAA;IAC1C,IAAI,OAAO,CAAC,WAAW,KAAK,OAAO,CAAC,EAAE;QAAE,OAAO,SAAS,CAAA;IAExD,MAAM,iBAAiB,GACrB,CAAC,IAAI,IAAI,IAAI,CAAC,WAAW,KAAK,OAAO,CAAC,WAAW,IAAI,kBAAkB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACxF,IAAI,CAAC,iBAAiB;QAAE,OAAO,SAAS,CAAA;IAExC,OAAO;QACL,IAAI,EAAE,oBAAoB;QAC1B,EAAE,EAAE,GAAG,OAAO,CAAC,EAAE,IAAI,OAAO,CAAC,WAAW,EAAE;QAC1C,SAAS,EAAE,OAAO,CAAC,WAAW;QAC9B,oBAAoB,EAAE,IAAI;QAC1B,iBAAiB,EAAE,OAAO,CAAC,YAAY,IAAI,KAAK;KACjD,CAAA;AACH,CAAC","sourcesContent":["import type { MessageResource } from '../types/resources/message'\nimport dayjs from './dayjs'\nimport { isSystemMessage } from './system_messages'\n\nconst FIVE_MINUTES_MS = 5 * 60 * 1000\n\nexport type DateSeparator = { type: 'DateSeparator'; id: string; date: string }\n\nexport type ReplyShadowMessage = {\n type: 'ReplyShadowMessage'\n id: string\n messageId: string\n isReplyShadowMessage: boolean\n nextRendersAuthor: boolean\n}\n\nexport type EnrichedMessage = MessageResource | DateSeparator | ReplyShadowMessage\n\ninterface GroupMessagesProps {\n ms: MessageResource[]\n inReplyScreen?: boolean\n}\n\nexport function groupMessages({ ms, inReplyScreen }: GroupMessagesProps): EnrichedMessage[] {\n const items: EnrichedMessage[] = []\n let myLatestSeen = false\n let nextNeighborEnriched: MessageResource | undefined\n\n ms.forEach((message, i) => {\n const { prev } = neighborsOf(ms, i)\n const next = nextNeighborEnriched\n\n if (isSystemMessage(message)) {\n const enriched = enrichSystemMessage(message, next)\n items.push(enriched)\n if (datesDifferBetween(message, prev)) items.push(dateSeparator(message))\n nextNeighborEnriched = enriched\n return\n }\n\n const isMyLatest = !myLatestSeen && message.mine\n if (isMyLatest) myLatestSeen = true\n\n const enriched = enrichRegularMessage(message, prev, next, isMyLatest, !!inReplyScreen)\n items.push(enriched)\n\n const shadow = replyShadowFor(enriched, prev)\n if (shadow) items.push(shadow)\n\n if (!prev || datesDifferBetween(message, prev)) {\n items.push(dateSeparator(message))\n }\n\n nextNeighborEnriched = enriched\n })\n\n return items\n}\n\nfunction neighborsOf<T>(arr: T[], i: number): { prev: T | undefined; next: T | undefined } {\n return { prev: arr[i + 1], next: arr[i - 1] }\n}\n\nfunction enrichSystemMessage(\n message: MessageResource,\n next: MessageResource | undefined\n): MessageResource {\n return {\n ...message,\n myLatestInConversation: false,\n lastInGroup: true,\n renderAuthor: false,\n nextRendersAuthor: next?.renderAuthor,\n isReplyShadowMessage: false,\n nextIsReplyShadowMessage: false,\n threadPosition: null,\n }\n}\n\nfunction enrichRegularMessage(\n message: MessageResource,\n prev: MessageResource | undefined,\n next: MessageResource | undefined,\n isMyLatest: boolean,\n inReplyScreen: boolean\n): MessageResource {\n const inThread = message.replyRootId !== null\n const showThreadDetails = !inReplyScreen && inThread\n\n return {\n ...message,\n myLatestInConversation: isMyLatest,\n lastInGroup: !next || startsNewGroup(next, message),\n renderAuthor: !message.mine && (!prev || startsNewGroup(message, prev)),\n nextRendersAuthor: next?.renderAuthor,\n isReplyShadowMessage: false,\n nextIsReplyShadowMessage: nextIntroducesReplyShadow(next, message),\n threadPosition: showThreadDetails ? threadPositionFor(message, next) : null,\n prevIsMyReply: showThreadDetails ? prev?.mine : undefined,\n nextIsMyReply: showThreadDetails ? next?.mine : undefined,\n }\n}\n\nfunction startsNewGroup(a: MessageResource, b: MessageResource): boolean {\n return (\n a.author?.id !== b.author?.id ||\n differsByMoreThan5Min(a, b) ||\n a.replyRootId !== b.replyRootId ||\n datesDifferBetween(a, b)\n )\n}\n\nfunction differsByMoreThan5Min(a: MessageResource, b: MessageResource): boolean {\n return Math.abs(toMillis(a.createdAt) - toMillis(b.createdAt)) > FIVE_MINUTES_MS\n}\n\nfunction toMillis(iso: string): number {\n return new Date(iso).getTime()\n}\n\nfunction datesDifferBetween(a: MessageResource, b: MessageResource | undefined): boolean {\n if (!b) return false\n return dateKey(a) !== dateKey(b)\n}\n\nfunction dateKey(message: MessageResource): string {\n return dayjs(message.createdAt).format('YYYY-MM-DD')\n}\n\nfunction dateSeparator(message: MessageResource): DateSeparator {\n return { type: 'DateSeparator', id: `day-divider-${message.id}`, date: dateKey(message) }\n}\n\nfunction threadPositionFor(\n message: MessageResource,\n next: MessageResource | undefined\n): 'first' | 'center' | 'last' | null {\n const isThreadRoot = message.replyRootId === message.id\n const isLast =\n !next || next.replyRootId !== message.replyRootId || datesDifferBetween(next, message)\n\n if (isThreadRoot && isLast) return null\n if (isThreadRoot) return 'first'\n if (isLast) return 'last'\n return 'center'\n}\n\nfunction nextIntroducesReplyShadow(\n next: MessageResource | undefined,\n current: MessageResource\n): boolean {\n if (!next) return false\n const nextInThread = next.replyRootId !== null\n const nextIsThreadRoot = next.replyRootId === next.id\n const differentThread = next.replyRootId !== current.replyRootId\n return nextInThread && !nextIsThreadRoot && (differentThread || datesDifferBetween(next, current))\n}\n\nfunction replyShadowFor(\n message: MessageResource,\n prev: MessageResource | undefined\n): ReplyShadowMessage | undefined {\n if (!message.replyRootId) return undefined\n if (message.replyRootId === message.id) return undefined\n\n const enteringNewThread =\n !prev || prev.replyRootId !== message.replyRootId || datesDifferBetween(prev, message)\n if (!enteringNewThread) return undefined\n\n return {\n type: 'ReplyShadowMessage',\n id: `${message.id}-${message.replyRootId}`,\n messageId: message.replyRootId,\n isReplyShadowMessage: true,\n nextRendersAuthor: message.renderAuthor ?? false,\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@planningcenter/chat-react-native",
|
|
3
|
-
"version": "3.38.0-rc.
|
|
3
|
+
"version": "3.38.0-rc.11",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"react-native": "./src/index.tsx",
|
|
@@ -26,9 +26,9 @@
|
|
|
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.38.0-rc.11",
|
|
29
30
|
"lodash-inflection": "^1.5.0",
|
|
30
|
-
"react-compiler-runtime": "^1.0.0"
|
|
31
|
-
"rn-emoji-keyboard": "1.7.0"
|
|
31
|
+
"react-compiler-runtime": "^1.0.0"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
34
|
"@planningcenter/datetime-fmt": ">=2.0.0",
|
|
@@ -72,5 +72,5 @@
|
|
|
72
72
|
"react-native-url-polyfill": "^2.0.0",
|
|
73
73
|
"typescript": "~5.9.2"
|
|
74
74
|
},
|
|
75
|
-
"gitHead": "
|
|
75
|
+
"gitHead": "02d0d805d82b046009c238100d6ec601b4295b39"
|
|
76
76
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { QueryClientProvider } from '@tanstack/react-query'
|
|
2
|
+
import { renderHook, waitFor } from '@testing-library/react-native'
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import { buildTestQueryClient } from '../../__utils__/query_client'
|
|
5
|
+
import { useGroupChatConversationPayload } from '../../hooks/groups/use_group_chat_conversation_payload'
|
|
6
|
+
import * as useApiClientModule from '../../hooks/use_api_client'
|
|
7
|
+
|
|
8
|
+
const mockGroupsPost = (impl: jest.Mock) => {
|
|
9
|
+
jest.spyOn(useApiClientModule, 'useApiClient').mockReturnValue({
|
|
10
|
+
groups: { post: impl },
|
|
11
|
+
} as unknown as ReturnType<typeof useApiClientModule.useApiClient>)
|
|
12
|
+
return impl
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const resolveWith = (value: string) =>
|
|
16
|
+
jest.fn().mockResolvedValue({
|
|
17
|
+
data: { type: 'GroupChatConversationPayload', id: '1', value },
|
|
18
|
+
links: {},
|
|
19
|
+
meta: {},
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
describe('useGroupChatConversationPayload', () => {
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
jest.restoreAllMocks()
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('refetches on mount even when a fresh payload is already cached', async () => {
|
|
28
|
+
// Prime the cache as if a payload was fetched moments ago. The hook's 25-min staleTime
|
|
29
|
+
// means the default refetchOnMount would treat this as fresh and skip the request, so a
|
|
30
|
+
// group composition change on web would stay invisible until the cache is dropped (app
|
|
31
|
+
// restart). refetchOnMount: 'always' must override that and refetch on every mount.
|
|
32
|
+
const queryClient = buildTestQueryClient()
|
|
33
|
+
queryClient.setQueryData(['groups', '/me/groups/1/chat_conversation_payload'], {
|
|
34
|
+
data: { type: 'GroupChatConversationPayload', id: '1', value: 'stale' },
|
|
35
|
+
links: {},
|
|
36
|
+
meta: {},
|
|
37
|
+
})
|
|
38
|
+
const post = mockGroupsPost(resolveWith('fresh'))
|
|
39
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
40
|
+
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
const { result } = renderHook(() => useGroupChatConversationPayload({ groupId: 1 }), {
|
|
44
|
+
wrapper,
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
await waitFor(() => expect(post).toHaveBeenCalledTimes(1))
|
|
48
|
+
await waitFor(() => expect(result.current.payload).toBe('fresh'))
|
|
49
|
+
})
|
|
50
|
+
})
|
package/src/__tests__/jest.ts
CHANGED
|
@@ -4,8 +4,8 @@ describe('jestTransformPackages', () => {
|
|
|
4
4
|
it('exports an array of package patterns', () => {
|
|
5
5
|
expect(jestTransformPackages).toEqual([
|
|
6
6
|
'@planningcenter/chat-react-native',
|
|
7
|
+
'@planningcenter/emoji-keyboard',
|
|
7
8
|
'@fortawesome',
|
|
8
|
-
'rn-emoji-keyboard',
|
|
9
9
|
])
|
|
10
10
|
})
|
|
11
11
|
})
|
|
@@ -369,4 +369,36 @@ describe('error handling', () => {
|
|
|
369
369
|
).rejects.toHaveProperty('status', 401)
|
|
370
370
|
}
|
|
371
371
|
})
|
|
372
|
+
|
|
373
|
+
describe('non-401 errors', () => {
|
|
374
|
+
it('attaches the parsed body so the message survives to the UI', async () => {
|
|
375
|
+
const url = '/records'
|
|
376
|
+
|
|
377
|
+
MockServer.post(
|
|
378
|
+
url,
|
|
379
|
+
{
|
|
380
|
+
errors: [
|
|
381
|
+
{
|
|
382
|
+
status: '422',
|
|
383
|
+
title: 'Unprocessable Entity',
|
|
384
|
+
detail: 'Conversations including a minor must have at least two confirmed adults.',
|
|
385
|
+
},
|
|
386
|
+
],
|
|
387
|
+
},
|
|
388
|
+
422,
|
|
389
|
+
{ once: true }
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
const rejection = (await client
|
|
393
|
+
.post({ url, data: { data: { type: 'Record', attributes: {} } } })
|
|
394
|
+
.catch(e => e)) as FailedResponse
|
|
395
|
+
|
|
396
|
+
expect(rejection).toHaveProperty('status', 422)
|
|
397
|
+
expect(rejection.errors?.[0]?.detail).toEqual(
|
|
398
|
+
'Conversations including a minor must have at least two confirmed adults.'
|
|
399
|
+
)
|
|
400
|
+
// A 422 must not be mistaken for an auth failure
|
|
401
|
+
expect(onUnauthorizedResponse).not.toHaveBeenCalled()
|
|
402
|
+
})
|
|
403
|
+
})
|
|
372
404
|
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { render } from '@testing-library/react-native'
|
|
2
|
+
import { createRef } from 'react'
|
|
3
|
+
import { FlatList } from 'react-native-gesture-handler'
|
|
4
|
+
import { MessageList } from '../message_list'
|
|
5
|
+
|
|
6
|
+
describe('MessageList', () => {
|
|
7
|
+
it('renders with the gesture-handler FlatList so overscroll cannot drag an enclosing sheet', () => {
|
|
8
|
+
const listRef = createRef<FlatList>()
|
|
9
|
+
|
|
10
|
+
const screen = render(<MessageList listRef={listRef} data={[]} renderItem={() => null} />)
|
|
11
|
+
|
|
12
|
+
expect(screen.UNSAFE_getByType(FlatList)).toBeTruthy()
|
|
13
|
+
})
|
|
14
|
+
})
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { useEffect } from 'react'
|
|
2
|
-
import {
|
|
2
|
+
import { View, Pressable, StyleSheet } from 'react-native'
|
|
3
3
|
import Animated, {
|
|
4
|
-
|
|
4
|
+
useSharedValue,
|
|
5
|
+
useAnimatedStyle,
|
|
5
6
|
interpolate,
|
|
7
|
+
Extrapolation,
|
|
6
8
|
ReduceMotion,
|
|
7
|
-
useAnimatedStyle,
|
|
8
|
-
useSharedValue,
|
|
9
9
|
withSpring,
|
|
10
|
-
withTiming,
|
|
11
10
|
} from 'react-native-reanimated'
|
|
12
11
|
import { useTheme } from '../../hooks'
|
|
13
12
|
import { platformFontWeightMedium } from '../../utils'
|
|
@@ -16,17 +15,11 @@ import { Icon, Text } from '../display'
|
|
|
16
15
|
interface JumpToBottomButtonProps {
|
|
17
16
|
onPress: () => void
|
|
18
17
|
visible: boolean
|
|
19
|
-
loading?: boolean
|
|
20
18
|
}
|
|
21
19
|
|
|
22
|
-
export const JumpToBottomButton = ({
|
|
23
|
-
onPress,
|
|
24
|
-
visible,
|
|
25
|
-
loading = false,
|
|
26
|
-
}: JumpToBottomButtonProps) => {
|
|
20
|
+
export const JumpToBottomButton = ({ onPress, visible }: JumpToBottomButtonProps) => {
|
|
27
21
|
const styles = useStyles()
|
|
28
22
|
const progress = useSharedValue(0)
|
|
29
|
-
const loadingProgress = useSharedValue(0)
|
|
30
23
|
|
|
31
24
|
useEffect(() => {
|
|
32
25
|
progress.value = withSpring(visible ? 1 : 0, {
|
|
@@ -38,13 +31,6 @@ export const JumpToBottomButton = ({
|
|
|
38
31
|
})
|
|
39
32
|
}, [visible, progress])
|
|
40
33
|
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
loadingProgress.value = withTiming(loading ? 1 : 0, {
|
|
43
|
-
duration: 750,
|
|
44
|
-
reduceMotion: ReduceMotion.System,
|
|
45
|
-
})
|
|
46
|
-
}, [loading, loadingProgress])
|
|
47
|
-
|
|
48
34
|
const animatedStyle = useAnimatedStyle(() => {
|
|
49
35
|
return {
|
|
50
36
|
opacity: progress.value,
|
|
@@ -59,34 +45,16 @@ export const JumpToBottomButton = ({
|
|
|
59
45
|
}
|
|
60
46
|
})
|
|
61
47
|
|
|
62
|
-
const iconStyle = useAnimatedStyle(() => ({ opacity: 1 - loadingProgress.value }))
|
|
63
|
-
const spinnerStyle = useAnimatedStyle(() => ({ opacity: loadingProgress.value }))
|
|
64
|
-
|
|
65
48
|
return (
|
|
66
49
|
<View>
|
|
67
|
-
<Animated.View
|
|
68
|
-
style={[styles.container, animatedStyle]}
|
|
69
|
-
pointerEvents={visible ? 'auto' : 'none'}
|
|
70
|
-
>
|
|
50
|
+
<Animated.View style={[styles.container, animatedStyle]}>
|
|
71
51
|
<Pressable
|
|
72
52
|
onPress={onPress}
|
|
73
|
-
disabled={loading}
|
|
74
|
-
accessibilityRole="button"
|
|
75
|
-
accessibilityLabel="Jump to most recent message"
|
|
76
|
-
accessibilityState={{ busy: loading }}
|
|
77
|
-
hitSlop={hitSlop}
|
|
78
53
|
style={({ pressed }) => [styles.button, pressed && styles.pressed]}
|
|
79
54
|
>
|
|
80
|
-
<
|
|
81
|
-
<Animated.View style={[styles.glyphLayer, iconStyle]}>
|
|
82
|
-
<Icon name="general.downArrow" style={styles.icon} />
|
|
83
|
-
</Animated.View>
|
|
84
|
-
<Animated.View style={[styles.glyphLayer, spinnerStyle]}>
|
|
85
|
-
<ActivityIndicator size="small" color={styles.icon.color} style={styles.spinner} />
|
|
86
|
-
</Animated.View>
|
|
87
|
-
</View>
|
|
55
|
+
<Icon name="general.downArrow" style={styles.icon} />
|
|
88
56
|
<Text variant="tertiary" style={styles.text}>
|
|
89
|
-
|
|
57
|
+
Jump to bottom
|
|
90
58
|
</Text>
|
|
91
59
|
</Pressable>
|
|
92
60
|
</Animated.View>
|
|
@@ -94,8 +62,6 @@ export const JumpToBottomButton = ({
|
|
|
94
62
|
)
|
|
95
63
|
}
|
|
96
64
|
|
|
97
|
-
const hitSlop = { top: 12, bottom: 12, left: 12, right: 12 }
|
|
98
|
-
|
|
99
65
|
const useStyles = () => {
|
|
100
66
|
const { colors } = useTheme()
|
|
101
67
|
|
|
@@ -127,25 +93,10 @@ const useStyles = () => {
|
|
|
127
93
|
color: colors.fillColorNeutral100Inverted,
|
|
128
94
|
fontWeight: platformFontWeightMedium,
|
|
129
95
|
},
|
|
130
|
-
glyph: {
|
|
131
|
-
width: 14,
|
|
132
|
-
height: 14,
|
|
133
|
-
alignItems: 'center',
|
|
134
|
-
justifyContent: 'center',
|
|
135
|
-
},
|
|
136
|
-
glyphLayer: {
|
|
137
|
-
position: 'absolute',
|
|
138
|
-
alignItems: 'center',
|
|
139
|
-
justifyContent: 'center',
|
|
140
|
-
},
|
|
141
96
|
icon: {
|
|
142
97
|
color: colors.fillColorNeutral100Inverted,
|
|
143
98
|
fontSize: 14,
|
|
144
99
|
},
|
|
145
|
-
spinner: {
|
|
146
|
-
width: 14,
|
|
147
|
-
height: 14,
|
|
148
|
-
},
|
|
149
100
|
pressed: {
|
|
150
101
|
transform: [{ scale: 0.95 }],
|
|
151
102
|
},
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { RefObject } from 'react'
|
|
2
|
+
import { StyleSheet, type FlatListProps } from 'react-native'
|
|
3
|
+
import { FlatList } from 'react-native-gesture-handler'
|
|
4
|
+
import type { EnrichedMessage } from '../../utils/group_messages'
|
|
5
|
+
|
|
6
|
+
const extractItemKey = (item: EnrichedMessage) => String(item.id)
|
|
7
|
+
const maintainVisibleContentPosition = { minIndexForVisible: 0 }
|
|
8
|
+
|
|
9
|
+
type MessageListProps = Pick<
|
|
10
|
+
FlatListProps<EnrichedMessage>,
|
|
11
|
+
| 'data'
|
|
12
|
+
| 'renderItem'
|
|
13
|
+
| 'onScroll'
|
|
14
|
+
| 'onScrollBeginDrag'
|
|
15
|
+
| 'viewabilityConfigCallbackPairs'
|
|
16
|
+
| 'onContentSizeChange'
|
|
17
|
+
| 'onScrollToIndexFailed'
|
|
18
|
+
| 'onEndReached'
|
|
19
|
+
| 'ListHeaderComponent'
|
|
20
|
+
> & {
|
|
21
|
+
listRef: RefObject<FlatList | null>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function MessageList({ listRef, ...props }: MessageListProps) {
|
|
25
|
+
return (
|
|
26
|
+
<FlatList
|
|
27
|
+
inverted
|
|
28
|
+
ref={listRef}
|
|
29
|
+
contentContainerStyle={styles.listContainer}
|
|
30
|
+
maintainVisibleContentPosition={maintainVisibleContentPosition}
|
|
31
|
+
keyExtractor={extractItemKey}
|
|
32
|
+
scrollEventThrottle={64}
|
|
33
|
+
{...props}
|
|
34
|
+
/>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const styles = StyleSheet.create({
|
|
39
|
+
listContainer: {
|
|
40
|
+
paddingVertical: 12,
|
|
41
|
+
},
|
|
42
|
+
})
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
import { Avatar, Icon, IconProps, Image, Text } from '../display'
|
|
29
29
|
import { TheirReplyConnector, MyReplyConnector } from './reply_connectors'
|
|
30
30
|
|
|
31
|
-
interface ReplyShadowMessageProps {
|
|
31
|
+
interface ReplyShadowMessageProps extends MessageResource {
|
|
32
32
|
messageId: string
|
|
33
33
|
conversation_id: number
|
|
34
34
|
inReplyScreen?: boolean
|
|
@@ -52,6 +52,7 @@ interface ConversationAvatarProps {
|
|
|
52
52
|
showFallback?: boolean
|
|
53
53
|
fallbackIconName?: IconString
|
|
54
54
|
style?: ViewStyle
|
|
55
|
+
maxFontSizeMultiplier?: AvatarRootProps['maxFontSizeMultiplier']
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
export function ConversationAvatar({
|
|
@@ -60,30 +61,31 @@ export function ConversationAvatar({
|
|
|
60
61
|
showFallback = false,
|
|
61
62
|
fallbackIconName = 'general.person',
|
|
62
63
|
style,
|
|
64
|
+
maxFontSizeMultiplier,
|
|
63
65
|
}: ConversationAvatarProps) {
|
|
64
66
|
const avatar = resolveAvatar(conversation)
|
|
67
|
+
const sizeProps = { size, maxFontSizeMultiplier, style }
|
|
65
68
|
|
|
66
69
|
switch (avatar.kind) {
|
|
67
70
|
case 'image':
|
|
68
71
|
return (
|
|
69
|
-
<AvatarPrimitive.Root
|
|
72
|
+
<AvatarPrimitive.Root {...sizeProps}>
|
|
70
73
|
<AvatarPrimitive.Mask>
|
|
71
74
|
<AvatarPrimitive.Image sourceUri={avatar.url} />
|
|
72
75
|
</AvatarPrimitive.Mask>
|
|
73
76
|
</AvatarPrimitive.Root>
|
|
74
77
|
)
|
|
75
78
|
case 'icon':
|
|
76
|
-
return <IconAvatar iconKey={avatar.iconKey} color={avatar.color}
|
|
79
|
+
return <IconAvatar iconKey={avatar.iconKey} color={avatar.color} {...sizeProps} />
|
|
77
80
|
case 'emoji':
|
|
78
|
-
return <EmojiAvatar emoji={avatar.emoji} color={avatar.color}
|
|
81
|
+
return <EmojiAvatar emoji={avatar.emoji} color={avatar.color} {...sizeProps} />
|
|
79
82
|
case 'group':
|
|
80
83
|
return (
|
|
81
84
|
<AvatarGroup
|
|
82
85
|
sourceUris={avatar.sources || []}
|
|
83
|
-
size={size}
|
|
84
86
|
showFallback={showFallback || !avatar.sources || avatar.sources.length === 0}
|
|
85
87
|
fallbackIconName={fallbackIconName}
|
|
86
|
-
|
|
88
|
+
{...sizeProps}
|
|
87
89
|
/>
|
|
88
90
|
)
|
|
89
91
|
}
|
|
@@ -20,13 +20,21 @@ interface EmojiAvatarProps {
|
|
|
20
20
|
emoji: string
|
|
21
21
|
color?: string | null
|
|
22
22
|
size?: AvatarRootProps['size']
|
|
23
|
+
maxFontSizeMultiplier?: AvatarRootProps['maxFontSizeMultiplier']
|
|
24
|
+
style?: AvatarRootProps['style']
|
|
23
25
|
}
|
|
24
26
|
|
|
25
|
-
export function EmojiAvatar({
|
|
27
|
+
export function EmojiAvatar({
|
|
28
|
+
emoji,
|
|
29
|
+
color,
|
|
30
|
+
size = 'lg',
|
|
31
|
+
maxFontSizeMultiplier,
|
|
32
|
+
style,
|
|
33
|
+
}: EmojiAvatarProps) {
|
|
26
34
|
const gradientProps = getAvatarGradientProps(color)
|
|
27
35
|
|
|
28
36
|
return (
|
|
29
|
-
<AvatarPrimitive.Root size={size}>
|
|
37
|
+
<AvatarPrimitive.Root size={size} maxFontSizeMultiplier={maxFontSizeMultiplier} style={style}>
|
|
30
38
|
<AvatarPrimitive.Mask>
|
|
31
39
|
<LinearGradient {...gradientProps} style={styles.gradientFill}>
|
|
32
40
|
<View style={styles.contentContainer}>
|
|
@@ -26,13 +26,21 @@ interface IconAvatarProps {
|
|
|
26
26
|
iconKey: string
|
|
27
27
|
color?: string | null
|
|
28
28
|
size?: AvatarRootProps['size']
|
|
29
|
+
maxFontSizeMultiplier?: AvatarRootProps['maxFontSizeMultiplier']
|
|
30
|
+
style?: AvatarRootProps['style']
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
function findIcon(iconKey: string): IconDefinition | undefined {
|
|
32
34
|
return findIconDefinition({ prefix: 'fas', iconName: iconKey as IconName })
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
export function IconAvatar({
|
|
37
|
+
export function IconAvatar({
|
|
38
|
+
iconKey,
|
|
39
|
+
color,
|
|
40
|
+
size = 'lg',
|
|
41
|
+
maxFontSizeMultiplier,
|
|
42
|
+
style,
|
|
43
|
+
}: IconAvatarProps) {
|
|
36
44
|
const gradientProps = getAvatarGradientProps(color)
|
|
37
45
|
const iconDef = findIcon(iconKey)
|
|
38
46
|
|
|
@@ -41,7 +49,7 @@ export function IconAvatar({ iconKey, color, size = 'lg' }: IconAvatarProps) {
|
|
|
41
49
|
}
|
|
42
50
|
|
|
43
51
|
return (
|
|
44
|
-
<AvatarPrimitive.Root size={size}>
|
|
52
|
+
<AvatarPrimitive.Root size={size} maxFontSizeMultiplier={maxFontSizeMultiplier} style={style}>
|
|
45
53
|
<AvatarPrimitive.Mask>
|
|
46
54
|
<LinearGradient {...gradientProps} style={styles.gradientFill}>
|
|
47
55
|
<View style={styles.contentContainer}>
|
|
@@ -1,59 +1,31 @@
|
|
|
1
|
-
import React, { createContext, PropsWithChildren, useContext, useMemo
|
|
1
|
+
import React, { createContext, PropsWithChildren, useContext, useMemo } from 'react'
|
|
2
2
|
|
|
3
3
|
interface ConversationContextValue {
|
|
4
4
|
conversationId: number
|
|
5
5
|
currentPageReplyRootId: string | null
|
|
6
|
-
initialMessageId: string | null
|
|
7
|
-
setInitialMessageId: (id: string | null) => void
|
|
8
|
-
initialMessageIdIsAnchor: boolean
|
|
9
|
-
atEndOfMessageHistory: boolean
|
|
10
|
-
setAtEndOfMessageHistory: (atEnd: boolean) => void
|
|
11
6
|
}
|
|
12
7
|
|
|
13
8
|
interface ConversationContextProviderProps extends PropsWithChildren {
|
|
14
9
|
conversationId: number
|
|
15
10
|
currentPageReplyRootId: string | null
|
|
16
|
-
initialMessageId?: string | null
|
|
17
|
-
initialMessageIdIsAnchor?: boolean
|
|
18
11
|
}
|
|
19
12
|
|
|
20
13
|
const ConversationContext = createContext<ConversationContextValue>({
|
|
21
14
|
conversationId: 0,
|
|
22
15
|
currentPageReplyRootId: null,
|
|
23
|
-
initialMessageId: null,
|
|
24
|
-
setInitialMessageId: () => {},
|
|
25
|
-
initialMessageIdIsAnchor: false,
|
|
26
|
-
atEndOfMessageHistory: false,
|
|
27
|
-
setAtEndOfMessageHistory: () => {},
|
|
28
16
|
})
|
|
29
17
|
|
|
30
18
|
export const ConversationContextProvider = ({
|
|
31
19
|
children,
|
|
32
20
|
conversationId,
|
|
33
21
|
currentPageReplyRootId,
|
|
34
|
-
initialMessageId: initialMessageIdProp = null,
|
|
35
|
-
initialMessageIdIsAnchor = false,
|
|
36
22
|
}: ConversationContextProviderProps) => {
|
|
37
|
-
const [initialMessageId, setInitialMessageId] = useState(initialMessageIdProp)
|
|
38
|
-
const [atEndOfMessageHistory, setAtEndOfMessageHistory] = useState(false)
|
|
39
|
-
|
|
40
23
|
const value = useMemo(
|
|
41
24
|
() => ({
|
|
42
25
|
conversationId,
|
|
43
26
|
currentPageReplyRootId,
|
|
44
|
-
initialMessageId,
|
|
45
|
-
setInitialMessageId,
|
|
46
|
-
initialMessageIdIsAnchor,
|
|
47
|
-
atEndOfMessageHistory,
|
|
48
|
-
setAtEndOfMessageHistory,
|
|
49
27
|
}),
|
|
50
|
-
[
|
|
51
|
-
conversationId,
|
|
52
|
-
currentPageReplyRootId,
|
|
53
|
-
initialMessageId,
|
|
54
|
-
initialMessageIdIsAnchor,
|
|
55
|
-
atEndOfMessageHistory,
|
|
56
|
-
]
|
|
28
|
+
[conversationId, currentPageReplyRootId]
|
|
57
29
|
)
|
|
58
30
|
|
|
59
31
|
return <ConversationContext.Provider value={value}>{children}</ConversationContext.Provider>
|
package/src/hooks/index.ts
CHANGED
|
@@ -16,6 +16,7 @@ export * from './use_message_reaction_toggle'
|
|
|
16
16
|
export * from './use_new_conversation_entry'
|
|
17
17
|
export * from './use_organization'
|
|
18
18
|
export * from './use_people_person'
|
|
19
|
+
export * from './use_preview_avatar_diameter'
|
|
19
20
|
export * from './use_product_analytics'
|
|
20
21
|
export * from './use_qualified_by_age'
|
|
21
22
|
export * from './use_scalable_number_of_lines'
|