@planningcenter/chat-react-native 4.2.1-qa-staging.5 → 4.2.1-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.
Files changed (185) hide show
  1. package/build/components/conversation/message_form.d.ts +1 -2
  2. package/build/components/conversation/message_form.d.ts.map +1 -1
  3. package/build/components/conversation/message_form.js +51 -54
  4. package/build/components/conversation/message_form.js.map +1 -1
  5. package/build/components/conversation/message_list.d.ts +2 -3
  6. package/build/components/conversation/message_list.d.ts.map +1 -1
  7. package/build/components/conversation/message_list.js +66 -152
  8. package/build/components/conversation/message_list.js.map +1 -1
  9. package/build/contexts/api_provider.d.ts.map +1 -1
  10. package/build/contexts/api_provider.js +1 -2
  11. package/build/contexts/api_provider.js.map +1 -1
  12. package/build/contexts/conversation_context.d.ts +1 -3
  13. package/build/contexts/conversation_context.d.ts.map +1 -1
  14. package/build/contexts/conversation_context.js +18 -23
  15. package/build/contexts/conversation_context.js.map +1 -1
  16. package/build/hooks/index.d.ts +0 -1
  17. package/build/hooks/index.d.ts.map +1 -1
  18. package/build/hooks/index.js +0 -1
  19. package/build/hooks/index.js.map +1 -1
  20. package/build/hooks/paginator_meta.d.ts +1 -3
  21. package/build/hooks/paginator_meta.d.ts.map +1 -1
  22. package/build/hooks/paginator_meta.js +8 -25
  23. package/build/hooks/paginator_meta.js.map +1 -1
  24. package/build/hooks/use_api.d.ts.map +1 -1
  25. package/build/hooks/use_api.js +8 -5
  26. package/build/hooks/use_api.js.map +1 -1
  27. package/build/hooks/use_conversation_messages.d.ts +2 -9
  28. package/build/hooks/use_conversation_messages.d.ts.map +1 -1
  29. package/build/hooks/use_conversation_messages.js +45 -84
  30. package/build/hooks/use_conversation_messages.js.map +1 -1
  31. package/build/hooks/use_conversation_messages_jolt_events.d.ts +1 -17
  32. package/build/hooks/use_conversation_messages_jolt_events.d.ts.map +1 -1
  33. package/build/hooks/use_conversation_messages_jolt_events.js +80 -89
  34. package/build/hooks/use_conversation_messages_jolt_events.js.map +1 -1
  35. package/build/hooks/use_mark_latest_message_read.d.ts +1 -2
  36. package/build/hooks/use_mark_latest_message_read.d.ts.map +1 -1
  37. package/build/hooks/use_mark_latest_message_read.js +3 -4
  38. package/build/hooks/use_mark_latest_message_read.js.map +1 -1
  39. package/build/hooks/use_message_create_or_update.d.ts.map +1 -1
  40. package/build/hooks/use_message_create_or_update.js +80 -91
  41. package/build/hooks/use_message_create_or_update.js.map +1 -1
  42. package/build/hooks/use_message_reaction_toggle.d.ts.map +1 -1
  43. package/build/hooks/use_message_reaction_toggle.js +40 -45
  44. package/build/hooks/use_message_reaction_toggle.js.map +1 -1
  45. package/build/hooks/use_suspense_api.d.ts.map +1 -1
  46. package/build/hooks/use_suspense_api.js +8 -5
  47. package/build/hooks/use_suspense_api.js.map +1 -1
  48. package/build/screens/conversation_new/components/services_form.js +8 -9
  49. package/build/screens/conversation_new/components/services_form.js.map +1 -1
  50. package/build/screens/conversation_screen.d.ts +0 -1
  51. package/build/screens/conversation_screen.d.ts.map +1 -1
  52. package/build/screens/conversation_screen.js +254 -481
  53. package/build/screens/conversation_screen.js.map +1 -1
  54. package/build/screens/message_actions_screen.d.ts.map +1 -1
  55. package/build/screens/message_actions_screen.js +26 -21
  56. package/build/screens/message_actions_screen.js.map +1 -1
  57. package/build/screens/message_report_screen.d.ts.map +1 -1
  58. package/build/screens/message_report_screen.js +44 -17
  59. package/build/screens/message_report_screen.js.map +1 -1
  60. package/build/screens/reactions_screen.d.ts.map +1 -1
  61. package/build/screens/reactions_screen.js +88 -70
  62. package/build/screens/reactions_screen.js.map +1 -1
  63. package/build/types/api_primitives.d.ts +0 -2
  64. package/build/types/api_primitives.d.ts.map +1 -1
  65. package/build/types/api_primitives.js.map +1 -1
  66. package/build/types/resources/member.d.ts +0 -1
  67. package/build/types/resources/member.d.ts.map +1 -1
  68. package/build/types/resources/member.js.map +1 -1
  69. package/build/utils/cache/messages_cache.d.ts +0 -1
  70. package/build/utils/cache/messages_cache.d.ts.map +1 -1
  71. package/build/utils/cache/messages_cache.js +0 -3
  72. package/build/utils/cache/messages_cache.js.map +1 -1
  73. package/build/utils/cache/optimistically_update_message.d.ts +1 -2
  74. package/build/utils/cache/optimistically_update_message.d.ts.map +1 -1
  75. package/build/utils/cache/optimistically_update_message.js +4 -4
  76. package/build/utils/cache/optimistically_update_message.js.map +1 -1
  77. package/build/utils/client/types.d.ts +0 -1
  78. package/build/utils/client/types.d.ts.map +1 -1
  79. package/build/utils/client/types.js.map +1 -1
  80. package/build/utils/generate_placeholder_ulid.d.ts +6 -0
  81. package/build/utils/generate_placeholder_ulid.d.ts.map +1 -1
  82. package/build/utils/generate_placeholder_ulid.js +2 -2
  83. package/build/utils/generate_placeholder_ulid.js.map +1 -1
  84. package/build/utils/request/get_messages.d.ts +6 -18
  85. package/build/utils/request/get_messages.d.ts.map +1 -1
  86. package/build/utils/request/get_messages.js +12 -10
  87. package/build/utils/request/get_messages.js.map +1 -1
  88. package/package.json +3 -3
  89. package/src/__tests__/hooks/paginator_meta.test.ts +2 -73
  90. package/src/components/conversation/__tests__/message_list.test.tsx +0 -42
  91. package/src/components/conversation/message_form.tsx +0 -3
  92. package/src/components/conversation/message_list.tsx +4 -58
  93. package/src/contexts/api_provider.tsx +1 -2
  94. package/src/contexts/conversation_context.tsx +1 -6
  95. package/src/hooks/index.ts +0 -1
  96. package/src/hooks/paginator_meta.ts +5 -29
  97. package/src/hooks/use_api.ts +5 -4
  98. package/src/hooks/use_conversation_messages.ts +11 -52
  99. package/src/hooks/use_conversation_messages_jolt_events.ts +43 -62
  100. package/src/hooks/use_mark_latest_message_read.ts +3 -4
  101. package/src/hooks/use_message_create_or_update.ts +8 -8
  102. package/src/hooks/use_message_reaction_toggle.ts +1 -3
  103. package/src/hooks/use_suspense_api.ts +5 -4
  104. package/src/screens/conversation_new/components/services_form.tsx +5 -5
  105. package/src/screens/conversation_screen.tsx +14 -123
  106. package/src/screens/message_actions_screen.tsx +5 -3
  107. package/src/screens/message_report_screen.tsx +3 -2
  108. package/src/screens/reactions_screen.tsx +4 -2
  109. package/src/types/api_primitives.ts +0 -2
  110. package/src/types/resources/member.ts +0 -1
  111. package/src/utils/cache/__tests__/messages_cache.test.ts +1 -14
  112. package/src/utils/cache/messages_cache.ts +0 -3
  113. package/src/utils/cache/optimistically_update_message.ts +5 -2
  114. package/src/utils/client/types.ts +0 -1
  115. package/src/utils/generate_placeholder_ulid.ts +2 -2
  116. package/src/utils/request/get_messages.ts +14 -12
  117. package/build/components/conversation/anchor_not_found_boundary.d.ts +0 -18
  118. package/build/components/conversation/anchor_not_found_boundary.d.ts.map +0 -1
  119. package/build/components/conversation/anchor_not_found_boundary.js +0 -20
  120. package/build/components/conversation/anchor_not_found_boundary.js.map +0 -1
  121. package/build/hooks/use_conversation_message_lookup.d.ts +0 -7
  122. package/build/hooks/use_conversation_message_lookup.d.ts.map +0 -1
  123. package/build/hooks/use_conversation_message_lookup.js +0 -52
  124. package/build/hooks/use_conversation_message_lookup.js.map +0 -1
  125. package/build/hooks/use_direct_message_eligibility.d.ts +0 -14
  126. package/build/hooks/use_direct_message_eligibility.d.ts.map +0 -1
  127. package/build/hooks/use_direct_message_eligibility.js +0 -87
  128. package/build/hooks/use_direct_message_eligibility.js.map +0 -1
  129. package/build/hooks/use_fetch_newer_messages.d.ts +0 -9
  130. package/build/hooks/use_fetch_newer_messages.d.ts.map +0 -1
  131. package/build/hooks/use_fetch_newer_messages.js +0 -69
  132. package/build/hooks/use_fetch_newer_messages.js.map +0 -1
  133. package/build/hooks/use_fetch_newer_on_foreground.d.ts +0 -8
  134. package/build/hooks/use_fetch_newer_on_foreground.d.ts.map +0 -1
  135. package/build/hooks/use_fetch_newer_on_foreground.js +0 -42
  136. package/build/hooks/use_fetch_newer_on_foreground.js.map +0 -1
  137. package/build/hooks/use_held_back_messages.d.ts +0 -11
  138. package/build/hooks/use_held_back_messages.d.ts.map +0 -1
  139. package/build/hooks/use_held_back_messages.js +0 -24
  140. package/build/hooks/use_held_back_messages.js.map +0 -1
  141. package/build/utils/cache/find_message_in_caches.d.ts +0 -4
  142. package/build/utils/cache/find_message_in_caches.d.ts.map +0 -1
  143. package/build/utils/cache/find_message_in_caches.js +0 -16
  144. package/build/utils/cache/find_message_in_caches.js.map +0 -1
  145. package/build/utils/cache/invalidate_refetchable_queries.d.ts +0 -3
  146. package/build/utils/cache/invalidate_refetchable_queries.d.ts.map +0 -1
  147. package/build/utils/cache/invalidate_refetchable_queries.js +0 -8
  148. package/build/utils/cache/invalidate_refetchable_queries.js.map +0 -1
  149. package/build/utils/cache/prepend_newer_page.d.ts +0 -7
  150. package/build/utils/cache/prepend_newer_page.d.ts.map +0 -1
  151. package/build/utils/cache/prepend_newer_page.js +0 -15
  152. package/build/utils/cache/prepend_newer_page.js.map +0 -1
  153. package/build/utils/gated_message_cache.d.ts +0 -7
  154. package/build/utils/gated_message_cache.d.ts.map +0 -1
  155. package/build/utils/gated_message_cache.js +0 -13
  156. package/build/utils/gated_message_cache.js.map +0 -1
  157. package/build/utils/ulid.d.ts +0 -3
  158. package/build/utils/ulid.d.ts.map +0 -1
  159. package/build/utils/ulid.js +0 -7
  160. package/build/utils/ulid.js.map +0 -1
  161. package/src/__tests__/hooks/use_conversation_messages.test.ts +0 -22
  162. package/src/__tests__/hooks/use_conversation_messages_jolt_events.test.ts +0 -33
  163. package/src/__tests__/hooks/use_direct_message_eligibility.test.tsx +0 -83
  164. package/src/__tests__/screens/conversation_new/components/services_form.test.tsx +0 -91
  165. package/src/__tests__/utils/gated_message_cache.test.ts +0 -26
  166. package/src/__tests__/utils/request/get_messages.test.ts +0 -24
  167. package/src/__tests__/utils/ulid.test.ts +0 -15
  168. package/src/components/conversation/__tests__/anchor_not_found_boundary.test.tsx +0 -51
  169. package/src/components/conversation/anchor_not_found_boundary.tsx +0 -21
  170. package/src/hooks/__tests__/use_fetch_newer_messages.test.tsx +0 -95
  171. package/src/hooks/__tests__/use_held_back_messages.test.tsx +0 -60
  172. package/src/hooks/__tests__/use_message_reaction_toggle.test.tsx +0 -73
  173. package/src/hooks/use_conversation_message_lookup.ts +0 -29
  174. package/src/hooks/use_direct_message_eligibility.ts +0 -40
  175. package/src/hooks/use_fetch_newer_messages.ts +0 -52
  176. package/src/hooks/use_fetch_newer_on_foreground.ts +0 -25
  177. package/src/hooks/use_held_back_messages.ts +0 -33
  178. package/src/utils/cache/__tests__/find_message_in_caches.test.ts +0 -37
  179. package/src/utils/cache/__tests__/invalidate_refetchable_queries.test.ts +0 -23
  180. package/src/utils/cache/__tests__/prepend_newer_page.test.ts +0 -34
  181. package/src/utils/cache/find_message_in_caches.ts +0 -20
  182. package/src/utils/cache/invalidate_refetchable_queries.ts +0 -9
  183. package/src/utils/cache/prepend_newer_page.ts +0 -19
  184. package/src/utils/gated_message_cache.ts +0 -19
  185. package/src/utils/ulid.ts +0 -11
@@ -1,24 +0,0 @@
1
- import { useCallback, useEffect, useRef } from 'react';
2
- export const useHeldBackMessages = ({
3
- fetchNewer,
4
- newestMessageId,
5
- readingHistory,
6
- caughtUp
7
- }) => {
8
- const heldBackSince = useRef(null);
9
- const onSkippedNewMessage = useCallback(() => {
10
- heldBackSince.current ??= newestMessageId ?? null;
11
- }, [newestMessageId]);
12
- useEffect(() => {
13
- const since = heldBackSince.current;
14
- if (readingHistory || !caughtUp || !since) return;
15
- heldBackSince.current = null;
16
- fetchNewer(since).catch(() => {
17
- heldBackSince.current ??= since;
18
- });
19
- }, [caughtUp, fetchNewer, readingHistory]);
20
- return {
21
- onSkippedNewMessage
22
- };
23
- };
24
- //# sourceMappingURL=use_held_back_messages.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"use_held_back_messages.js","names":["useCallback","useEffect","useRef","useHeldBackMessages","fetchNewer","newestMessageId","readingHistory","caughtUp","heldBackSince","onSkippedNewMessage","current","since","catch"],"sources":["../../src/hooks/use_held_back_messages.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react'\n\ntype Options = {\n fetchNewer: (newestMessageId: string) => Promise<void>\n newestMessageId?: string\n readingHistory: boolean\n caughtUp: boolean\n}\n\nexport const useHeldBackMessages = ({\n fetchNewer,\n newestMessageId,\n readingHistory,\n caughtUp,\n}: Options) => {\n const heldBackSince = useRef<string | null>(null)\n\n const onSkippedNewMessage = useCallback(() => {\n heldBackSince.current ??= newestMessageId ?? null\n }, [newestMessageId])\n\n useEffect(() => {\n const since = heldBackSince.current\n if (readingHistory || !caughtUp || !since) return\n\n heldBackSince.current = null\n fetchNewer(since).catch(() => {\n heldBackSince.current ??= since\n })\n }, [caughtUp, fetchNewer, readingHistory])\n\n return { onSkippedNewMessage }\n}\n"],"mappings":"AAAA,SAASA,WAAW,EAAEC,SAAS,EAAEC,MAAM,QAAQ,OAAO;AAStD,OAAO,MAAMC,mBAAmB,GAAGA,CAAC;EAClCC,UAAU;EACVC,eAAe;EACfC,cAAc;EACdC;AACO,CAAC,KAAK;EACb,MAAMC,aAAa,GAAGN,MAAM,CAAgB,IAAI,CAAC;EAEjD,MAAMO,mBAAmB,GAAGT,WAAW,CAAC,MAAM;IAC5CQ,aAAa,CAACE,OAAO,KAAKL,eAAe,IAAI,IAAI;EACnD,CAAC,EAAE,CAACA,eAAe,CAAC,CAAC;EAErBJ,SAAS,CAAC,MAAM;IACd,MAAMU,KAAK,GAAGH,aAAa,CAACE,OAAO;IACnC,IAAIJ,cAAc,IAAI,CAACC,QAAQ,IAAI,CAACI,KAAK,EAAE;IAE3CH,aAAa,CAACE,OAAO,GAAG,IAAI;IAC5BN,UAAU,CAACO,KAAK,CAAC,CAACC,KAAK,CAAC,MAAM;MAC5BJ,aAAa,CAACE,OAAO,KAAKC,KAAK;IACjC,CAAC,CAAC;EACJ,CAAC,EAAE,CAACJ,QAAQ,EAAEH,UAAU,EAAEE,cAAc,CAAC,CAAC;EAE1C,OAAO;IAAEG;EAAoB,CAAC;AAChC,CAAC","ignoreList":[]}
@@ -1,4 +0,0 @@
1
- import { QueryClient } from '@tanstack/react-query';
2
- import { MessageResource } from '../../types';
3
- export declare const findMessageInCaches: (queryClient: QueryClient, conversation_id: number, message_id: string) => MessageResource | undefined;
4
- //# sourceMappingURL=find_message_in_caches.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"find_message_in_caches.d.ts","sourceRoot":"","sources":["../../../src/utils/cache/find_message_in_caches.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACjE,OAAO,EAAiB,eAAe,EAAE,MAAM,aAAa,CAAA;AAK5D,eAAO,MAAM,mBAAmB,GAC9B,aAAa,WAAW,EACxB,iBAAiB,MAAM,EACvB,YAAY,MAAM,KACjB,eAAe,GAAG,SASpB,CAAA"}
@@ -1,16 +0,0 @@
1
- import { getMessagesRequestArgs } from '../request/get_messages';
2
- export const findMessageInCaches = (queryClient, conversation_id, message_id) => {
3
- const {
4
- url
5
- } = getMessagesRequestArgs({
6
- conversation_id
7
- });
8
- for (const [, data] of queryClient.getQueriesData({
9
- queryKey: [url]
10
- })) {
11
- const found = data?.pages.flatMap(page => page.data).find(message => message.id === message_id);
12
- if (found) return found;
13
- }
14
- return undefined;
15
- };
16
- //# sourceMappingURL=find_message_in_caches.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"find_message_in_caches.js","names":["getMessagesRequestArgs","findMessageInCaches","queryClient","conversation_id","message_id","url","data","getQueriesData","queryKey","found","pages","flatMap","page","find","message","id","undefined"],"sources":["../../../src/utils/cache/find_message_in_caches.ts"],"sourcesContent":["import { InfiniteData, QueryClient } from '@tanstack/react-query'\nimport { ApiCollection, MessageResource } from '../../types'\nimport { getMessagesRequestArgs } from '../request/get_messages'\n\ntype QueryData = InfiniteData<ApiCollection<MessageResource>>\n\nexport const findMessageInCaches = (\n queryClient: QueryClient,\n conversation_id: number,\n message_id: string\n): MessageResource | undefined => {\n const { url } = getMessagesRequestArgs({ conversation_id })\n\n for (const [, data] of queryClient.getQueriesData<QueryData>({ queryKey: [url] })) {\n const found = data?.pages.flatMap(page => page.data).find(message => message.id === message_id)\n if (found) return found\n }\n\n return undefined\n}\n"],"mappings":"AAEA,SAASA,sBAAsB,QAAQ,yBAAyB;AAIhE,OAAO,MAAMC,mBAAmB,GAAGA,CACjCC,WAAwB,EACxBC,eAAuB,EACvBC,UAAkB,KACc;EAChC,MAAM;IAAEC;EAAI,CAAC,GAAGL,sBAAsB,CAAC;IAAEG;EAAgB,CAAC,CAAC;EAE3D,KAAK,MAAM,GAAGG,IAAI,CAAC,IAAIJ,WAAW,CAACK,cAAc,CAAY;IAAEC,QAAQ,EAAE,CAACH,GAAG;EAAE,CAAC,CAAC,EAAE;IACjF,MAAMI,KAAK,GAAGH,IAAI,EAAEI,KAAK,CAACC,OAAO,CAACC,IAAI,IAAIA,IAAI,CAACN,IAAI,CAAC,CAACO,IAAI,CAACC,OAAO,IAAIA,OAAO,CAACC,EAAE,KAAKX,UAAU,CAAC;IAC/F,IAAIK,KAAK,EAAE,OAAOA,KAAK;EACzB;EAEA,OAAOO,SAAS;AAClB,CAAC","ignoreList":[]}
@@ -1,3 +0,0 @@
1
- import { QueryClient } from '@tanstack/react-query';
2
- export declare const invalidateRefetchableQueries: (queryClient: QueryClient) => Promise<void>;
3
- //# sourceMappingURL=invalidate_refetchable_queries.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"invalidate_refetchable_queries.d.ts","sourceRoot":"","sources":["../../../src/utils/cache/invalidate_refetchable_queries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAO1D,eAAO,MAAM,4BAA4B,GAAI,aAAa,WAAW,kBACoB,CAAA"}
@@ -1,8 +0,0 @@
1
- const isAnchoredMessagesWindow = query => {
2
- const [, params] = query.queryKey;
3
- return !!params?.where?.id_around;
4
- };
5
- export const invalidateRefetchableQueries = queryClient => queryClient.invalidateQueries({
6
- predicate: query => !isAnchoredMessagesWindow(query)
7
- });
8
- //# sourceMappingURL=invalidate_refetchable_queries.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"invalidate_refetchable_queries.js","names":["isAnchoredMessagesWindow","query","params","queryKey","where","id_around","invalidateRefetchableQueries","queryClient","invalidateQueries","predicate"],"sources":["../../../src/utils/cache/invalidate_refetchable_queries.ts"],"sourcesContent":["import { Query, QueryClient } from '@tanstack/react-query'\n\nconst isAnchoredMessagesWindow = (query: Query) => {\n const [, params] = query.queryKey as [unknown, { where?: { id_around?: string } } | undefined]\n return !!params?.where?.id_around\n}\n\nexport const invalidateRefetchableQueries = (queryClient: QueryClient) =>\n queryClient.invalidateQueries({ predicate: query => !isAnchoredMessagesWindow(query) })\n"],"mappings":"AAEA,MAAMA,wBAAwB,GAAIC,KAAY,IAAK;EACjD,MAAM,GAAGC,MAAM,CAAC,GAAGD,KAAK,CAACE,QAAqE;EAC9F,OAAO,CAAC,CAACD,MAAM,EAAEE,KAAK,EAAEC,SAAS;AACnC,CAAC;AAED,OAAO,MAAMC,4BAA4B,GAAIC,WAAwB,IACnEA,WAAW,CAACC,iBAAiB,CAAC;EAAEC,SAAS,EAAER,KAAK,IAAI,CAACD,wBAAwB,CAACC,KAAK;AAAE,CAAC,CAAC","ignoreList":[]}
@@ -1,7 +0,0 @@
1
- import { InfiniteData } from '@tanstack/react-query';
2
- import { ApiCollection, ResourceObject } from '../../types';
3
- import { RequestData } from '../client';
4
- type PagesData<T extends ResourceObject> = InfiniteData<ApiCollection<T>, Partial<RequestData>>;
5
- export declare function prependNewerPage<T extends ResourceObject>(data: PagesData<T> | undefined, pages: ApiCollection<T>[]): PagesData<T> | undefined;
6
- export {};
7
- //# sourceMappingURL=prepend_newer_page.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"prepend_newer_page.d.ts","sourceRoot":"","sources":["../../../src/utils/cache/prepend_newer_page.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAEvC,KAAK,SAAS,CAAC,CAAC,SAAS,cAAc,IAAI,YAAY,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC,CAAA;AAE/F,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,cAAc,EACvD,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,EAC9B,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,GACxB,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAS1B"}
@@ -1,15 +0,0 @@
1
- export function prependNewerPage(data, pages) {
2
- if (!data || data.pages.length === 0) return data;
3
- const knownIds = new Set(data.pages.flatMap(page => page.data.map(record => record.id)));
4
- const fresh = pages.flatMap(page => page.data).filter(record => !knownIds.has(record.id));
5
- if (fresh.length === 0) return data;
6
- const [first, ...rest] = data.pages;
7
- return {
8
- ...data,
9
- pages: [{
10
- ...first,
11
- data: [...fresh, ...first.data]
12
- }, ...rest]
13
- };
14
- }
15
- //# sourceMappingURL=prepend_newer_page.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"prepend_newer_page.js","names":["prependNewerPage","data","pages","length","knownIds","Set","flatMap","page","map","record","id","fresh","filter","has","first","rest"],"sources":["../../../src/utils/cache/prepend_newer_page.ts"],"sourcesContent":["import { InfiniteData } from '@tanstack/react-query'\nimport { ApiCollection, ResourceObject } from '../../types'\nimport { RequestData } from '../client'\n\ntype PagesData<T extends ResourceObject> = InfiniteData<ApiCollection<T>, Partial<RequestData>>\n\nexport function prependNewerPage<T extends ResourceObject>(\n data: PagesData<T> | undefined,\n pages: ApiCollection<T>[]\n): PagesData<T> | undefined {\n if (!data || data.pages.length === 0) return data\n\n const knownIds = new Set(data.pages.flatMap(page => page.data.map(record => record.id)))\n const fresh = pages.flatMap(page => page.data).filter(record => !knownIds.has(record.id))\n if (fresh.length === 0) return data\n\n const [first, ...rest] = data.pages\n return { ...data, pages: [{ ...first, data: [...fresh, ...first.data] }, ...rest] }\n}\n"],"mappings":"AAMA,OAAO,SAASA,gBAAgBA,CAC9BC,IAA8B,EAC9BC,KAAyB,EACC;EAC1B,IAAI,CAACD,IAAI,IAAIA,IAAI,CAACC,KAAK,CAACC,MAAM,KAAK,CAAC,EAAE,OAAOF,IAAI;EAEjD,MAAMG,QAAQ,GAAG,IAAIC,GAAG,CAACJ,IAAI,CAACC,KAAK,CAACI,OAAO,CAACC,IAAI,IAAIA,IAAI,CAACN,IAAI,CAACO,GAAG,CAACC,MAAM,IAAIA,MAAM,CAACC,EAAE,CAAC,CAAC,CAAC;EACxF,MAAMC,KAAK,GAAGT,KAAK,CAACI,OAAO,CAACC,IAAI,IAAIA,IAAI,CAACN,IAAI,CAAC,CAACW,MAAM,CAACH,MAAM,IAAI,CAACL,QAAQ,CAACS,GAAG,CAACJ,MAAM,CAACC,EAAE,CAAC,CAAC;EACzF,IAAIC,KAAK,CAACR,MAAM,KAAK,CAAC,EAAE,OAAOF,IAAI;EAEnC,MAAM,CAACa,KAAK,EAAE,GAAGC,IAAI,CAAC,GAAGd,IAAI,CAACC,KAAK;EACnC,OAAO;IAAE,GAAGD,IAAI;IAAEC,KAAK,EAAE,CAAC;MAAE,GAAGY,KAAK;MAAEb,IAAI,EAAE,CAAC,GAAGU,KAAK,EAAE,GAAGG,KAAK,CAACb,IAAI;IAAE,CAAC,EAAE,GAAGc,IAAI;EAAE,CAAC;AACrF","ignoreList":[]}
@@ -1,7 +0,0 @@
1
- import type { MessagesCache } from '../hooks/use_conversation_messages_jolt_events';
2
- export declare const gatedMessageCache: ({ queryKey, acceptNewMessages, onSkippedNewMessage, }: {
3
- queryKey: unknown[];
4
- acceptNewMessages: boolean;
5
- onSkippedNewMessage?: () => void;
6
- }) => MessagesCache;
7
- //# sourceMappingURL=gated_message_cache.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"gated_message_cache.d.ts","sourceRoot":"","sources":["../../src/utils/gated_message_cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gDAAgD,CAAA;AAEnF,eAAO,MAAM,iBAAiB,GAAI,uDAI/B;IACD,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,iBAAiB,EAAE,OAAO,CAAA;IAC1B,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAA;CACjC,KAAG,aAQF,CAAA"}
@@ -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":[]}
@@ -1,3 +0,0 @@
1
- export declare const ULID_CHARS = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
2
- export declare function decodeUlidTime(ulid: string): number;
3
- //# sourceMappingURL=ulid.d.ts.map
@@ -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"}
@@ -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
@@ -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,83 +0,0 @@
1
- import { QueryClientProvider } from '@tanstack/react-query'
2
- import { renderHook, waitFor } from '@testing-library/react-native'
3
- import React from 'react'
4
- import { buildTestQueryClient } from '../../__utils__/query_client'
5
- import { useApiClient } from '../../hooks/use_api_client'
6
- import { useDirectMessageEligibility } from '../../hooks/use_direct_message_eligibility'
7
-
8
- jest.mock('../../hooks/use_api_client')
9
-
10
- const mockedUseApiClient = useApiClient as jest.MockedFunction<typeof useApiClient>
11
- const mockGet = jest.fn()
12
-
13
- beforeEach(() => {
14
- mockGet.mockReset()
15
- mockedUseApiClient.mockReturnValue({ chat: { get: mockGet } } as any)
16
- })
17
-
18
- const createWrapper = () => {
19
- const queryClient = buildTestQueryClient()
20
- return ({ children }: { children: React.ReactNode }) => (
21
- <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
22
- )
23
- }
24
-
25
- const args = { groupId: 'Groups-Group-42', personId: 75179873 }
26
-
27
- test("reads the member's eligibility from the chat group", async () => {
28
- mockGet.mockResolvedValue({
29
- data: {
30
- id: '75179873',
31
- type: 'Member',
32
- canDirectMessage: false,
33
- directMessagesDisabled: true,
34
- },
35
- })
36
-
37
- const { result } = renderHook(() => useDirectMessageEligibility(args), {
38
- wrapper: createWrapper(),
39
- })
40
-
41
- await waitFor(() => expect(result.current.eligibility).toBeDefined())
42
-
43
- expect(mockGet).toHaveBeenCalledWith({
44
- url: '/me/groups/Groups-Group-42/members/75179873',
45
- data: { fields: { Member: ['can_direct_message', 'direct_messages_disabled'] } },
46
- })
47
- expect(result.current.eligibility).toEqual({
48
- canDirectMessage: false,
49
- directMessagesDisabled: true,
50
- })
51
- })
52
-
53
- test('has no answer until the member loads', () => {
54
- mockGet.mockReturnValue(new Promise(() => {}))
55
-
56
- const { result } = renderHook(() => useDirectMessageEligibility(args), {
57
- wrapper: createWrapper(),
58
- })
59
-
60
- expect(result.current.eligibility).toBeUndefined()
61
- expect(result.current.isLoading).toBe(true)
62
- })
63
-
64
- test('reports a failed lookup without an answer', async () => {
65
- mockGet.mockRejectedValue(new Error('404'))
66
-
67
- const { result } = renderHook(() => useDirectMessageEligibility(args), {
68
- wrapper: createWrapper(),
69
- })
70
-
71
- await waitFor(() => expect(result.current.isError).toBe(true))
72
-
73
- expect(result.current.eligibility).toBeUndefined()
74
- expect(result.current.isLoading).toBe(false)
75
- })
76
-
77
- test('does not ask when disabled', () => {
78
- renderHook(() => useDirectMessageEligibility({ ...args, enabled: false }), {
79
- wrapper: createWrapper(),
80
- })
81
-
82
- expect(mockGet).not.toHaveBeenCalled()
83
- })
@@ -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
- }