@planningcenter/chat-react-native 4.2.0-rc.0 → 4.2.0-rc.2

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 (90) hide show
  1. package/build/components/conversation/message.d.ts +3 -2
  2. package/build/components/conversation/message.d.ts.map +1 -1
  3. package/build/components/conversation/message.js +23 -22
  4. package/build/components/conversation/message.js.map +1 -1
  5. package/build/components/conversation/message_list.d.ts +21 -0
  6. package/build/components/conversation/message_list.d.ts.map +1 -0
  7. package/build/components/conversation/message_list.js +273 -0
  8. package/build/components/conversation/message_list.js.map +1 -0
  9. package/build/components/conversation/message_read_receipts.d.ts +2 -4
  10. package/build/components/conversation/message_read_receipts.d.ts.map +1 -1
  11. package/build/components/conversation/message_read_receipts.js +2 -3
  12. package/build/components/conversation/message_read_receipts.js.map +1 -1
  13. package/build/components/conversation/reply_shadow_message.d.ts +2 -1
  14. package/build/components/conversation/reply_shadow_message.d.ts.map +1 -1
  15. package/build/components/conversation/reply_shadow_message.js +47 -46
  16. package/build/components/conversation/reply_shadow_message.js.map +1 -1
  17. package/build/contexts/api_provider.d.ts.map +1 -1
  18. package/build/contexts/api_provider.js +12 -1
  19. package/build/contexts/api_provider.js.map +1 -1
  20. package/build/hooks/use_conversation_messages.d.ts.map +1 -1
  21. package/build/hooks/use_conversation_messages.js +40 -28
  22. package/build/hooks/use_conversation_messages.js.map +1 -1
  23. package/build/hooks/use_conversation_messages_jolt_events.d.ts.map +1 -1
  24. package/build/hooks/use_conversation_messages_jolt_events.js +6 -1
  25. package/build/hooks/use_conversation_messages_jolt_events.js.map +1 -1
  26. package/build/hooks/use_features.d.ts +16 -0
  27. package/build/hooks/use_features.d.ts.map +1 -1
  28. package/build/hooks/use_features.js +43 -47
  29. package/build/hooks/use_features.js.map +1 -1
  30. package/build/screens/conversation_screen.d.ts.map +1 -1
  31. package/build/screens/conversation_screen.js +184 -365
  32. package/build/screens/conversation_screen.js.map +1 -1
  33. package/build/screens/conversations/components/search_list_header_component.d.ts +2 -0
  34. package/build/screens/conversations/components/search_list_header_component.d.ts.map +1 -0
  35. package/build/screens/conversations/components/search_list_header_component.js +496 -0
  36. package/build/screens/conversations/components/search_list_header_component.js.map +1 -0
  37. package/build/screens/conversations/conversations_screen.d.ts.map +1 -1
  38. package/build/screens/conversations/conversations_screen.js +28 -16
  39. package/build/screens/conversations/conversations_screen.js.map +1 -1
  40. package/build/types/resources/message.d.ts +1 -0
  41. package/build/types/resources/message.d.ts.map +1 -1
  42. package/build/types/resources/message.js.map +1 -1
  43. package/build/utils/cache/messages_cache.d.ts +1 -0
  44. package/build/utils/cache/messages_cache.d.ts.map +1 -1
  45. package/build/utils/cache/messages_cache.js +10 -1
  46. package/build/utils/cache/messages_cache.js.map +1 -1
  47. package/build/utils/client/transform_response.d.ts.map +1 -1
  48. package/build/utils/client/transform_response.js +9 -5
  49. package/build/utils/client/transform_response.js.map +1 -1
  50. package/build/utils/group_messages.d.ts +1 -0
  51. package/build/utils/group_messages.d.ts.map +1 -1
  52. package/build/utils/group_messages.js +10 -1
  53. package/build/utils/group_messages.js.map +1 -1
  54. package/build/utils/replace_equal_by_id.d.ts +6 -0
  55. package/build/utils/replace_equal_by_id.d.ts.map +1 -0
  56. package/build/utils/replace_equal_by_id.js +41 -0
  57. package/build/utils/replace_equal_by_id.js.map +1 -0
  58. package/build/utils/request/messages_data_options.d.ts.map +1 -1
  59. package/build/utils/request/messages_data_options.js +1 -1
  60. package/build/utils/request/messages_data_options.js.map +1 -1
  61. package/build/utils/time.d.ts +1 -0
  62. package/build/utils/time.d.ts.map +1 -1
  63. package/build/utils/time.js +1 -0
  64. package/build/utils/time.js.map +1 -1
  65. package/package.json +2 -2
  66. package/src/__tests__/hooks/use_features.test.ts +61 -0
  67. package/src/components/conversation/__tests__/message_list.test.tsx +277 -0
  68. package/src/components/conversation/__tests__/reply_shadow_message.test.tsx +119 -0
  69. package/src/components/conversation/message.tsx +15 -18
  70. package/src/components/conversation/message_list.tsx +157 -0
  71. package/src/components/conversation/message_read_receipts.tsx +3 -5
  72. package/src/components/conversation/reply_shadow_message.tsx +9 -7
  73. package/src/contexts/api_provider.tsx +4 -0
  74. package/src/hooks/use_conversation_messages.ts +5 -1
  75. package/src/hooks/use_conversation_messages_jolt_events.ts +7 -0
  76. package/src/hooks/use_features.ts +26 -14
  77. package/src/screens/conversation_screen.tsx +11 -104
  78. package/src/screens/conversations/components/search_list_header_component.tsx +291 -0
  79. package/src/screens/conversations/conversations_screen.tsx +6 -1
  80. package/src/types/resources/message.ts +1 -0
  81. package/src/utils/__tests__/group_messages.test.ts +48 -0
  82. package/src/utils/__tests__/replace_equal_by_id.test.ts +73 -0
  83. package/src/utils/cache/__tests__/messages_cache_reply_roots.test.ts +70 -0
  84. package/src/utils/cache/messages_cache.ts +21 -1
  85. package/src/utils/client/__tests__/transform_response.test.ts +62 -0
  86. package/src/utils/client/transform_response.ts +7 -5
  87. package/src/utils/group_messages.ts +18 -1
  88. package/src/utils/replace_equal_by_id.ts +42 -0
  89. package/src/utils/request/messages_data_options.ts +1 -1
  90. package/src/utils/time.ts +1 -0
@@ -1 +1 @@
1
- {"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../../src/types/resources/message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,8BAA8B,EAAE,MAAM,oCAAoC,CAAA;AACnF,OAAO,EAAE,uCAAuC,EAAE,MAAM,+CAA+C,CAAA;AACvG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAC9C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAEvD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,SAAS,CAAA;IACf,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,IAAI,EAAE,OAAO,CAAA;IACb,WAAW,EAAE,8BAA8B,EAAE,CAAA;IAC7C,MAAM,EAAE,cAAc,CAAA;IACtB,cAAc,EAAE,qBAAqB,EAAE,CAAA;IACvC,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,WAAW,EAAE,MAAM,CAAA;IACnB,uBAAuB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;IACzC,eAAe,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IAGnF,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,sBAAsB,CAAC,EAAE,OAAO,CAAA;IAChC,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,cAAc,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,IAAI,CAAA;IACnD,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,wBAAwB,CAAC,EAAE,OAAO,CAAA;IAGlC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oBAAoB,CAAC,EAAE,uCAAuC,EAAE,CAAA;CACjE"}
1
+ {"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../../src/types/resources/message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,8BAA8B,EAAE,MAAM,oCAAoC,CAAA;AACnF,OAAO,EAAE,uCAAuC,EAAE,MAAM,+CAA+C,CAAA;AACvG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAC9C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAEvD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,SAAS,CAAA;IACf,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,IAAI,EAAE,OAAO,CAAA;IACb,WAAW,EAAE,8BAA8B,EAAE,CAAA;IAC7C,MAAM,EAAE,cAAc,CAAA;IACtB,cAAc,EAAE,qBAAqB,EAAE,CAAA;IACvC,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,SAAS,CAAC,EAAE,eAAe,GAAG,IAAI,CAAA;IAClC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,WAAW,EAAE,MAAM,CAAA;IACnB,uBAAuB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;IACzC,eAAe,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IAGnF,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,sBAAsB,CAAC,EAAE,OAAO,CAAA;IAChC,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,cAAc,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,IAAI,CAAA;IACnD,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,wBAAwB,CAAC,EAAE,OAAO,CAAA;IAGlC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oBAAoB,CAAC,EAAE,uCAAuC,EAAE,CAAA;CACjE"}
@@ -1 +1 @@
1
- {"version":3,"file":"message.js","names":[],"sources":["../../../src/types/resources/message.ts"],"sourcesContent":["import { DenormalizedAttachmentResource } from './denormalized_attachment_resource'\nimport { DenormalizedAttachmentResourceForCreate } from './denormalized_attachment_resource_for_create'\nimport type { PersonResource } from './person'\nimport type { ReactionCountResource } from './reaction'\n\nexport interface MessageResource {\n type: 'Message'\n id: string\n text: string | null\n html: string\n createdAt: string\n deletedAt: string | null\n textEditedAt: string | null\n mine: boolean\n attachments: DenormalizedAttachmentResource[]\n author: PersonResource\n reactionCounts: ReactionCountResource[]\n replyCount: number\n replyRootId?: string | null\n idempotentKey?: string | null\n messageType: string\n personIdsForSystemEvent?: number[] | null\n systemTextParts?: { names: string[]; overflowCount: number; action: string } | null\n\n // Custom Local Properties we set for rendering\n renderAuthor?: boolean\n renderTime?: boolean\n myLatestInConversation?: boolean\n lastInGroup?: boolean\n threadPosition?: 'first' | 'center' | 'last' | null\n prevIsMyReply?: boolean\n nextIsMyReply?: boolean\n nextRendersAuthor?: boolean\n isReplyShadowMessage?: boolean\n nextIsReplyShadowMessage?: boolean\n\n // Properties for mutation state\n pending?: boolean // Indicates if the message is optimistically created and pending server response\n error?: string // Error message if the message failed to send\n attachmentsForCreate?: DenormalizedAttachmentResourceForCreate[]\n}\n"],"mappings":"","ignoreList":[]}
1
+ {"version":3,"file":"message.js","names":[],"sources":["../../../src/types/resources/message.ts"],"sourcesContent":["import { DenormalizedAttachmentResource } from './denormalized_attachment_resource'\nimport { DenormalizedAttachmentResourceForCreate } from './denormalized_attachment_resource_for_create'\nimport type { PersonResource } from './person'\nimport type { ReactionCountResource } from './reaction'\n\nexport interface MessageResource {\n type: 'Message'\n id: string\n text: string | null\n html: string\n createdAt: string\n deletedAt: string | null\n textEditedAt: string | null\n mine: boolean\n attachments: DenormalizedAttachmentResource[]\n author: PersonResource\n reactionCounts: ReactionCountResource[]\n replyCount: number\n replyRootId?: string | null\n replyRoot?: MessageResource | null\n idempotentKey?: string | null\n messageType: string\n personIdsForSystemEvent?: number[] | null\n systemTextParts?: { names: string[]; overflowCount: number; action: string } | null\n\n // Custom Local Properties we set for rendering\n renderAuthor?: boolean\n renderTime?: boolean\n myLatestInConversation?: boolean\n lastInGroup?: boolean\n threadPosition?: 'first' | 'center' | 'last' | null\n prevIsMyReply?: boolean\n nextIsMyReply?: boolean\n nextRendersAuthor?: boolean\n isReplyShadowMessage?: boolean\n nextIsReplyShadowMessage?: boolean\n\n // Properties for mutation state\n pending?: boolean // Indicates if the message is optimistically created and pending server response\n error?: string // Error message if the message failed to send\n attachmentsForCreate?: DenormalizedAttachmentResourceForCreate[]\n}\n"],"mappings":"","ignoreList":[]}
@@ -3,6 +3,7 @@ import { MessageResource } from '../../types';
3
3
  import { JoltReactionEvent } from '../../types/jolt_events';
4
4
  export declare function updateCacheWithMessage(queryClient: QueryClient, queryKey: unknown[], message: MessageResource, event: 'message.created' | 'message.updated'): void;
5
5
  export declare function updateCacheWithIndividualMessage(queryClient: QueryClient, queryKey: unknown[], message: MessageResource): void;
6
+ export declare function updateCacheReplyRoots(queryClient: QueryClient, queryKey: unknown[], root: MessageResource): void;
6
7
  export declare function updateCacheWithReaction(queryClient: QueryClient, queryKey: unknown[], event: JoltReactionEvent, currentPersonId: number): void;
7
8
  export declare function isTemporaryMessageId(messageId?: string | null): boolean;
8
9
  export declare function isNewMessage(message?: MessageResource): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"messages_cache.d.ts","sourceRoot":"","sources":["../../../src/utils/cache/messages_cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAEjE,OAAO,EAA8B,eAAe,EAAE,MAAM,aAAa,CAAA;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAM3D,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,iBAAiB,GAAG,iBAAiB,QA8B7C;AAID,wBAAgB,gCAAgC,CAC9C,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,EAAE,eAAe,QAgBzB;AAED,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,OAAO,EAAE,EACnB,KAAK,EAAE,iBAAiB,EACxB,eAAe,EAAE,MAAM,QAuCxB;AAGD,wBAAgB,oBAAoB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAEvE;AACD,wBAAgB,YAAY,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAE/D;AAED,wBAAgB,2BAA2B,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,mCAMtF;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,eAAe,EACvB,OAAO,CAAC,EAAE,eAAe,GACxB,eAAe,CAWjB"}
1
+ {"version":3,"file":"messages_cache.d.ts","sourceRoot":"","sources":["../../../src/utils/cache/messages_cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAEjE,OAAO,EAA8B,eAAe,EAAE,MAAM,aAAa,CAAA;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAU3D,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,iBAAiB,GAAG,iBAAiB,QA8B7C;AAID,wBAAgB,gCAAgC,CAC9C,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,EAAE,eAAe,QAgBzB;AAED,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,OAAO,EAAE,EACnB,IAAI,EAAE,eAAe,QAWtB;AAED,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,OAAO,EAAE,EACnB,KAAK,EAAE,iBAAiB,EACxB,eAAe,EAAE,MAAM,QAuCxB;AAGD,wBAAgB,oBAAoB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAEvE;AACD,wBAAgB,YAAY,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAE/D;AAED,wBAAgB,2BAA2B,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,mCAMtF;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,eAAe,EACvB,OAAO,CAAC,EAAE,eAAe,GACxB,eAAe,CAWjB"}
@@ -1,7 +1,7 @@
1
1
  import { getRequestQueryKey } from '../../hooks/use_suspense_api';
2
2
  import { transformReactionEventDataToReactionCountResource } from '../jolt/transform_reaction_event_data_to_reaction_count_resource';
3
3
  import { getMessagesRequestArgs } from '../request/get_messages';
4
- import { updateOrCreateRecordInPagesData, updateRecordInPagesData } from './page_mutations';
4
+ import { updateAllRecordsInPagesData, updateOrCreateRecordInPagesData, updateRecordInPagesData } from './page_mutations';
5
5
  import { deleteRecordInPagesData } from './page_mutations';
6
6
  export function updateCacheWithMessage(queryClient, queryKey, message, event) {
7
7
  queryClient.setQueryData(queryKey, prev => {
@@ -45,6 +45,15 @@ export function updateCacheWithIndividualMessage(queryClient, queryKey, message)
45
45
  };
46
46
  });
47
47
  }
48
+ export function updateCacheReplyRoots(queryClient, queryKey, root) {
49
+ queryClient.setQueryData(queryKey, prev => updateAllRecordsInPagesData({
50
+ data: prev,
51
+ processRecord: message => message.replyRoot?.id === root.id ? {
52
+ ...message,
53
+ replyRoot: mergeMessageUpdate(root, message.replyRoot)
54
+ } : message
55
+ }));
56
+ }
48
57
  export function updateCacheWithReaction(queryClient, queryKey, event, currentPersonId) {
49
58
  const message = {
50
59
  id: event.data.data.message_sort_key
@@ -1 +1 @@
1
- {"version":3,"file":"messages_cache.js","names":["getRequestQueryKey","transformReactionEventDataToReactionCountResource","getMessagesRequestArgs","updateOrCreateRecordInPagesData","updateRecordInPagesData","deleteRecordInPagesData","updateCacheWithMessage","queryClient","queryKey","message","event","setQueryData","prev","dataAfterTempRemoval","mine","idempotentKey","data","record","matchFn","existingMessage","isTemporaryMessageId","id","processRecord","current","mergeMessageUpdate","updateCacheWithIndividualMessage","links","meta","updateCacheWithReaction","currentPersonId","message_sort_key","oldMessage","reactionCounts","foundMatch","newReactionCounts","map","reactionCount","value","oldData","newReactionCount","count","messageId","endsWith","isNewMessage","getThreadedMessagesQueryKey","conversationId","replyRootId","requestArgs","conversation_id","reply_root_id"],"sources":["../../../src/utils/cache/messages_cache.ts"],"sourcesContent":["import { InfiniteData, QueryClient } from '@tanstack/react-query'\nimport { getRequestQueryKey } from '../../hooks/use_suspense_api'\nimport { ApiCollection, ApiResource, MessageResource } from '../../types'\nimport { JoltReactionEvent } from '../../types/jolt_events'\nimport { transformReactionEventDataToReactionCountResource } from '../jolt/transform_reaction_event_data_to_reaction_count_resource'\nimport { getMessagesRequestArgs } from '../request/get_messages'\nimport { updateOrCreateRecordInPagesData, updateRecordInPagesData } from './page_mutations'\nimport { deleteRecordInPagesData } from './page_mutations'\n\nexport function updateCacheWithMessage(\n queryClient: QueryClient,\n queryKey: unknown[],\n message: MessageResource,\n event: 'message.created' | 'message.updated'\n) {\n queryClient.setQueryData<MessagesQueryData>(queryKey, prev => {\n if (event === 'message.created') {\n // Before adding the new message, remove its pending temporary counterpart.\n // The Jolt echo can arrive before the HTTP response has swapped it out.\n let dataAfterTempRemoval = prev\n if (prev && message.mine && message.idempotentKey) {\n dataAfterTempRemoval = deleteRecordInPagesData({\n data: prev,\n record: message,\n matchFn: existingMessage =>\n isTemporaryMessageId(existingMessage.id) &&\n existingMessage.idempotentKey === message.idempotentKey,\n })\n }\n\n return updateOrCreateRecordInPagesData({\n data: dataAfterTempRemoval,\n record: message,\n processRecord: (record, current) => mergeMessageUpdate(record, current),\n })\n } else {\n return updateRecordInPagesData({\n data: prev,\n record: message,\n processRecord: (record, current) => mergeMessageUpdate(record, current),\n })\n }\n })\n}\n\ntype IndividualMessageQueryData = ApiResource<MessageResource>\n\nexport function updateCacheWithIndividualMessage(\n queryClient: QueryClient,\n queryKey: unknown[],\n message: MessageResource\n) {\n queryClient.setQueryData<IndividualMessageQueryData>(queryKey, prev => {\n if (!prev) {\n return {\n data: message,\n links: {},\n meta: {},\n }\n }\n\n return {\n ...prev,\n data: mergeMessageUpdate(message, prev.data),\n }\n })\n}\n\nexport function updateCacheWithReaction(\n queryClient: QueryClient,\n queryKey: unknown[],\n event: JoltReactionEvent,\n currentPersonId: number\n) {\n const message = { id: event.data.data.message_sort_key } as MessageResource\n queryClient.setQueryData<MessagesQueryData>(queryKey, prev =>\n updateRecordInPagesData({\n data: prev,\n record: message,\n processRecord: (record, oldMessage) => {\n const reactionCounts = oldMessage.reactionCounts || []\n let foundMatch = false\n let newReactionCounts = reactionCounts.map(reactionCount => {\n if (reactionCount.value === event.data.data.value) {\n foundMatch = true\n return transformReactionEventDataToReactionCountResource({\n data: event.data.data,\n oldData: reactionCount,\n event: event.event,\n currentPersonId,\n })\n }\n return reactionCount\n })\n\n if (!foundMatch) {\n const newReactionCount = transformReactionEventDataToReactionCountResource({\n data: event.data.data,\n event: event.event,\n currentPersonId,\n })\n\n if (newReactionCount?.count) {\n newReactionCounts = [...newReactionCounts, newReactionCount]\n }\n }\n\n return { ...oldMessage, reactionCounts: newReactionCounts }\n },\n })\n )\n}\n\ntype MessagesQueryData = InfiniteData<ApiCollection<MessageResource>>\nexport function isTemporaryMessageId(messageId?: string | null): boolean {\n return !!messageId && messageId.endsWith('-temp')\n}\nexport function isNewMessage(message?: MessageResource): boolean {\n return !message?.id || isTemporaryMessageId(message.id)\n}\n\nexport function getThreadedMessagesQueryKey(conversationId: number, replyRootId: string) {\n const requestArgs = getMessagesRequestArgs({\n conversation_id: conversationId,\n reply_root_id: replyRootId,\n })\n return getRequestQueryKey(requestArgs)\n}\n\nexport function mergeMessageUpdate(\n record: MessageResource,\n current?: MessageResource\n): MessageResource {\n if (!current) {\n return record\n }\n\n return {\n ...current,\n ...record,\n // Preserve reactions from cache - they're managed via separate reaction events\n reactionCounts: current.reactionCounts || [],\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"messages_cache.js","names":["getRequestQueryKey","transformReactionEventDataToReactionCountResource","getMessagesRequestArgs","updateAllRecordsInPagesData","updateOrCreateRecordInPagesData","updateRecordInPagesData","deleteRecordInPagesData","updateCacheWithMessage","queryClient","queryKey","message","event","setQueryData","prev","dataAfterTempRemoval","mine","idempotentKey","data","record","matchFn","existingMessage","isTemporaryMessageId","id","processRecord","current","mergeMessageUpdate","updateCacheWithIndividualMessage","links","meta","updateCacheReplyRoots","root","replyRoot","updateCacheWithReaction","currentPersonId","message_sort_key","oldMessage","reactionCounts","foundMatch","newReactionCounts","map","reactionCount","value","oldData","newReactionCount","count","messageId","endsWith","isNewMessage","getThreadedMessagesQueryKey","conversationId","replyRootId","requestArgs","conversation_id","reply_root_id"],"sources":["../../../src/utils/cache/messages_cache.ts"],"sourcesContent":["import { InfiniteData, QueryClient } from '@tanstack/react-query'\nimport { getRequestQueryKey } from '../../hooks/use_suspense_api'\nimport { ApiCollection, ApiResource, MessageResource } from '../../types'\nimport { JoltReactionEvent } from '../../types/jolt_events'\nimport { transformReactionEventDataToReactionCountResource } from '../jolt/transform_reaction_event_data_to_reaction_count_resource'\nimport { getMessagesRequestArgs } from '../request/get_messages'\nimport {\n updateAllRecordsInPagesData,\n updateOrCreateRecordInPagesData,\n updateRecordInPagesData,\n} from './page_mutations'\nimport { deleteRecordInPagesData } from './page_mutations'\n\nexport function updateCacheWithMessage(\n queryClient: QueryClient,\n queryKey: unknown[],\n message: MessageResource,\n event: 'message.created' | 'message.updated'\n) {\n queryClient.setQueryData<MessagesQueryData>(queryKey, prev => {\n if (event === 'message.created') {\n // Before adding the new message, remove its pending temporary counterpart.\n // The Jolt echo can arrive before the HTTP response has swapped it out.\n let dataAfterTempRemoval = prev\n if (prev && message.mine && message.idempotentKey) {\n dataAfterTempRemoval = deleteRecordInPagesData({\n data: prev,\n record: message,\n matchFn: existingMessage =>\n isTemporaryMessageId(existingMessage.id) &&\n existingMessage.idempotentKey === message.idempotentKey,\n })\n }\n\n return updateOrCreateRecordInPagesData({\n data: dataAfterTempRemoval,\n record: message,\n processRecord: (record, current) => mergeMessageUpdate(record, current),\n })\n } else {\n return updateRecordInPagesData({\n data: prev,\n record: message,\n processRecord: (record, current) => mergeMessageUpdate(record, current),\n })\n }\n })\n}\n\ntype IndividualMessageQueryData = ApiResource<MessageResource>\n\nexport function updateCacheWithIndividualMessage(\n queryClient: QueryClient,\n queryKey: unknown[],\n message: MessageResource\n) {\n queryClient.setQueryData<IndividualMessageQueryData>(queryKey, prev => {\n if (!prev) {\n return {\n data: message,\n links: {},\n meta: {},\n }\n }\n\n return {\n ...prev,\n data: mergeMessageUpdate(message, prev.data),\n }\n })\n}\n\nexport function updateCacheReplyRoots(\n queryClient: QueryClient,\n queryKey: unknown[],\n root: MessageResource\n) {\n queryClient.setQueryData<MessagesQueryData>(queryKey, prev =>\n updateAllRecordsInPagesData({\n data: prev,\n processRecord: message =>\n message.replyRoot?.id === root.id\n ? { ...message, replyRoot: mergeMessageUpdate(root, message.replyRoot) }\n : message,\n })\n )\n}\n\nexport function updateCacheWithReaction(\n queryClient: QueryClient,\n queryKey: unknown[],\n event: JoltReactionEvent,\n currentPersonId: number\n) {\n const message = { id: event.data.data.message_sort_key } as MessageResource\n queryClient.setQueryData<MessagesQueryData>(queryKey, prev =>\n updateRecordInPagesData({\n data: prev,\n record: message,\n processRecord: (record, oldMessage) => {\n const reactionCounts = oldMessage.reactionCounts || []\n let foundMatch = false\n let newReactionCounts = reactionCounts.map(reactionCount => {\n if (reactionCount.value === event.data.data.value) {\n foundMatch = true\n return transformReactionEventDataToReactionCountResource({\n data: event.data.data,\n oldData: reactionCount,\n event: event.event,\n currentPersonId,\n })\n }\n return reactionCount\n })\n\n if (!foundMatch) {\n const newReactionCount = transformReactionEventDataToReactionCountResource({\n data: event.data.data,\n event: event.event,\n currentPersonId,\n })\n\n if (newReactionCount?.count) {\n newReactionCounts = [...newReactionCounts, newReactionCount]\n }\n }\n\n return { ...oldMessage, reactionCounts: newReactionCounts }\n },\n })\n )\n}\n\ntype MessagesQueryData = InfiniteData<ApiCollection<MessageResource>>\nexport function isTemporaryMessageId(messageId?: string | null): boolean {\n return !!messageId && messageId.endsWith('-temp')\n}\nexport function isNewMessage(message?: MessageResource): boolean {\n return !message?.id || isTemporaryMessageId(message.id)\n}\n\nexport function getThreadedMessagesQueryKey(conversationId: number, replyRootId: string) {\n const requestArgs = getMessagesRequestArgs({\n conversation_id: conversationId,\n reply_root_id: replyRootId,\n })\n return getRequestQueryKey(requestArgs)\n}\n\nexport function mergeMessageUpdate(\n record: MessageResource,\n current?: MessageResource\n): MessageResource {\n if (!current) {\n return record\n }\n\n return {\n ...current,\n ...record,\n // Preserve reactions from cache - they're managed via separate reaction events\n reactionCounts: current.reactionCounts || [],\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"transform_response.d.ts","sourceRoot":"","sources":["../../../src/utils/client/transform_response.ts"],"names":[],"mappings":"AAiGA,QAAA,MAAM,iBAAiB,yBAAyD,CAAA;AAEhF,eAAe,iBAAiB,CAAA"}
1
+ {"version":3,"file":"transform_response.d.ts","sourceRoot":"","sources":["../../../src/utils/client/transform_response.ts"],"names":[],"mappings":"AAmGA,QAAA,MAAM,iBAAiB,yBAAyD,CAAA;AAEhF,eAAe,iBAAiB,CAAA"}
@@ -36,21 +36,25 @@ const transformRelationships = relationships => {
36
36
  }
37
37
  }, {});
38
38
  };
39
- const addIncludedRecords = (relationships, included, seen, parent) => {
39
+ const addIncludedRecords = (record, included, seen, parent) => {
40
+ const {
41
+ relationships
42
+ } = record;
40
43
  return Object.keys(relationships || {}).reduce((acc, key) => {
41
44
  const relationship = relationships[key];
42
45
  if (_.isArray(relationship.data)) {
43
46
  const records = _(relationship.data).map(elem => _.find(included, elem)).compact() // account for orphans
44
47
  .value();
45
- return _.set(acc, _.pluralize(key), _.map(records, record => transformRecord(record, included, seen)));
48
+ return _.set(acc, _.pluralize(key), _.map(records, related => transformRecord(related, included, seen)));
46
49
  } else {
47
50
  const [id, type] = _.at(relationship, ['data.id', 'data.type']);
48
51
  if (id && type) {
49
- const record = _.find(included, {
52
+ const isSelfReference = id === record.id && type === record.type;
53
+ const related = isSelfReference ? undefined : _.find(included, {
50
54
  id,
51
55
  type
52
56
  });
53
- return _.set(acc, key, record ? transformRecord(record, included, seen) : null);
57
+ return _.set(acc, key, related ? transformRecord(related, included, seen) : null);
54
58
  } else {
55
59
  return acc;
56
60
  }
@@ -77,7 +81,7 @@ const transformRecord = (record, included, seen) => {
77
81
  ..._.mapKeys(attributes, (v, k) => k === 'length' ? `${type.charAt(0).toLowerCase()}${type.slice(1)}Length` : k, {})
78
82
  };
79
83
  seen.set(record, transformed);
80
- return addIncludedRecords(relationships, included, seen, transformed);
84
+ return addIncludedRecords(record, included, seen, transformed);
81
85
  }
82
86
  };
83
87
  const defaultTransformResponse = response => {
@@ -1 +1 @@
1
- {"version":3,"file":"transform_response.js","names":["_","inflection","keysToCamelCase","mixin","singular","plural","transformId","id","thru","parseInt","int","transformRelationships","relationships","Object","keys","reduce","acc","key","relationship","isArray","data","ids","map","singularize","get","type","addIncludedRecords","included","seen","parent","records","elem","find","compact","value","set","pluralize","record","transformRecord","at","attributes","links","has","transformed","mapKeys","v","k","charAt","toLowerCase","slice","defaultTransformResponse","response","isEmpty","meta","WeakMap","result","d","transformResponse","flowRight"],"sources":["../../../src/utils/client/transform_response.ts"],"sourcesContent":["// @ts-nocheck\n// TODO: Fix this file but write tests first\n\nimport _ from 'lodash'\nimport inflection from 'lodash-inflection/lib/lodash-inflection'\nimport { keysToCamelCase } from './utils'\n\n_.mixin({ ...inflection })\n\n// Teach some bad grammar\n_.singular(/^(media)$/i, '$1')\n_.plural(/^(media)$/i, '$1')\n\n// eslint-disable-next-line eqeqeq\nconst transformId = id => _.thru(parseInt(id, 10), int => (int == id ? int : id))\n\nconst transformRelationships = relationships => {\n return Object.keys(relationships || {}).reduce((acc, key) => {\n const relationship = relationships[key]\n if (_.isArray(relationship.data)) {\n const ids = _.map(relationship.data, ({ id }) => transformId(id))\n return { ...acc, [`${_.singularize(key)}_ids`]: ids }\n } else {\n const id = _.get(relationship, 'data.id')\n const type = _.get(relationship, 'data.type', null)\n return {\n ...acc,\n [`${key}_id`]: id ? transformId(id) : null,\n [`${key}_type`]: type,\n }\n }\n }, {})\n}\n\nconst addIncludedRecords = (relationships, included, seen, parent) => {\n return Object.keys(relationships || {}).reduce((acc, key) => {\n const relationship = relationships[key]\n if (_.isArray(relationship.data)) {\n const records = _(relationship.data)\n .map(elem => _.find(included, elem))\n .compact() // account for orphans\n .value()\n return _.set(\n acc,\n _.pluralize(key),\n _.map(records, record => transformRecord(record, included, seen))\n )\n } else {\n const [id, type] = _.at(relationship, ['data.id', 'data.type'])\n if (id && type) {\n const record = _.find(included, { id, type })\n return _.set(acc, key, record ? transformRecord(record, included, seen) : null)\n } else {\n return acc\n }\n }\n }, parent)\n}\n\nconst transformRecord = (record, included, seen) => {\n if (!record) return record\n const { id, attributes, links, relationships, type } = record\n\n if (seen.has(record)) {\n return seen.get(record)\n } else {\n const transformed = {\n id: transformId(id),\n type,\n links,\n ...transformRelationships(relationships),\n ..._.mapKeys(\n attributes,\n (v, k) => (k === 'length' ? `${type.charAt(0).toLowerCase()}${type.slice(1)}Length` : k),\n {}\n ),\n }\n\n seen.set(record, transformed)\n\n return addIncludedRecords(relationships, included, seen, transformed)\n }\n}\n\nconst defaultTransformResponse = response => {\n if (_.isEmpty(response)) return response\n const { data, included, meta, links } = response\n const seen = new WeakMap()\n\n if (_.isArray(data)) {\n const result = data.map(d => transformRecord(d, included, seen))\n return { data: result, meta, links, ...data }\n } else {\n return { data: { ...transformRecord(data, included, seen) }, meta, links }\n }\n}\n\nconst transformResponse = _.flowRight(keysToCamelCase, defaultTransformResponse)\n\nexport default transformResponse\n"],"mappings":"AAAA;AACA;;AAEA,OAAOA,CAAC,MAAM,QAAQ;AACtB,OAAOC,UAAU,MAAM,yCAAyC;AAChE,SAASC,eAAe,QAAQ,SAAS;AAEzCF,CAAC,CAACG,KAAK,CAAC;EAAE,GAAGF;AAAW,CAAC,CAAC;;AAE1B;AACAD,CAAC,CAACI,QAAQ,CAAC,YAAY,EAAE,IAAI,CAAC;AAC9BJ,CAAC,CAACK,MAAM,CAAC,YAAY,EAAE,IAAI,CAAC;;AAE5B;AACA,MAAMC,WAAW,GAAGC,EAAE,IAAIP,CAAC,CAACQ,IAAI,CAACC,QAAQ,CAACF,EAAE,EAAE,EAAE,CAAC,EAAEG,GAAG,IAAKA,GAAG,IAAIH,EAAE,GAAGG,GAAG,GAAGH,EAAG,CAAC;AAEjF,MAAMI,sBAAsB,GAAGC,aAAa,IAAI;EAC9C,OAAOC,MAAM,CAACC,IAAI,CAACF,aAAa,IAAI,CAAC,CAAC,CAAC,CAACG,MAAM,CAAC,CAACC,GAAG,EAAEC,GAAG,KAAK;IAC3D,MAAMC,YAAY,GAAGN,aAAa,CAACK,GAAG,CAAC;IACvC,IAAIjB,CAAC,CAACmB,OAAO,CAACD,YAAY,CAACE,IAAI,CAAC,EAAE;MAChC,MAAMC,GAAG,GAAGrB,CAAC,CAACsB,GAAG,CAACJ,YAAY,CAACE,IAAI,EAAE,CAAC;QAAEb;MAAG,CAAC,KAAKD,WAAW,CAACC,EAAE,CAAC,CAAC;MACjE,OAAO;QAAE,GAAGS,GAAG;QAAE,CAAC,GAAGhB,CAAC,CAACuB,WAAW,CAACN,GAAG,CAAC,MAAM,GAAGI;MAAI,CAAC;IACvD,CAAC,MAAM;MACL,MAAMd,EAAE,GAAGP,CAAC,CAACwB,GAAG,CAACN,YAAY,EAAE,SAAS,CAAC;MACzC,MAAMO,IAAI,GAAGzB,CAAC,CAACwB,GAAG,CAACN,YAAY,EAAE,WAAW,EAAE,IAAI,CAAC;MACnD,OAAO;QACL,GAAGF,GAAG;QACN,CAAC,GAAGC,GAAG,KAAK,GAAGV,EAAE,GAAGD,WAAW,CAACC,EAAE,CAAC,GAAG,IAAI;QAC1C,CAAC,GAAGU,GAAG,OAAO,GAAGQ;MACnB,CAAC;IACH;EACF,CAAC,EAAE,CAAC,CAAC,CAAC;AACR,CAAC;AAED,MAAMC,kBAAkB,GAAGA,CAACd,aAAa,EAAEe,QAAQ,EAAEC,IAAI,EAAEC,MAAM,KAAK;EACpE,OAAOhB,MAAM,CAACC,IAAI,CAACF,aAAa,IAAI,CAAC,CAAC,CAAC,CAACG,MAAM,CAAC,CAACC,GAAG,EAAEC,GAAG,KAAK;IAC3D,MAAMC,YAAY,GAAGN,aAAa,CAACK,GAAG,CAAC;IACvC,IAAIjB,CAAC,CAACmB,OAAO,CAACD,YAAY,CAACE,IAAI,CAAC,EAAE;MAChC,MAAMU,OAAO,GAAG9B,CAAC,CAACkB,YAAY,CAACE,IAAI,CAAC,CACjCE,GAAG,CAACS,IAAI,IAAI/B,CAAC,CAACgC,IAAI,CAACL,QAAQ,EAAEI,IAAI,CAAC,CAAC,CACnCE,OAAO,CAAC,CAAC,CAAC;MAAA,CACVC,KAAK,CAAC,CAAC;MACV,OAAOlC,CAAC,CAACmC,GAAG,CACVnB,GAAG,EACHhB,CAAC,CAACoC,SAAS,CAACnB,GAAG,CAAC,EAChBjB,CAAC,CAACsB,GAAG,CAACQ,OAAO,EAAEO,MAAM,IAAIC,eAAe,CAACD,MAAM,EAAEV,QAAQ,EAAEC,IAAI,CAAC,CAClE,CAAC;IACH,CAAC,MAAM;MACL,MAAM,CAACrB,EAAE,EAAEkB,IAAI,CAAC,GAAGzB,CAAC,CAACuC,EAAE,CAACrB,YAAY,EAAE,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;MAC/D,IAAIX,EAAE,IAAIkB,IAAI,EAAE;QACd,MAAMY,MAAM,GAAGrC,CAAC,CAACgC,IAAI,CAACL,QAAQ,EAAE;UAAEpB,EAAE;UAAEkB;QAAK,CAAC,CAAC;QAC7C,OAAOzB,CAAC,CAACmC,GAAG,CAACnB,GAAG,EAAEC,GAAG,EAAEoB,MAAM,GAAGC,eAAe,CAACD,MAAM,EAAEV,QAAQ,EAAEC,IAAI,CAAC,GAAG,IAAI,CAAC;MACjF,CAAC,MAAM;QACL,OAAOZ,GAAG;MACZ;IACF;EACF,CAAC,EAAEa,MAAM,CAAC;AACZ,CAAC;AAED,MAAMS,eAAe,GAAGA,CAACD,MAAM,EAAEV,QAAQ,EAAEC,IAAI,KAAK;EAClD,IAAI,CAACS,MAAM,EAAE,OAAOA,MAAM;EAC1B,MAAM;IAAE9B,EAAE;IAAEiC,UAAU;IAAEC,KAAK;IAAE7B,aAAa;IAAEa;EAAK,CAAC,GAAGY,MAAM;EAE7D,IAAIT,IAAI,CAACc,GAAG,CAACL,MAAM,CAAC,EAAE;IACpB,OAAOT,IAAI,CAACJ,GAAG,CAACa,MAAM,CAAC;EACzB,CAAC,MAAM;IACL,MAAMM,WAAW,GAAG;MAClBpC,EAAE,EAAED,WAAW,CAACC,EAAE,CAAC;MACnBkB,IAAI;MACJgB,KAAK;MACL,GAAG9B,sBAAsB,CAACC,aAAa,CAAC;MACxC,GAAGZ,CAAC,CAAC4C,OAAO,CACVJ,UAAU,EACV,CAACK,CAAC,EAAEC,CAAC,KAAMA,CAAC,KAAK,QAAQ,GAAG,GAAGrB,IAAI,CAACsB,MAAM,CAAC,CAAC,CAAC,CAACC,WAAW,CAAC,CAAC,GAAGvB,IAAI,CAACwB,KAAK,CAAC,CAAC,CAAC,QAAQ,GAAGH,CAAE,EACxF,CAAC,CACH;IACF,CAAC;IAEDlB,IAAI,CAACO,GAAG,CAACE,MAAM,EAAEM,WAAW,CAAC;IAE7B,OAAOjB,kBAAkB,CAACd,aAAa,EAAEe,QAAQ,EAAEC,IAAI,EAAEe,WAAW,CAAC;EACvE;AACF,CAAC;AAED,MAAMO,wBAAwB,GAAGC,QAAQ,IAAI;EAC3C,IAAInD,CAAC,CAACoD,OAAO,CAACD,QAAQ,CAAC,EAAE,OAAOA,QAAQ;EACxC,MAAM;IAAE/B,IAAI;IAAEO,QAAQ;IAAE0B,IAAI;IAAEZ;EAAM,CAAC,GAAGU,QAAQ;EAChD,MAAMvB,IAAI,GAAG,IAAI0B,OAAO,CAAC,CAAC;EAE1B,IAAItD,CAAC,CAACmB,OAAO,CAACC,IAAI,CAAC,EAAE;IACnB,MAAMmC,MAAM,GAAGnC,IAAI,CAACE,GAAG,CAACkC,CAAC,IAAIlB,eAAe,CAACkB,CAAC,EAAE7B,QAAQ,EAAEC,IAAI,CAAC,CAAC;IAChE,OAAO;MAAER,IAAI,EAAEmC,MAAM;MAAEF,IAAI;MAAEZ,KAAK;MAAE,GAAGrB;IAAK,CAAC;EAC/C,CAAC,MAAM;IACL,OAAO;MAAEA,IAAI,EAAE;QAAE,GAAGkB,eAAe,CAAClB,IAAI,EAAEO,QAAQ,EAAEC,IAAI;MAAE,CAAC;MAAEyB,IAAI;MAAEZ;IAAM,CAAC;EAC5E;AACF,CAAC;AAED,MAAMgB,iBAAiB,GAAGzD,CAAC,CAAC0D,SAAS,CAACxD,eAAe,EAAEgD,wBAAwB,CAAC;AAEhF,eAAeO,iBAAiB","ignoreList":[]}
1
+ {"version":3,"file":"transform_response.js","names":["_","inflection","keysToCamelCase","mixin","singular","plural","transformId","id","thru","parseInt","int","transformRelationships","relationships","Object","keys","reduce","acc","key","relationship","isArray","data","ids","map","singularize","get","type","addIncludedRecords","record","included","seen","parent","records","elem","find","compact","value","set","pluralize","related","transformRecord","at","isSelfReference","undefined","attributes","links","has","transformed","mapKeys","v","k","charAt","toLowerCase","slice","defaultTransformResponse","response","isEmpty","meta","WeakMap","result","d","transformResponse","flowRight"],"sources":["../../../src/utils/client/transform_response.ts"],"sourcesContent":["// @ts-nocheck\n// TODO: Fix this file but write tests first\n\nimport _ from 'lodash'\nimport inflection from 'lodash-inflection/lib/lodash-inflection'\nimport { keysToCamelCase } from './utils'\n\n_.mixin({ ...inflection })\n\n// Teach some bad grammar\n_.singular(/^(media)$/i, '$1')\n_.plural(/^(media)$/i, '$1')\n\n// eslint-disable-next-line eqeqeq\nconst transformId = id => _.thru(parseInt(id, 10), int => (int == id ? int : id))\n\nconst transformRelationships = relationships => {\n return Object.keys(relationships || {}).reduce((acc, key) => {\n const relationship = relationships[key]\n if (_.isArray(relationship.data)) {\n const ids = _.map(relationship.data, ({ id }) => transformId(id))\n return { ...acc, [`${_.singularize(key)}_ids`]: ids }\n } else {\n const id = _.get(relationship, 'data.id')\n const type = _.get(relationship, 'data.type', null)\n return {\n ...acc,\n [`${key}_id`]: id ? transformId(id) : null,\n [`${key}_type`]: type,\n }\n }\n }, {})\n}\n\nconst addIncludedRecords = (record, included, seen, parent) => {\n const { relationships } = record\n return Object.keys(relationships || {}).reduce((acc, key) => {\n const relationship = relationships[key]\n if (_.isArray(relationship.data)) {\n const records = _(relationship.data)\n .map(elem => _.find(included, elem))\n .compact() // account for orphans\n .value()\n return _.set(\n acc,\n _.pluralize(key),\n _.map(records, related => transformRecord(related, included, seen))\n )\n } else {\n const [id, type] = _.at(relationship, ['data.id', 'data.type'])\n if (id && type) {\n const isSelfReference = id === record.id && type === record.type\n const related = isSelfReference ? undefined : _.find(included, { id, type })\n return _.set(acc, key, related ? transformRecord(related, included, seen) : null)\n } else {\n return acc\n }\n }\n }, parent)\n}\n\nconst transformRecord = (record, included, seen) => {\n if (!record) return record\n const { id, attributes, links, relationships, type } = record\n\n if (seen.has(record)) {\n return seen.get(record)\n } else {\n const transformed = {\n id: transformId(id),\n type,\n links,\n ...transformRelationships(relationships),\n ..._.mapKeys(\n attributes,\n (v, k) => (k === 'length' ? `${type.charAt(0).toLowerCase()}${type.slice(1)}Length` : k),\n {}\n ),\n }\n\n seen.set(record, transformed)\n\n return addIncludedRecords(record, included, seen, transformed)\n }\n}\n\nconst defaultTransformResponse = response => {\n if (_.isEmpty(response)) return response\n const { data, included, meta, links } = response\n const seen = new WeakMap()\n\n if (_.isArray(data)) {\n const result = data.map(d => transformRecord(d, included, seen))\n return { data: result, meta, links, ...data }\n } else {\n return { data: { ...transformRecord(data, included, seen) }, meta, links }\n }\n}\n\nconst transformResponse = _.flowRight(keysToCamelCase, defaultTransformResponse)\n\nexport default transformResponse\n"],"mappings":"AAAA;AACA;;AAEA,OAAOA,CAAC,MAAM,QAAQ;AACtB,OAAOC,UAAU,MAAM,yCAAyC;AAChE,SAASC,eAAe,QAAQ,SAAS;AAEzCF,CAAC,CAACG,KAAK,CAAC;EAAE,GAAGF;AAAW,CAAC,CAAC;;AAE1B;AACAD,CAAC,CAACI,QAAQ,CAAC,YAAY,EAAE,IAAI,CAAC;AAC9BJ,CAAC,CAACK,MAAM,CAAC,YAAY,EAAE,IAAI,CAAC;;AAE5B;AACA,MAAMC,WAAW,GAAGC,EAAE,IAAIP,CAAC,CAACQ,IAAI,CAACC,QAAQ,CAACF,EAAE,EAAE,EAAE,CAAC,EAAEG,GAAG,IAAKA,GAAG,IAAIH,EAAE,GAAGG,GAAG,GAAGH,EAAG,CAAC;AAEjF,MAAMI,sBAAsB,GAAGC,aAAa,IAAI;EAC9C,OAAOC,MAAM,CAACC,IAAI,CAACF,aAAa,IAAI,CAAC,CAAC,CAAC,CAACG,MAAM,CAAC,CAACC,GAAG,EAAEC,GAAG,KAAK;IAC3D,MAAMC,YAAY,GAAGN,aAAa,CAACK,GAAG,CAAC;IACvC,IAAIjB,CAAC,CAACmB,OAAO,CAACD,YAAY,CAACE,IAAI,CAAC,EAAE;MAChC,MAAMC,GAAG,GAAGrB,CAAC,CAACsB,GAAG,CAACJ,YAAY,CAACE,IAAI,EAAE,CAAC;QAAEb;MAAG,CAAC,KAAKD,WAAW,CAACC,EAAE,CAAC,CAAC;MACjE,OAAO;QAAE,GAAGS,GAAG;QAAE,CAAC,GAAGhB,CAAC,CAACuB,WAAW,CAACN,GAAG,CAAC,MAAM,GAAGI;MAAI,CAAC;IACvD,CAAC,MAAM;MACL,MAAMd,EAAE,GAAGP,CAAC,CAACwB,GAAG,CAACN,YAAY,EAAE,SAAS,CAAC;MACzC,MAAMO,IAAI,GAAGzB,CAAC,CAACwB,GAAG,CAACN,YAAY,EAAE,WAAW,EAAE,IAAI,CAAC;MACnD,OAAO;QACL,GAAGF,GAAG;QACN,CAAC,GAAGC,GAAG,KAAK,GAAGV,EAAE,GAAGD,WAAW,CAACC,EAAE,CAAC,GAAG,IAAI;QAC1C,CAAC,GAAGU,GAAG,OAAO,GAAGQ;MACnB,CAAC;IACH;EACF,CAAC,EAAE,CAAC,CAAC,CAAC;AACR,CAAC;AAED,MAAMC,kBAAkB,GAAGA,CAACC,MAAM,EAAEC,QAAQ,EAAEC,IAAI,EAAEC,MAAM,KAAK;EAC7D,MAAM;IAAElB;EAAc,CAAC,GAAGe,MAAM;EAChC,OAAOd,MAAM,CAACC,IAAI,CAACF,aAAa,IAAI,CAAC,CAAC,CAAC,CAACG,MAAM,CAAC,CAACC,GAAG,EAAEC,GAAG,KAAK;IAC3D,MAAMC,YAAY,GAAGN,aAAa,CAACK,GAAG,CAAC;IACvC,IAAIjB,CAAC,CAACmB,OAAO,CAACD,YAAY,CAACE,IAAI,CAAC,EAAE;MAChC,MAAMW,OAAO,GAAG/B,CAAC,CAACkB,YAAY,CAACE,IAAI,CAAC,CACjCE,GAAG,CAACU,IAAI,IAAIhC,CAAC,CAACiC,IAAI,CAACL,QAAQ,EAAEI,IAAI,CAAC,CAAC,CACnCE,OAAO,CAAC,CAAC,CAAC;MAAA,CACVC,KAAK,CAAC,CAAC;MACV,OAAOnC,CAAC,CAACoC,GAAG,CACVpB,GAAG,EACHhB,CAAC,CAACqC,SAAS,CAACpB,GAAG,CAAC,EAChBjB,CAAC,CAACsB,GAAG,CAACS,OAAO,EAAEO,OAAO,IAAIC,eAAe,CAACD,OAAO,EAAEV,QAAQ,EAAEC,IAAI,CAAC,CACpE,CAAC;IACH,CAAC,MAAM;MACL,MAAM,CAACtB,EAAE,EAAEkB,IAAI,CAAC,GAAGzB,CAAC,CAACwC,EAAE,CAACtB,YAAY,EAAE,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;MAC/D,IAAIX,EAAE,IAAIkB,IAAI,EAAE;QACd,MAAMgB,eAAe,GAAGlC,EAAE,KAAKoB,MAAM,CAACpB,EAAE,IAAIkB,IAAI,KAAKE,MAAM,CAACF,IAAI;QAChE,MAAMa,OAAO,GAAGG,eAAe,GAAGC,SAAS,GAAG1C,CAAC,CAACiC,IAAI,CAACL,QAAQ,EAAE;UAAErB,EAAE;UAAEkB;QAAK,CAAC,CAAC;QAC5E,OAAOzB,CAAC,CAACoC,GAAG,CAACpB,GAAG,EAAEC,GAAG,EAAEqB,OAAO,GAAGC,eAAe,CAACD,OAAO,EAAEV,QAAQ,EAAEC,IAAI,CAAC,GAAG,IAAI,CAAC;MACnF,CAAC,MAAM;QACL,OAAOb,GAAG;MACZ;IACF;EACF,CAAC,EAAEc,MAAM,CAAC;AACZ,CAAC;AAED,MAAMS,eAAe,GAAGA,CAACZ,MAAM,EAAEC,QAAQ,EAAEC,IAAI,KAAK;EAClD,IAAI,CAACF,MAAM,EAAE,OAAOA,MAAM;EAC1B,MAAM;IAAEpB,EAAE;IAAEoC,UAAU;IAAEC,KAAK;IAAEhC,aAAa;IAAEa;EAAK,CAAC,GAAGE,MAAM;EAE7D,IAAIE,IAAI,CAACgB,GAAG,CAAClB,MAAM,CAAC,EAAE;IACpB,OAAOE,IAAI,CAACL,GAAG,CAACG,MAAM,CAAC;EACzB,CAAC,MAAM;IACL,MAAMmB,WAAW,GAAG;MAClBvC,EAAE,EAAED,WAAW,CAACC,EAAE,CAAC;MACnBkB,IAAI;MACJmB,KAAK;MACL,GAAGjC,sBAAsB,CAACC,aAAa,CAAC;MACxC,GAAGZ,CAAC,CAAC+C,OAAO,CACVJ,UAAU,EACV,CAACK,CAAC,EAAEC,CAAC,KAAMA,CAAC,KAAK,QAAQ,GAAG,GAAGxB,IAAI,CAACyB,MAAM,CAAC,CAAC,CAAC,CAACC,WAAW,CAAC,CAAC,GAAG1B,IAAI,CAAC2B,KAAK,CAAC,CAAC,CAAC,QAAQ,GAAGH,CAAE,EACxF,CAAC,CACH;IACF,CAAC;IAEDpB,IAAI,CAACO,GAAG,CAACT,MAAM,EAAEmB,WAAW,CAAC;IAE7B,OAAOpB,kBAAkB,CAACC,MAAM,EAAEC,QAAQ,EAAEC,IAAI,EAAEiB,WAAW,CAAC;EAChE;AACF,CAAC;AAED,MAAMO,wBAAwB,GAAGC,QAAQ,IAAI;EAC3C,IAAItD,CAAC,CAACuD,OAAO,CAACD,QAAQ,CAAC,EAAE,OAAOA,QAAQ;EACxC,MAAM;IAAElC,IAAI;IAAEQ,QAAQ;IAAE4B,IAAI;IAAEZ;EAAM,CAAC,GAAGU,QAAQ;EAChD,MAAMzB,IAAI,GAAG,IAAI4B,OAAO,CAAC,CAAC;EAE1B,IAAIzD,CAAC,CAACmB,OAAO,CAACC,IAAI,CAAC,EAAE;IACnB,MAAMsC,MAAM,GAAGtC,IAAI,CAACE,GAAG,CAACqC,CAAC,IAAIpB,eAAe,CAACoB,CAAC,EAAE/B,QAAQ,EAAEC,IAAI,CAAC,CAAC;IAChE,OAAO;MAAET,IAAI,EAAEsC,MAAM;MAAEF,IAAI;MAAEZ,KAAK;MAAE,GAAGxB;IAAK,CAAC;EAC/C,CAAC,MAAM;IACL,OAAO;MAAEA,IAAI,EAAE;QAAE,GAAGmB,eAAe,CAACnB,IAAI,EAAEQ,QAAQ,EAAEC,IAAI;MAAE,CAAC;MAAE2B,IAAI;MAAEZ;IAAM,CAAC;EAC5E;AACF,CAAC;AAED,MAAMgB,iBAAiB,GAAG5D,CAAC,CAAC6D,SAAS,CAAC3D,eAAe,EAAEmD,wBAAwB,CAAC;AAEhF,eAAeO,iBAAiB","ignoreList":[]}
@@ -8,6 +8,7 @@ export type ReplyShadowMessage = {
8
8
  type: 'ReplyShadowMessage';
9
9
  id: string;
10
10
  messageId: string;
11
+ replyRoot: MessageResource | null;
11
12
  isReplyShadowMessage: boolean;
12
13
  nextRendersAuthor: boolean;
13
14
  };
@@ -1 +1 @@
1
- {"version":3,"file":"group_messages.d.ts","sourceRoot":"","sources":["../../src/utils/group_messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAMjE,MAAM,MAAM,aAAa,GAAG;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAE/E,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,oBAAoB,CAAA;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,CAAA;IACjB,oBAAoB,EAAE,OAAO,CAAA;IAC7B,iBAAiB,EAAE,OAAO,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,eAAe,GAAG,eAAe,GAAG,aAAa,GAAG,kBAAkB,CAAA;AAElF,UAAU,kBAAkB;IAC1B,EAAE,EAAE,eAAe,EAAE,CAAA;IACrB,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,kBAAkB,GAAG,eAAe,EAAE,CAkC1F"}
1
+ {"version":3,"file":"group_messages.d.ts","sourceRoot":"","sources":["../../src/utils/group_messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAMjE,MAAM,MAAM,aAAa,GAAG;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAE/E,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,oBAAoB,CAAA;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,eAAe,GAAG,IAAI,CAAA;IACjC,oBAAoB,EAAE,OAAO,CAAA;IAC7B,iBAAiB,EAAE,OAAO,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,eAAe,GAAG,eAAe,GAAG,aAAa,GAAG,kBAAkB,CAAA;AAElF,UAAU,kBAAkB;IAC1B,EAAE,EAAE,eAAe,EAAE,CAAA;IACrB,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,kBAAkB,GAAG,eAAe,EAAE,CAyC1F"}
@@ -6,6 +6,7 @@ export function groupMessages({
6
6
  inReplyScreen
7
7
  }) {
8
8
  const items = [];
9
+ const messagesById = new Map(ms.map(message => [message.id, message]));
9
10
  let myLatestSeen = false;
10
11
  let nextNeighborEnriched;
11
12
  ms.forEach((message, i) => {
@@ -22,7 +23,10 @@ export function groupMessages({
22
23
  }
23
24
  const isMyLatest = !myLatestSeen && message.mine;
24
25
  if (isMyLatest) myLatestSeen = true;
25
- const enriched = enrichRegularMessage(message, prev, next, isMyLatest, !!inReplyScreen);
26
+ const enriched = enrichRegularMessage({
27
+ ...message,
28
+ replyRoot: resolveReplyRoot(message, messagesById)
29
+ }, prev, next, isMyLatest, !!inReplyScreen);
26
30
  items.push(enriched);
27
31
  const shadow = replyShadowFor(enriched, prev);
28
32
  if (shadow) items.push(shadow);
@@ -39,6 +43,10 @@ function neighborsOf(arr, i) {
39
43
  next: arr[i - 1]
40
44
  };
41
45
  }
46
+ function resolveReplyRoot(message, messagesById) {
47
+ if (!message.replyRootId || message.replyRootId === message.id) return null;
48
+ return messagesById.get(message.replyRootId) ?? message.replyRoot ?? null;
49
+ }
42
50
  function enrichSystemMessage(message, next) {
43
51
  return {
44
52
  ...message,
@@ -114,6 +122,7 @@ function replyShadowFor(message, prev) {
114
122
  type: 'ReplyShadowMessage',
115
123
  id: `${message.id}-${message.replyRootId}`,
116
124
  messageId: message.replyRootId,
125
+ replyRoot: message.replyRoot ?? null,
117
126
  isReplyShadowMessage: true,
118
127
  nextRendersAuthor: message.renderAuthor ?? false
119
128
  };
@@ -1 +1 @@
1
- {"version":3,"file":"group_messages.js","names":["dayjs","isSystemMessage","FIVE_MINUTES_MS","groupMessages","ms","inReplyScreen","items","myLatestSeen","nextNeighborEnriched","forEach","message","i","prev","neighborsOf","next","enriched","enrichSystemMessage","push","datesDifferBetween","dateSeparator","isMyLatest","mine","enrichRegularMessage","shadow","replyShadowFor","arr","myLatestInConversation","lastInGroup","renderAuthor","nextRendersAuthor","isReplyShadowMessage","nextIsReplyShadowMessage","threadPosition","inThread","replyRootId","showThreadDetails","startsNewGroup","nextIntroducesReplyShadow","threadPositionFor","prevIsMyReply","undefined","nextIsMyReply","a","b","author","id","differsByMoreThan5Min","Math","abs","toMillis","createdAt","iso","Date","getTime","dateKey","format","type","date","isThreadRoot","isLast","current","nextInThread","nextIsThreadRoot","differentThread","enteringNewThread","messageId"],"sources":["../../src/utils/group_messages.ts"],"sourcesContent":["import type { MessageResource } from '../types/resources/message'\nimport dayjs from './dayjs'\nimport { isSystemMessage } from './system_messages'\n\nconst FIVE_MINUTES_MS = 5 * 60 * 1000\n\nexport type DateSeparator = { type: 'DateSeparator'; id: string; date: string }\n\nexport type ReplyShadowMessage = {\n type: 'ReplyShadowMessage'\n id: string\n messageId: string\n isReplyShadowMessage: boolean\n nextRendersAuthor: boolean\n}\n\nexport type EnrichedMessage = MessageResource | DateSeparator | ReplyShadowMessage\n\ninterface GroupMessagesProps {\n ms: MessageResource[]\n inReplyScreen?: boolean\n}\n\nexport function groupMessages({ ms, inReplyScreen }: GroupMessagesProps): EnrichedMessage[] {\n const items: EnrichedMessage[] = []\n let myLatestSeen = false\n let nextNeighborEnriched: MessageResource | undefined\n\n ms.forEach((message, i) => {\n const { prev } = neighborsOf(ms, i)\n const next = nextNeighborEnriched\n\n if (isSystemMessage(message)) {\n const enriched = enrichSystemMessage(message, next)\n items.push(enriched)\n if (datesDifferBetween(message, prev)) items.push(dateSeparator(message))\n nextNeighborEnriched = enriched\n return\n }\n\n const isMyLatest = !myLatestSeen && message.mine\n if (isMyLatest) myLatestSeen = true\n\n const enriched = enrichRegularMessage(message, prev, next, isMyLatest, !!inReplyScreen)\n items.push(enriched)\n\n const shadow = replyShadowFor(enriched, prev)\n if (shadow) items.push(shadow)\n\n if (!prev || datesDifferBetween(message, prev)) {\n items.push(dateSeparator(message))\n }\n\n nextNeighborEnriched = enriched\n })\n\n return items\n}\n\nfunction neighborsOf<T>(arr: T[], i: number): { prev: T | undefined; next: T | undefined } {\n return { prev: arr[i + 1], next: arr[i - 1] }\n}\n\nfunction enrichSystemMessage(\n message: MessageResource,\n next: MessageResource | undefined\n): MessageResource {\n return {\n ...message,\n myLatestInConversation: false,\n lastInGroup: true,\n renderAuthor: false,\n nextRendersAuthor: next?.renderAuthor,\n isReplyShadowMessage: false,\n nextIsReplyShadowMessage: false,\n threadPosition: null,\n }\n}\n\nfunction enrichRegularMessage(\n message: MessageResource,\n prev: MessageResource | undefined,\n next: MessageResource | undefined,\n isMyLatest: boolean,\n inReplyScreen: boolean\n): MessageResource {\n const inThread = message.replyRootId !== null\n const showThreadDetails = !inReplyScreen && inThread\n\n return {\n ...message,\n myLatestInConversation: isMyLatest,\n lastInGroup: !next || startsNewGroup(next, message),\n renderAuthor: !message.mine && (!prev || startsNewGroup(message, prev)),\n nextRendersAuthor: next?.renderAuthor,\n isReplyShadowMessage: false,\n nextIsReplyShadowMessage: nextIntroducesReplyShadow(next, message),\n threadPosition: showThreadDetails ? threadPositionFor(message, next) : null,\n prevIsMyReply: showThreadDetails ? prev?.mine : undefined,\n nextIsMyReply: showThreadDetails ? next?.mine : undefined,\n }\n}\n\nfunction startsNewGroup(a: MessageResource, b: MessageResource): boolean {\n return (\n a.author?.id !== b.author?.id ||\n differsByMoreThan5Min(a, b) ||\n a.replyRootId !== b.replyRootId ||\n datesDifferBetween(a, b)\n )\n}\n\nfunction differsByMoreThan5Min(a: MessageResource, b: MessageResource): boolean {\n return Math.abs(toMillis(a.createdAt) - toMillis(b.createdAt)) > FIVE_MINUTES_MS\n}\n\nfunction toMillis(iso: string): number {\n return new Date(iso).getTime()\n}\n\nfunction datesDifferBetween(a: MessageResource, b: MessageResource | undefined): boolean {\n if (!b) return false\n return dateKey(a) !== dateKey(b)\n}\n\nfunction dateKey(message: MessageResource): string {\n return dayjs(message.createdAt).format('YYYY-MM-DD')\n}\n\nfunction dateSeparator(message: MessageResource): DateSeparator {\n return { type: 'DateSeparator', id: `day-divider-${message.id}`, date: dateKey(message) }\n}\n\nfunction threadPositionFor(\n message: MessageResource,\n next: MessageResource | undefined\n): 'first' | 'center' | 'last' | null {\n const isThreadRoot = message.replyRootId === message.id\n const isLast =\n !next || next.replyRootId !== message.replyRootId || datesDifferBetween(next, message)\n\n if (isThreadRoot && isLast) return null\n if (isThreadRoot) return 'first'\n if (isLast) return 'last'\n return 'center'\n}\n\nfunction nextIntroducesReplyShadow(\n next: MessageResource | undefined,\n current: MessageResource\n): boolean {\n if (!next) return false\n const nextInThread = next.replyRootId !== null\n const nextIsThreadRoot = next.replyRootId === next.id\n const differentThread = next.replyRootId !== current.replyRootId\n return nextInThread && !nextIsThreadRoot && (differentThread || datesDifferBetween(next, current))\n}\n\nfunction replyShadowFor(\n message: MessageResource,\n prev: MessageResource | undefined\n): ReplyShadowMessage | undefined {\n if (!message.replyRootId) return undefined\n if (message.replyRootId === message.id) return undefined\n\n const enteringNewThread =\n !prev || prev.replyRootId !== message.replyRootId || datesDifferBetween(prev, message)\n if (!enteringNewThread) return undefined\n\n return {\n type: 'ReplyShadowMessage',\n id: `${message.id}-${message.replyRootId}`,\n messageId: message.replyRootId,\n isReplyShadowMessage: true,\n nextRendersAuthor: message.renderAuthor ?? false,\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"group_messages.js","names":["dayjs","isSystemMessage","FIVE_MINUTES_MS","groupMessages","ms","inReplyScreen","items","messagesById","Map","map","message","id","myLatestSeen","nextNeighborEnriched","forEach","i","prev","neighborsOf","next","enriched","enrichSystemMessage","push","datesDifferBetween","dateSeparator","isMyLatest","mine","enrichRegularMessage","replyRoot","resolveReplyRoot","shadow","replyShadowFor","arr","replyRootId","get","myLatestInConversation","lastInGroup","renderAuthor","nextRendersAuthor","isReplyShadowMessage","nextIsReplyShadowMessage","threadPosition","inThread","showThreadDetails","startsNewGroup","nextIntroducesReplyShadow","threadPositionFor","prevIsMyReply","undefined","nextIsMyReply","a","b","author","differsByMoreThan5Min","Math","abs","toMillis","createdAt","iso","Date","getTime","dateKey","format","type","date","isThreadRoot","isLast","current","nextInThread","nextIsThreadRoot","differentThread","enteringNewThread","messageId"],"sources":["../../src/utils/group_messages.ts"],"sourcesContent":["import type { MessageResource } from '../types/resources/message'\nimport dayjs from './dayjs'\nimport { isSystemMessage } from './system_messages'\n\nconst FIVE_MINUTES_MS = 5 * 60 * 1000\n\nexport type DateSeparator = { type: 'DateSeparator'; id: string; date: string }\n\nexport type ReplyShadowMessage = {\n type: 'ReplyShadowMessage'\n id: string\n messageId: string\n replyRoot: MessageResource | null\n isReplyShadowMessage: boolean\n nextRendersAuthor: boolean\n}\n\nexport type EnrichedMessage = MessageResource | DateSeparator | ReplyShadowMessage\n\ninterface GroupMessagesProps {\n ms: MessageResource[]\n inReplyScreen?: boolean\n}\n\nexport function groupMessages({ ms, inReplyScreen }: GroupMessagesProps): EnrichedMessage[] {\n const items: EnrichedMessage[] = []\n const messagesById = new Map(ms.map(message => [message.id, message]))\n let myLatestSeen = false\n let nextNeighborEnriched: MessageResource | undefined\n\n ms.forEach((message, i) => {\n const { prev } = neighborsOf(ms, i)\n const next = nextNeighborEnriched\n\n if (isSystemMessage(message)) {\n const enriched = enrichSystemMessage(message, next)\n items.push(enriched)\n if (datesDifferBetween(message, prev)) items.push(dateSeparator(message))\n nextNeighborEnriched = enriched\n return\n }\n\n const isMyLatest = !myLatestSeen && message.mine\n if (isMyLatest) myLatestSeen = true\n\n const enriched = enrichRegularMessage(\n { ...message, replyRoot: resolveReplyRoot(message, messagesById) },\n prev,\n next,\n isMyLatest,\n !!inReplyScreen\n )\n items.push(enriched)\n\n const shadow = replyShadowFor(enriched, prev)\n if (shadow) items.push(shadow)\n\n if (!prev || datesDifferBetween(message, prev)) {\n items.push(dateSeparator(message))\n }\n\n nextNeighborEnriched = enriched\n })\n\n return items\n}\n\nfunction neighborsOf<T>(arr: T[], i: number): { prev: T | undefined; next: T | undefined } {\n return { prev: arr[i + 1], next: arr[i - 1] }\n}\n\nfunction resolveReplyRoot(\n message: MessageResource,\n messagesById: Map<string, MessageResource>\n): MessageResource | null {\n if (!message.replyRootId || message.replyRootId === message.id) return null\n return messagesById.get(message.replyRootId) ?? message.replyRoot ?? null\n}\n\nfunction enrichSystemMessage(\n message: MessageResource,\n next: MessageResource | undefined\n): MessageResource {\n return {\n ...message,\n myLatestInConversation: false,\n lastInGroup: true,\n renderAuthor: false,\n nextRendersAuthor: next?.renderAuthor,\n isReplyShadowMessage: false,\n nextIsReplyShadowMessage: false,\n threadPosition: null,\n }\n}\n\nfunction enrichRegularMessage(\n message: MessageResource,\n prev: MessageResource | undefined,\n next: MessageResource | undefined,\n isMyLatest: boolean,\n inReplyScreen: boolean\n): MessageResource {\n const inThread = message.replyRootId !== null\n const showThreadDetails = !inReplyScreen && inThread\n\n return {\n ...message,\n myLatestInConversation: isMyLatest,\n lastInGroup: !next || startsNewGroup(next, message),\n renderAuthor: !message.mine && (!prev || startsNewGroup(message, prev)),\n nextRendersAuthor: next?.renderAuthor,\n isReplyShadowMessage: false,\n nextIsReplyShadowMessage: nextIntroducesReplyShadow(next, message),\n threadPosition: showThreadDetails ? threadPositionFor(message, next) : null,\n prevIsMyReply: showThreadDetails ? prev?.mine : undefined,\n nextIsMyReply: showThreadDetails ? next?.mine : undefined,\n }\n}\n\nfunction startsNewGroup(a: MessageResource, b: MessageResource): boolean {\n return (\n a.author?.id !== b.author?.id ||\n differsByMoreThan5Min(a, b) ||\n a.replyRootId !== b.replyRootId ||\n datesDifferBetween(a, b)\n )\n}\n\nfunction differsByMoreThan5Min(a: MessageResource, b: MessageResource): boolean {\n return Math.abs(toMillis(a.createdAt) - toMillis(b.createdAt)) > FIVE_MINUTES_MS\n}\n\nfunction toMillis(iso: string): number {\n return new Date(iso).getTime()\n}\n\nfunction datesDifferBetween(a: MessageResource, b: MessageResource | undefined): boolean {\n if (!b) return false\n return dateKey(a) !== dateKey(b)\n}\n\nfunction dateKey(message: MessageResource): string {\n return dayjs(message.createdAt).format('YYYY-MM-DD')\n}\n\nfunction dateSeparator(message: MessageResource): DateSeparator {\n return { type: 'DateSeparator', id: `day-divider-${message.id}`, date: dateKey(message) }\n}\n\nfunction threadPositionFor(\n message: MessageResource,\n next: MessageResource | undefined\n): 'first' | 'center' | 'last' | null {\n const isThreadRoot = message.replyRootId === message.id\n const isLast =\n !next || next.replyRootId !== message.replyRootId || datesDifferBetween(next, message)\n\n if (isThreadRoot && isLast) return null\n if (isThreadRoot) return 'first'\n if (isLast) return 'last'\n return 'center'\n}\n\nfunction nextIntroducesReplyShadow(\n next: MessageResource | undefined,\n current: MessageResource\n): boolean {\n if (!next) return false\n const nextInThread = next.replyRootId !== null\n const nextIsThreadRoot = next.replyRootId === next.id\n const differentThread = next.replyRootId !== current.replyRootId\n return nextInThread && !nextIsThreadRoot && (differentThread || datesDifferBetween(next, current))\n}\n\nfunction replyShadowFor(\n message: MessageResource,\n prev: MessageResource | undefined\n): ReplyShadowMessage | undefined {\n if (!message.replyRootId) return undefined\n if (message.replyRootId === message.id) return undefined\n\n const enteringNewThread =\n !prev || prev.replyRootId !== message.replyRootId || datesDifferBetween(prev, message)\n if (!enteringNewThread) return undefined\n\n return {\n type: 'ReplyShadowMessage',\n id: `${message.id}-${message.replyRootId}`,\n messageId: message.replyRootId,\n replyRoot: message.replyRoot ?? null,\n isReplyShadowMessage: true,\n nextRendersAuthor: message.renderAuthor ?? false,\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,6 @@
1
+ /** replaceEqualById
2
+ * TanStack's default structural sharing compares arrays by index, so an insert or
3
+ * delete gives every shifted record a new identity. Match by id instead.
4
+ */
5
+ export declare function replaceEqualById(oldData: unknown, newData: unknown): unknown;
6
+ //# sourceMappingURL=replace_equal_by_id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replace_equal_by_id.d.ts","sourceRoot":"","sources":["../../src/utils/replace_equal_by_id.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAgC5E"}
@@ -0,0 +1,41 @@
1
+ import { replaceEqualDeep } from '@tanstack/react-query';
2
+ /** replaceEqualById
3
+ * TanStack's default structural sharing compares arrays by index, so an insert or
4
+ * delete gives every shifted record a new identity. Match by id instead.
5
+ */
6
+ export function replaceEqualById(oldData, newData) {
7
+ const prev = oldData;
8
+ const next = newData;
9
+ if (!prev?.pages || !next?.pages) return replaceEqualDeep(prev, next);
10
+ const prevById = new Map();
11
+ prev.pages.forEach(page => page.data.forEach(record => prevById.set(record.id, record)));
12
+ let changed = prev.pages.length !== next.pages.length;
13
+ const pages = next.pages.map((page, index) => {
14
+ const prevPage = prev.pages[index];
15
+ const data = page.data.map(record => {
16
+ const previous = prevById.get(record.id);
17
+ return previous ? replaceEqualDeep(previous, record) : record;
18
+ });
19
+ const links = replaceEqualDeep(prevPage?.links, page.links);
20
+ const meta = replaceEqualDeep(prevPage?.meta, page.meta);
21
+ const sameRecords = !!prevPage && data.length === prevPage.data.length && data.every((record, i) => record === prevPage.data[i]);
22
+ if (prevPage && sameRecords && links === prevPage.links && meta === prevPage.meta) {
23
+ return prevPage;
24
+ }
25
+ changed = true;
26
+ return {
27
+ ...page,
28
+ data,
29
+ links,
30
+ meta
31
+ };
32
+ });
33
+ const pageParams = replaceEqualDeep(prev.pageParams, next.pageParams);
34
+ if (!changed && pageParams === prev.pageParams) return prev;
35
+ return {
36
+ ...next,
37
+ pages,
38
+ pageParams
39
+ };
40
+ }
41
+ //# sourceMappingURL=replace_equal_by_id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replace_equal_by_id.js","names":["replaceEqualDeep","replaceEqualById","oldData","newData","prev","next","pages","prevById","Map","forEach","page","data","record","set","id","changed","length","map","index","prevPage","previous","get","links","meta","sameRecords","every","i","pageParams"],"sources":["../../src/utils/replace_equal_by_id.ts"],"sourcesContent":["import { InfiniteData, replaceEqualDeep } from '@tanstack/react-query'\nimport { ApiCollection, ResourceObject } from '../types'\n\ntype Pages = InfiniteData<ApiCollection<ResourceObject>>\n\n/** replaceEqualById\n * TanStack's default structural sharing compares arrays by index, so an insert or\n * delete gives every shifted record a new identity. Match by id instead.\n */\nexport function replaceEqualById(oldData: unknown, newData: unknown): unknown {\n const prev = oldData as Pages | undefined\n const next = newData as Pages\n if (!prev?.pages || !next?.pages) return replaceEqualDeep(prev, next)\n\n const prevById = new Map<ResourceObject['id'], ResourceObject>()\n prev.pages.forEach(page => page.data.forEach(record => prevById.set(record.id, record)))\n\n let changed = prev.pages.length !== next.pages.length\n const pages = next.pages.map((page, index) => {\n const prevPage = prev.pages[index]\n const data = page.data.map(record => {\n const previous = prevById.get(record.id)\n return previous ? replaceEqualDeep(previous, record) : record\n })\n const links = replaceEqualDeep(prevPage?.links, page.links)\n const meta = replaceEqualDeep(prevPage?.meta, page.meta)\n const sameRecords =\n !!prevPage &&\n data.length === prevPage.data.length &&\n data.every((record, i) => record === prevPage.data[i])\n\n if (prevPage && sameRecords && links === prevPage.links && meta === prevPage.meta) {\n return prevPage\n }\n changed = true\n return { ...page, data, links, meta }\n })\n const pageParams = replaceEqualDeep(prev.pageParams, next.pageParams)\n\n if (!changed && pageParams === prev.pageParams) return prev\n return { ...next, pages, pageParams }\n}\n"],"mappings":"AAAA,SAAuBA,gBAAgB,QAAQ,uBAAuB;AAKtE;AACA;AACA;AACA;AACA,OAAO,SAASC,gBAAgBA,CAACC,OAAgB,EAAEC,OAAgB,EAAW;EAC5E,MAAMC,IAAI,GAAGF,OAA4B;EACzC,MAAMG,IAAI,GAAGF,OAAgB;EAC7B,IAAI,CAACC,IAAI,EAAEE,KAAK,IAAI,CAACD,IAAI,EAAEC,KAAK,EAAE,OAAON,gBAAgB,CAACI,IAAI,EAAEC,IAAI,CAAC;EAErE,MAAME,QAAQ,GAAG,IAAIC,GAAG,CAAuC,CAAC;EAChEJ,IAAI,CAACE,KAAK,CAACG,OAAO,CAACC,IAAI,IAAIA,IAAI,CAACC,IAAI,CAACF,OAAO,CAACG,MAAM,IAAIL,QAAQ,CAACM,GAAG,CAACD,MAAM,CAACE,EAAE,EAAEF,MAAM,CAAC,CAAC,CAAC;EAExF,IAAIG,OAAO,GAAGX,IAAI,CAACE,KAAK,CAACU,MAAM,KAAKX,IAAI,CAACC,KAAK,CAACU,MAAM;EACrD,MAAMV,KAAK,GAAGD,IAAI,CAACC,KAAK,CAACW,GAAG,CAAC,CAACP,IAAI,EAAEQ,KAAK,KAAK;IAC5C,MAAMC,QAAQ,GAAGf,IAAI,CAACE,KAAK,CAACY,KAAK,CAAC;IAClC,MAAMP,IAAI,GAAGD,IAAI,CAACC,IAAI,CAACM,GAAG,CAACL,MAAM,IAAI;MACnC,MAAMQ,QAAQ,GAAGb,QAAQ,CAACc,GAAG,CAACT,MAAM,CAACE,EAAE,CAAC;MACxC,OAAOM,QAAQ,GAAGpB,gBAAgB,CAACoB,QAAQ,EAAER,MAAM,CAAC,GAAGA,MAAM;IAC/D,CAAC,CAAC;IACF,MAAMU,KAAK,GAAGtB,gBAAgB,CAACmB,QAAQ,EAAEG,KAAK,EAAEZ,IAAI,CAACY,KAAK,CAAC;IAC3D,MAAMC,IAAI,GAAGvB,gBAAgB,CAACmB,QAAQ,EAAEI,IAAI,EAAEb,IAAI,CAACa,IAAI,CAAC;IACxD,MAAMC,WAAW,GACf,CAAC,CAACL,QAAQ,IACVR,IAAI,CAACK,MAAM,KAAKG,QAAQ,CAACR,IAAI,CAACK,MAAM,IACpCL,IAAI,CAACc,KAAK,CAAC,CAACb,MAAM,EAAEc,CAAC,KAAKd,MAAM,KAAKO,QAAQ,CAACR,IAAI,CAACe,CAAC,CAAC,CAAC;IAExD,IAAIP,QAAQ,IAAIK,WAAW,IAAIF,KAAK,KAAKH,QAAQ,CAACG,KAAK,IAAIC,IAAI,KAAKJ,QAAQ,CAACI,IAAI,EAAE;MACjF,OAAOJ,QAAQ;IACjB;IACAJ,OAAO,GAAG,IAAI;IACd,OAAO;MAAE,GAAGL,IAAI;MAAEC,IAAI;MAAEW,KAAK;MAAEC;IAAK,CAAC;EACvC,CAAC,CAAC;EACF,MAAMI,UAAU,GAAG3B,gBAAgB,CAACI,IAAI,CAACuB,UAAU,EAAEtB,IAAI,CAACsB,UAAU,CAAC;EAErE,IAAI,CAACZ,OAAO,IAAIY,UAAU,KAAKvB,IAAI,CAACuB,UAAU,EAAE,OAAOvB,IAAI;EAC3D,OAAO;IAAE,GAAGC,IAAI;IAAEC,KAAK;IAAEqB;EAAW,CAAC;AACvC","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"messages_data_options.d.ts","sourceRoot":"","sources":["../../../src/utils/request/messages_data_options.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB;;;;CAmB5B,CAAA;AAED,eAAO,MAAM,kBAAkB,UAAgC,CAAA"}
1
+ {"version":3,"file":"messages_data_options.d.ts","sourceRoot":"","sources":["../../../src/utils/request/messages_data_options.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB;;;;CAmB5B,CAAA;AAED,eAAO,MAAM,kBAAkB,UAAmE,CAAA"}
@@ -3,5 +3,5 @@ export const getMessageFields = {
3
3
  Person: ['name', 'avatar'],
4
4
  ReactionCount: ['value', 'count', 'mine', 'message_id', 'author_ids']
5
5
  };
6
- export const getMessagesInclude = ['author', 'reaction_counts'];
6
+ export const getMessagesInclude = ['author', 'reaction_counts', 'reply_root', 'reply_root.author'];
7
7
  //# sourceMappingURL=messages_data_options.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"messages_data_options.js","names":["getMessageFields","Message","Person","ReactionCount","getMessagesInclude"],"sources":["../../../src/utils/request/messages_data_options.ts"],"sourcesContent":["export const getMessageFields = {\n Message: [\n 'text',\n 'text_edited_at',\n 'mine',\n 'attachments',\n 'created_at',\n 'deleted_at',\n 'author',\n 'reaction_counts',\n 'reply_count',\n 'reply_root',\n 'idempotent_key',\n 'message_type',\n 'person_ids_for_system_event',\n 'system_text_parts',\n ],\n Person: ['name', 'avatar'],\n ReactionCount: ['value', 'count', 'mine', 'message_id', 'author_ids'],\n}\n\nexport const getMessagesInclude = ['author', 'reaction_counts']\n"],"mappings":"AAAA,OAAO,MAAMA,gBAAgB,GAAG;EAC9BC,OAAO,EAAE,CACP,MAAM,EACN,gBAAgB,EAChB,MAAM,EACN,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,QAAQ,EACR,iBAAiB,EACjB,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,6BAA6B,EAC7B,mBAAmB,CACpB;EACDC,MAAM,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC;EAC1BC,aAAa,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY;AACtE,CAAC;AAED,OAAO,MAAMC,kBAAkB,GAAG,CAAC,QAAQ,EAAE,iBAAiB,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"messages_data_options.js","names":["getMessageFields","Message","Person","ReactionCount","getMessagesInclude"],"sources":["../../../src/utils/request/messages_data_options.ts"],"sourcesContent":["export const getMessageFields = {\n Message: [\n 'text',\n 'text_edited_at',\n 'mine',\n 'attachments',\n 'created_at',\n 'deleted_at',\n 'author',\n 'reaction_counts',\n 'reply_count',\n 'reply_root',\n 'idempotent_key',\n 'message_type',\n 'person_ids_for_system_event',\n 'system_text_parts',\n ],\n Person: ['name', 'avatar'],\n ReactionCount: ['value', 'count', 'mine', 'message_id', 'author_ids'],\n}\n\nexport const getMessagesInclude = ['author', 'reaction_counts', 'reply_root', 'reply_root.author']\n"],"mappings":"AAAA,OAAO,MAAMA,gBAAgB,GAAG;EAC9BC,OAAO,EAAE,CACP,MAAM,EACN,gBAAgB,EAChB,MAAM,EACN,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,QAAQ,EACR,iBAAiB,EACjB,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,6BAA6B,EAC7B,mBAAmB,CACpB;EACDC,MAAM,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC;EAC1BC,aAAa,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY;AACtE,CAAC;AAED,OAAO,MAAMC,kBAAkB,GAAG,CAAC,QAAQ,EAAE,iBAAiB,EAAE,YAAY,EAAE,mBAAmB,CAAC","ignoreList":[]}
@@ -1,6 +1,7 @@
1
1
  type TimeUnit = 'hours' | 'minutes' | 'seconds';
2
2
  export declare function toMs(value: number, unit: TimeUnit): number;
3
3
  export declare const FIVE_SECONDS: number;
4
+ export declare const ONE_MINUTE: number;
4
5
  export declare const FIVE_MINUTES: number;
5
6
  export declare const THIRTY_MINUTES: number;
6
7
  export declare const ONE_HOUR: number;
@@ -1 +1 @@
1
- {"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../../src/utils/time.ts"],"names":[],"mappings":"AAAA,KAAK,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAA;AAE/C,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,CAS1D;AAED,eAAO,MAAM,YAAY,QAAqB,CAAA;AAC9C,eAAO,MAAM,YAAY,QAAqB,CAAA;AAC9C,eAAO,MAAM,cAAc,QAAsB,CAAA;AACjD,eAAO,MAAM,QAAQ,QAAmB,CAAA"}
1
+ {"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../../src/utils/time.ts"],"names":[],"mappings":"AAAA,KAAK,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAA;AAE/C,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,CAS1D;AAED,eAAO,MAAM,YAAY,QAAqB,CAAA;AAC9C,eAAO,MAAM,UAAU,QAAqB,CAAA;AAC5C,eAAO,MAAM,YAAY,QAAqB,CAAA;AAC9C,eAAO,MAAM,cAAc,QAAsB,CAAA;AACjD,eAAO,MAAM,QAAQ,QAAmB,CAAA"}
@@ -9,6 +9,7 @@ export function toMs(value, unit) {
9
9
  }
10
10
  }
11
11
  export const FIVE_SECONDS = toMs(5, 'seconds');
12
+ export const ONE_MINUTE = toMs(1, 'minutes');
12
13
  export const FIVE_MINUTES = toMs(5, 'minutes');
13
14
  export const THIRTY_MINUTES = toMs(30, 'minutes');
14
15
  export const ONE_HOUR = toMs(1, 'hours');
@@ -1 +1 @@
1
- {"version":3,"file":"time.js","names":["toMs","value","unit","FIVE_SECONDS","FIVE_MINUTES","THIRTY_MINUTES","ONE_HOUR"],"sources":["../../src/utils/time.ts"],"sourcesContent":["type TimeUnit = 'hours' | 'minutes' | 'seconds'\n\nexport function toMs(value: number, unit: TimeUnit): number {\n switch (unit) {\n case 'hours':\n return value * 60 * 60 * 1000\n case 'minutes':\n return value * 60 * 1000\n case 'seconds':\n return value * 1000\n }\n}\n\nexport const FIVE_SECONDS = toMs(5, 'seconds')\nexport const FIVE_MINUTES = toMs(5, 'minutes')\nexport const THIRTY_MINUTES = toMs(30, 'minutes')\nexport const ONE_HOUR = toMs(1, 'hours')\n"],"mappings":"AAEA,OAAO,SAASA,IAAIA,CAACC,KAAa,EAAEC,IAAc,EAAU;EAC1D,QAAQA,IAAI;IACV,KAAK,OAAO;MACV,OAAOD,KAAK,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI;IAC/B,KAAK,SAAS;MACZ,OAAOA,KAAK,GAAG,EAAE,GAAG,IAAI;IAC1B,KAAK,SAAS;MACZ,OAAOA,KAAK,GAAG,IAAI;EACvB;AACF;AAEA,OAAO,MAAME,YAAY,GAAGH,IAAI,CAAC,CAAC,EAAE,SAAS,CAAC;AAC9C,OAAO,MAAMI,YAAY,GAAGJ,IAAI,CAAC,CAAC,EAAE,SAAS,CAAC;AAC9C,OAAO,MAAMK,cAAc,GAAGL,IAAI,CAAC,EAAE,EAAE,SAAS,CAAC;AACjD,OAAO,MAAMM,QAAQ,GAAGN,IAAI,CAAC,CAAC,EAAE,OAAO,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"time.js","names":["toMs","value","unit","FIVE_SECONDS","ONE_MINUTE","FIVE_MINUTES","THIRTY_MINUTES","ONE_HOUR"],"sources":["../../src/utils/time.ts"],"sourcesContent":["type TimeUnit = 'hours' | 'minutes' | 'seconds'\n\nexport function toMs(value: number, unit: TimeUnit): number {\n switch (unit) {\n case 'hours':\n return value * 60 * 60 * 1000\n case 'minutes':\n return value * 60 * 1000\n case 'seconds':\n return value * 1000\n }\n}\n\nexport const FIVE_SECONDS = toMs(5, 'seconds')\nexport const ONE_MINUTE = toMs(1, 'minutes')\nexport const FIVE_MINUTES = toMs(5, 'minutes')\nexport const THIRTY_MINUTES = toMs(30, 'minutes')\nexport const ONE_HOUR = toMs(1, 'hours')\n"],"mappings":"AAEA,OAAO,SAASA,IAAIA,CAACC,KAAa,EAAEC,IAAc,EAAU;EAC1D,QAAQA,IAAI;IACV,KAAK,OAAO;MACV,OAAOD,KAAK,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI;IAC/B,KAAK,SAAS;MACZ,OAAOA,KAAK,GAAG,EAAE,GAAG,IAAI;IAC1B,KAAK,SAAS;MACZ,OAAOA,KAAK,GAAG,IAAI;EACvB;AACF;AAEA,OAAO,MAAME,YAAY,GAAGH,IAAI,CAAC,CAAC,EAAE,SAAS,CAAC;AAC9C,OAAO,MAAMI,UAAU,GAAGJ,IAAI,CAAC,CAAC,EAAE,SAAS,CAAC;AAC5C,OAAO,MAAMK,YAAY,GAAGL,IAAI,CAAC,CAAC,EAAE,SAAS,CAAC;AAC9C,OAAO,MAAMM,cAAc,GAAGN,IAAI,CAAC,EAAE,EAAE,SAAS,CAAC;AACjD,OAAO,MAAMO,QAAQ,GAAGP,IAAI,CAAC,CAAC,EAAE,OAAO,CAAC","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planningcenter/chat-react-native",
3
- "version": "4.2.0-rc.0",
3
+ "version": "4.2.0-rc.2",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
@@ -84,5 +84,5 @@
84
84
  "sf-symbols-typescript": "^2.2.0",
85
85
  "typescript": "~6.0.3"
86
86
  },
87
- "gitHead": "c3c00b88ac61f4be9a12571182d60e01017f8dde"
87
+ "gitHead": "94508a030b77e376d9a99fd0883290d8a71e778f"
88
88
  }
@@ -0,0 +1,61 @@
1
+ import { buildTestQueryClient } from '../../__utils__/query_client'
2
+ import type { ApiClient } from '../../hooks/use_api_client'
3
+ import { featuresQueryOptions } from '../../hooks/use_features'
4
+
5
+ const features = {
6
+ data: [{ type: 'Feature', id: '1', name: 'ROLLOUT_search', enabled: true }],
7
+ links: {},
8
+ meta: { count: 1, totalCount: 1 },
9
+ }
10
+
11
+ const buildClient = (get: jest.Mock) => ({ chat: { get } }) as unknown as ApiClient
12
+
13
+ const fetchTwice = async (get: jest.Mock, secondsBetween: number) => {
14
+ const queryClient = buildTestQueryClient()
15
+ const options = featuresQueryOptions(buildClient(get))
16
+
17
+ await queryClient.fetchQuery(options)
18
+ jest.advanceTimersByTime(secondsBetween * 1000)
19
+ await queryClient.fetchQuery(options)
20
+ }
21
+
22
+ describe('featuresQueryOptions', () => {
23
+ beforeEach(() => {
24
+ jest.useFakeTimers()
25
+ })
26
+
27
+ afterEach(() => {
28
+ jest.useRealTimers()
29
+ })
30
+
31
+ it('keeps successful flags fresh for five minutes', async () => {
32
+ const get = jest.fn().mockResolvedValue(features)
33
+
34
+ await fetchTwice(get, 4 * 60)
35
+
36
+ expect(get).toHaveBeenCalledTimes(1)
37
+ })
38
+
39
+ it('refetches right away after a failed fetch', async () => {
40
+ const get = jest.fn().mockRejectedValue({ status: 503 })
41
+
42
+ await fetchTwice(get, 1)
43
+
44
+ expect(get).toHaveBeenCalledTimes(2)
45
+ })
46
+
47
+ it('backs off for a minute after a 429', async () => {
48
+ const get = jest.fn().mockRejectedValue({ status: 429 })
49
+ const queryClient = buildTestQueryClient()
50
+ const options = featuresQueryOptions(buildClient(get))
51
+
52
+ await queryClient.fetchQuery(options)
53
+ jest.advanceTimersByTime(30 * 1000)
54
+ await queryClient.fetchQuery(options)
55
+ expect(get).toHaveBeenCalledTimes(1)
56
+
57
+ jest.advanceTimersByTime(31 * 1000)
58
+ await queryClient.fetchQuery(options)
59
+ expect(get).toHaveBeenCalledTimes(2)
60
+ })
61
+ })