@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
|
@@ -124,77 +124,6 @@ describe('groupMessages — nextRendersAuthor mirrors the newer enriched neighbo
|
|
|
124
124
|
})
|
|
125
125
|
})
|
|
126
126
|
|
|
127
|
-
describe('groupMessages — unread divider', () => {
|
|
128
|
-
it('inserts the divider between the read and unread boundary when jumpToUnreadActive', () => {
|
|
129
|
-
const messages = [
|
|
130
|
-
message('04', { createdAt: '2026-01-01T00:04:00Z' }),
|
|
131
|
-
message('03', { createdAt: '2026-01-01T00:03:00Z' }),
|
|
132
|
-
message('02', { createdAt: '2026-01-01T00:02:00Z' }),
|
|
133
|
-
message('01', { createdAt: '2026-01-01T00:01:00Z' }),
|
|
134
|
-
]
|
|
135
|
-
|
|
136
|
-
const enriched = groupMessages({
|
|
137
|
-
ms: messages,
|
|
138
|
-
jumpToUnreadActive: true,
|
|
139
|
-
initialMessageId: '02',
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
const dividerIdx = enriched.findIndex(item => 'type' in item && item.type === 'UnreadDivider')
|
|
143
|
-
const msg03Idx = enriched.findIndex(
|
|
144
|
-
item => 'id' in item && item.id === '03' && !('type' in item && item.type !== 'Message')
|
|
145
|
-
)
|
|
146
|
-
const msg02Idx = enriched.findIndex(
|
|
147
|
-
item => 'id' in item && item.id === '02' && !('type' in item && item.type !== 'Message')
|
|
148
|
-
)
|
|
149
|
-
|
|
150
|
-
expect(dividerIdx).toBeGreaterThan(msg03Idx)
|
|
151
|
-
expect(dividerIdx).toBeLessThan(msg02Idx)
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
it('does not insert the divider when jumpToUnreadActive is false', () => {
|
|
155
|
-
const enriched = groupMessages({
|
|
156
|
-
ms: [message('02'), message('01')],
|
|
157
|
-
jumpToUnreadActive: false,
|
|
158
|
-
initialMessageId: '01',
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
expect(enriched.some(item => 'type' in item && item.type === 'UnreadDivider')).toBe(false)
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
it('does not insert the divider when no message crosses the boundary', () => {
|
|
165
|
-
const enriched = groupMessages({
|
|
166
|
-
ms: [message('05'), message('04'), message('03')],
|
|
167
|
-
jumpToUnreadActive: true,
|
|
168
|
-
initialMessageId: '02',
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
expect(enriched.some(item => 'type' in item && item.type === 'UnreadDivider')).toBe(false)
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
it('orders ULID-style ids consistently with backend sort_key comparisons', () => {
|
|
175
|
-
const enriched = groupMessages({
|
|
176
|
-
ms: [
|
|
177
|
-
message('01KQSTAY189PHCJBT8T13R9VMP'),
|
|
178
|
-
message('01KQST9HZAB10K3CXR7TYN2QWE'),
|
|
179
|
-
message('01KQST73KZRPXNRDA7TYN19KXQ'),
|
|
180
|
-
message('01KQST5JKZRPXNRDA7TYN19ABC'),
|
|
181
|
-
],
|
|
182
|
-
jumpToUnreadActive: true,
|
|
183
|
-
initialMessageId: '01KQST73KZRPXNRDA7TYN19KXQ',
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
const dividerIdx = enriched.findIndex(item => 'type' in item && item.type === 'UnreadDivider')
|
|
187
|
-
const newerIdx = enriched.findIndex(
|
|
188
|
-
item =>
|
|
189
|
-
'id' in item &&
|
|
190
|
-
item.id === '01KQST9HZAB10K3CXR7TYN2QWE' &&
|
|
191
|
-
!('type' in item && item.type !== 'Message')
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
expect(dividerIdx).toBeGreaterThan(newerIdx)
|
|
195
|
-
})
|
|
196
|
-
})
|
|
197
|
-
|
|
198
127
|
describe('groupMessages — system messages', () => {
|
|
199
128
|
it('flags lastInGroup true and renderAuthor false on system messages', () => {
|
|
200
129
|
const messages = [
|
|
@@ -131,11 +131,6 @@ export function getThreadedMessagesQueryKey(conversationId: number, replyRootId:
|
|
|
131
131
|
return getRequestQueryKey(requestArgs)
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
export function hasUnloadedNewerPages(queryClient: QueryClient, queryKey: unknown[]): boolean {
|
|
135
|
-
const data = queryClient.getQueryData<MessagesQueryData>(queryKey)
|
|
136
|
-
return !!data?.pages?.[0]?.meta?.next?.idGt
|
|
137
|
-
}
|
|
138
|
-
|
|
139
134
|
export function mergeMessageUpdate(
|
|
140
135
|
record: MessageResource,
|
|
141
136
|
current?: MessageResource
|
|
@@ -5,15 +5,18 @@ const buildResponse = (status: number) => new Response('', { status })
|
|
|
5
5
|
|
|
6
6
|
describe('instrumentedFetch', () => {
|
|
7
7
|
let reportError: jest.SpyInstance
|
|
8
|
+
let warnSpy: jest.SpyInstance
|
|
8
9
|
let fetchSpy: jest.SpyInstance
|
|
9
10
|
|
|
10
11
|
beforeEach(() => {
|
|
11
12
|
reportError = jest.spyOn(Log, 'reportError').mockImplementation(() => {})
|
|
13
|
+
warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {})
|
|
12
14
|
fetchSpy = jest.spyOn(globalThis, 'fetch')
|
|
13
15
|
})
|
|
14
16
|
|
|
15
17
|
afterEach(() => {
|
|
16
18
|
reportError.mockRestore()
|
|
19
|
+
warnSpy.mockRestore()
|
|
17
20
|
fetchSpy.mockRestore()
|
|
18
21
|
})
|
|
19
22
|
|
|
@@ -27,6 +30,7 @@ describe('instrumentedFetch', () => {
|
|
|
27
30
|
|
|
28
31
|
expect(result).toBe(response)
|
|
29
32
|
expect(reportError).not.toHaveBeenCalled()
|
|
33
|
+
expect(warnSpy).not.toHaveBeenCalled()
|
|
30
34
|
})
|
|
31
35
|
|
|
32
36
|
it.each([400, 422, 500])(
|
|
@@ -63,7 +67,7 @@ describe('instrumentedFetch', () => {
|
|
|
63
67
|
expect(reportError).not.toHaveBeenCalled()
|
|
64
68
|
})
|
|
65
69
|
|
|
66
|
-
it('
|
|
70
|
+
it('warns on network failures instead of reporting them', async () => {
|
|
67
71
|
const networkError = new TypeError('Network request failed')
|
|
68
72
|
fetchSpy.mockRejectedValueOnce(networkError)
|
|
69
73
|
|
|
@@ -71,10 +75,10 @@ describe('instrumentedFetch', () => {
|
|
|
71
75
|
instrumentedFetch('https://api.example.com/conversations/123', { method: 'GET' })
|
|
72
76
|
).rejects.toBe(networkError)
|
|
73
77
|
|
|
74
|
-
expect(reportError).
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
expect(
|
|
78
|
+
expect(reportError).not.toHaveBeenCalled()
|
|
79
|
+
expect(warnSpy).toHaveBeenCalledTimes(1)
|
|
80
|
+
const [message, context] = warnSpy.mock.calls[0]
|
|
81
|
+
expect(message).toContain('Network failure GET /conversations/:id')
|
|
78
82
|
expect(context.tags).toMatchObject({
|
|
79
83
|
'http.method': 'GET',
|
|
80
84
|
'http.path': '/conversations/:id',
|
|
@@ -105,16 +105,18 @@ export class Client {
|
|
|
105
105
|
return makeRequest(requestArgs).catch(this.handleNotOk)
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
handleNotOk = async (response: Response) => {
|
|
108
|
+
handleNotOk = async (response: Response | Error) => {
|
|
109
|
+
if (!(response instanceof Response)) return Promise.reject(response)
|
|
110
|
+
|
|
111
|
+
const errorData = await this.parseErrorResponse(response)
|
|
112
|
+
const notOkResponse = response.clone() as FailedResponse
|
|
113
|
+
notOkResponse.errors = errorData.errors || []
|
|
114
|
+
|
|
109
115
|
if (response.status === 401) {
|
|
110
|
-
|
|
111
|
-
this.onUnauthorizedResponse?.({
|
|
112
|
-
...response,
|
|
113
|
-
errors: errorData.errors || [],
|
|
114
|
-
} as FailedResponse)
|
|
116
|
+
this.onUnauthorizedResponse?.(notOkResponse)
|
|
115
117
|
}
|
|
116
118
|
|
|
117
|
-
return Promise.reject(
|
|
119
|
+
return Promise.reject(notOkResponse)
|
|
118
120
|
}
|
|
119
121
|
|
|
120
122
|
parseErrorResponse = async (response: Response): Promise<Partial<FailedResponse>> => {
|
|
@@ -14,7 +14,7 @@ export async function instrumentedFetch(url: string, init: RequestInit): Promise
|
|
|
14
14
|
if (!response.ok) reportHttpError(response, method, url)
|
|
15
15
|
return response
|
|
16
16
|
} catch (networkError) {
|
|
17
|
-
|
|
17
|
+
warnNetworkError(networkError as Error, method, url)
|
|
18
18
|
throw networkError
|
|
19
19
|
}
|
|
20
20
|
}
|
|
@@ -38,12 +38,9 @@ function reportHttpError(response: Response, method: string, url: string) {
|
|
|
38
38
|
})
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
function
|
|
41
|
+
function warnNetworkError(networkError: Error, method: string, url: string) {
|
|
42
42
|
const path = templatePath(url)
|
|
43
|
-
|
|
44
|
-
error.name = 'NetworkError'
|
|
45
|
-
|
|
46
|
-
Log.reportError(error, {
|
|
43
|
+
console.warn(`Network failure ${method} ${path}: ${networkError.message}`, {
|
|
47
44
|
scope: 'http',
|
|
48
45
|
tags: {
|
|
49
46
|
...SHARED_TAGS,
|
|
@@ -4,12 +4,8 @@ import { isSystemMessage } from './system_messages'
|
|
|
4
4
|
|
|
5
5
|
const FIVE_MINUTES_MS = 5 * 60 * 1000
|
|
6
6
|
|
|
7
|
-
export const UNREAD_DIVIDER_KEY = 'unread-divider'
|
|
8
|
-
|
|
9
7
|
export type DateSeparator = { type: 'DateSeparator'; id: string; date: string }
|
|
10
8
|
|
|
11
|
-
export type UnreadDividerItem = { type: 'UnreadDivider'; id: typeof UNREAD_DIVIDER_KEY }
|
|
12
|
-
|
|
13
9
|
export type ReplyShadowMessage = {
|
|
14
10
|
type: 'ReplyShadowMessage'
|
|
15
11
|
id: string
|
|
@@ -18,25 +14,14 @@ export type ReplyShadowMessage = {
|
|
|
18
14
|
nextRendersAuthor: boolean
|
|
19
15
|
}
|
|
20
16
|
|
|
21
|
-
export type EnrichedMessage =
|
|
22
|
-
| MessageResource
|
|
23
|
-
| DateSeparator
|
|
24
|
-
| UnreadDividerItem
|
|
25
|
-
| ReplyShadowMessage
|
|
17
|
+
export type EnrichedMessage = MessageResource | DateSeparator | ReplyShadowMessage
|
|
26
18
|
|
|
27
19
|
interface GroupMessagesProps {
|
|
28
20
|
ms: MessageResource[]
|
|
29
21
|
inReplyScreen?: boolean
|
|
30
|
-
jumpToUnreadActive?: boolean
|
|
31
|
-
initialMessageId?: string | null
|
|
32
22
|
}
|
|
33
23
|
|
|
34
|
-
export function groupMessages({
|
|
35
|
-
ms,
|
|
36
|
-
inReplyScreen,
|
|
37
|
-
jumpToUnreadActive,
|
|
38
|
-
initialMessageId,
|
|
39
|
-
}: GroupMessagesProps): EnrichedMessage[] {
|
|
24
|
+
export function groupMessages({ ms, inReplyScreen }: GroupMessagesProps): EnrichedMessage[] {
|
|
40
25
|
const items: EnrichedMessage[] = []
|
|
41
26
|
let myLatestSeen = false
|
|
42
27
|
let nextNeighborEnriched: MessageResource | undefined
|
|
@@ -48,9 +33,6 @@ export function groupMessages({
|
|
|
48
33
|
if (isSystemMessage(message)) {
|
|
49
34
|
const enriched = enrichSystemMessage(message, next)
|
|
50
35
|
items.push(enriched)
|
|
51
|
-
if (crossesUnreadBoundary(message, prev, jumpToUnreadActive, initialMessageId)) {
|
|
52
|
-
items.push(unreadDivider())
|
|
53
|
-
}
|
|
54
36
|
if (datesDifferBetween(message, prev)) items.push(dateSeparator(message))
|
|
55
37
|
nextNeighborEnriched = enriched
|
|
56
38
|
return
|
|
@@ -62,10 +44,6 @@ export function groupMessages({
|
|
|
62
44
|
const enriched = enrichRegularMessage(message, prev, next, isMyLatest, !!inReplyScreen)
|
|
63
45
|
items.push(enriched)
|
|
64
46
|
|
|
65
|
-
if (crossesUnreadBoundary(message, prev, jumpToUnreadActive, initialMessageId)) {
|
|
66
|
-
items.push(unreadDivider())
|
|
67
|
-
}
|
|
68
|
-
|
|
69
47
|
const shadow = replyShadowFor(enriched, prev)
|
|
70
48
|
if (shadow) items.push(shadow)
|
|
71
49
|
|
|
@@ -79,24 +57,6 @@ export function groupMessages({
|
|
|
79
57
|
return items
|
|
80
58
|
}
|
|
81
59
|
|
|
82
|
-
function crossesUnreadBoundary(
|
|
83
|
-
message: MessageResource,
|
|
84
|
-
prev: MessageResource | undefined,
|
|
85
|
-
jumpToUnreadActive: boolean | undefined,
|
|
86
|
-
initialMessageId: string | null | undefined
|
|
87
|
-
): boolean {
|
|
88
|
-
if (!jumpToUnreadActive) return false
|
|
89
|
-
if (!initialMessageId) return false
|
|
90
|
-
if (!prev) return false
|
|
91
|
-
return (
|
|
92
|
-
prev.id.localeCompare(initialMessageId) <= 0 && message.id.localeCompare(initialMessageId) > 0
|
|
93
|
-
)
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function unreadDivider(): UnreadDividerItem {
|
|
97
|
-
return { type: 'UnreadDivider', id: UNREAD_DIVIDER_KEY }
|
|
98
|
-
}
|
|
99
|
-
|
|
100
60
|
function neighborsOf<T>(arr: T[], i: number): { prev: T | undefined; next: T | undefined } {
|
|
101
61
|
return { prev: arr[i + 1], next: arr[i - 1] }
|
|
102
62
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"unread_divider.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/unread_divider.tsx"],"names":[],"mappings":"AAQA,UAAU,kBAAkB;IAC1B,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAMD,wBAAgB,aAAa,CAAC,EAAE,YAAoB,EAAE,EAAE,kBAAkB,sCAqBzE"}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { StyleSheet, View } from 'react-native';
|
|
2
|
-
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
|
|
3
|
-
import Svg, { Defs, Path, Pattern, Rect } from 'react-native-svg';
|
|
4
|
-
import { useConversationContext } from '../../contexts/conversation_context';
|
|
5
|
-
import { useTheme } from '../../hooks';
|
|
6
|
-
import { CONVERSATION_MESSAGE_LIST_PADDING_HORIZONTAL } from '../../utils/styles';
|
|
7
|
-
import { Text } from '../display';
|
|
8
|
-
const WAVE_WIDTH = 16;
|
|
9
|
-
const WAVE_HEIGHT = 8;
|
|
10
|
-
const FADE_DURATION = 750;
|
|
11
|
-
export function UnreadDivider({ scrolledPast = false }) {
|
|
12
|
-
const styles = useStyles();
|
|
13
|
-
const { atEndOfMessageHistory } = useConversationContext();
|
|
14
|
-
if (scrolledPast || atEndOfMessageHistory)
|
|
15
|
-
return null;
|
|
16
|
-
return (<Animated.View entering={FadeIn.duration(FADE_DURATION)} exiting={FadeOut.duration(FADE_DURATION)} style={styles.container} accessibilityRole="header" accessibilityLabel="Unread messages start here">
|
|
17
|
-
<SquigglyLine />
|
|
18
|
-
<Text variant="footnote" style={styles.label}>
|
|
19
|
-
New
|
|
20
|
-
</Text>
|
|
21
|
-
<SquigglyLine />
|
|
22
|
-
</Animated.View>);
|
|
23
|
-
}
|
|
24
|
-
function SquigglyLine() {
|
|
25
|
-
const { colors } = useTheme();
|
|
26
|
-
return (<View style={squigglyStyle.container}>
|
|
27
|
-
<Svg width="100%" height={WAVE_HEIGHT}>
|
|
28
|
-
<Defs>
|
|
29
|
-
<Pattern id="wave" x="0" y="0" width={WAVE_WIDTH} height={WAVE_HEIGHT} patternUnits="userSpaceOnUse">
|
|
30
|
-
<Path d="M 0 4 Q 4 0 8 4 T 16 4" stroke={colors.interaction} strokeWidth={1.5} fill="none"/>
|
|
31
|
-
</Pattern>
|
|
32
|
-
</Defs>
|
|
33
|
-
<Rect x="0" y="0" width="100%" height={WAVE_HEIGHT} fill="url(#wave)"/>
|
|
34
|
-
</Svg>
|
|
35
|
-
</View>);
|
|
36
|
-
}
|
|
37
|
-
const squigglyStyle = StyleSheet.create({
|
|
38
|
-
container: {
|
|
39
|
-
flex: 1,
|
|
40
|
-
height: WAVE_HEIGHT,
|
|
41
|
-
},
|
|
42
|
-
});
|
|
43
|
-
const useStyles = () => {
|
|
44
|
-
const { colors } = useTheme();
|
|
45
|
-
return StyleSheet.create({
|
|
46
|
-
container: {
|
|
47
|
-
alignItems: 'center',
|
|
48
|
-
flexDirection: 'row',
|
|
49
|
-
paddingHorizontal: CONVERSATION_MESSAGE_LIST_PADDING_HORIZONTAL,
|
|
50
|
-
paddingVertical: 8,
|
|
51
|
-
gap: 8,
|
|
52
|
-
},
|
|
53
|
-
label: {
|
|
54
|
-
color: colors.interaction,
|
|
55
|
-
fontWeight: '600',
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
};
|
|
59
|
-
//# sourceMappingURL=unread_divider.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"unread_divider.js","sourceRoot":"","sources":["../../../src/components/conversation/unread_divider.tsx"],"names":[],"mappings":"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","sourcesContent":["import { StyleSheet, View } from 'react-native'\nimport Animated, { FadeIn, FadeOut } from 'react-native-reanimated'\nimport Svg, { Defs, Path, Pattern, Rect } from 'react-native-svg'\nimport { useConversationContext } from '../../contexts/conversation_context'\nimport { useTheme } from '../../hooks'\nimport { CONVERSATION_MESSAGE_LIST_PADDING_HORIZONTAL } from '../../utils/styles'\nimport { Text } from '../display'\n\ninterface UnreadDividerProps {\n scrolledPast?: boolean\n}\n\nconst WAVE_WIDTH = 16\nconst WAVE_HEIGHT = 8\nconst FADE_DURATION = 750\n\nexport function UnreadDivider({ scrolledPast = false }: UnreadDividerProps) {\n const styles = useStyles()\n const { atEndOfMessageHistory } = useConversationContext()\n\n if (scrolledPast || atEndOfMessageHistory) return null\n\n return (\n <Animated.View\n entering={FadeIn.duration(FADE_DURATION)}\n exiting={FadeOut.duration(FADE_DURATION)}\n style={styles.container}\n accessibilityRole=\"header\"\n accessibilityLabel=\"Unread messages start here\"\n >\n <SquigglyLine />\n <Text variant=\"footnote\" style={styles.label}>\n New\n </Text>\n <SquigglyLine />\n </Animated.View>\n )\n}\n\nfunction SquigglyLine() {\n const { colors } = useTheme()\n return (\n <View style={squigglyStyle.container}>\n <Svg width=\"100%\" height={WAVE_HEIGHT}>\n <Defs>\n <Pattern\n id=\"wave\"\n x=\"0\"\n y=\"0\"\n width={WAVE_WIDTH}\n height={WAVE_HEIGHT}\n patternUnits=\"userSpaceOnUse\"\n >\n <Path\n d=\"M 0 4 Q 4 0 8 4 T 16 4\"\n stroke={colors.interaction}\n strokeWidth={1.5}\n fill=\"none\"\n />\n </Pattern>\n </Defs>\n <Rect x=\"0\" y=\"0\" width=\"100%\" height={WAVE_HEIGHT} fill=\"url(#wave)\" />\n </Svg>\n </View>\n )\n}\n\nconst squigglyStyle = StyleSheet.create({\n container: {\n flex: 1,\n height: WAVE_HEIGHT,\n },\n})\n\nconst useStyles = () => {\n const { colors } = useTheme()\n return StyleSheet.create({\n container: {\n alignItems: 'center',\n flexDirection: 'row',\n paddingHorizontal: CONVERSATION_MESSAGE_LIST_PADDING_HORIZONTAL,\n paddingVertical: 8,\n gap: 8,\n },\n label: {\n color: colors.interaction,\n fontWeight: '600',\n },\n })\n}\n"]}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { ViewToken } from 'react-native';
|
|
2
|
-
import type { ViewabilityObserver } from '../utils/message_viewability';
|
|
3
|
-
interface UseFlatListViewabilityArgs<Item> {
|
|
4
|
-
observers: ViewabilityObserver<Item>[];
|
|
5
|
-
itemVisiblePercentThreshold?: number;
|
|
6
|
-
}
|
|
7
|
-
export declare function useFlatListViewability<Item>({ observers, itemVisiblePercentThreshold, }: UseFlatListViewabilityArgs<Item>): {
|
|
8
|
-
viewabilityConfigCallbackPairs: {
|
|
9
|
-
viewabilityConfig: {
|
|
10
|
-
itemVisiblePercentThreshold: number;
|
|
11
|
-
};
|
|
12
|
-
onViewableItemsChanged: ({ viewableItems, changed, }: {
|
|
13
|
-
viewableItems: ViewToken[];
|
|
14
|
-
changed: ViewToken[];
|
|
15
|
-
}) => void;
|
|
16
|
-
}[];
|
|
17
|
-
onScrollBeginDrag: () => void;
|
|
18
|
-
};
|
|
19
|
-
export {};
|
|
20
|
-
//# sourceMappingURL=use_flat_list_viewability.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"use_flat_list_viewability.d.ts","sourceRoot":"","sources":["../../src/hooks/use_flat_list_viewability.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAA;AAEvE,UAAU,0BAA0B,CAAC,IAAI;IACvC,SAAS,EAAE,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAA;IACtC,2BAA2B,CAAC,EAAE,MAAM,CAAA;CACrC;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,EAC3C,SAAS,EACT,2BAAgC,GACjC,EAAE,0BAA0B,CAAC,IAAI,CAAC;;;;;8DAkB1B;YACD,aAAa,EAAE,SAAS,EAAE,CAAA;YAC1B,OAAO,EAAE,SAAS,EAAE,CAAA;SACrB;;;EAYN"}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef } from 'react';
|
|
2
|
-
export function useFlatListViewability({ observers, itemVisiblePercentThreshold = 50, }) {
|
|
3
|
-
const userHasScrolledRef = useRef(false);
|
|
4
|
-
const observersRef = useRef(observers);
|
|
5
|
-
useEffect(() => {
|
|
6
|
-
observersRef.current = observers;
|
|
7
|
-
});
|
|
8
|
-
const onScrollBeginDrag = useCallback(() => {
|
|
9
|
-
userHasScrolledRef.current = true;
|
|
10
|
-
}, []);
|
|
11
|
-
const viewabilityConfigCallbackPairs = useRef([
|
|
12
|
-
{
|
|
13
|
-
viewabilityConfig: { itemVisiblePercentThreshold },
|
|
14
|
-
onViewableItemsChanged: ({ viewableItems, changed, }) => {
|
|
15
|
-
const event = {
|
|
16
|
-
viewableItems: viewableItems.map((toEntry)),
|
|
17
|
-
changed: changed.map((toEntry)),
|
|
18
|
-
userHasScrolled: userHasScrolledRef.current,
|
|
19
|
-
};
|
|
20
|
-
for (const observer of observersRef.current)
|
|
21
|
-
observer(event);
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
]).current;
|
|
25
|
-
return { viewabilityConfigCallbackPairs, onScrollBeginDrag };
|
|
26
|
-
}
|
|
27
|
-
function toEntry(token) {
|
|
28
|
-
return { key: token.key, isViewable: !!token.isViewable, item: token.item };
|
|
29
|
-
}
|
|
30
|
-
//# sourceMappingURL=use_flat_list_viewability.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"use_flat_list_viewability.js","sourceRoot":"","sources":["../../src/hooks/use_flat_list_viewability.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AAStD,MAAM,UAAU,sBAAsB,CAAO,EAC3C,SAAS,EACT,2BAA2B,GAAG,EAAE,GACC;IACjC,MAAM,kBAAkB,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;IACxC,MAAM,YAAY,GAAG,MAAM,CAAC,SAAS,CAAC,CAAA;IAEtC,SAAS,CAAC,GAAG,EAAE;QACb,YAAY,CAAC,OAAO,GAAG,SAAS,CAAA;IAClC,CAAC,CAAC,CAAA;IAEF,MAAM,iBAAiB,GAAG,WAAW,CAAC,GAAG,EAAE;QACzC,kBAAkB,CAAC,OAAO,GAAG,IAAI,CAAA;IACnC,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,8BAA8B,GAAG,MAAM,CAAC;QAC5C;YACE,iBAAiB,EAAE,EAAE,2BAA2B,EAAE;YAClD,sBAAsB,EAAE,CAAC,EACvB,aAAa,EACb,OAAO,GAIR,EAAE,EAAE;gBACH,MAAM,KAAK,GAAG;oBACZ,aAAa,EAAE,aAAa,CAAC,GAAG,CAAC,CAAA,OAAa,CAAA,CAAC;oBAC/C,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAA,OAAa,CAAA,CAAC;oBACnC,eAAe,EAAE,kBAAkB,CAAC,OAAO;iBAC5C,CAAA;gBACD,KAAK,MAAM,QAAQ,IAAI,YAAY,CAAC,OAAO;oBAAE,QAAQ,CAAC,KAAK,CAAC,CAAA;YAC9D,CAAC;SACF;KACF,CAAC,CAAC,OAAO,CAAA;IAEV,OAAO,EAAE,8BAA8B,EAAE,iBAAiB,EAAE,CAAA;AAC9D,CAAC;AAED,SAAS,OAAO,CAAO,KAAgB;IACrC,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE,KAAK,CAAC,IAAY,EAAE,CAAA;AACrF,CAAC","sourcesContent":["import { useCallback, useEffect, useRef } from 'react'\nimport type { ViewToken } from 'react-native'\nimport type { ViewabilityObserver } from '../utils/message_viewability'\n\ninterface UseFlatListViewabilityArgs<Item> {\n observers: ViewabilityObserver<Item>[]\n itemVisiblePercentThreshold?: number\n}\n\nexport function useFlatListViewability<Item>({\n observers,\n itemVisiblePercentThreshold = 50,\n}: UseFlatListViewabilityArgs<Item>) {\n const userHasScrolledRef = useRef(false)\n const observersRef = useRef(observers)\n\n useEffect(() => {\n observersRef.current = observers\n })\n\n const onScrollBeginDrag = useCallback(() => {\n userHasScrolledRef.current = true\n }, [])\n\n const viewabilityConfigCallbackPairs = useRef([\n {\n viewabilityConfig: { itemVisiblePercentThreshold },\n onViewableItemsChanged: ({\n viewableItems,\n changed,\n }: {\n viewableItems: ViewToken[]\n changed: ViewToken[]\n }) => {\n const event = {\n viewableItems: viewableItems.map(toEntry<Item>),\n changed: changed.map(toEntry<Item>),\n userHasScrolled: userHasScrolledRef.current,\n }\n for (const observer of observersRef.current) observer(event)\n },\n },\n ]).current\n\n return { viewabilityConfigCallbackPairs, onScrollBeginDrag }\n}\n\nfunction toEntry<Item>(token: ViewToken) {\n return { key: token.key, isViewable: !!token.isViewable, item: token.item as Item }\n}\n"]}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { RefObject } from 'react';
|
|
2
|
-
import type { FlatList } from 'react-native';
|
|
3
|
-
export declare function useJumpToBottomAction({ listRef }: {
|
|
4
|
-
listRef: RefObject<FlatList | null>;
|
|
5
|
-
}): {
|
|
6
|
-
handleJumpToBottom: () => void;
|
|
7
|
-
isJumpingToBottom: boolean;
|
|
8
|
-
};
|
|
9
|
-
//# sourceMappingURL=use_jump_to_bottom_action.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"use_jump_to_bottom_action.d.ts","sourceRoot":"","sources":["../../src/hooks/use_jump_to_bottom_action.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAA4C,MAAM,OAAO,CAAA;AAC3E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAU5C,wBAAgB,qBAAqB,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAA;CAAE;;;EA8DzF"}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { useQueryClient } from '@tanstack/react-query';
|
|
2
|
-
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
|
-
import { useConversationContext } from '../contexts/conversation_context';
|
|
4
|
-
import { Haptic } from '../utils/native_adapters';
|
|
5
|
-
import { getMessagesQueryKey, getMessagesRequestArgs } from '../utils/request/get_messages';
|
|
6
|
-
import { useApiClient } from './use_api_client';
|
|
7
|
-
import { useJumpToUnreadGates } from './use_jump_to_unread_gates';
|
|
8
|
-
const LATEST_PAGE_SIZE = 25;
|
|
9
|
-
export function useJumpToBottomAction({ listRef }) {
|
|
10
|
-
const { jumpToUnreadEnabled } = useJumpToUnreadGates();
|
|
11
|
-
const { conversationId, currentPageReplyRootId, initialMessageId, setInitialMessageId } = useConversationContext();
|
|
12
|
-
const queryClient = useQueryClient();
|
|
13
|
-
const apiClient = useApiClient();
|
|
14
|
-
const [isJumpingToBottom, setIsJumpingToBottom] = useState(false);
|
|
15
|
-
const mountedRef = useRef(true);
|
|
16
|
-
useEffect(() => () => {
|
|
17
|
-
mountedRef.current = false;
|
|
18
|
-
}, []);
|
|
19
|
-
const handleJumpToBottom = useCallback(() => {
|
|
20
|
-
Haptic.impactLight();
|
|
21
|
-
listRef.current?.scrollToOffset({ offset: 0, animated: true });
|
|
22
|
-
if (!jumpToUnreadEnabled || !initialMessageId)
|
|
23
|
-
return;
|
|
24
|
-
const queryKey = getMessagesQueryKey({
|
|
25
|
-
conversation_id: conversationId,
|
|
26
|
-
reply_root_id: currentPageReplyRootId,
|
|
27
|
-
});
|
|
28
|
-
const args = getMessagesRequestArgs({
|
|
29
|
-
conversation_id: conversationId,
|
|
30
|
-
reply_root_id: currentPageReplyRootId,
|
|
31
|
-
});
|
|
32
|
-
setIsJumpingToBottom(true);
|
|
33
|
-
queryClient
|
|
34
|
-
.cancelQueries({ queryKey })
|
|
35
|
-
.then(() => apiClient.chat.get({
|
|
36
|
-
url: args.url,
|
|
37
|
-
data: { ...args.data, perPage: LATEST_PAGE_SIZE },
|
|
38
|
-
}))
|
|
39
|
-
.then(latest => {
|
|
40
|
-
if (!mountedRef.current)
|
|
41
|
-
return;
|
|
42
|
-
queryClient.setQueryData(queryKey, { pages: [latest], pageParams: [{}] });
|
|
43
|
-
setInitialMessageId(null);
|
|
44
|
-
listRef.current?.scrollToOffset({ offset: 0, animated: false });
|
|
45
|
-
})
|
|
46
|
-
.finally(() => {
|
|
47
|
-
if (mountedRef.current)
|
|
48
|
-
setIsJumpingToBottom(false);
|
|
49
|
-
});
|
|
50
|
-
}, [
|
|
51
|
-
jumpToUnreadEnabled,
|
|
52
|
-
initialMessageId,
|
|
53
|
-
setInitialMessageId,
|
|
54
|
-
queryClient,
|
|
55
|
-
apiClient,
|
|
56
|
-
conversationId,
|
|
57
|
-
currentPageReplyRootId,
|
|
58
|
-
listRef,
|
|
59
|
-
]);
|
|
60
|
-
return { handleJumpToBottom, isJumpingToBottom };
|
|
61
|
-
}
|
|
62
|
-
//# sourceMappingURL=use_jump_to_bottom_action.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"use_jump_to_bottom_action.js","sourceRoot":"","sources":["../../src/hooks/use_jump_to_bottom_action.ts"],"names":[],"mappings":"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","sourcesContent":["import { useQueryClient } from '@tanstack/react-query'\nimport { RefObject, useCallback, useEffect, useRef, useState } from 'react'\nimport type { FlatList } from 'react-native'\nimport { useConversationContext } from '../contexts/conversation_context'\nimport { ApiCollection, MessageResource } from '../types'\nimport { Haptic } from '../utils/native_adapters'\nimport { getMessagesQueryKey, getMessagesRequestArgs } from '../utils/request/get_messages'\nimport { useApiClient } from './use_api_client'\nimport { useJumpToUnreadGates } from './use_jump_to_unread_gates'\n\nconst LATEST_PAGE_SIZE = 25\n\nexport function useJumpToBottomAction({ listRef }: { listRef: RefObject<FlatList | null> }) {\n const { jumpToUnreadEnabled } = useJumpToUnreadGates()\n const { conversationId, currentPageReplyRootId, initialMessageId, setInitialMessageId } =\n useConversationContext()\n const queryClient = useQueryClient()\n const apiClient = useApiClient()\n const [isJumpingToBottom, setIsJumpingToBottom] = useState(false)\n\n const mountedRef = useRef(true)\n useEffect(\n () => () => {\n mountedRef.current = false\n },\n []\n )\n\n const handleJumpToBottom = useCallback(() => {\n Haptic.impactLight()\n listRef.current?.scrollToOffset({ offset: 0, animated: true })\n\n if (!jumpToUnreadEnabled || !initialMessageId) return\n\n const queryKey = getMessagesQueryKey({\n conversation_id: conversationId,\n reply_root_id: currentPageReplyRootId,\n })\n const args = getMessagesRequestArgs({\n conversation_id: conversationId,\n reply_root_id: currentPageReplyRootId,\n })\n\n setIsJumpingToBottom(true)\n\n queryClient\n .cancelQueries({ queryKey })\n .then(() =>\n apiClient.chat.get<ApiCollection<MessageResource>>({\n url: args.url,\n data: { ...args.data, perPage: LATEST_PAGE_SIZE },\n })\n )\n .then(latest => {\n if (!mountedRef.current) return\n queryClient.setQueryData(queryKey, { pages: [latest], pageParams: [{}] })\n setInitialMessageId(null)\n listRef.current?.scrollToOffset({ offset: 0, animated: false })\n })\n .finally(() => {\n if (mountedRef.current) setIsJumpingToBottom(false)\n })\n }, [\n jumpToUnreadEnabled,\n initialMessageId,\n setInitialMessageId,\n queryClient,\n apiClient,\n conversationId,\n currentPageReplyRootId,\n listRef,\n ])\n\n return { handleJumpToBottom, isJumpingToBottom }\n}\n"]}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { RefObject } from 'react';
|
|
2
|
-
import type { FlatList } from 'react-native';
|
|
3
|
-
interface UseJumpToUnreadAnchorArgs<T> {
|
|
4
|
-
listRef: RefObject<FlatList<T> | null>;
|
|
5
|
-
items: T[];
|
|
6
|
-
}
|
|
7
|
-
interface ScrollToIndexFailInfo {
|
|
8
|
-
index: number;
|
|
9
|
-
highestMeasuredFrameIndex: number;
|
|
10
|
-
averageItemLength: number;
|
|
11
|
-
}
|
|
12
|
-
export declare function useJumpToUnreadAnchor<T extends {
|
|
13
|
-
id?: string | number;
|
|
14
|
-
}>({ listRef, items, }: UseJumpToUnreadAnchorArgs<T>): {
|
|
15
|
-
onScrollBeginDrag: () => void;
|
|
16
|
-
onContentSizeChange: () => void;
|
|
17
|
-
onScrollToIndexFailed: (info: ScrollToIndexFailInfo) => void;
|
|
18
|
-
};
|
|
19
|
-
export {};
|
|
20
|
-
//# sourceMappingURL=use_jump_to_unread_anchor.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"use_jump_to_unread_anchor.d.ts","sourceRoot":"","sources":["../../src/hooks/use_jump_to_unread_anchor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAkC,MAAM,OAAO,CAAA;AACjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAI5C,UAAU,yBAAyB,CAAC,CAAC;IACnC,OAAO,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;IACtC,KAAK,EAAE,CAAC,EAAE,CAAA;CACX;AAED,UAAU,qBAAqB;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,yBAAyB,EAAE,MAAM,CAAA;IACjC,iBAAiB,EAAE,MAAM,CAAA;CAC1B;AAED,wBAAgB,qBAAqB,CAAC,CAAC,SAAS;IAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,EAAE,EACxE,OAAO,EACP,KAAK,GACN,EAAE,yBAAyB,CAAC,CAAC,CAAC;;;kCAiCpB,qBAAqB;EAe/B"}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef } from 'react';
|
|
2
|
-
import { useConversationContext } from '../contexts/conversation_context';
|
|
3
|
-
import { useJumpToUnreadGates } from './use_jump_to_unread_gates';
|
|
4
|
-
export function useJumpToUnreadAnchor({ listRef, items, }) {
|
|
5
|
-
const { jumpToUnreadActive } = useJumpToUnreadGates();
|
|
6
|
-
const { initialMessageId } = useConversationContext();
|
|
7
|
-
const hasAnchoredRef = useRef(false);
|
|
8
|
-
const userTouchedRef = useRef(false);
|
|
9
|
-
const retryTimerRef = useRef(null);
|
|
10
|
-
useEffect(() => {
|
|
11
|
-
return () => {
|
|
12
|
-
if (retryTimerRef.current)
|
|
13
|
-
clearTimeout(retryTimerRef.current);
|
|
14
|
-
retryTimerRef.current = null;
|
|
15
|
-
};
|
|
16
|
-
}, []);
|
|
17
|
-
const onScrollBeginDrag = useCallback(() => {
|
|
18
|
-
userTouchedRef.current = true;
|
|
19
|
-
if (retryTimerRef.current) {
|
|
20
|
-
clearTimeout(retryTimerRef.current);
|
|
21
|
-
retryTimerRef.current = null;
|
|
22
|
-
}
|
|
23
|
-
}, []);
|
|
24
|
-
const onContentSizeChange = useCallback(() => {
|
|
25
|
-
if (hasAnchoredRef.current)
|
|
26
|
-
return;
|
|
27
|
-
if (!jumpToUnreadActive || !initialMessageId)
|
|
28
|
-
return;
|
|
29
|
-
if (userTouchedRef.current)
|
|
30
|
-
return;
|
|
31
|
-
const index = items.findIndex(item => String(item.id ?? '') === initialMessageId);
|
|
32
|
-
if (index < 0)
|
|
33
|
-
return;
|
|
34
|
-
hasAnchoredRef.current = true;
|
|
35
|
-
listRef.current?.scrollToIndex({ index, viewPosition: 0.25, animated: false });
|
|
36
|
-
}, [jumpToUnreadActive, initialMessageId, items, listRef]);
|
|
37
|
-
const onScrollToIndexFailed = useCallback((info) => {
|
|
38
|
-
if (userTouchedRef.current)
|
|
39
|
-
return;
|
|
40
|
-
const offset = info.averageItemLength * info.index;
|
|
41
|
-
listRef.current?.scrollToOffset({ offset, animated: false });
|
|
42
|
-
if (retryTimerRef.current)
|
|
43
|
-
clearTimeout(retryTimerRef.current);
|
|
44
|
-
retryTimerRef.current = setTimeout(() => {
|
|
45
|
-
retryTimerRef.current = null;
|
|
46
|
-
if (userTouchedRef.current)
|
|
47
|
-
return;
|
|
48
|
-
listRef.current?.scrollToIndex({ index: info.index, viewPosition: 0.25, animated: false });
|
|
49
|
-
}, 50);
|
|
50
|
-
}, [listRef]);
|
|
51
|
-
return { onScrollBeginDrag, onContentSizeChange, onScrollToIndexFailed };
|
|
52
|
-
}
|
|
53
|
-
//# sourceMappingURL=use_jump_to_unread_anchor.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"use_jump_to_unread_anchor.js","sourceRoot":"","sources":["../../src/hooks/use_jump_to_unread_anchor.ts"],"names":[],"mappings":"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","sourcesContent":["import { RefObject, useCallback, useEffect, useRef } from 'react'\nimport type { FlatList } from 'react-native'\nimport { useConversationContext } from '../contexts/conversation_context'\nimport { useJumpToUnreadGates } from './use_jump_to_unread_gates'\n\ninterface UseJumpToUnreadAnchorArgs<T> {\n listRef: RefObject<FlatList<T> | null>\n items: T[]\n}\n\ninterface ScrollToIndexFailInfo {\n index: number\n highestMeasuredFrameIndex: number\n averageItemLength: number\n}\n\nexport function useJumpToUnreadAnchor<T extends { id?: string | number }>({\n listRef,\n items,\n}: UseJumpToUnreadAnchorArgs<T>) {\n const { jumpToUnreadActive } = useJumpToUnreadGates()\n const { initialMessageId } = useConversationContext()\n const hasAnchoredRef = useRef(false)\n const userTouchedRef = useRef(false)\n const retryTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n useEffect(() => {\n return () => {\n if (retryTimerRef.current) clearTimeout(retryTimerRef.current)\n retryTimerRef.current = null\n }\n }, [])\n\n const onScrollBeginDrag = useCallback(() => {\n userTouchedRef.current = true\n if (retryTimerRef.current) {\n clearTimeout(retryTimerRef.current)\n retryTimerRef.current = null\n }\n }, [])\n\n const onContentSizeChange = useCallback(() => {\n if (hasAnchoredRef.current) return\n if (!jumpToUnreadActive || !initialMessageId) return\n if (userTouchedRef.current) return\n const index = items.findIndex(item => String(item.id ?? '') === initialMessageId)\n if (index < 0) return\n hasAnchoredRef.current = true\n listRef.current?.scrollToIndex({ index, viewPosition: 0.25, animated: false })\n }, [jumpToUnreadActive, initialMessageId, items, listRef])\n\n const onScrollToIndexFailed = useCallback(\n (info: ScrollToIndexFailInfo) => {\n if (userTouchedRef.current) return\n const offset = info.averageItemLength * info.index\n listRef.current?.scrollToOffset({ offset, animated: false })\n if (retryTimerRef.current) clearTimeout(retryTimerRef.current)\n retryTimerRef.current = setTimeout(() => {\n retryTimerRef.current = null\n if (userTouchedRef.current) return\n listRef.current?.scrollToIndex({ index: info.index, viewPosition: 0.25, animated: false })\n }, 50)\n },\n [listRef]\n )\n\n return { onScrollBeginDrag, onContentSizeChange, onScrollToIndexFailed }\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"use_jump_to_unread_gates.d.ts","sourceRoot":"","sources":["../../src/hooks/use_jump_to_unread_gates.ts"],"names":[],"mappings":"AAGA,wBAAgB,oBAAoB;;;EAMnC"}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { useConversationContext } from '../contexts/conversation_context';
|
|
2
|
-
import { availableFeatures, useFeatures } from './use_features';
|
|
3
|
-
export function useJumpToUnreadGates() {
|
|
4
|
-
const { featureEnabled } = useFeatures();
|
|
5
|
-
const { initialMessageIdIsAnchor } = useConversationContext();
|
|
6
|
-
const jumpToUnreadEnabled = featureEnabled(availableFeatures.jump_to_unread);
|
|
7
|
-
const jumpToUnreadActive = jumpToUnreadEnabled && initialMessageIdIsAnchor;
|
|
8
|
-
return { jumpToUnreadEnabled, jumpToUnreadActive };
|
|
9
|
-
}
|
|
10
|
-
//# sourceMappingURL=use_jump_to_unread_gates.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"use_jump_to_unread_gates.js","sourceRoot":"","sources":["../../src/hooks/use_jump_to_unread_gates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAA;AACzE,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAE/D,MAAM,UAAU,oBAAoB;IAClC,MAAM,EAAE,cAAc,EAAE,GAAG,WAAW,EAAE,CAAA;IACxC,MAAM,EAAE,wBAAwB,EAAE,GAAG,sBAAsB,EAAE,CAAA;IAC7D,MAAM,mBAAmB,GAAG,cAAc,CAAC,iBAAiB,CAAC,cAAc,CAAC,CAAA;IAC5E,MAAM,kBAAkB,GAAG,mBAAmB,IAAI,wBAAwB,CAAA;IAC1E,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,CAAA;AACpD,CAAC","sourcesContent":["import { useConversationContext } from '../contexts/conversation_context'\nimport { availableFeatures, useFeatures } from './use_features'\n\nexport function useJumpToUnreadGates() {\n const { featureEnabled } = useFeatures()\n const { initialMessageIdIsAnchor } = useConversationContext()\n const jumpToUnreadEnabled = featureEnabled(availableFeatures.jump_to_unread)\n const jumpToUnreadActive = jumpToUnreadEnabled && initialMessageIdIsAnchor\n return { jumpToUnreadEnabled, jumpToUnreadActive }\n}\n"]}
|