@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,8 +1,7 @@
1
1
  import dayjs from './dayjs';
2
2
  import { isSystemMessage } from './system_messages';
3
3
  const FIVE_MINUTES_MS = 5 * 60 * 1000;
4
- export const UNREAD_DIVIDER_KEY = 'unread-divider';
5
- export function groupMessages({ ms, inReplyScreen, jumpToUnreadActive, initialMessageId, }) {
4
+ export function groupMessages({ ms, inReplyScreen }) {
6
5
  const items = [];
7
6
  let myLatestSeen = false;
8
7
  let nextNeighborEnriched;
@@ -12,9 +11,6 @@ export function groupMessages({ ms, inReplyScreen, jumpToUnreadActive, initialMe
12
11
  if (isSystemMessage(message)) {
13
12
  const enriched = enrichSystemMessage(message, next);
14
13
  items.push(enriched);
15
- if (crossesUnreadBoundary(message, prev, jumpToUnreadActive, initialMessageId)) {
16
- items.push(unreadDivider());
17
- }
18
14
  if (datesDifferBetween(message, prev))
19
15
  items.push(dateSeparator(message));
20
16
  nextNeighborEnriched = enriched;
@@ -25,9 +21,6 @@ export function groupMessages({ ms, inReplyScreen, jumpToUnreadActive, initialMe
25
21
  myLatestSeen = true;
26
22
  const enriched = enrichRegularMessage(message, prev, next, isMyLatest, !!inReplyScreen);
27
23
  items.push(enriched);
28
- if (crossesUnreadBoundary(message, prev, jumpToUnreadActive, initialMessageId)) {
29
- items.push(unreadDivider());
30
- }
31
24
  const shadow = replyShadowFor(enriched, prev);
32
25
  if (shadow)
33
26
  items.push(shadow);
@@ -38,18 +31,6 @@ export function groupMessages({ ms, inReplyScreen, jumpToUnreadActive, initialMe
38
31
  });
39
32
  return items;
40
33
  }
41
- function crossesUnreadBoundary(message, prev, jumpToUnreadActive, initialMessageId) {
42
- if (!jumpToUnreadActive)
43
- return false;
44
- if (!initialMessageId)
45
- return false;
46
- if (!prev)
47
- return false;
48
- return (prev.id.localeCompare(initialMessageId) <= 0 && message.id.localeCompare(initialMessageId) > 0);
49
- }
50
- function unreadDivider() {
51
- return { type: 'UnreadDivider', id: UNREAD_DIVIDER_KEY };
52
- }
53
34
  function neighborsOf(arr, i) {
54
35
  return { prev: arr[i + 1], next: arr[i - 1] };
55
36
  }
@@ -1 +1 @@
1
- {"version":3,"file":"group_messages.js","sourceRoot":"","sources":["../../src/utils/group_messages.ts"],"names":[],"mappings":"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","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 const UNREAD_DIVIDER_KEY = 'unread-divider'\n\nexport type DateSeparator = { type: 'DateSeparator'; id: string; date: string }\n\nexport type UnreadDividerItem = { type: 'UnreadDivider'; id: typeof UNREAD_DIVIDER_KEY }\n\nexport type ReplyShadowMessage = {\n type: 'ReplyShadowMessage'\n id: string\n messageId: string\n isReplyShadowMessage: boolean\n nextRendersAuthor: boolean\n}\n\nexport type EnrichedMessage =\n | MessageResource\n | DateSeparator\n | UnreadDividerItem\n | ReplyShadowMessage\n\ninterface GroupMessagesProps {\n ms: MessageResource[]\n inReplyScreen?: boolean\n jumpToUnreadActive?: boolean\n initialMessageId?: string | null\n}\n\nexport function groupMessages({\n ms,\n inReplyScreen,\n jumpToUnreadActive,\n initialMessageId,\n}: 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 (crossesUnreadBoundary(message, prev, jumpToUnreadActive, initialMessageId)) {\n items.push(unreadDivider())\n }\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 if (crossesUnreadBoundary(message, prev, jumpToUnreadActive, initialMessageId)) {\n items.push(unreadDivider())\n }\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 crossesUnreadBoundary(\n message: MessageResource,\n prev: MessageResource | undefined,\n jumpToUnreadActive: boolean | undefined,\n initialMessageId: string | null | undefined\n): boolean {\n if (!jumpToUnreadActive) return false\n if (!initialMessageId) return false\n if (!prev) return false\n return (\n prev.id.localeCompare(initialMessageId) <= 0 && message.id.localeCompare(initialMessageId) > 0\n )\n}\n\nfunction unreadDivider(): UnreadDividerItem {\n return { type: 'UnreadDivider', id: UNREAD_DIVIDER_KEY }\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"]}
1
+ {"version":3,"file":"group_messages.js","sourceRoot":"","sources":["../../src/utils/group_messages.ts"],"names":[],"mappings":"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","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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planningcenter/chat-react-native",
3
- "version": "3.38.0-rc.1",
3
+ "version": "3.38.0-rc.11",
4
4
  "description": "",
5
5
  "main": "build/index.js",
6
6
  "react-native": "./src/index.tsx",
@@ -26,9 +26,9 @@
26
26
  "dependencies": {
27
27
  "@fortawesome/fontawesome-svg-core": "^7.2.0",
28
28
  "@fortawesome/react-native-fontawesome": "^0.3.2",
29
+ "@planningcenter/emoji-keyboard": "3.38.0-rc.11",
29
30
  "lodash-inflection": "^1.5.0",
30
- "react-compiler-runtime": "^1.0.0",
31
- "rn-emoji-keyboard": "1.7.0"
31
+ "react-compiler-runtime": "^1.0.0"
32
32
  },
33
33
  "peerDependencies": {
34
34
  "@planningcenter/datetime-fmt": ">=2.0.0",
@@ -72,5 +72,5 @@
72
72
  "react-native-url-polyfill": "^2.0.0",
73
73
  "typescript": "~5.9.2"
74
74
  },
75
- "gitHead": "addadedc983bf11d717e5528d8d4d143a7acd4c9"
75
+ "gitHead": "02d0d805d82b046009c238100d6ec601b4295b39"
76
76
  }
@@ -0,0 +1,50 @@
1
+ import { QueryClientProvider } from '@tanstack/react-query'
2
+ import { renderHook, waitFor } from '@testing-library/react-native'
3
+ import React from 'react'
4
+ import { buildTestQueryClient } from '../../__utils__/query_client'
5
+ import { useGroupChatConversationPayload } from '../../hooks/groups/use_group_chat_conversation_payload'
6
+ import * as useApiClientModule from '../../hooks/use_api_client'
7
+
8
+ const mockGroupsPost = (impl: jest.Mock) => {
9
+ jest.spyOn(useApiClientModule, 'useApiClient').mockReturnValue({
10
+ groups: { post: impl },
11
+ } as unknown as ReturnType<typeof useApiClientModule.useApiClient>)
12
+ return impl
13
+ }
14
+
15
+ const resolveWith = (value: string) =>
16
+ jest.fn().mockResolvedValue({
17
+ data: { type: 'GroupChatConversationPayload', id: '1', value },
18
+ links: {},
19
+ meta: {},
20
+ })
21
+
22
+ describe('useGroupChatConversationPayload', () => {
23
+ afterEach(() => {
24
+ jest.restoreAllMocks()
25
+ })
26
+
27
+ it('refetches on mount even when a fresh payload is already cached', async () => {
28
+ // Prime the cache as if a payload was fetched moments ago. The hook's 25-min staleTime
29
+ // means the default refetchOnMount would treat this as fresh and skip the request, so a
30
+ // group composition change on web would stay invisible until the cache is dropped (app
31
+ // restart). refetchOnMount: 'always' must override that and refetch on every mount.
32
+ const queryClient = buildTestQueryClient()
33
+ queryClient.setQueryData(['groups', '/me/groups/1/chat_conversation_payload'], {
34
+ data: { type: 'GroupChatConversationPayload', id: '1', value: 'stale' },
35
+ links: {},
36
+ meta: {},
37
+ })
38
+ const post = mockGroupsPost(resolveWith('fresh'))
39
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
40
+ <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
41
+ )
42
+
43
+ const { result } = renderHook(() => useGroupChatConversationPayload({ groupId: 1 }), {
44
+ wrapper,
45
+ })
46
+
47
+ await waitFor(() => expect(post).toHaveBeenCalledTimes(1))
48
+ await waitFor(() => expect(result.current.payload).toBe('fresh'))
49
+ })
50
+ })
@@ -4,8 +4,8 @@ describe('jestTransformPackages', () => {
4
4
  it('exports an array of package patterns', () => {
5
5
  expect(jestTransformPackages).toEqual([
6
6
  '@planningcenter/chat-react-native',
7
+ '@planningcenter/emoji-keyboard',
7
8
  '@fortawesome',
8
- 'rn-emoji-keyboard',
9
9
  ])
10
10
  })
11
11
  })
@@ -369,4 +369,36 @@ describe('error handling', () => {
369
369
  ).rejects.toHaveProperty('status', 401)
370
370
  }
371
371
  })
372
+
373
+ describe('non-401 errors', () => {
374
+ it('attaches the parsed body so the message survives to the UI', async () => {
375
+ const url = '/records'
376
+
377
+ MockServer.post(
378
+ url,
379
+ {
380
+ errors: [
381
+ {
382
+ status: '422',
383
+ title: 'Unprocessable Entity',
384
+ detail: 'Conversations including a minor must have at least two confirmed adults.',
385
+ },
386
+ ],
387
+ },
388
+ 422,
389
+ { once: true }
390
+ )
391
+
392
+ const rejection = (await client
393
+ .post({ url, data: { data: { type: 'Record', attributes: {} } } })
394
+ .catch(e => e)) as FailedResponse
395
+
396
+ expect(rejection).toHaveProperty('status', 422)
397
+ expect(rejection.errors?.[0]?.detail).toEqual(
398
+ 'Conversations including a minor must have at least two confirmed adults.'
399
+ )
400
+ // A 422 must not be mistaken for an auth failure
401
+ expect(onUnauthorizedResponse).not.toHaveBeenCalled()
402
+ })
403
+ })
372
404
  })
@@ -0,0 +1,14 @@
1
+ import { render } from '@testing-library/react-native'
2
+ import { createRef } from 'react'
3
+ import { FlatList } from 'react-native-gesture-handler'
4
+ import { MessageList } from '../message_list'
5
+
6
+ describe('MessageList', () => {
7
+ it('renders with the gesture-handler FlatList so overscroll cannot drag an enclosing sheet', () => {
8
+ const listRef = createRef<FlatList>()
9
+
10
+ const screen = render(<MessageList listRef={listRef} data={[]} renderItem={() => null} />)
11
+
12
+ expect(screen.UNSAFE_getByType(FlatList)).toBeTruthy()
13
+ })
14
+ })
@@ -1,13 +1,12 @@
1
1
  import { useEffect } from 'react'
2
- import { ActivityIndicator, Pressable, StyleSheet, View } from 'react-native'
2
+ import { View, Pressable, StyleSheet } from 'react-native'
3
3
  import Animated, {
4
- Extrapolation,
4
+ useSharedValue,
5
+ useAnimatedStyle,
5
6
  interpolate,
7
+ Extrapolation,
6
8
  ReduceMotion,
7
- useAnimatedStyle,
8
- useSharedValue,
9
9
  withSpring,
10
- withTiming,
11
10
  } from 'react-native-reanimated'
12
11
  import { useTheme } from '../../hooks'
13
12
  import { platformFontWeightMedium } from '../../utils'
@@ -16,17 +15,11 @@ import { Icon, Text } from '../display'
16
15
  interface JumpToBottomButtonProps {
17
16
  onPress: () => void
18
17
  visible: boolean
19
- loading?: boolean
20
18
  }
21
19
 
22
- export const JumpToBottomButton = ({
23
- onPress,
24
- visible,
25
- loading = false,
26
- }: JumpToBottomButtonProps) => {
20
+ export const JumpToBottomButton = ({ onPress, visible }: JumpToBottomButtonProps) => {
27
21
  const styles = useStyles()
28
22
  const progress = useSharedValue(0)
29
- const loadingProgress = useSharedValue(0)
30
23
 
31
24
  useEffect(() => {
32
25
  progress.value = withSpring(visible ? 1 : 0, {
@@ -38,13 +31,6 @@ export const JumpToBottomButton = ({
38
31
  })
39
32
  }, [visible, progress])
40
33
 
41
- useEffect(() => {
42
- loadingProgress.value = withTiming(loading ? 1 : 0, {
43
- duration: 750,
44
- reduceMotion: ReduceMotion.System,
45
- })
46
- }, [loading, loadingProgress])
47
-
48
34
  const animatedStyle = useAnimatedStyle(() => {
49
35
  return {
50
36
  opacity: progress.value,
@@ -59,34 +45,16 @@ export const JumpToBottomButton = ({
59
45
  }
60
46
  })
61
47
 
62
- const iconStyle = useAnimatedStyle(() => ({ opacity: 1 - loadingProgress.value }))
63
- const spinnerStyle = useAnimatedStyle(() => ({ opacity: loadingProgress.value }))
64
-
65
48
  return (
66
49
  <View>
67
- <Animated.View
68
- style={[styles.container, animatedStyle]}
69
- pointerEvents={visible ? 'auto' : 'none'}
70
- >
50
+ <Animated.View style={[styles.container, animatedStyle]}>
71
51
  <Pressable
72
52
  onPress={onPress}
73
- disabled={loading}
74
- accessibilityRole="button"
75
- accessibilityLabel="Jump to most recent message"
76
- accessibilityState={{ busy: loading }}
77
- hitSlop={hitSlop}
78
53
  style={({ pressed }) => [styles.button, pressed && styles.pressed]}
79
54
  >
80
- <View style={styles.glyph}>
81
- <Animated.View style={[styles.glyphLayer, iconStyle]}>
82
- <Icon name="general.downArrow" style={styles.icon} />
83
- </Animated.View>
84
- <Animated.View style={[styles.glyphLayer, spinnerStyle]}>
85
- <ActivityIndicator size="small" color={styles.icon.color} style={styles.spinner} />
86
- </Animated.View>
87
- </View>
55
+ <Icon name="general.downArrow" style={styles.icon} />
88
56
  <Text variant="tertiary" style={styles.text}>
89
- {loading ? 'Jumping to latest…' : 'Jump to bottom'}
57
+ Jump to bottom
90
58
  </Text>
91
59
  </Pressable>
92
60
  </Animated.View>
@@ -94,8 +62,6 @@ export const JumpToBottomButton = ({
94
62
  )
95
63
  }
96
64
 
97
- const hitSlop = { top: 12, bottom: 12, left: 12, right: 12 }
98
-
99
65
  const useStyles = () => {
100
66
  const { colors } = useTheme()
101
67
 
@@ -127,25 +93,10 @@ const useStyles = () => {
127
93
  color: colors.fillColorNeutral100Inverted,
128
94
  fontWeight: platformFontWeightMedium,
129
95
  },
130
- glyph: {
131
- width: 14,
132
- height: 14,
133
- alignItems: 'center',
134
- justifyContent: 'center',
135
- },
136
- glyphLayer: {
137
- position: 'absolute',
138
- alignItems: 'center',
139
- justifyContent: 'center',
140
- },
141
96
  icon: {
142
97
  color: colors.fillColorNeutral100Inverted,
143
98
  fontSize: 14,
144
99
  },
145
- spinner: {
146
- width: 14,
147
- height: 14,
148
- },
149
100
  pressed: {
150
101
  transform: [{ scale: 0.95 }],
151
102
  },
@@ -0,0 +1,42 @@
1
+ import { RefObject } from 'react'
2
+ import { StyleSheet, type FlatListProps } from 'react-native'
3
+ import { FlatList } from 'react-native-gesture-handler'
4
+ import type { EnrichedMessage } from '../../utils/group_messages'
5
+
6
+ const extractItemKey = (item: EnrichedMessage) => String(item.id)
7
+ const maintainVisibleContentPosition = { minIndexForVisible: 0 }
8
+
9
+ type MessageListProps = Pick<
10
+ FlatListProps<EnrichedMessage>,
11
+ | 'data'
12
+ | 'renderItem'
13
+ | 'onScroll'
14
+ | 'onScrollBeginDrag'
15
+ | 'viewabilityConfigCallbackPairs'
16
+ | 'onContentSizeChange'
17
+ | 'onScrollToIndexFailed'
18
+ | 'onEndReached'
19
+ | 'ListHeaderComponent'
20
+ > & {
21
+ listRef: RefObject<FlatList | null>
22
+ }
23
+
24
+ export function MessageList({ listRef, ...props }: MessageListProps) {
25
+ return (
26
+ <FlatList
27
+ inverted
28
+ ref={listRef}
29
+ contentContainerStyle={styles.listContainer}
30
+ maintainVisibleContentPosition={maintainVisibleContentPosition}
31
+ keyExtractor={extractItemKey}
32
+ scrollEventThrottle={64}
33
+ {...props}
34
+ />
35
+ )
36
+ }
37
+
38
+ const styles = StyleSheet.create({
39
+ listContainer: {
40
+ paddingVertical: 12,
41
+ },
42
+ })
@@ -28,7 +28,7 @@ import {
28
28
  import { Avatar, Icon, IconProps, Image, Text } from '../display'
29
29
  import { TheirReplyConnector, MyReplyConnector } from './reply_connectors'
30
30
 
31
- interface ReplyShadowMessageProps {
31
+ interface ReplyShadowMessageProps extends MessageResource {
32
32
  messageId: string
33
33
  conversation_id: number
34
34
  inReplyScreen?: boolean
@@ -52,6 +52,7 @@ interface ConversationAvatarProps {
52
52
  showFallback?: boolean
53
53
  fallbackIconName?: IconString
54
54
  style?: ViewStyle
55
+ maxFontSizeMultiplier?: AvatarRootProps['maxFontSizeMultiplier']
55
56
  }
56
57
 
57
58
  export function ConversationAvatar({
@@ -60,30 +61,31 @@ export function ConversationAvatar({
60
61
  showFallback = false,
61
62
  fallbackIconName = 'general.person',
62
63
  style,
64
+ maxFontSizeMultiplier,
63
65
  }: ConversationAvatarProps) {
64
66
  const avatar = resolveAvatar(conversation)
67
+ const sizeProps = { size, maxFontSizeMultiplier, style }
65
68
 
66
69
  switch (avatar.kind) {
67
70
  case 'image':
68
71
  return (
69
- <AvatarPrimitive.Root size={size} style={style}>
72
+ <AvatarPrimitive.Root {...sizeProps}>
70
73
  <AvatarPrimitive.Mask>
71
74
  <AvatarPrimitive.Image sourceUri={avatar.url} />
72
75
  </AvatarPrimitive.Mask>
73
76
  </AvatarPrimitive.Root>
74
77
  )
75
78
  case 'icon':
76
- return <IconAvatar iconKey={avatar.iconKey} color={avatar.color} size={size} />
79
+ return <IconAvatar iconKey={avatar.iconKey} color={avatar.color} {...sizeProps} />
77
80
  case 'emoji':
78
- return <EmojiAvatar emoji={avatar.emoji} color={avatar.color} size={size} />
81
+ return <EmojiAvatar emoji={avatar.emoji} color={avatar.color} {...sizeProps} />
79
82
  case 'group':
80
83
  return (
81
84
  <AvatarGroup
82
85
  sourceUris={avatar.sources || []}
83
- size={size}
84
86
  showFallback={showFallback || !avatar.sources || avatar.sources.length === 0}
85
87
  fallbackIconName={fallbackIconName}
86
- style={style}
88
+ {...sizeProps}
87
89
  />
88
90
  )
89
91
  }
@@ -20,13 +20,21 @@ interface EmojiAvatarProps {
20
20
  emoji: string
21
21
  color?: string | null
22
22
  size?: AvatarRootProps['size']
23
+ maxFontSizeMultiplier?: AvatarRootProps['maxFontSizeMultiplier']
24
+ style?: AvatarRootProps['style']
23
25
  }
24
26
 
25
- export function EmojiAvatar({ emoji, color, size = 'lg' }: EmojiAvatarProps) {
27
+ export function EmojiAvatar({
28
+ emoji,
29
+ color,
30
+ size = 'lg',
31
+ maxFontSizeMultiplier,
32
+ style,
33
+ }: EmojiAvatarProps) {
26
34
  const gradientProps = getAvatarGradientProps(color)
27
35
 
28
36
  return (
29
- <AvatarPrimitive.Root size={size}>
37
+ <AvatarPrimitive.Root size={size} maxFontSizeMultiplier={maxFontSizeMultiplier} style={style}>
30
38
  <AvatarPrimitive.Mask>
31
39
  <LinearGradient {...gradientProps} style={styles.gradientFill}>
32
40
  <View style={styles.contentContainer}>
@@ -26,13 +26,21 @@ interface IconAvatarProps {
26
26
  iconKey: string
27
27
  color?: string | null
28
28
  size?: AvatarRootProps['size']
29
+ maxFontSizeMultiplier?: AvatarRootProps['maxFontSizeMultiplier']
30
+ style?: AvatarRootProps['style']
29
31
  }
30
32
 
31
33
  function findIcon(iconKey: string): IconDefinition | undefined {
32
34
  return findIconDefinition({ prefix: 'fas', iconName: iconKey as IconName })
33
35
  }
34
36
 
35
- export function IconAvatar({ iconKey, color, size = 'lg' }: IconAvatarProps) {
37
+ export function IconAvatar({
38
+ iconKey,
39
+ color,
40
+ size = 'lg',
41
+ maxFontSizeMultiplier,
42
+ style,
43
+ }: IconAvatarProps) {
36
44
  const gradientProps = getAvatarGradientProps(color)
37
45
  const iconDef = findIcon(iconKey)
38
46
 
@@ -41,7 +49,7 @@ export function IconAvatar({ iconKey, color, size = 'lg' }: IconAvatarProps) {
41
49
  }
42
50
 
43
51
  return (
44
- <AvatarPrimitive.Root size={size}>
52
+ <AvatarPrimitive.Root size={size} maxFontSizeMultiplier={maxFontSizeMultiplier} style={style}>
45
53
  <AvatarPrimitive.Mask>
46
54
  <LinearGradient {...gradientProps} style={styles.gradientFill}>
47
55
  <View style={styles.contentContainer}>
@@ -1,59 +1,31 @@
1
- import React, { createContext, PropsWithChildren, useContext, useMemo, useState } from 'react'
1
+ import React, { createContext, PropsWithChildren, useContext, useMemo } from 'react'
2
2
 
3
3
  interface ConversationContextValue {
4
4
  conversationId: number
5
5
  currentPageReplyRootId: string | null
6
- initialMessageId: string | null
7
- setInitialMessageId: (id: string | null) => void
8
- initialMessageIdIsAnchor: boolean
9
- atEndOfMessageHistory: boolean
10
- setAtEndOfMessageHistory: (atEnd: boolean) => void
11
6
  }
12
7
 
13
8
  interface ConversationContextProviderProps extends PropsWithChildren {
14
9
  conversationId: number
15
10
  currentPageReplyRootId: string | null
16
- initialMessageId?: string | null
17
- initialMessageIdIsAnchor?: boolean
18
11
  }
19
12
 
20
13
  const ConversationContext = createContext<ConversationContextValue>({
21
14
  conversationId: 0,
22
15
  currentPageReplyRootId: null,
23
- initialMessageId: null,
24
- setInitialMessageId: () => {},
25
- initialMessageIdIsAnchor: false,
26
- atEndOfMessageHistory: false,
27
- setAtEndOfMessageHistory: () => {},
28
16
  })
29
17
 
30
18
  export const ConversationContextProvider = ({
31
19
  children,
32
20
  conversationId,
33
21
  currentPageReplyRootId,
34
- initialMessageId: initialMessageIdProp = null,
35
- initialMessageIdIsAnchor = false,
36
22
  }: ConversationContextProviderProps) => {
37
- const [initialMessageId, setInitialMessageId] = useState(initialMessageIdProp)
38
- const [atEndOfMessageHistory, setAtEndOfMessageHistory] = useState(false)
39
-
40
23
  const value = useMemo(
41
24
  () => ({
42
25
  conversationId,
43
26
  currentPageReplyRootId,
44
- initialMessageId,
45
- setInitialMessageId,
46
- initialMessageIdIsAnchor,
47
- atEndOfMessageHistory,
48
- setAtEndOfMessageHistory,
49
27
  }),
50
- [
51
- conversationId,
52
- currentPageReplyRootId,
53
- initialMessageId,
54
- initialMessageIdIsAnchor,
55
- atEndOfMessageHistory,
56
- ]
28
+ [conversationId, currentPageReplyRootId]
57
29
  )
58
30
 
59
31
  return <ConversationContext.Provider value={value}>{children}</ConversationContext.Provider>
@@ -26,6 +26,7 @@ export function useGroupChatConversationPayload({ groupId, enabled = true }: Pro
26
26
  },
27
27
  }),
28
28
  staleTime: STALE_TIME_MS,
29
+ refetchOnMount: 'always',
29
30
  enabled,
30
31
  })
31
32
 
@@ -16,6 +16,7 @@ export * from './use_message_reaction_toggle'
16
16
  export * from './use_new_conversation_entry'
17
17
  export * from './use_organization'
18
18
  export * from './use_people_person'
19
+ export * from './use_preview_avatar_diameter'
19
20
  export * from './use_product_analytics'
20
21
  export * from './use_qualified_by_age'
21
22
  export * from './use_scalable_number_of_lines'