@planningcenter/chat-react-native 4.2.1-qa-staging.5 → 4.3.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/components/conversation/message_form.d.ts +1 -2
- package/build/components/conversation/message_form.d.ts.map +1 -1
- package/build/components/conversation/message_form.js +51 -54
- package/build/components/conversation/message_form.js.map +1 -1
- package/build/components/conversation/message_list.d.ts +2 -3
- package/build/components/conversation/message_list.d.ts.map +1 -1
- package/build/components/conversation/message_list.js +66 -152
- package/build/components/conversation/message_list.js.map +1 -1
- package/build/contexts/api_provider.d.ts.map +1 -1
- package/build/contexts/api_provider.js +1 -2
- package/build/contexts/api_provider.js.map +1 -1
- package/build/contexts/conversation_context.d.ts +1 -3
- package/build/contexts/conversation_context.d.ts.map +1 -1
- package/build/contexts/conversation_context.js +18 -23
- package/build/contexts/conversation_context.js.map +1 -1
- package/build/hooks/paginator_meta.d.ts +1 -3
- package/build/hooks/paginator_meta.d.ts.map +1 -1
- package/build/hooks/paginator_meta.js +8 -25
- package/build/hooks/paginator_meta.js.map +1 -1
- package/build/hooks/use_api.d.ts.map +1 -1
- package/build/hooks/use_api.js +8 -5
- package/build/hooks/use_api.js.map +1 -1
- package/build/hooks/use_conversation_messages.d.ts +2 -9
- package/build/hooks/use_conversation_messages.d.ts.map +1 -1
- package/build/hooks/use_conversation_messages.js +45 -84
- package/build/hooks/use_conversation_messages.js.map +1 -1
- package/build/hooks/use_conversation_messages_jolt_events.d.ts +1 -17
- package/build/hooks/use_conversation_messages_jolt_events.d.ts.map +1 -1
- package/build/hooks/use_conversation_messages_jolt_events.js +80 -89
- package/build/hooks/use_conversation_messages_jolt_events.js.map +1 -1
- package/build/hooks/use_mark_latest_message_read.d.ts +1 -2
- package/build/hooks/use_mark_latest_message_read.d.ts.map +1 -1
- package/build/hooks/use_mark_latest_message_read.js +3 -4
- package/build/hooks/use_mark_latest_message_read.js.map +1 -1
- package/build/hooks/use_message_create_or_update.d.ts.map +1 -1
- package/build/hooks/use_message_create_or_update.js +80 -91
- package/build/hooks/use_message_create_or_update.js.map +1 -1
- package/build/hooks/use_message_reaction_toggle.d.ts.map +1 -1
- package/build/hooks/use_message_reaction_toggle.js +40 -45
- package/build/hooks/use_message_reaction_toggle.js.map +1 -1
- package/build/hooks/use_suspense_api.d.ts.map +1 -1
- package/build/hooks/use_suspense_api.js +8 -5
- package/build/hooks/use_suspense_api.js.map +1 -1
- package/build/screens/conversation_new/components/services_form.js +8 -9
- package/build/screens/conversation_new/components/services_form.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 +254 -481
- package/build/screens/conversation_screen.js.map +1 -1
- package/build/screens/message_actions_screen.d.ts.map +1 -1
- package/build/screens/message_actions_screen.js +26 -21
- package/build/screens/message_actions_screen.js.map +1 -1
- package/build/screens/message_report_screen.d.ts.map +1 -1
- package/build/screens/message_report_screen.js +44 -17
- package/build/screens/message_report_screen.js.map +1 -1
- package/build/screens/reactions_screen.d.ts.map +1 -1
- package/build/screens/reactions_screen.js +88 -70
- package/build/screens/reactions_screen.js.map +1 -1
- package/build/types/api_primitives.d.ts +0 -2
- package/build/types/api_primitives.d.ts.map +1 -1
- package/build/types/api_primitives.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 -3
- package/build/utils/cache/messages_cache.js.map +1 -1
- package/build/utils/cache/optimistically_update_message.d.ts +1 -2
- package/build/utils/cache/optimistically_update_message.d.ts.map +1 -1
- package/build/utils/cache/optimistically_update_message.js +4 -4
- package/build/utils/cache/optimistically_update_message.js.map +1 -1
- package/build/utils/client/types.d.ts +0 -1
- package/build/utils/client/types.d.ts.map +1 -1
- package/build/utils/client/types.js.map +1 -1
- package/build/utils/generate_placeholder_ulid.d.ts +6 -0
- package/build/utils/generate_placeholder_ulid.d.ts.map +1 -1
- package/build/utils/generate_placeholder_ulid.js +2 -2
- package/build/utils/generate_placeholder_ulid.js.map +1 -1
- package/build/utils/request/get_messages.d.ts +6 -18
- package/build/utils/request/get_messages.d.ts.map +1 -1
- package/build/utils/request/get_messages.js +12 -10
- package/build/utils/request/get_messages.js.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/hooks/paginator_meta.test.ts +2 -73
- package/src/components/conversation/__tests__/message_list.test.tsx +0 -42
- package/src/components/conversation/message_form.tsx +0 -3
- package/src/components/conversation/message_list.tsx +4 -58
- package/src/contexts/api_provider.tsx +1 -2
- package/src/contexts/conversation_context.tsx +1 -6
- package/src/hooks/paginator_meta.ts +5 -29
- package/src/hooks/use_api.ts +5 -4
- package/src/hooks/use_conversation_messages.ts +11 -52
- package/src/hooks/use_conversation_messages_jolt_events.ts +43 -62
- package/src/hooks/use_mark_latest_message_read.ts +3 -4
- package/src/hooks/use_message_create_or_update.ts +8 -8
- package/src/hooks/use_message_reaction_toggle.ts +1 -3
- package/src/hooks/use_suspense_api.ts +5 -4
- package/src/screens/conversation_new/components/services_form.tsx +5 -5
- package/src/screens/conversation_screen.tsx +14 -123
- package/src/screens/message_actions_screen.tsx +5 -3
- package/src/screens/message_report_screen.tsx +3 -2
- package/src/screens/reactions_screen.tsx +4 -2
- package/src/types/api_primitives.ts +0 -2
- package/src/utils/cache/__tests__/messages_cache.test.ts +1 -14
- package/src/utils/cache/messages_cache.ts +0 -3
- package/src/utils/cache/optimistically_update_message.ts +5 -2
- package/src/utils/client/types.ts +0 -1
- package/src/utils/generate_placeholder_ulid.ts +2 -2
- package/src/utils/request/get_messages.ts +14 -12
- package/build/components/conversation/anchor_not_found_boundary.d.ts +0 -18
- package/build/components/conversation/anchor_not_found_boundary.d.ts.map +0 -1
- package/build/components/conversation/anchor_not_found_boundary.js +0 -20
- package/build/components/conversation/anchor_not_found_boundary.js.map +0 -1
- package/build/hooks/use_conversation_message_lookup.d.ts +0 -7
- package/build/hooks/use_conversation_message_lookup.d.ts.map +0 -1
- package/build/hooks/use_conversation_message_lookup.js +0 -52
- package/build/hooks/use_conversation_message_lookup.js.map +0 -1
- package/build/hooks/use_fetch_newer_messages.d.ts +0 -9
- package/build/hooks/use_fetch_newer_messages.d.ts.map +0 -1
- package/build/hooks/use_fetch_newer_messages.js +0 -69
- package/build/hooks/use_fetch_newer_messages.js.map +0 -1
- package/build/hooks/use_fetch_newer_on_foreground.d.ts +0 -8
- package/build/hooks/use_fetch_newer_on_foreground.d.ts.map +0 -1
- package/build/hooks/use_fetch_newer_on_foreground.js +0 -42
- package/build/hooks/use_fetch_newer_on_foreground.js.map +0 -1
- package/build/hooks/use_held_back_messages.d.ts +0 -11
- package/build/hooks/use_held_back_messages.d.ts.map +0 -1
- package/build/hooks/use_held_back_messages.js +0 -24
- package/build/hooks/use_held_back_messages.js.map +0 -1
- package/build/utils/cache/find_message_in_caches.d.ts +0 -4
- package/build/utils/cache/find_message_in_caches.d.ts.map +0 -1
- package/build/utils/cache/find_message_in_caches.js +0 -16
- package/build/utils/cache/find_message_in_caches.js.map +0 -1
- package/build/utils/cache/invalidate_refetchable_queries.d.ts +0 -3
- package/build/utils/cache/invalidate_refetchable_queries.d.ts.map +0 -1
- package/build/utils/cache/invalidate_refetchable_queries.js +0 -8
- package/build/utils/cache/invalidate_refetchable_queries.js.map +0 -1
- package/build/utils/cache/prepend_newer_page.d.ts +0 -7
- package/build/utils/cache/prepend_newer_page.d.ts.map +0 -1
- package/build/utils/cache/prepend_newer_page.js +0 -15
- package/build/utils/cache/prepend_newer_page.js.map +0 -1
- package/build/utils/gated_message_cache.d.ts +0 -7
- package/build/utils/gated_message_cache.d.ts.map +0 -1
- package/build/utils/gated_message_cache.js +0 -13
- package/build/utils/gated_message_cache.js.map +0 -1
- package/build/utils/ulid.d.ts +0 -3
- package/build/utils/ulid.d.ts.map +0 -1
- package/build/utils/ulid.js +0 -7
- package/build/utils/ulid.js.map +0 -1
- package/src/__tests__/hooks/use_conversation_messages.test.ts +0 -22
- package/src/__tests__/hooks/use_conversation_messages_jolt_events.test.ts +0 -33
- package/src/__tests__/screens/conversation_new/components/services_form.test.tsx +0 -91
- package/src/__tests__/utils/gated_message_cache.test.ts +0 -26
- package/src/__tests__/utils/request/get_messages.test.ts +0 -24
- package/src/__tests__/utils/ulid.test.ts +0 -15
- package/src/components/conversation/__tests__/anchor_not_found_boundary.test.tsx +0 -51
- package/src/components/conversation/anchor_not_found_boundary.tsx +0 -21
- package/src/hooks/__tests__/use_fetch_newer_messages.test.tsx +0 -95
- package/src/hooks/__tests__/use_held_back_messages.test.tsx +0 -60
- package/src/hooks/__tests__/use_message_reaction_toggle.test.tsx +0 -73
- package/src/hooks/use_conversation_message_lookup.ts +0 -29
- package/src/hooks/use_fetch_newer_messages.ts +0 -52
- package/src/hooks/use_fetch_newer_on_foreground.ts +0 -25
- package/src/hooks/use_held_back_messages.ts +0 -33
- package/src/utils/cache/__tests__/find_message_in_caches.test.ts +0 -37
- package/src/utils/cache/__tests__/invalidate_refetchable_queries.test.ts +0 -23
- package/src/utils/cache/__tests__/prepend_newer_page.test.ts +0 -34
- package/src/utils/cache/find_message_in_caches.ts +0 -20
- package/src/utils/cache/invalidate_refetchable_queries.ts +0 -9
- package/src/utils/cache/prepend_newer_page.ts +0 -19
- package/src/utils/gated_message_cache.ts +0 -19
- package/src/utils/ulid.ts +0 -11
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export const gatedMessageCache = ({
|
|
2
|
-
queryKey,
|
|
3
|
-
acceptNewMessages,
|
|
4
|
-
onSkippedNewMessage
|
|
5
|
-
}) => ({
|
|
6
|
-
queryKey,
|
|
7
|
-
canUpdate: event => {
|
|
8
|
-
if (event !== 'message.created' || acceptNewMessages) return true;
|
|
9
|
-
onSkippedNewMessage?.();
|
|
10
|
-
return false;
|
|
11
|
-
}
|
|
12
|
-
});
|
|
13
|
-
//# sourceMappingURL=gated_message_cache.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"gated_message_cache.js","names":["gatedMessageCache","queryKey","acceptNewMessages","onSkippedNewMessage","canUpdate","event"],"sources":["../../src/utils/gated_message_cache.ts"],"sourcesContent":["import type { MessagesCache } from '../hooks/use_conversation_messages_jolt_events'\n\nexport const gatedMessageCache = ({\n queryKey,\n acceptNewMessages,\n onSkippedNewMessage,\n}: {\n queryKey: unknown[]\n acceptNewMessages: boolean\n onSkippedNewMessage?: () => void\n}): MessagesCache => ({\n queryKey,\n canUpdate: event => {\n if (event !== 'message.created' || acceptNewMessages) return true\n\n onSkippedNewMessage?.()\n return false\n },\n})\n"],"mappings":"AAEA,OAAO,MAAMA,iBAAiB,GAAGA,CAAC;EAChCC,QAAQ;EACRC,iBAAiB;EACjBC;AAKF,CAAC,MAAqB;EACpBF,QAAQ;EACRG,SAAS,EAAEC,KAAK,IAAI;IAClB,IAAIA,KAAK,KAAK,iBAAiB,IAAIH,iBAAiB,EAAE,OAAO,IAAI;IAEjEC,mBAAmB,GAAG,CAAC;IACvB,OAAO,KAAK;EACd;AACF,CAAC,CAAC","ignoreList":[]}
|
package/build/utils/ulid.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ulid.d.ts","sourceRoot":"","sources":["../../src/utils/ulid.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,qCAAqC,CAAA;AAI5D,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMnD"}
|
package/build/utils/ulid.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export const ULID_CHARS = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; // Crockford's Base32
|
|
2
|
-
|
|
3
|
-
const TIME_LENGTH = 10;
|
|
4
|
-
export function decodeUlidTime(ulid) {
|
|
5
|
-
return ulid.slice(0, TIME_LENGTH).toUpperCase().split('').reduce((time, char) => time * 32 + ULID_CHARS.indexOf(char), 0);
|
|
6
|
-
}
|
|
7
|
-
//# sourceMappingURL=ulid.js.map
|
package/build/utils/ulid.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ulid.js","names":["ULID_CHARS","TIME_LENGTH","decodeUlidTime","ulid","slice","toUpperCase","split","reduce","time","char","indexOf"],"sources":["../../src/utils/ulid.ts"],"sourcesContent":["export const ULID_CHARS = '0123456789ABCDEFGHJKMNPQRSTVWXYZ' // Crockford's Base32\n\nconst TIME_LENGTH = 10\n\nexport function decodeUlidTime(ulid: string): number {\n return ulid\n .slice(0, TIME_LENGTH)\n .toUpperCase()\n .split('')\n .reduce((time, char) => time * 32 + ULID_CHARS.indexOf(char), 0)\n}\n"],"mappings":"AAAA,OAAO,MAAMA,UAAU,GAAG,kCAAkC,EAAC;;AAE7D,MAAMC,WAAW,GAAG,EAAE;AAEtB,OAAO,SAASC,cAAcA,CAACC,IAAY,EAAU;EACnD,OAAOA,IAAI,CACRC,KAAK,CAAC,CAAC,EAAEH,WAAW,CAAC,CACrBI,WAAW,CAAC,CAAC,CACbC,KAAK,CAAC,EAAE,CAAC,CACTC,MAAM,CAAC,CAACC,IAAI,EAAEC,IAAI,KAAKD,IAAI,GAAG,EAAE,GAAGR,UAAU,CAACU,OAAO,CAACD,IAAI,CAAC,EAAE,CAAC,CAAC;AACpE","ignoreList":[]}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { anchoredRetry } from '../../hooks/use_conversation_messages'
|
|
2
|
-
import { FailedResponse } from '../../types/api_primitives'
|
|
3
|
-
import { ResponseError } from '../../utils/response_error'
|
|
4
|
-
|
|
5
|
-
const responseError = (status: number) =>
|
|
6
|
-
new ResponseError({ status, statusText: 'nope' } as unknown as FailedResponse)
|
|
7
|
-
|
|
8
|
-
describe('anchoredRetry', () => {
|
|
9
|
-
it('gives up straight away on a missing anchor', () => {
|
|
10
|
-
expect(anchoredRetry(0, responseError(404))).toBe(false)
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
it('does not hammer a rate limit', () => {
|
|
14
|
-
expect(anchoredRetry(0, responseError(429))).toBe(false)
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
it('retries other failures a few times', () => {
|
|
18
|
-
expect(anchoredRetry(0, responseError(500))).toBe(true)
|
|
19
|
-
expect(anchoredRetry(2, new Error('network'))).toBe(true)
|
|
20
|
-
expect(anchoredRetry(3, responseError(500))).toBe(false)
|
|
21
|
-
})
|
|
22
|
-
})
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { queryKeysForEvent } from '../../hooks/use_conversation_messages_jolt_events'
|
|
2
|
-
|
|
3
|
-
const cache = { queryKey: ['shown'] }
|
|
4
|
-
const gated = { queryKey: ['shown'], canUpdate: (e: string) => e !== 'message.created' }
|
|
5
|
-
const topLevel = { sort_key: '01M', reply_root_id: null }
|
|
6
|
-
const replyInThread = { sort_key: '01R', reply_root_id: '01ROOT' }
|
|
7
|
-
const replyElsewhere = { sort_key: '01S', reply_root_id: '01OTHER' }
|
|
8
|
-
const theRoot = { sort_key: '01ROOT', reply_root_id: '01ROOT' }
|
|
9
|
-
|
|
10
|
-
describe('queryKeysForEvent', () => {
|
|
11
|
-
it('writes every message event to the conversation view', () => {
|
|
12
|
-
const view = { replyRootId: null, cache }
|
|
13
|
-
|
|
14
|
-
expect(queryKeysForEvent('message.created', view, topLevel)).toEqual([['shown']])
|
|
15
|
-
expect(queryKeysForEvent('message.created', view, replyInThread)).toEqual([['shown']])
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
it('writes only this thread and its root to a reply view', () => {
|
|
19
|
-
const view = { replyRootId: '01ROOT', cache }
|
|
20
|
-
|
|
21
|
-
expect(queryKeysForEvent('message.created', view, replyInThread)).toEqual([['shown']])
|
|
22
|
-
expect(queryKeysForEvent('message.updated', view, theRoot)).toEqual([['shown']])
|
|
23
|
-
expect(queryKeysForEvent('message.created', view, topLevel)).toEqual([])
|
|
24
|
-
expect(queryKeysForEvent('message.created', view, replyElsewhere)).toEqual([])
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
it('honors the gate on the shown cache', () => {
|
|
28
|
-
const view = { replyRootId: null, cache: gated }
|
|
29
|
-
|
|
30
|
-
expect(queryKeysForEvent('message.created', view, topLevel)).toEqual([])
|
|
31
|
-
expect(queryKeysForEvent('reaction', view, topLevel)).toEqual([['shown']])
|
|
32
|
-
})
|
|
33
|
-
})
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { DefaultTheme, ThemeProvider } from '@react-navigation/native'
|
|
2
|
-
import { render } from '@testing-library/react-native'
|
|
3
|
-
import React from 'react'
|
|
4
|
-
import { SafeAreaProvider } from 'react-native-safe-area-context'
|
|
5
|
-
import { useFindOrCreateServicesConversation } from '../../../../hooks/services/use_find_or_create_services_conversation'
|
|
6
|
-
import { useTeamMembersForNewConversation } from '../../../../hooks/services/use_team_members_for_new_conversation'
|
|
7
|
-
import { ServicesForm } from '../../../../screens/conversation_new/components/services_form'
|
|
8
|
-
|
|
9
|
-
jest.mock('react-native-reanimated', () => ({
|
|
10
|
-
...jest.requireActual('react-native-reanimated/mock'),
|
|
11
|
-
LayoutAnimationConfig: ({ children }: { children: React.ReactNode }) => children,
|
|
12
|
-
}))
|
|
13
|
-
jest.mock('@react-navigation/native', () => ({
|
|
14
|
-
...jest.requireActual('@react-navigation/native'),
|
|
15
|
-
useNavigation: () => ({ navigate: jest.fn(), dispatch: jest.fn(), getParent: () => undefined }),
|
|
16
|
-
}))
|
|
17
|
-
jest.mock('../../../../hooks/use_api_client', () => ({ useApiClient: () => ({}) }))
|
|
18
|
-
jest.mock('../../../../hooks/use_upload_client', () => ({ useUploadClient: () => ({}) }))
|
|
19
|
-
jest.mock('../../../../hooks/use_features', () => ({
|
|
20
|
-
...jest.requireActual('../../../../hooks/use_features'),
|
|
21
|
-
useFeatures: () => ({ features: [], featureEnabled: () => false }),
|
|
22
|
-
}))
|
|
23
|
-
jest.mock('../../../../hooks/use_conversation_validate', () => ({
|
|
24
|
-
useConversationValidate: () => ({ warnings: [], validationPending: false }),
|
|
25
|
-
}))
|
|
26
|
-
jest.mock('../../../../hooks/services/use_services_chat_conversation_payload', () => ({
|
|
27
|
-
useServicesChatConversationPayload: () => ({ payload: undefined, isLoading: false }),
|
|
28
|
-
}))
|
|
29
|
-
jest.mock('../../../../hooks/services/use_services_team', () => ({
|
|
30
|
-
useServicesTeams: () => [],
|
|
31
|
-
}))
|
|
32
|
-
jest.mock('../../../../hooks/services/use_team_plans', () => ({
|
|
33
|
-
useTeamPlans: () => ({
|
|
34
|
-
planOptions: [],
|
|
35
|
-
isFetching: false,
|
|
36
|
-
isError: false,
|
|
37
|
-
findPlan: () => undefined,
|
|
38
|
-
}),
|
|
39
|
-
}))
|
|
40
|
-
jest.mock('../../../../hooks/services/use_team_members_for_new_conversation', () => ({
|
|
41
|
-
useTeamMembersForNewConversation: jest.fn(() => ({ members: [], isError: false })),
|
|
42
|
-
}))
|
|
43
|
-
jest.mock('../../../../hooks/services/use_find_or_create_services_conversation', () => ({
|
|
44
|
-
useFindOrCreateServicesConversation: jest.fn(() => ({
|
|
45
|
-
mutate: jest.fn(),
|
|
46
|
-
isPending: false,
|
|
47
|
-
selectionHasConversation: false,
|
|
48
|
-
isLoadingConversationCheck: false,
|
|
49
|
-
})),
|
|
50
|
-
}))
|
|
51
|
-
|
|
52
|
-
const mockedFindOrCreate = jest.mocked(useFindOrCreateServicesConversation)
|
|
53
|
-
const mockedTeamMembers = jest.mocked(useTeamMembersForNewConversation)
|
|
54
|
-
|
|
55
|
-
const latestArgs = <T extends jest.Mock>(mock: T) => mock.mock.calls.at(-1)?.[0]
|
|
56
|
-
|
|
57
|
-
const safeAreaMetrics = {
|
|
58
|
-
frame: { x: 0, y: 0, width: 390, height: 844 },
|
|
59
|
-
insets: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const renderForm = (props: { initialPlanId?: number } = {}) =>
|
|
63
|
-
render(
|
|
64
|
-
<SafeAreaProvider initialMetrics={safeAreaMetrics}>
|
|
65
|
-
<ThemeProvider value={DefaultTheme}>
|
|
66
|
-
<ServicesForm initialTeamIds={[1, 2]} {...props} />
|
|
67
|
-
</ThemeProvider>
|
|
68
|
-
</SafeAreaProvider>
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
describe('ServicesForm', () => {
|
|
72
|
-
beforeEach(() => {
|
|
73
|
-
jest.clearAllMocks()
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
it('filters by plan when opened from a plan', () => {
|
|
77
|
-
const { getByRole } = renderForm({ initialPlanId: 42 })
|
|
78
|
-
|
|
79
|
-
expect(getByRole('switch').props.value).toBe(true)
|
|
80
|
-
expect(latestArgs(mockedFindOrCreate)).toMatchObject({ teamIds: [1, 2], planId: 42 })
|
|
81
|
-
expect(latestArgs(mockedTeamMembers)).toMatchObject({ teamIds: [1, 2], planId: 42 })
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
it('does not filter by plan when opened without a plan', () => {
|
|
85
|
-
const { getByRole } = renderForm()
|
|
86
|
-
|
|
87
|
-
expect(getByRole('switch').props.value).toBe(false)
|
|
88
|
-
expect(latestArgs(mockedFindOrCreate)).toMatchObject({ teamIds: [1, 2], planId: undefined })
|
|
89
|
-
expect(latestArgs(mockedTeamMembers)).toMatchObject({ teamIds: [1, 2], planId: undefined })
|
|
90
|
-
})
|
|
91
|
-
})
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { gatedMessageCache } from '../../utils/gated_message_cache'
|
|
2
|
-
|
|
3
|
-
describe('gatedMessageCache', () => {
|
|
4
|
-
const queryKey = ['messages']
|
|
5
|
-
|
|
6
|
-
it('keeps new messages out while they are not accepted and reports the skip', () => {
|
|
7
|
-
const onSkippedNewMessage = jest.fn()
|
|
8
|
-
const cache = gatedMessageCache({ queryKey, acceptNewMessages: false, onSkippedNewMessage })
|
|
9
|
-
|
|
10
|
-
expect(cache.queryKey).toBe(queryKey)
|
|
11
|
-
expect(cache.canUpdate?.('message.created')).toBe(false)
|
|
12
|
-
expect(onSkippedNewMessage).toHaveBeenCalledTimes(1)
|
|
13
|
-
expect(cache.canUpdate?.('message.updated')).toBe(true)
|
|
14
|
-
expect(cache.canUpdate?.('message.destroyed')).toBe(true)
|
|
15
|
-
expect(cache.canUpdate?.('reaction')).toBe(true)
|
|
16
|
-
expect(onSkippedNewMessage).toHaveBeenCalledTimes(1)
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
it('lets new messages in once they are accepted', () => {
|
|
20
|
-
const onSkippedNewMessage = jest.fn()
|
|
21
|
-
const cache = gatedMessageCache({ queryKey, acceptNewMessages: true, onSkippedNewMessage })
|
|
22
|
-
|
|
23
|
-
expect(cache.canUpdate?.('message.created')).toBe(true)
|
|
24
|
-
expect(onSkippedNewMessage).not.toHaveBeenCalled()
|
|
25
|
-
})
|
|
26
|
-
})
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { getMessagesQueryKey, getMessagesRequestArgs } from '../../../utils/request/get_messages'
|
|
2
|
-
|
|
3
|
-
describe('getMessagesRequestArgs', () => {
|
|
4
|
-
it('requests the newest page by default', () => {
|
|
5
|
-
const { data } = getMessagesRequestArgs({ conversation_id: 1 })
|
|
6
|
-
|
|
7
|
-
expect(data).not.toHaveProperty('where')
|
|
8
|
-
expect(data).not.toHaveProperty('order')
|
|
9
|
-
})
|
|
10
|
-
|
|
11
|
-
it('opens around the anchor when one is given', () => {
|
|
12
|
-
const { data } = getMessagesRequestArgs({ conversation_id: 1, anchor: '01ANCHOR' })
|
|
13
|
-
|
|
14
|
-
expect(data).toMatchObject({ where: { id_around: '01ANCHOR' } })
|
|
15
|
-
expect(data).not.toHaveProperty('order')
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
it('keeps anchored and live queries in separate cache entries', () => {
|
|
19
|
-
const live = getMessagesQueryKey({ conversation_id: 1 })
|
|
20
|
-
const anchored = getMessagesQueryKey({ conversation_id: 1, anchor: '01ANCHOR' })
|
|
21
|
-
|
|
22
|
-
expect(anchored).not.toEqual(live)
|
|
23
|
-
})
|
|
24
|
-
})
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { generatePlaceholderUlid } from '../../utils/generate_placeholder_ulid'
|
|
2
|
-
import { decodeUlidTime } from '../../utils/ulid'
|
|
3
|
-
|
|
4
|
-
describe('decodeUlidTime', () => {
|
|
5
|
-
it('decodes the spec example', () => {
|
|
6
|
-
expect(decodeUlidTime('01ARYZ6S41TSV4RRFFQ69G5FAV')).toBe(1469918176385)
|
|
7
|
-
})
|
|
8
|
-
|
|
9
|
-
it('round-trips a placeholder ulid', () => {
|
|
10
|
-
const now = Date.now()
|
|
11
|
-
jest.spyOn(Date, 'now').mockReturnValue(now)
|
|
12
|
-
|
|
13
|
-
expect(decodeUlidTime(generatePlaceholderUlid())).toBe(now)
|
|
14
|
-
})
|
|
15
|
-
})
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { render, screen } from '@testing-library/react-native'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
import { Text } from 'react-native'
|
|
4
|
-
import { FailedResponse } from '../../../types/api_primitives'
|
|
5
|
-
import { ResponseError } from '../../../utils/response_error'
|
|
6
|
-
import { AnchorNotFoundBoundary } from '../anchor_not_found_boundary'
|
|
7
|
-
|
|
8
|
-
const responseError = (status: number) =>
|
|
9
|
-
new ResponseError({ status, statusText: 'nope' } as unknown as FailedResponse)
|
|
10
|
-
|
|
11
|
-
function Thrower({ error }: { error: Error | null }) {
|
|
12
|
-
if (error) throw error
|
|
13
|
-
return <Text>content</Text>
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
describe('AnchorNotFoundBoundary', () => {
|
|
17
|
-
beforeEach(() => jest.spyOn(console, 'error').mockImplementation(() => {}))
|
|
18
|
-
|
|
19
|
-
it('reports a missing anchor and renders nothing while the parent drops it', () => {
|
|
20
|
-
const onAnchorNotFound = jest.fn()
|
|
21
|
-
|
|
22
|
-
render(
|
|
23
|
-
<AnchorNotFoundBoundary onAnchorNotFound={onAnchorNotFound}>
|
|
24
|
-
<Thrower error={responseError(404)} />
|
|
25
|
-
</AnchorNotFoundBoundary>
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
expect(onAnchorNotFound).toHaveBeenCalledTimes(1)
|
|
29
|
-
expect(screen.queryByText('content')).toBeNull()
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
it('lets every other error through untouched', () => {
|
|
33
|
-
const onAnchorNotFound = jest.fn()
|
|
34
|
-
|
|
35
|
-
expect(() =>
|
|
36
|
-
render(
|
|
37
|
-
<AnchorNotFoundBoundary onAnchorNotFound={onAnchorNotFound}>
|
|
38
|
-
<Thrower error={responseError(403)} />
|
|
39
|
-
</AnchorNotFoundBoundary>
|
|
40
|
-
)
|
|
41
|
-
).toThrow()
|
|
42
|
-
expect(() =>
|
|
43
|
-
render(
|
|
44
|
-
<AnchorNotFoundBoundary onAnchorNotFound={onAnchorNotFound}>
|
|
45
|
-
<Thrower error={new Error('render blew up')} />
|
|
46
|
-
</AnchorNotFoundBoundary>
|
|
47
|
-
)
|
|
48
|
-
).toThrow('render blew up')
|
|
49
|
-
expect(onAnchorNotFound).not.toHaveBeenCalled()
|
|
50
|
-
})
|
|
51
|
-
})
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import React, { PropsWithChildren } from 'react'
|
|
2
|
-
import { ResponseError } from '../../utils/response_error'
|
|
3
|
-
|
|
4
|
-
type Props = PropsWithChildren<{ onAnchorNotFound: () => void }>
|
|
5
|
-
|
|
6
|
-
export class AnchorNotFoundBoundary extends React.Component<Props, { recovering: boolean }> {
|
|
7
|
-
state = { recovering: false }
|
|
8
|
-
|
|
9
|
-
static getDerivedStateFromError(error: Error) {
|
|
10
|
-
if (error instanceof ResponseError && error.status === 404) return { recovering: true }
|
|
11
|
-
throw error
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
componentDidCatch() {
|
|
15
|
-
this.props.onAnchorNotFound()
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
render() {
|
|
19
|
-
return this.state.recovering ? null : this.props.children
|
|
20
|
-
}
|
|
21
|
-
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { InfiniteData, QueryClientProvider } from '@tanstack/react-query'
|
|
2
|
-
import { act, renderHook } from '@testing-library/react-native'
|
|
3
|
-
import React from 'react'
|
|
4
|
-
import { buildTestQueryClient } from '../../__utils__/query_client'
|
|
5
|
-
import type { ApiCollection, MessageResource } from '../../types'
|
|
6
|
-
import { getMessagesQueryKey } from '../../utils/request/get_messages'
|
|
7
|
-
import { ApiClient, useApiClient } from '../use_api_client'
|
|
8
|
-
import { useFetchNewerMessages } from '../use_fetch_newer_messages'
|
|
9
|
-
|
|
10
|
-
jest.mock('../use_api_client')
|
|
11
|
-
|
|
12
|
-
type QueryData = InfiniteData<ApiCollection<MessageResource>>
|
|
13
|
-
|
|
14
|
-
const page = (ids: string[], next?: { idGt: string }) => ({
|
|
15
|
-
data: ids.map(id => ({ id, type: 'Message' }) as unknown as MessageResource),
|
|
16
|
-
links: {},
|
|
17
|
-
meta: { count: ids.length, totalCount: ids.length, next },
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
const queryKey = getMessagesQueryKey({ conversation_id: 1 })
|
|
21
|
-
let queryClient = buildTestQueryClient()
|
|
22
|
-
const get = jest.fn()
|
|
23
|
-
|
|
24
|
-
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
25
|
-
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
const cachedIds = () =>
|
|
29
|
-
queryClient.getQueryData<QueryData>(queryKey)?.pages.flatMap(p => p.data.map(m => m.id))
|
|
30
|
-
|
|
31
|
-
beforeEach(() => {
|
|
32
|
-
queryClient = buildTestQueryClient()
|
|
33
|
-
queryClient.setQueryData<QueryData>(queryKey, {
|
|
34
|
-
pages: [page(['01C', '01B']), page(['01A'])],
|
|
35
|
-
pageParams: [{}, { where: { id_lt: '01B' } }],
|
|
36
|
-
})
|
|
37
|
-
get.mockReset()
|
|
38
|
-
;(useApiClient as jest.MockedFunction<typeof useApiClient>).mockReturnValue({
|
|
39
|
-
chat: { get },
|
|
40
|
-
} as unknown as ApiClient)
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
describe('useFetchNewerMessages', () => {
|
|
44
|
-
it('follows meta.next until everything newer is in the cache', async () => {
|
|
45
|
-
get
|
|
46
|
-
.mockResolvedValueOnce(page(['01D', '01E'], { idGt: '01E' }))
|
|
47
|
-
.mockResolvedValueOnce(page(['01F']))
|
|
48
|
-
const { result } = renderHook(() => useFetchNewerMessages({ conversation_id: 1, queryKey }), {
|
|
49
|
-
wrapper,
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
await act(() => result.current('01C'))
|
|
53
|
-
|
|
54
|
-
expect(get).toHaveBeenCalledTimes(2)
|
|
55
|
-
expect(get.mock.calls[1][0].data.where).toEqual({ id_gt: '01E' })
|
|
56
|
-
expect(cachedIds()).toEqual(['01D', '01E', '01F', '01C', '01B', '01A'])
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
it('rejects when the request fails so the caller can try again later', async () => {
|
|
60
|
-
get.mockRejectedValueOnce(new Error('offline'))
|
|
61
|
-
const { result } = renderHook(() => useFetchNewerMessages({ conversation_id: 1, queryKey }), {
|
|
62
|
-
wrapper,
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
await expect(act(() => result.current('01C'))).rejects.toThrow('offline')
|
|
66
|
-
expect(cachedIds()).toEqual(['01C', '01B', '01A'])
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
it('hands off instead of refetching when more is left than it will walk', async () => {
|
|
70
|
-
for (let i = 0; i < 5; i++) get.mockResolvedValueOnce(page([`01D${i}`], { idGt: `01D${i}` }))
|
|
71
|
-
const onTooFarBehind = jest.fn()
|
|
72
|
-
const invalidate = jest.spyOn(queryClient, 'invalidateQueries')
|
|
73
|
-
const { result } = renderHook(
|
|
74
|
-
() => useFetchNewerMessages({ conversation_id: 1, queryKey, onTooFarBehind }),
|
|
75
|
-
{ wrapper }
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
await act(() => result.current('01C'))
|
|
79
|
-
|
|
80
|
-
expect(onTooFarBehind).toHaveBeenCalledTimes(1)
|
|
81
|
-
expect(invalidate).not.toHaveBeenCalled()
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
it('refetches the live cache when more is left and nobody takes over', async () => {
|
|
85
|
-
for (let i = 0; i < 5; i++) get.mockResolvedValueOnce(page([`01D${i}`], { idGt: `01D${i}` }))
|
|
86
|
-
const invalidate = jest.spyOn(queryClient, 'invalidateQueries')
|
|
87
|
-
const { result } = renderHook(() => useFetchNewerMessages({ conversation_id: 1, queryKey }), {
|
|
88
|
-
wrapper,
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
await act(() => result.current('01C'))
|
|
92
|
-
|
|
93
|
-
expect(invalidate).toHaveBeenCalledWith({ queryKey })
|
|
94
|
-
})
|
|
95
|
-
})
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { act, renderHook } from '@testing-library/react-native'
|
|
2
|
-
import { useHeldBackMessages } from '../use_held_back_messages'
|
|
3
|
-
|
|
4
|
-
type Props = { newestMessageId?: string; readingHistory: boolean; caughtUp: boolean }
|
|
5
|
-
|
|
6
|
-
const setup = (fetchNewer: jest.Mock, initial: Props) =>
|
|
7
|
-
renderHook((props: Props) => useHeldBackMessages({ fetchNewer, ...props }), {
|
|
8
|
-
initialProps: initial,
|
|
9
|
-
})
|
|
10
|
-
|
|
11
|
-
describe('useHeldBackMessages', () => {
|
|
12
|
-
it('catches up from where the first skip happened, not from the newest message', async () => {
|
|
13
|
-
const fetchNewer = jest.fn().mockResolvedValue(undefined)
|
|
14
|
-
const { result, rerender } = setup(fetchNewer, {
|
|
15
|
-
newestMessageId: 'A',
|
|
16
|
-
readingHistory: true,
|
|
17
|
-
caughtUp: true,
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
act(() => result.current.onSkippedNewMessage())
|
|
21
|
-
rerender({ newestMessageId: 'B', readingHistory: true, caughtUp: true })
|
|
22
|
-
await act(async () => rerender({ newestMessageId: 'B', readingHistory: false, caughtUp: true }))
|
|
23
|
-
|
|
24
|
-
expect(fetchNewer).toHaveBeenCalledTimes(1)
|
|
25
|
-
expect(fetchNewer).toHaveBeenCalledWith('A')
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
it('keeps the cursor when the catch-up fails and tries again next time', async () => {
|
|
29
|
-
const fetchNewer = jest
|
|
30
|
-
.fn()
|
|
31
|
-
.mockRejectedValueOnce(new Error('offline'))
|
|
32
|
-
.mockResolvedValue(undefined)
|
|
33
|
-
const { result, rerender } = setup(fetchNewer, {
|
|
34
|
-
newestMessageId: 'A',
|
|
35
|
-
readingHistory: true,
|
|
36
|
-
caughtUp: true,
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
act(() => result.current.onSkippedNewMessage())
|
|
40
|
-
await act(async () => rerender({ newestMessageId: 'A', readingHistory: false, caughtUp: true }))
|
|
41
|
-
rerender({ newestMessageId: 'C', readingHistory: true, caughtUp: true })
|
|
42
|
-
await act(async () => rerender({ newestMessageId: 'C', readingHistory: false, caughtUp: true }))
|
|
43
|
-
|
|
44
|
-
expect(fetchNewer).toHaveBeenCalledTimes(2)
|
|
45
|
-
expect(fetchNewer).toHaveBeenLastCalledWith('A')
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
it('does nothing when nothing was held back', async () => {
|
|
49
|
-
const fetchNewer = jest.fn()
|
|
50
|
-
const { rerender } = setup(fetchNewer, {
|
|
51
|
-
newestMessageId: 'A',
|
|
52
|
-
readingHistory: true,
|
|
53
|
-
caughtUp: true,
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
await act(async () => rerender({ newestMessageId: 'A', readingHistory: false, caughtUp: true }))
|
|
57
|
-
|
|
58
|
-
expect(fetchNewer).not.toHaveBeenCalled()
|
|
59
|
-
})
|
|
60
|
-
})
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { InfiniteData, QueryClientProvider } from '@tanstack/react-query'
|
|
2
|
-
import { act, renderHook } from '@testing-library/react-native'
|
|
3
|
-
import React from 'react'
|
|
4
|
-
import { buildTestQueryClient } from '../../__utils__/query_client'
|
|
5
|
-
import { ConversationContextProvider } from '../../contexts/conversation_context'
|
|
6
|
-
import type { ApiCollection, MessageResource } from '../../types'
|
|
7
|
-
import { getMessagesQueryKey } from '../../utils/request/get_messages'
|
|
8
|
-
import { ApiClient, useApiClient } from '../use_api_client'
|
|
9
|
-
import { useMessageReactionToggle } from '../use_message_reaction_toggle'
|
|
10
|
-
|
|
11
|
-
jest.mock('../use_api_client')
|
|
12
|
-
|
|
13
|
-
type QueryData = InfiniteData<ApiCollection<MessageResource>>
|
|
14
|
-
|
|
15
|
-
const message = { id: '01M', type: 'Message', reactionCounts: [] } as unknown as MessageResource
|
|
16
|
-
const liveKey = getMessagesQueryKey({ conversation_id: 1 })
|
|
17
|
-
const anchoredKey = getMessagesQueryKey({ conversation_id: 1, anchor: '01A' })
|
|
18
|
-
const seed = (): QueryData => ({
|
|
19
|
-
pages: [{ data: [message], links: {}, meta: { count: 1, totalCount: 1 } }],
|
|
20
|
-
pageParams: [{}],
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
const heartsIn = (queryClient: ReturnType<typeof buildTestQueryClient>, key: unknown[]) =>
|
|
24
|
-
queryClient
|
|
25
|
-
.getQueryData<QueryData>(key)
|
|
26
|
-
?.pages[0].data[0].reactionCounts.map(r => `${r.value}:${r.count}`)
|
|
27
|
-
|
|
28
|
-
describe('useMessageReactionToggle', () => {
|
|
29
|
-
beforeEach(() => {
|
|
30
|
-
;(useApiClient as jest.MockedFunction<typeof useApiClient>).mockReturnValue({
|
|
31
|
-
chat: { post: jest.fn(() => new Promise(() => {})) },
|
|
32
|
-
} as unknown as ApiClient)
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
it('applies the optimistic reaction to the cache the screen is showing', async () => {
|
|
36
|
-
const queryClient = buildTestQueryClient({ defaultOptions: { queries: { gcTime: Infinity } } })
|
|
37
|
-
queryClient.setQueryData<QueryData>(anchoredKey, seed())
|
|
38
|
-
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
39
|
-
<QueryClientProvider client={queryClient}>
|
|
40
|
-
<ConversationContextProvider
|
|
41
|
-
conversationId={1}
|
|
42
|
-
currentPageReplyRootId={null}
|
|
43
|
-
messagesQueryKey={anchoredKey}
|
|
44
|
-
>
|
|
45
|
-
{children}
|
|
46
|
-
</ConversationContextProvider>
|
|
47
|
-
</QueryClientProvider>
|
|
48
|
-
)
|
|
49
|
-
const { result } = renderHook(() => useMessageReactionToggle({ conversation_id: 1, message }), {
|
|
50
|
-
wrapper,
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
await act(async () => result.current.handleReactionToggle({ value: 'heart', mine: false }))
|
|
54
|
-
|
|
55
|
-
expect(heartsIn(queryClient, anchoredKey)).toEqual(['heart:1'])
|
|
56
|
-
expect(queryClient.getQueryData(liveKey)).toBeUndefined()
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
it('falls back to the live cache when no screen key is provided', async () => {
|
|
60
|
-
const queryClient = buildTestQueryClient({ defaultOptions: { queries: { gcTime: Infinity } } })
|
|
61
|
-
queryClient.setQueryData<QueryData>(liveKey, seed())
|
|
62
|
-
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
63
|
-
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
64
|
-
)
|
|
65
|
-
const { result } = renderHook(() => useMessageReactionToggle({ conversation_id: 1, message }), {
|
|
66
|
-
wrapper,
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
await act(async () => result.current.handleReactionToggle({ value: 'heart', mine: false }))
|
|
70
|
-
|
|
71
|
-
expect(heartsIn(queryClient, liveKey)).toEqual(['heart:1'])
|
|
72
|
-
})
|
|
73
|
-
})
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { useQueryClient } from '@tanstack/react-query'
|
|
2
|
-
import { useCallback, useSyncExternalStore } from 'react'
|
|
3
|
-
import { findMessageInCaches } from '../utils/cache/find_message_in_caches'
|
|
4
|
-
import { useConversationMessage } from './use_conversation_message'
|
|
5
|
-
|
|
6
|
-
type Options = {
|
|
7
|
-
conversation_id: number
|
|
8
|
-
message_id: string
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export const useConversationMessageLookup = ({ conversation_id, message_id }: Options) => {
|
|
12
|
-
const queryClient = useQueryClient()
|
|
13
|
-
const subscribe = useCallback(
|
|
14
|
-
(onChange: () => void) => queryClient.getQueryCache().subscribe(onChange),
|
|
15
|
-
[queryClient]
|
|
16
|
-
)
|
|
17
|
-
const getSnapshot = useCallback(
|
|
18
|
-
() => findMessageInCaches(queryClient, conversation_id, message_id),
|
|
19
|
-
[conversation_id, message_id, queryClient]
|
|
20
|
-
)
|
|
21
|
-
const cached = useSyncExternalStore(subscribe, getSnapshot)
|
|
22
|
-
const fetched = useConversationMessage({
|
|
23
|
-
conversation_id,
|
|
24
|
-
messageId: message_id,
|
|
25
|
-
enabled: !cached,
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
return cached ?? fetched.message
|
|
29
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { useQueryClient } from '@tanstack/react-query'
|
|
2
|
-
import { useCallback } from 'react'
|
|
3
|
-
import { ApiCollection, MessageResource } from '../types'
|
|
4
|
-
import { prependNewerPage } from '../utils/cache/prepend_newer_page'
|
|
5
|
-
import { getMessagesRequestArgs } from '../utils/request/get_messages'
|
|
6
|
-
import { useApiClient } from './use_api_client'
|
|
7
|
-
|
|
8
|
-
const MAX_PAGES = 5
|
|
9
|
-
|
|
10
|
-
type Options = {
|
|
11
|
-
conversation_id: number
|
|
12
|
-
reply_root_id?: string | null
|
|
13
|
-
queryKey: unknown[]
|
|
14
|
-
onTooFarBehind?: () => void
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export const useFetchNewerMessages = ({
|
|
18
|
-
conversation_id,
|
|
19
|
-
reply_root_id,
|
|
20
|
-
queryKey,
|
|
21
|
-
onTooFarBehind,
|
|
22
|
-
}: Options) => {
|
|
23
|
-
const queryClient = useQueryClient()
|
|
24
|
-
const apiClient = useApiClient()
|
|
25
|
-
|
|
26
|
-
return useCallback(
|
|
27
|
-
async (newestMessageId: string) => {
|
|
28
|
-
const { url, data } = getMessagesRequestArgs({ conversation_id, reply_root_id })
|
|
29
|
-
const pages: ApiCollection<MessageResource>[] = []
|
|
30
|
-
let cursor: string | undefined = newestMessageId
|
|
31
|
-
|
|
32
|
-
while (cursor && pages.length < MAX_PAGES) {
|
|
33
|
-
const page: ApiCollection<MessageResource> = await apiClient.chat.get({
|
|
34
|
-
url,
|
|
35
|
-
data: { ...data, where: { id_gt: cursor }, order: 'asc' },
|
|
36
|
-
})
|
|
37
|
-
pages.push(page)
|
|
38
|
-
cursor = page.meta?.next?.idGt
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
queryClient.setQueryData<Parameters<typeof prependNewerPage<MessageResource>>[0]>(
|
|
42
|
-
queryKey,
|
|
43
|
-
prev => prependNewerPage(prev, pages)
|
|
44
|
-
)
|
|
45
|
-
const moreThanWeFetched = !!cursor
|
|
46
|
-
if (!moreThanWeFetched) return
|
|
47
|
-
if (onTooFarBehind) onTooFarBehind()
|
|
48
|
-
else await queryClient.invalidateQueries({ queryKey })
|
|
49
|
-
},
|
|
50
|
-
[apiClient, conversation_id, onTooFarBehind, queryClient, queryKey, reply_root_id]
|
|
51
|
-
)
|
|
52
|
-
}
|