@planningcenter/chat-react-native 3.38.0-rc.1 → 3.38.0-rc.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/build/components/conversation/jump_to_bottom_button.d.ts +1 -2
  2. package/build/components/conversation/jump_to_bottom_button.d.ts.map +1 -1
  3. package/build/components/conversation/jump_to_bottom_button.js +7 -39
  4. package/build/components/conversation/jump_to_bottom_button.js.map +1 -1
  5. package/build/components/conversation/message_list.d.ts +10 -0
  6. package/build/components/conversation/message_list.d.ts.map +1 -0
  7. package/build/components/conversation/message_list.js +13 -0
  8. package/build/components/conversation/message_list.js.map +1 -0
  9. package/build/components/conversation/reply_shadow_message.d.ts +2 -1
  10. package/build/components/conversation/reply_shadow_message.d.ts.map +1 -1
  11. package/build/components/conversation/reply_shadow_message.js.map +1 -1
  12. package/build/components/display/conversation_avatar.d.ts +2 -1
  13. package/build/components/display/conversation_avatar.d.ts.map +1 -1
  14. package/build/components/display/conversation_avatar.js +6 -5
  15. package/build/components/display/conversation_avatar.js.map +1 -1
  16. package/build/components/display/emoji_avatar.d.ts +3 -1
  17. package/build/components/display/emoji_avatar.d.ts.map +1 -1
  18. package/build/components/display/emoji_avatar.js +2 -2
  19. package/build/components/display/emoji_avatar.js.map +1 -1
  20. package/build/components/display/icon_avatar.d.ts +3 -1
  21. package/build/components/display/icon_avatar.d.ts.map +1 -1
  22. package/build/components/display/icon_avatar.js +2 -2
  23. package/build/components/display/icon_avatar.js.map +1 -1
  24. package/build/contexts/conversation_context.d.ts +1 -8
  25. package/build/contexts/conversation_context.d.ts.map +1 -1
  26. package/build/contexts/conversation_context.js +3 -21
  27. package/build/contexts/conversation_context.js.map +1 -1
  28. package/build/hooks/groups/use_group_chat_conversation_payload.d.ts.map +1 -1
  29. package/build/hooks/groups/use_group_chat_conversation_payload.js +1 -0
  30. package/build/hooks/groups/use_group_chat_conversation_payload.js.map +1 -1
  31. package/build/hooks/index.d.ts +1 -0
  32. package/build/hooks/index.d.ts.map +1 -1
  33. package/build/hooks/index.js +1 -0
  34. package/build/hooks/index.js.map +1 -1
  35. package/build/hooks/use_conversation_messages.d.ts +6 -15
  36. package/build/hooks/use_conversation_messages.d.ts.map +1 -1
  37. package/build/hooks/use_conversation_messages.js +9 -62
  38. package/build/hooks/use_conversation_messages.js.map +1 -1
  39. package/build/hooks/use_conversation_messages_jolt_events.d.ts.map +1 -1
  40. package/build/hooks/use_conversation_messages_jolt_events.js +4 -4
  41. package/build/hooks/use_conversation_messages_jolt_events.js.map +1 -1
  42. package/build/hooks/use_conversations_actions.d.ts +0 -5
  43. package/build/hooks/use_conversations_actions.d.ts.map +1 -1
  44. package/build/hooks/use_conversations_actions.js +0 -12
  45. package/build/hooks/use_conversations_actions.js.map +1 -1
  46. package/build/hooks/use_features.d.ts +0 -1
  47. package/build/hooks/use_features.d.ts.map +1 -1
  48. package/build/hooks/use_features.js +0 -1
  49. package/build/hooks/use_features.js.map +1 -1
  50. package/build/hooks/use_mark_latest_message_read.d.ts +1 -1
  51. package/build/hooks/use_mark_latest_message_read.d.ts.map +1 -1
  52. package/build/hooks/use_mark_latest_message_read.js +1 -17
  53. package/build/hooks/use_mark_latest_message_read.js.map +1 -1
  54. package/build/hooks/use_preview_avatar_diameter.d.ts +2 -0
  55. package/build/hooks/use_preview_avatar_diameter.d.ts.map +1 -0
  56. package/build/hooks/use_preview_avatar_diameter.js +11 -0
  57. package/build/hooks/use_preview_avatar_diameter.js.map +1 -0
  58. package/build/hooks/use_suspense_api.d.ts +0 -1
  59. package/build/hooks/use_suspense_api.d.ts.map +1 -1
  60. package/build/hooks/use_suspense_api.js +1 -1
  61. package/build/hooks/use_suspense_api.js.map +1 -1
  62. package/build/jest.js +1 -1
  63. package/build/jest.js.map +1 -1
  64. package/build/screens/avatar_picker/avatar_picker_screen.d.ts.map +1 -1
  65. package/build/screens/avatar_picker/avatar_picker_screen.js +11 -9
  66. package/build/screens/avatar_picker/avatar_picker_screen.js.map +1 -1
  67. package/build/screens/avatar_picker/avatar_preview.d.ts.map +1 -1
  68. package/build/screens/avatar_picker/avatar_preview.js +13 -5
  69. package/build/screens/avatar_picker/avatar_preview.js.map +1 -1
  70. package/build/screens/avatar_picker/emoji_tab.d.ts.map +1 -1
  71. package/build/screens/avatar_picker/emoji_tab.js +3 -7
  72. package/build/screens/avatar_picker/emoji_tab.js.map +1 -1
  73. package/build/screens/avatar_picker/upload_tab.d.ts.map +1 -1
  74. package/build/screens/avatar_picker/upload_tab.js +2 -1
  75. package/build/screens/avatar_picker/upload_tab.js.map +1 -1
  76. package/build/screens/conversation_details_screen.d.ts.map +1 -1
  77. package/build/screens/conversation_details_screen.js +5 -2
  78. package/build/screens/conversation_details_screen.js.map +1 -1
  79. package/build/screens/conversation_filter_recipients/components/header_row.d.ts.map +1 -1
  80. package/build/screens/conversation_filter_recipients/components/header_row.js +3 -2
  81. package/build/screens/conversation_filter_recipients/components/header_row.js.map +1 -1
  82. package/build/screens/conversation_filter_recipients/hooks/use_flattened_array_of_service_types_with_teams.d.ts.map +1 -1
  83. package/build/screens/conversation_filter_recipients/hooks/use_flattened_array_of_service_types_with_teams.js +47 -18
  84. package/build/screens/conversation_filter_recipients/hooks/use_flattened_array_of_service_types_with_teams.js.map +1 -1
  85. package/build/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.d.ts +2 -1
  86. package/build/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.d.ts.map +1 -1
  87. package/build/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.js +23 -26
  88. package/build/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.js.map +1 -1
  89. package/build/screens/conversation_filter_recipients/types.d.ts +1 -1
  90. package/build/screens/conversation_filter_recipients/types.d.ts.map +1 -1
  91. package/build/screens/conversation_filter_recipients/types.js.map +1 -1
  92. package/build/screens/conversation_screen.d.ts +0 -1
  93. package/build/screens/conversation_screen.d.ts.map +1 -1
  94. package/build/screens/conversation_screen.js +45 -96
  95. package/build/screens/conversation_screen.js.map +1 -1
  96. package/build/screens/conversation_select_recipients/components/recipient_link_row.d.ts +1 -1
  97. package/build/screens/conversation_select_recipients/components/recipient_link_row.d.ts.map +1 -1
  98. package/build/screens/conversation_select_recipients/components/recipient_link_row.js +3 -3
  99. package/build/screens/conversation_select_recipients/components/recipient_link_row.js.map +1 -1
  100. package/build/screens/conversation_select_recipients/components/team_recipient_row.d.ts.map +1 -1
  101. package/build/screens/conversation_select_recipients/components/team_recipient_row.js +1 -1
  102. package/build/screens/conversation_select_recipients/components/team_recipient_row.js.map +1 -1
  103. package/build/screens/team_conversation_screen.d.ts.map +1 -1
  104. package/build/screens/team_conversation_screen.js +24 -1
  105. package/build/screens/team_conversation_screen.js.map +1 -1
  106. package/build/utils/cache/messages_cache.d.ts +0 -1
  107. package/build/utils/cache/messages_cache.d.ts.map +1 -1
  108. package/build/utils/cache/messages_cache.js +0 -4
  109. package/build/utils/cache/messages_cache.js.map +1 -1
  110. package/build/utils/client/client.d.ts +1 -1
  111. package/build/utils/client/client.d.ts.map +1 -1
  112. package/build/utils/client/client.js +7 -6
  113. package/build/utils/client/client.js.map +1 -1
  114. package/build/utils/client/instrumented_fetch.js +3 -5
  115. package/build/utils/client/instrumented_fetch.js.map +1 -1
  116. package/build/utils/group_messages.d.ts +2 -9
  117. package/build/utils/group_messages.d.ts.map +1 -1
  118. package/build/utils/group_messages.js +1 -20
  119. package/build/utils/group_messages.js.map +1 -1
  120. package/package.json +4 -4
  121. package/src/__tests__/hooks/use_group_chat_conversation_payload.test.tsx +50 -0
  122. package/src/__tests__/jest.ts +1 -1
  123. package/src/__tests__/utils/client.ts +32 -0
  124. package/src/components/conversation/__tests__/message_list.test.tsx +14 -0
  125. package/src/components/conversation/jump_to_bottom_button.tsx +8 -57
  126. package/src/components/conversation/message_list.tsx +42 -0
  127. package/src/components/conversation/reply_shadow_message.tsx +1 -1
  128. package/src/components/display/conversation_avatar.tsx +7 -5
  129. package/src/components/display/emoji_avatar.tsx +10 -2
  130. package/src/components/display/icon_avatar.tsx +10 -2
  131. package/src/contexts/conversation_context.tsx +2 -30
  132. package/src/hooks/groups/use_group_chat_conversation_payload.ts +1 -0
  133. package/src/hooks/index.ts +1 -0
  134. package/src/hooks/use_conversation_messages.ts +20 -120
  135. package/src/hooks/use_conversation_messages_jolt_events.ts +3 -4
  136. package/src/hooks/use_conversations_actions.ts +0 -15
  137. package/src/hooks/use_features.ts +0 -1
  138. package/src/hooks/use_mark_latest_message_read.ts +2 -16
  139. package/src/hooks/use_preview_avatar_diameter.ts +12 -0
  140. package/src/hooks/use_suspense_api.ts +1 -1
  141. package/src/jest.ts +1 -1
  142. package/src/screens/avatar_picker/avatar_picker_screen.tsx +25 -9
  143. package/src/screens/avatar_picker/avatar_preview.tsx +14 -5
  144. package/src/screens/avatar_picker/emoji_tab.tsx +3 -6
  145. package/src/screens/avatar_picker/upload_tab.tsx +2 -0
  146. package/src/screens/conversation_details_screen.tsx +10 -1
  147. package/src/screens/conversation_filter_recipients/components/header_row.tsx +3 -2
  148. package/src/screens/conversation_filter_recipients/hooks/__tests__/use_service_types_with_teams.test.ts +108 -0
  149. package/src/screens/conversation_filter_recipients/hooks/use_flattened_array_of_service_types_with_teams.tsx +46 -19
  150. package/src/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.ts +31 -29
  151. package/src/screens/conversation_filter_recipients/types.tsx +1 -1
  152. package/src/screens/conversation_screen.tsx +69 -186
  153. package/src/screens/conversation_select_recipients/components/recipient_link_row.tsx +6 -4
  154. package/src/screens/conversation_select_recipients/components/team_recipient_row.tsx +2 -1
  155. package/src/screens/team_conversation_screen.tsx +33 -1
  156. package/src/utils/__tests__/group_messages.test.ts +0 -71
  157. package/src/utils/cache/messages_cache.ts +0 -5
  158. package/src/utils/client/__tests__/instrumented_fetch.test.ts +9 -5
  159. package/src/utils/client/client.ts +9 -7
  160. package/src/utils/client/instrumented_fetch.ts +3 -6
  161. package/src/utils/group_messages.ts +2 -42
  162. package/build/components/conversation/unread_divider.d.ts +0 -6
  163. package/build/components/conversation/unread_divider.d.ts.map +0 -1
  164. package/build/components/conversation/unread_divider.js +0 -59
  165. package/build/components/conversation/unread_divider.js.map +0 -1
  166. package/build/hooks/use_flat_list_viewability.d.ts +0 -20
  167. package/build/hooks/use_flat_list_viewability.d.ts.map +0 -1
  168. package/build/hooks/use_flat_list_viewability.js +0 -30
  169. package/build/hooks/use_flat_list_viewability.js.map +0 -1
  170. package/build/hooks/use_jump_to_bottom_action.d.ts +0 -9
  171. package/build/hooks/use_jump_to_bottom_action.d.ts.map +0 -1
  172. package/build/hooks/use_jump_to_bottom_action.js +0 -62
  173. package/build/hooks/use_jump_to_bottom_action.js.map +0 -1
  174. package/build/hooks/use_jump_to_unread_anchor.d.ts +0 -20
  175. package/build/hooks/use_jump_to_unread_anchor.d.ts.map +0 -1
  176. package/build/hooks/use_jump_to_unread_anchor.js +0 -53
  177. package/build/hooks/use_jump_to_unread_anchor.js.map +0 -1
  178. package/build/hooks/use_jump_to_unread_gates.d.ts +0 -5
  179. package/build/hooks/use_jump_to_unread_gates.d.ts.map +0 -1
  180. package/build/hooks/use_jump_to_unread_gates.js +0 -10
  181. package/build/hooks/use_jump_to_unread_gates.js.map +0 -1
  182. package/build/hooks/use_scroll_tracking.d.ts +0 -13
  183. package/build/hooks/use_scroll_tracking.d.ts.map +0 -1
  184. package/build/hooks/use_scroll_tracking.js +0 -45
  185. package/build/hooks/use_scroll_tracking.js.map +0 -1
  186. package/build/hooks/use_track_highest_seen_message.d.ts +0 -4
  187. package/build/hooks/use_track_highest_seen_message.d.ts.map +0 -1
  188. package/build/hooks/use_track_highest_seen_message.js +0 -35
  189. package/build/hooks/use_track_highest_seen_message.js.map +0 -1
  190. package/build/utils/conversation_messages.d.ts +0 -10
  191. package/build/utils/conversation_messages.d.ts.map +0 -1
  192. package/build/utils/conversation_messages.js +0 -22
  193. package/build/utils/conversation_messages.js.map +0 -1
  194. package/build/utils/highest_seen_tracker.d.ts +0 -12
  195. package/build/utils/highest_seen_tracker.d.ts.map +0 -1
  196. package/build/utils/highest_seen_tracker.js +0 -37
  197. package/build/utils/highest_seen_tracker.js.map +0 -1
  198. package/build/utils/message_viewability.d.ts +0 -24
  199. package/build/utils/message_viewability.d.ts.map +0 -1
  200. package/build/utils/message_viewability.js +0 -29
  201. package/build/utils/message_viewability.js.map +0 -1
  202. package/build/utils/unread_divider_helpers.d.ts +0 -18
  203. package/build/utils/unread_divider_helpers.d.ts.map +0 -1
  204. package/build/utils/unread_divider_helpers.js +0 -13
  205. package/build/utils/unread_divider_helpers.js.map +0 -1
  206. package/src/__tests__/hooks/use_conversation_messages.test.tsx +0 -109
  207. package/src/__tests__/hooks/use_mark_latest_message_read.test.tsx +0 -154
  208. package/src/__tests__/utils/cache/messages_cache.test.ts +0 -54
  209. package/src/components/conversation/unread_divider.tsx +0 -90
  210. package/src/hooks/use_flat_list_viewability.ts +0 -50
  211. package/src/hooks/use_jump_to_bottom_action.ts +0 -75
  212. package/src/hooks/use_jump_to_unread_anchor.ts +0 -68
  213. package/src/hooks/use_jump_to_unread_gates.ts +0 -10
  214. package/src/hooks/use_scroll_tracking.ts +0 -64
  215. package/src/hooks/use_track_highest_seen_message.ts +0 -43
  216. package/src/utils/__tests__/conversation_messages.test.ts +0 -105
  217. package/src/utils/__tests__/highest_seen_tracker.test.ts +0 -82
  218. package/src/utils/__tests__/message_viewability.test.ts +0 -168
  219. package/src/utils/__tests__/unread_divider_helpers.test.ts +0 -85
  220. package/src/utils/conversation_messages.ts +0 -37
  221. package/src/utils/highest_seen_tracker.ts +0 -42
  222. package/src/utils/message_viewability.ts +0 -49
  223. package/src/utils/unread_divider_helpers.ts +0 -25
@@ -1,168 +0,0 @@
1
- import {
2
- detectDividerExitTowardNewer,
3
- reportViewableMessages,
4
- type ViewabilityEvent,
5
- } from '../message_viewability'
6
-
7
- type Msg = { id: string; type: 'Message' }
8
-
9
- const event = (overrides: Partial<ViewabilityEvent<Msg>> = {}): ViewabilityEvent<Msg> => ({
10
- viewableItems: [],
11
- changed: [],
12
- userHasScrolled: true,
13
- ...overrides,
14
- })
15
-
16
- describe('reportViewableMessages', () => {
17
- it('fires onMessageSeen for every viewable Message item by id', () => {
18
- const onSeen = jest.fn()
19
- const observer = reportViewableMessages<Msg>(onSeen)
20
-
21
- observer(
22
- event({
23
- viewableItems: [
24
- { key: '10', isViewable: true, item: { id: '10', type: 'Message' } },
25
- { key: '11', isViewable: true, item: { id: '11', type: 'Message' } },
26
- ],
27
- })
28
- )
29
-
30
- expect(onSeen).toHaveBeenCalledTimes(2)
31
- expect(onSeen).toHaveBeenNthCalledWith(1, '10')
32
- expect(onSeen).toHaveBeenNthCalledWith(2, '11')
33
- })
34
-
35
- it('skips non-Message items (dividers, separators, shadows)', () => {
36
- const onSeen = jest.fn()
37
- const observer = reportViewableMessages<{ id?: string; type?: string }>(onSeen)
38
-
39
- observer(
40
- event({
41
- viewableItems: [
42
- { key: 'divider', isViewable: true, item: { id: 'divider', type: 'UnreadDivider' } },
43
- {
44
- key: 'day-divider-05',
45
- isViewable: true,
46
- item: { id: 'day-divider-05', type: 'DateSeparator' },
47
- },
48
- { key: '5', isViewable: true, item: { id: '5', type: 'Message' } },
49
- ],
50
- })
51
- )
52
-
53
- expect(onSeen).toHaveBeenCalledTimes(1)
54
- expect(onSeen).toHaveBeenCalledWith('5')
55
- })
56
-
57
- it('does not fire before the user has scrolled', () => {
58
- const onSeen = jest.fn()
59
- const observer = reportViewableMessages<Msg>(onSeen)
60
-
61
- observer(
62
- event({
63
- userHasScrolled: false,
64
- viewableItems: [{ key: '10', isViewable: true, item: { id: '10', type: 'Message' } }],
65
- })
66
- )
67
-
68
- expect(onSeen).not.toHaveBeenCalled()
69
- })
70
- })
71
-
72
- describe('detectDividerExitTowardNewer', () => {
73
- const baseArgs = { dividerKey: 'unread-divider', initialMessageId: '050' }
74
-
75
- it('fires onExited when the divider leaves and only newer messages remain visible', () => {
76
- const onExited = jest.fn()
77
- const observer = detectDividerExitTowardNewer<{ id: string; type?: string }>({
78
- ...baseArgs,
79
- onExited,
80
- })
81
-
82
- observer(
83
- event({
84
- changed: [
85
- {
86
- key: 'unread-divider',
87
- isViewable: false,
88
- item: { id: 'unread-divider', type: 'UnreadDivider' },
89
- },
90
- ],
91
- viewableItems: [
92
- { key: '055', isViewable: true, item: { id: '055', type: 'Message' } },
93
- { key: '056', isViewable: true, item: { id: '056', type: 'Message' } },
94
- ],
95
- })
96
- )
97
-
98
- expect(onExited).toHaveBeenCalledTimes(1)
99
- })
100
-
101
- it('does not fire when divider leaves toward older messages', () => {
102
- const onExited = jest.fn()
103
- const observer = detectDividerExitTowardNewer<{ id: string; type?: string }>({
104
- ...baseArgs,
105
- onExited,
106
- })
107
-
108
- observer(
109
- event({
110
- changed: [
111
- {
112
- key: 'unread-divider',
113
- isViewable: false,
114
- item: { id: 'unread-divider', type: 'UnreadDivider' },
115
- },
116
- ],
117
- viewableItems: [
118
- { key: '045', isViewable: true, item: { id: '045', type: 'Message' } },
119
- { key: '048', isViewable: true, item: { id: '048', type: 'Message' } },
120
- ],
121
- })
122
- )
123
-
124
- expect(onExited).not.toHaveBeenCalled()
125
- })
126
-
127
- it('does not fire before the user has scrolled', () => {
128
- const onExited = jest.fn()
129
- const observer = detectDividerExitTowardNewer<{ id: string; type?: string }>({
130
- ...baseArgs,
131
- onExited,
132
- })
133
-
134
- observer(
135
- event({
136
- userHasScrolled: false,
137
- changed: [
138
- {
139
- key: 'unread-divider',
140
- isViewable: false,
141
- item: { id: 'unread-divider', type: 'UnreadDivider' },
142
- },
143
- ],
144
- viewableItems: [{ key: '055', isViewable: true, item: { id: '055', type: 'Message' } }],
145
- })
146
- )
147
-
148
- expect(onExited).not.toHaveBeenCalled()
149
- })
150
-
151
- it('no-ops when initialMessageId is null (observer is always installed)', () => {
152
- const onExited = jest.fn()
153
- const observer = detectDividerExitTowardNewer<{ id: string; type?: string }>({
154
- dividerKey: 'unread-divider',
155
- initialMessageId: null,
156
- onExited,
157
- })
158
-
159
- observer(
160
- event({
161
- changed: [{ key: 'unread-divider', isViewable: false, item: { id: 'unread-divider' } }],
162
- viewableItems: [{ key: '055', isViewable: true, item: { id: '055' } }],
163
- })
164
- )
165
-
166
- expect(onExited).not.toHaveBeenCalled()
167
- })
168
- })
@@ -1,85 +0,0 @@
1
- import { dividerExitedTowardNewer } from '../unread_divider_helpers'
2
-
3
- const dividerKey = 'unread-divider'
4
- const initialMessageId = '050'
5
- const msg = (id: string) => ({ item: { id, type: 'Message' } })
6
-
7
- describe('dividerExitedTowardNewer', () => {
8
- it('returns false while the divider is still viewable', () => {
9
- const result = dividerExitedTowardNewer({
10
- changed: [{ key: dividerKey, isViewable: true }],
11
- viewableItems: [
12
- msg('049'),
13
- { item: { id: dividerKey, type: 'UnreadDivider' } },
14
- msg('050'),
15
- msg('051'),
16
- ],
17
- dividerKey,
18
- initialMessageId,
19
- })
20
- expect(result).toBe(false)
21
- })
22
-
23
- it('returns true when divider exits and only newer messages are visible (scrolled toward newer)', () => {
24
- const result = dividerExitedTowardNewer({
25
- changed: [{ key: dividerKey, isViewable: false }],
26
- viewableItems: [msg('055'), msg('056'), msg('057')],
27
- dividerKey,
28
- initialMessageId,
29
- })
30
- expect(result).toBe(true)
31
- })
32
-
33
- it('returns false when divider exits and only older messages are visible (scrolled toward older)', () => {
34
- const result = dividerExitedTowardNewer({
35
- changed: [{ key: dividerKey, isViewable: false }],
36
- viewableItems: [msg('040'), msg('045'), msg('048')],
37
- dividerKey,
38
- initialMessageId,
39
- })
40
- expect(result).toBe(false)
41
- })
42
-
43
- it('returns false when divider exits but the visible window straddles the boundary', () => {
44
- const result = dividerExitedTowardNewer({
45
- changed: [{ key: dividerKey, isViewable: false }],
46
- viewableItems: [msg('048'), msg('049'), msg('055')],
47
- dividerKey,
48
- initialMessageId,
49
- })
50
- expect(result).toBe(false)
51
- })
52
-
53
- it('returns false when the divider entry is not in the changed set', () => {
54
- const result = dividerExitedTowardNewer({
55
- changed: [{ key: '055', isViewable: true }],
56
- viewableItems: [msg('055'), msg('056')],
57
- dividerKey,
58
- initialMessageId,
59
- })
60
- expect(result).toBe(false)
61
- })
62
-
63
- it('returns false when no message items are visible (only the divider was)', () => {
64
- const result = dividerExitedTowardNewer({
65
- changed: [{ key: dividerKey, isViewable: false }],
66
- viewableItems: [],
67
- dividerKey,
68
- initialMessageId,
69
- })
70
- expect(result).toBe(false)
71
- })
72
-
73
- it('ignores non-Message items (date separators, reply shadows) when deciding direction', () => {
74
- const result = dividerExitedTowardNewer({
75
- changed: [{ key: dividerKey, isViewable: false }],
76
- viewableItems: [
77
- { item: { id: 'day-divider-200', type: 'DateSeparator' } },
78
- { item: { id: '055-rootA', type: 'ReplyShadowMessage' } },
79
- ],
80
- dividerKey,
81
- initialMessageId,
82
- })
83
- expect(result).toBe(false)
84
- })
85
- })
@@ -1,37 +0,0 @@
1
- import { ApiCollection, MessageResource } from '../types'
2
- import { RequestData } from './client'
3
-
4
- export type MessagesPageParam = Partial<RequestData> & {
5
- order?: 'asc' | 'desc'
6
- }
7
-
8
- export const anchoredSeedPageParams = (anchor: string): MessagesPageParam[] => [
9
- { where: { id_gte: anchor }, order: 'asc' },
10
- { where: { id_lt: anchor }, order: 'desc' },
11
- ]
12
-
13
- export const olderPageParam = (
14
- page: ApiCollection<MessageResource>
15
- ): MessagesPageParam | undefined => {
16
- const idLt = page.meta?.next?.idLt
17
- if (!idLt) return undefined
18
- return { where: { id_lt: idLt }, order: 'desc' }
19
- }
20
-
21
- export const newerPageParam = (
22
- page: ApiCollection<MessageResource>
23
- ): MessagesPageParam | undefined => {
24
- const idGt = page.meta?.next?.idGt
25
- if (!idGt) return undefined
26
- return { where: { id_gt: idGt }, order: 'asc' }
27
- }
28
-
29
- export const sortAndFilterMessages = (pages: ApiCollection<MessageResource>[]): MessageResource[] =>
30
- pages
31
- .flatMap(page => page.data)
32
- .filter(
33
- message =>
34
- (!message.deletedAt || message.replyRootId) &&
35
- (message.attachments?.length || message.text?.length)
36
- )
37
- .sort((a, b) => -a.id.localeCompare(b.id))
@@ -1,42 +0,0 @@
1
- export const FLUSH_DELAY_MS = 2000
2
-
3
- type SendFn = (args: { conversationId: number; sortKey: string }) => void
4
-
5
- export function makeHighestSeenTracker(
6
- conversationId: number,
7
- send: SendFn,
8
- flushDelayMs: number = FLUSH_DELAY_MS
9
- ) {
10
- let highest: string | null = null
11
- let lastSent: string | null = null
12
- let timer: ReturnType<typeof setTimeout> | null = null
13
-
14
- const fire = () => {
15
- timer = null
16
- if (!highest || highest === lastSent) return
17
- lastSent = highest
18
- send({ conversationId, sortKey: highest })
19
- }
20
-
21
- return {
22
- onSeen(sortKey: string) {
23
- if (highest && sortKey.localeCompare(highest) <= 0) return
24
- highest = sortKey
25
- if (timer) clearTimeout(timer)
26
- timer = setTimeout(fire, flushDelayMs)
27
- },
28
- flushNow() {
29
- if (timer) {
30
- clearTimeout(timer)
31
- timer = null
32
- }
33
- fire()
34
- },
35
- cancel() {
36
- if (timer) {
37
- clearTimeout(timer)
38
- timer = null
39
- }
40
- },
41
- }
42
- }
@@ -1,49 +0,0 @@
1
- import { dividerExitedTowardNewer } from './unread_divider_helpers'
2
-
3
- export interface ViewableEntry<Item> {
4
- key: string
5
- isViewable: boolean
6
- item: Item
7
- }
8
-
9
- export interface ViewabilityEvent<Item> {
10
- viewableItems: ViewableEntry<Item>[]
11
- changed: ViewableEntry<Item>[]
12
- userHasScrolled: boolean
13
- }
14
-
15
- export type ViewabilityObserver<Item> = (event: ViewabilityEvent<Item>) => void
16
-
17
- export function reportViewableMessages<Item extends { id?: string; type?: string }>(
18
- onMessageSeen: (id: string) => void
19
- ): ViewabilityObserver<Item> {
20
- return ({ viewableItems, userHasScrolled }) => {
21
- if (!userHasScrolled) return
22
- for (const entry of viewableItems) {
23
- if (entry.item?.type !== 'Message') continue
24
- const id = entry.item?.id
25
- if (typeof id === 'string') onMessageSeen(id)
26
- }
27
- }
28
- }
29
-
30
- export function detectDividerExitTowardNewer<Item extends { id?: string; type?: string }>({
31
- dividerKey,
32
- initialMessageId,
33
- onExited,
34
- }: {
35
- dividerKey: string
36
- initialMessageId: string | null
37
- onExited: () => void
38
- }): ViewabilityObserver<Item> {
39
- return ({ viewableItems, changed, userHasScrolled }) => {
40
- if (!userHasScrolled || !initialMessageId) return
41
- const exited = dividerExitedTowardNewer({
42
- changed,
43
- viewableItems,
44
- dividerKey,
45
- initialMessageId,
46
- })
47
- if (exited) onExited()
48
- }
49
- }
@@ -1,25 +0,0 @@
1
- type ViewableChangeEntry = { key: string; isViewable: boolean }
2
- type ViewableItem = { item: { id?: string; type?: string } }
3
-
4
- export function dividerExitedTowardNewer({
5
- changed,
6
- viewableItems,
7
- dividerKey,
8
- initialMessageId,
9
- }: {
10
- changed: ViewableChangeEntry[]
11
- viewableItems: ViewableItem[]
12
- dividerKey: string
13
- initialMessageId: string
14
- }): boolean {
15
- const dividerExited = changed.some(c => c.key === dividerKey && !c.isViewable)
16
- if (!dividerExited) return false
17
-
18
- const visibleMessageIds = viewableItems
19
- .filter(v => v.item?.type === 'Message')
20
- .map(v => v.item?.id)
21
- .filter((id): id is string => !!id)
22
- if (visibleMessageIds.length === 0) return false
23
-
24
- return visibleMessageIds.every(id => id.localeCompare(initialMessageId) > 0)
25
- }