@droppii-org/chat-mobile 0.2.81 → 0.2.83

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 (175) hide show
  1. package/lib/module/components/ThreadCard/MentionBadge.js +24 -0
  2. package/lib/module/components/ThreadCard/MentionBadge.js.map +1 -0
  3. package/lib/module/components/ThreadCard/ThreadCard.js +5 -1
  4. package/lib/module/components/ThreadCard/ThreadCard.js.map +1 -1
  5. package/lib/module/components/messages/linkMessage/LinkPreviewCard.js +67 -0
  6. package/lib/module/components/messages/linkMessage/LinkPreviewCard.js.map +1 -0
  7. package/lib/module/components/messages/linkMessage/index.js +49 -92
  8. package/lib/module/components/messages/linkMessage/index.js.map +1 -1
  9. package/lib/module/components/messages/quotedMessage/QuotedMessagePreviewStrip.js +152 -0
  10. package/lib/module/components/messages/quotedMessage/QuotedMessagePreviewStrip.js.map +1 -0
  11. package/lib/module/components/messages/quotedMessage/index.js +79 -55
  12. package/lib/module/components/messages/quotedMessage/index.js.map +1 -1
  13. package/lib/module/components/messages/stickerMessage/index.js +38 -13
  14. package/lib/module/components/messages/stickerMessage/index.js.map +1 -1
  15. package/lib/module/components/messages/textMessage/index.js +126 -13
  16. package/lib/module/components/messages/textMessage/index.js.map +1 -1
  17. package/lib/module/components/messages/types.js.map +1 -1
  18. package/lib/module/core/useChatListener.js +1 -1
  19. package/lib/module/core/useChatListener.js.map +1 -1
  20. package/lib/module/hooks/message/useSendMessage.js +64 -3
  21. package/lib/module/hooks/message/useSendMessage.js.map +1 -1
  22. package/lib/module/hooks/query-keys.js +4 -0
  23. package/lib/module/hooks/query-keys.js.map +1 -1
  24. package/lib/module/hooks/useChatCompose.js +3 -2
  25. package/lib/module/hooks/useChatCompose.js.map +1 -1
  26. package/lib/module/screens/chat-detail/ChatComposer.js +170 -48
  27. package/lib/module/screens/chat-detail/ChatComposer.js.map +1 -1
  28. package/lib/module/screens/chat-detail/ChatListLegend.js +16 -7
  29. package/lib/module/screens/chat-detail/ChatListLegend.js.map +1 -1
  30. package/lib/module/screens/chat-detail/components/ChatMessageInput.js +23 -2
  31. package/lib/module/screens/chat-detail/components/ChatMessageInput.js.map +1 -1
  32. package/lib/module/screens/chat-detail/components/MentionHighlightAdapter.js +51 -0
  33. package/lib/module/screens/chat-detail/components/MentionHighlightAdapter.js.map +1 -0
  34. package/lib/module/screens/chat-detail/components/MentionSuggestionItem.js +49 -0
  35. package/lib/module/screens/chat-detail/components/MentionSuggestionItem.js.map +1 -0
  36. package/lib/module/screens/chat-detail/components/MentionSuggestionList.js +114 -0
  37. package/lib/module/screens/chat-detail/components/MentionSuggestionList.js.map +1 -0
  38. package/lib/module/screens/chat-detail/hooks/useChatComposerState.js +8 -0
  39. package/lib/module/screens/chat-detail/hooks/useChatComposerState.js.map +1 -1
  40. package/lib/module/screens/chat-detail/hooks/useMentionComposer.js +251 -0
  41. package/lib/module/screens/chat-detail/hooks/useMentionComposer.js.map +1 -0
  42. package/lib/module/screens/chat-detail/legend/LegendChatDay.js +4 -9
  43. package/lib/module/screens/chat-detail/legend/LegendChatDay.js.map +1 -1
  44. package/lib/module/screens/chat-detail/legend/LegendChatMessage.js +14 -2
  45. package/lib/module/screens/chat-detail/legend/LegendChatMessage.js.map +1 -1
  46. package/lib/module/services/mentionSearch.js +49 -0
  47. package/lib/module/services/mentionSearch.js.map +1 -0
  48. package/lib/module/services/message.js +6 -1
  49. package/lib/module/services/message.js.map +1 -1
  50. package/lib/module/translation/resources/i18n.js +5 -0
  51. package/lib/module/translation/resources/i18n.js.map +1 -1
  52. package/lib/module/types/chat.js.map +1 -1
  53. package/lib/module/types/mention.js +2 -0
  54. package/lib/module/types/mention.js.map +1 -0
  55. package/lib/module/utils/legendListMessage.js +9 -1
  56. package/lib/module/utils/legendListMessage.js.map +1 -1
  57. package/lib/module/utils/mentionSerializer.js +113 -0
  58. package/lib/module/utils/mentionSerializer.js.map +1 -0
  59. package/lib/module/utils/mentions/index.js +7 -0
  60. package/lib/module/utils/mentions/index.js.map +1 -0
  61. package/lib/module/utils/mentions/mentionDetector.js +49 -0
  62. package/lib/module/utils/mentions/mentionDetector.js.map +1 -0
  63. package/lib/module/utils/mentions/mentionDiff.js +66 -0
  64. package/lib/module/utils/mentions/mentionDiff.js.map +1 -0
  65. package/lib/module/utils/mentions/mentionRange.js +188 -0
  66. package/lib/module/utils/mentions/mentionRange.js.map +1 -0
  67. package/lib/module/utils/mentions/mentionTokenize.js +50 -0
  68. package/lib/module/utils/mentions/mentionTokenize.js.map +1 -0
  69. package/lib/module/utils/messageUtils.js +6 -1
  70. package/lib/module/utils/messageUtils.js.map +1 -1
  71. package/lib/module/utils/url.js +33 -0
  72. package/lib/module/utils/url.js.map +1 -1
  73. package/lib/typescript/src/components/ThreadCard/MentionBadge.d.ts +7 -0
  74. package/lib/typescript/src/components/ThreadCard/MentionBadge.d.ts.map +1 -0
  75. package/lib/typescript/src/components/ThreadCard/ThreadCard.d.ts.map +1 -1
  76. package/lib/typescript/src/components/messages/linkMessage/LinkPreviewCard.d.ts +16 -0
  77. package/lib/typescript/src/components/messages/linkMessage/LinkPreviewCard.d.ts.map +1 -0
  78. package/lib/typescript/src/components/messages/linkMessage/index.d.ts +2 -2
  79. package/lib/typescript/src/components/messages/linkMessage/index.d.ts.map +1 -1
  80. package/lib/typescript/src/components/messages/quotedMessage/QuotedMessagePreviewStrip.d.ts +13 -0
  81. package/lib/typescript/src/components/messages/quotedMessage/QuotedMessagePreviewStrip.d.ts.map +1 -0
  82. package/lib/typescript/src/components/messages/quotedMessage/index.d.ts.map +1 -1
  83. package/lib/typescript/src/components/messages/stickerMessage/index.d.ts +2 -2
  84. package/lib/typescript/src/components/messages/stickerMessage/index.d.ts.map +1 -1
  85. package/lib/typescript/src/components/messages/textMessage/index.d.ts +4 -2
  86. package/lib/typescript/src/components/messages/textMessage/index.d.ts.map +1 -1
  87. package/lib/typescript/src/components/messages/types.d.ts +1 -0
  88. package/lib/typescript/src/components/messages/types.d.ts.map +1 -1
  89. package/lib/typescript/src/hooks/message/useSendMessage.d.ts +5 -2
  90. package/lib/typescript/src/hooks/message/useSendMessage.d.ts.map +1 -1
  91. package/lib/typescript/src/hooks/query-keys.d.ts +4 -0
  92. package/lib/typescript/src/hooks/query-keys.d.ts.map +1 -1
  93. package/lib/typescript/src/hooks/useChatCompose.d.ts +2 -1
  94. package/lib/typescript/src/hooks/useChatCompose.d.ts.map +1 -1
  95. package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts.map +1 -1
  96. package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts.map +1 -1
  97. package/lib/typescript/src/screens/chat-detail/components/ChatMessageInput.d.ts +10 -2
  98. package/lib/typescript/src/screens/chat-detail/components/ChatMessageInput.d.ts.map +1 -1
  99. package/lib/typescript/src/screens/chat-detail/components/MentionHighlightAdapter.d.ts +29 -0
  100. package/lib/typescript/src/screens/chat-detail/components/MentionHighlightAdapter.d.ts.map +1 -0
  101. package/lib/typescript/src/screens/chat-detail/components/MentionSuggestionItem.d.ts +8 -0
  102. package/lib/typescript/src/screens/chat-detail/components/MentionSuggestionItem.d.ts.map +1 -0
  103. package/lib/typescript/src/screens/chat-detail/components/MentionSuggestionList.d.ts +12 -0
  104. package/lib/typescript/src/screens/chat-detail/components/MentionSuggestionList.d.ts.map +1 -0
  105. package/lib/typescript/src/screens/chat-detail/hooks/useChatComposerState.d.ts +1 -1
  106. package/lib/typescript/src/screens/chat-detail/hooks/useChatComposerState.d.ts.map +1 -1
  107. package/lib/typescript/src/screens/chat-detail/hooks/useMentionComposer.d.ts +48 -0
  108. package/lib/typescript/src/screens/chat-detail/hooks/useMentionComposer.d.ts.map +1 -0
  109. package/lib/typescript/src/screens/chat-detail/legend/LegendChatDay.d.ts.map +1 -1
  110. package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts +2 -1
  111. package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts.map +1 -1
  112. package/lib/typescript/src/services/mentionSearch.d.ts +15 -0
  113. package/lib/typescript/src/services/mentionSearch.d.ts.map +1 -0
  114. package/lib/typescript/src/services/message.d.ts +1 -1
  115. package/lib/typescript/src/services/message.d.ts.map +1 -1
  116. package/lib/typescript/src/translation/resources/i18n.d.ts.map +1 -1
  117. package/lib/typescript/src/types/chat.d.ts +23 -0
  118. package/lib/typescript/src/types/chat.d.ts.map +1 -1
  119. package/lib/typescript/src/types/mention.d.ts +65 -0
  120. package/lib/typescript/src/types/mention.d.ts.map +1 -0
  121. package/lib/typescript/src/utils/legendListMessage.d.ts +4 -3
  122. package/lib/typescript/src/utils/legendListMessage.d.ts.map +1 -1
  123. package/lib/typescript/src/utils/mentionSerializer.d.ts +78 -0
  124. package/lib/typescript/src/utils/mentionSerializer.d.ts.map +1 -0
  125. package/lib/typescript/src/utils/mentions/index.d.ts +5 -0
  126. package/lib/typescript/src/utils/mentions/index.d.ts.map +1 -0
  127. package/lib/typescript/src/utils/mentions/mentionDetector.d.ts +18 -0
  128. package/lib/typescript/src/utils/mentions/mentionDetector.d.ts.map +1 -0
  129. package/lib/typescript/src/utils/mentions/mentionDiff.d.ts +20 -0
  130. package/lib/typescript/src/utils/mentions/mentionDiff.d.ts.map +1 -0
  131. package/lib/typescript/src/utils/mentions/mentionRange.d.ts +86 -0
  132. package/lib/typescript/src/utils/mentions/mentionRange.d.ts.map +1 -0
  133. package/lib/typescript/src/utils/mentions/mentionTokenize.d.ts +29 -0
  134. package/lib/typescript/src/utils/mentions/mentionTokenize.d.ts.map +1 -0
  135. package/lib/typescript/src/utils/messageUtils.d.ts.map +1 -1
  136. package/lib/typescript/src/utils/url.d.ts +12 -0
  137. package/lib/typescript/src/utils/url.d.ts.map +1 -1
  138. package/package.json +1 -1
  139. package/src/components/ThreadCard/MentionBadge.tsx +28 -0
  140. package/src/components/ThreadCard/ThreadCard.tsx +3 -0
  141. package/src/components/messages/linkMessage/LinkPreviewCard.tsx +92 -0
  142. package/src/components/messages/linkMessage/index.tsx +64 -121
  143. package/src/components/messages/quotedMessage/QuotedMessagePreviewStrip.tsx +168 -0
  144. package/src/components/messages/quotedMessage/index.tsx +95 -64
  145. package/src/components/messages/stickerMessage/index.tsx +40 -11
  146. package/src/components/messages/textMessage/index.tsx +159 -17
  147. package/src/components/messages/types.ts +1 -0
  148. package/src/core/useChatListener.ts +1 -1
  149. package/src/hooks/message/useSendMessage.ts +60 -1
  150. package/src/hooks/query-keys.ts +6 -0
  151. package/src/hooks/useChatCompose.ts +115 -107
  152. package/src/screens/chat-detail/ChatComposer.tsx +201 -67
  153. package/src/screens/chat-detail/ChatListLegend.tsx +19 -4
  154. package/src/screens/chat-detail/components/ChatMessageInput.tsx +35 -2
  155. package/src/screens/chat-detail/components/MentionHighlightAdapter.tsx +57 -0
  156. package/src/screens/chat-detail/components/MentionSuggestionItem.tsx +53 -0
  157. package/src/screens/chat-detail/components/MentionSuggestionList.tsx +133 -0
  158. package/src/screens/chat-detail/hooks/useChatComposerState.ts +7 -1
  159. package/src/screens/chat-detail/hooks/useMentionComposer.ts +344 -0
  160. package/src/screens/chat-detail/legend/LegendChatDay.tsx +6 -9
  161. package/src/screens/chat-detail/legend/LegendChatMessage.tsx +25 -2
  162. package/src/services/mentionSearch.ts +57 -0
  163. package/src/services/message.ts +9 -1
  164. package/src/translation/resources/i18n.ts +6 -0
  165. package/src/types/chat.ts +22 -0
  166. package/src/types/mention.ts +70 -0
  167. package/src/utils/legendListMessage.ts +13 -4
  168. package/src/utils/mentionSerializer.ts +155 -0
  169. package/src/utils/mentions/index.ts +16 -0
  170. package/src/utils/mentions/mentionDetector.ts +58 -0
  171. package/src/utils/mentions/mentionDiff.ts +95 -0
  172. package/src/utils/mentions/mentionRange.ts +207 -0
  173. package/src/utils/mentions/mentionTokenize.ts +53 -0
  174. package/src/utils/messageUtils.ts +6 -1
  175. package/src/utils/url.ts +30 -0
@@ -0,0 +1,65 @@
1
+ /**
2
+ * @mention composer types.
3
+ *
4
+ * All offsets throughout this module are raw JS string indices (UTF-16 code
5
+ * units) — the same unit RN's TextInput selection/cursor API uses on both
6
+ * platforms. This is a deliberate choice, not a default: it means offsets
7
+ * never need conversion at the RN boundary, at the cost of requiring
8
+ * surrogate-pair-aware handling wherever text is diffed (see mentionDiff.ts).
9
+ * AT Protocol-style byte offsets are unnecessary here because this data
10
+ * never crosses a non-JS boundary — see mentionSerializer.ts.
11
+ */
12
+ export interface Selection {
13
+ start: number;
14
+ end: number;
15
+ }
16
+ /**
17
+ * A committed mention: created only by explicit picker selection
18
+ * (insertMention), never inferred from text content. [start, end) excludes
19
+ * the trailing separator space inserted after the display name — see
20
+ * insertMention's doc comment for why.
21
+ */
22
+ export interface Mention {
23
+ id: string;
24
+ userId: string;
25
+ displayName: string;
26
+ start: number;
27
+ end: number;
28
+ }
29
+ /**
30
+ * The in-progress, uncommitted "@query" at the cursor. Freely editable
31
+ * character-by-character — never atomic, never promoted to a Mention except
32
+ * through explicit selection from the suggestion list.
33
+ */
34
+ export interface ActiveMention {
35
+ trigger: '@';
36
+ start: number;
37
+ end: number;
38
+ keyword: string;
39
+ }
40
+ export interface MentionSuggestion {
41
+ userId: string;
42
+ displayName: string;
43
+ avatar?: string;
44
+ }
45
+ /**
46
+ * The boundary between Part A (composer, no concrete OpenIM dependency) and
47
+ * Part B (OpenIM SDK integration). Part A depends only on this type,
48
+ * injected; Part B's `openIMMentionSearchSource` (src/services/mentionSearch.ts)
49
+ * implements it against the real SDK. Defined here, in Part A's own types
50
+ * file, rather than in the Part B service file, so the dependency direction
51
+ * stays correct: Part A defines the contract, Part B implements it.
52
+ */
53
+ export interface MentionSearchResult {
54
+ items: MentionSuggestion[];
55
+ /** True when `items.length === limit` — a same-size next page may exist. */
56
+ hasMore: boolean;
57
+ }
58
+ export interface MentionSearchSource {
59
+ search(params: {
60
+ keyword: string;
61
+ limit: number;
62
+ offset: number;
63
+ }): Promise<MentionSearchResult>;
64
+ }
65
+ //# sourceMappingURL=mention.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mention.d.ts","sourceRoot":"","sources":["../../../../src/types/mention.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,GAAG,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,4EAA4E;IAC5E,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,MAAM,EAAE;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;CAClC"}
@@ -9,9 +9,10 @@ export interface PrecomputedMessageData {
9
9
  messageId: string;
10
10
  dayStart: boolean;
11
11
  /**
12
- * More than GROUP_TIME_GAP has passed since the previous message. The list
13
- * marks these with a time label: the cluster break is real but, unlike a day
14
- * change, nothing else on screen would show why the run stopped.
12
+ * More than DIVIDER_TIME_GAP has passed since the previous message on the
13
+ * same day. The list marks these with a time-only divider so a long pause
14
+ * is visible even when nothing else on screen (day change, sender change)
15
+ * would show it.
15
16
  */
16
17
  timeBreak: boolean;
17
18
  createdAt: number;
@@ -1 +1 @@
1
- {"version":3,"file":"legendListMessage.d.ts","sourceRoot":"","sources":["../../../../src/utils/legendListMessage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAI9E;;GAEG;AACH,eAAO,MAAM,cAAc,GAAI,SAAS,WAAW,KAAG,MAkCrD,CAAC;AAEF,+EAA+E;AAC/E,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AAEjE,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,aAAa,CAAC;CAC9B;AAsCD,MAAM,WAAW,iBAAiB;IAChC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,GACnC,UAAU,WAAW,EAAE,EACvB,uCAA8C,iBAAsB,KACnE,GAAG,CAAC,MAAM,EAAE,sBAAsB,CA6EpC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAC3B,UAAU,WAAW,EAAE,KACtB,GAAG,CAAC,MAAM,EAAE,OAAO,CAYrB,CAAC"}
1
+ {"version":3,"file":"legendListMessage.d.ts","sourceRoot":"","sources":["../../../../src/utils/legendListMessage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAI9E;;GAEG;AACH,eAAO,MAAM,cAAc,GAAI,SAAS,WAAW,KAAG,MAkCrD,CAAC;AAEF,+EAA+E;AAC/E,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AAEjE,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,aAAa,CAAC;CAC9B;AA8CD,MAAM,WAAW,iBAAiB;IAChC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,GACnC,UAAU,WAAW,EAAE,EACvB,uCAA8C,iBAAsB,KACnE,GAAG,CAAC,MAAM,EAAE,sBAAsB,CA6EpC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAC3B,UAAU,WAAW,EAAE,KACtB,GAAG,CAAC,MAAM,EAAE,OAAO,CAYrB,CAAC"}
@@ -0,0 +1,78 @@
1
+ import type { AtUsersInfoItem } from '@droppii/openim-rn-client-sdk';
2
+ import type { ISerializedMentionEx } from '../types/chat';
3
+ import type { Mention } from '../types/mention';
4
+ /** Composer-internal marker for the "Tất cả" suggestion row / a committed
5
+ * "tag everyone" Mention — never sent on the wire, see AT_ALL_TAG below. */
6
+ export declare const ALL_MEMBERS_USER_ID = "ALL";
7
+ /**
8
+ * OpenIM's own protocol sentinel for "everyone in the group", used inside
9
+ * atUserIDList in place of enumerating every member. Confirmed against
10
+ * openimsdk-core (pkg/constant/constant.go:242 — `AtAllString = "AtAllTag"`,
11
+ * exposed client-side as `GetAtAllTag()`) and openim-server's group-message
12
+ * send path (internal/rpc/msg/send.go's `setConversationAtInfo`), which
13
+ * special-cases exactly this string and does the "who is everyone right
14
+ * now" resolution itself, server-side.
15
+ *
16
+ * This is why "Tất cả" must serialize to this one token instead of
17
+ * expanding to every resolved member's userID client-side (the previous
18
+ * approach): the Go core's CreateTextAtMessage hard-rejects any
19
+ * atUserIDList longer than 10 entries regardless of group size
20
+ * (openimsdk-core/internal/conversation_msg/create_message.go:80-82) — a
21
+ * >10-member group's "Tất cả" silently failed to send at all. Sending just
22
+ * `[AT_ALL_TAG]` keeps atUserIDList small no matter the group size, and is
23
+ * also the behavior the protocol actually expects.
24
+ */
25
+ export declare const AT_ALL_TAG = "AtAllTag";
26
+ /**
27
+ * OpenIM's Go core hard-rejects any AtMsgParams.atUserIDList longer than
28
+ * this (openimsdk-core/internal/conversation_msg/create_message.go:80-82,
29
+ * `if len(userIDList) > 10 { return nil, sdkerrs.ErrArgs }`) — not
30
+ * adjustable client-side. "Tất cả" no longer risks this (see AT_ALL_TAG),
31
+ * but tagging more than 10 *individual* members in one message still
32
+ * would, so serializeMentionsForSend caps atUserIDList/atUsersInfo at this
33
+ * count (keeping the first MAX_AT_USER_COUNT, in mention order) rather
34
+ * than letting the send fail outright the way "Tất cả" used to.
35
+ */
36
+ export declare const MAX_AT_USER_COUNT = 10;
37
+ export interface SerializedMentions {
38
+ atUserIDList: string[];
39
+ atUsersInfo: AtUsersInfoItem[];
40
+ exMentions: ISerializedMentionEx[];
41
+ /**
42
+ * True when more than MAX_AT_USER_COUNT distinct users were mentioned —
43
+ * atUserIDList/atUsersInfo were capped to the first MAX_AT_USER_COUNT (in
44
+ * mention order) so the send doesn't hit the Go core's hard rejection.
45
+ * `exMentions` is deliberately NOT capped: every mention still renders
46
+ * highlighted in the sent text (the sender sees what they typed), only
47
+ * native tag/notify is limited to the first MAX_AT_USER_COUNT. Callers
48
+ * use this flag to warn the user, not to block the send.
49
+ */
50
+ truncated: boolean;
51
+ }
52
+ /**
53
+ * Translates the composer's Mention[] (client-side ranges) into OpenIM's
54
+ * wire shapes for createTextAtMessage, plus our own ex.mentions channel. No
55
+ * group-member resolution needed here (or by the caller) any more — see
56
+ * AT_ALL_TAG's doc comment for why a "Tất cả" mention maps to that single
57
+ * sentinel rather than a per-member list.
58
+ */
59
+ export declare function serializeMentionsForSend(mentions: Mention[]): SerializedMentions;
60
+ interface ReceivedAtTextElem {
61
+ text: string;
62
+ atUserList: string[];
63
+ atUsersInfo?: AtUsersInfoItem[];
64
+ }
65
+ /**
66
+ * Reverse of serializeMentionsForSend, for rendering a received/sent
67
+ * AtTextMessage (Phase B3). Prefers `exMentions` (exact, our own channel);
68
+ * falls back to searching `atTextElem.text` for "@{groupNickname}" per
69
+ * atUsersInfo entry when ex is absent — an interop safety net, not the
70
+ * primary path. The fallback is inherently ambiguous if a member's
71
+ * nickname coincidentally appears elsewhere in the message as plain text;
72
+ * accepted as a known, documented limitation (see the mention plan's
73
+ * finding #3) rather than something client code can fully resolve without
74
+ * position data from the protocol.
75
+ */
76
+ export declare function deserializeMentionRanges(atTextElem: ReceivedAtTextElem, exMentions?: ISerializedMentionEx[]): Mention[];
77
+ export {};
78
+ //# sourceMappingURL=mentionSerializer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mentionSerializer.d.ts","sourceRoot":"","sources":["../../../../src/utils/mentionSerializer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEhD;4EAC4E;AAC5E,eAAO,MAAM,mBAAmB,QAAQ,CAAC;AAEzC;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,UAAU,aAAa,CAAC;AAErC;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,UAAU,EAAE,oBAAoB,EAAE,CAAC;IACnC;;;;;;;;OAQG;IACH,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,OAAO,EAAE,GAClB,kBAAkB,CAkCpB;AAED,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;CACjC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,kBAAkB,EAC9B,UAAU,CAAC,EAAE,oBAAoB,EAAE,GAClC,OAAO,EAAE,CAiCX"}
@@ -0,0 +1,5 @@
1
+ export { findActiveMention } from './mentionDetector';
2
+ export { diffTextChange, snapBoundary, type TextChangeDiff, } from './mentionDiff';
3
+ export { insertMention, reconcileMentions, normalizeDeleteRange, applyTextChange, type InsertMentionResult, type ReconcileMentionsResult, type ApplyTextChangeResult, } from './mentionRange';
4
+ export { tokenize, type Segment } from './mentionTokenize';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/utils/mentions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EACL,cAAc,EACd,YAAY,EACZ,KAAK,cAAc,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,EACf,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,GAC3B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { ActiveMention } from '../../types/mention';
2
+ /**
3
+ * Scans backward from `cursor` for an unclosed "@query" — the in-progress
4
+ * trigger that drives the suggestion list, not a committed mention.
5
+ *
6
+ * Only called for a collapsed cursor (selection.start === selection.end);
7
+ * the caller must resolve a range selection to `null` before calling this —
8
+ * a live "@query" only makes sense at a single insertion point.
9
+ *
10
+ * "@" itself counts as a boundary: the scan stops there whether or not it's
11
+ * a valid trigger, which is what prevents re-scanning back through an
12
+ * already-committed mention's "@Name " when the user starts typing a second
13
+ * "@" right after it (insertMention always appends a trailing space, a
14
+ * boundary char, so the previous mention's "@" is never reachable from the
15
+ * new query).
16
+ */
17
+ export declare function findActiveMention(text: string, cursor: number): ActiveMention | null;
18
+ //# sourceMappingURL=mentionDetector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mentionDetector.d.ts","sourceRoot":"","sources":["../../../../../src/utils/mentions/mentionDetector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AASzD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,aAAa,GAAG,IAAI,CA8BtB"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Snaps `index` so it never sits between a surrogate pair in `str` —
3
+ * without this, diffing e.g. "😀" vs "😁" (which differ only in their low
4
+ * surrogate) would compute a boundary that bisects the emoji. Snapping
5
+ * always shrinks the "common" region (treats one more code point as
6
+ * "changed"), which is always safe: being more conservative about what
7
+ * counts as touched only ever produces more mention-invalidation upstream,
8
+ * never corruption.
9
+ */
10
+ export declare function snapBoundary(str: string, index: number, direction: 1 | -1): number;
11
+ export interface TextChangeDiff {
12
+ /** [removedStart, removedEnd) in previousText coords — what was deleted. */
13
+ removedStart: number;
14
+ removedEnd: number;
15
+ /** [insertedStart, insertedEnd) in nextText coords — what was inserted. insertedStart === removedStart. */
16
+ insertedStart: number;
17
+ insertedEnd: number;
18
+ }
19
+ export declare function diffTextChange(previousText: string, nextText: string): TextChangeDiff;
20
+ //# sourceMappingURL=mentionDiff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mentionDiff.d.ts","sourceRoot":"","sources":["../../../../../src/utils/mentions/mentionDiff.ts"],"names":[],"mappings":"AAkBA;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,CAAC,GAAG,CAAC,CAAC,GAChB,MAAM,CAUR;AAED,MAAM,WAAW,cAAc;IAC7B,4EAA4E;IAC5E,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,2GAA2G;IAC3G,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,cAAc,CAC5B,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,GACf,cAAc,CAuChB"}
@@ -0,0 +1,86 @@
1
+ import type { ActiveMention, Mention } from '../../types/mention';
2
+ export interface InsertMentionResult {
3
+ text: string;
4
+ mentions: Mention[];
5
+ cursor: number;
6
+ }
7
+ /**
8
+ * Replaces the active "@query" range with "@DisplayName " and commits a new
9
+ * Mention. The trailing space is deliberately excluded from the mention's
10
+ * [start,end) range — it's a plain separator, not part of the token. This
11
+ * keeps the data model simple (no "our space" vs "user-typed space"
12
+ * special-casing) at the cost of removing a mention immediately after
13
+ * insertion taking two backspaces (first eats the space, second — now
14
+ * touching the mention's actual range — triggers atomic removal). Matches
15
+ * the spec's own fixture: "Hello @Thinh" with NO trailing space, cursor at
16
+ * the end, backspace removes the whole mention in one step, since there's
17
+ * no space to consume first.
18
+ */
19
+ export declare function insertMention(text: string, mentions: Mention[], activeMention: ActiveMention, selected: {
20
+ userId: string;
21
+ displayName: string;
22
+ }): InsertMentionResult;
23
+ export interface ReconcileMentionsResult {
24
+ mentions: Mention[];
25
+ invalidated: Mention[];
26
+ }
27
+ /**
28
+ * Re-derives mention ranges after an edit, given only the before/after full
29
+ * strings (RN's onChangeText carries no delta). Diffs the two strings to
30
+ * localize the changed region, then per mention: fully before the change ->
31
+ * kept as-is; fully after -> shifted by the length delta; any overlap at
32
+ * all -> invalidated (dropped, never left partially stale).
33
+ *
34
+ * Boundary semantics matter here: `m.end <= removedStart` (touching-but-
35
+ * before) is "kept", and `m.start >= removedEnd` (touching-but-after) is
36
+ * "shift" — so typing immediately after a mention, or deleting text
37
+ * strictly before one, never invalidates it. Anything else, even a
38
+ * 1-character overlap, does.
39
+ */
40
+ export declare function reconcileMentions({ previousText, nextText, mentions, }: {
41
+ previousText: string;
42
+ nextText: string;
43
+ mentions: Mention[];
44
+ }): ReconcileMentionsResult;
45
+ /**
46
+ * Expands a delete/replace range so it fully swallows any mention it
47
+ * merely intersects — this is what makes "atomic delete" actually happen.
48
+ * RN's Old-Architecture TextInput can't intercept a keypress before the
49
+ * native buffer mutates, so this doesn't run as a true pre-mutation guard;
50
+ * it runs on the range diffTextChange computed from an already-applied
51
+ * edit, and the caller (useMentionComposer) uses the expanded range to
52
+ * rebuild the text from previousText before React re-renders — see that
53
+ * hook's doc comment for the full repair sequence.
54
+ *
55
+ * The fixed-point loop is needed for the adjacent-mentions case: expanding
56
+ * to cover one mention can newly overlap a neighbor with zero gap between
57
+ * them (e.g. loaded from server data with no separator).
58
+ */
59
+ export declare function normalizeDeleteRange(range: {
60
+ start: number;
61
+ end: number;
62
+ }, mentions: Mention[]): {
63
+ start: number;
64
+ end: number;
65
+ };
66
+ export interface ApplyTextChangeResult {
67
+ text: string;
68
+ mentions: Mention[];
69
+ cursor: number;
70
+ /** True when this change touched a mention and it was atomically removed. */
71
+ mentionRemoved: boolean;
72
+ }
73
+ /**
74
+ * The full "atomic delete" pipeline, extracted as a pure function so it's
75
+ * unit-testable without a live TextInput: diff previousText -> nextTextRaw,
76
+ * expand the removed range to cover any touched mention, and if it
77
+ * expanded, discard nextTextRaw for that span and rebuild atomically from
78
+ * previousText — otherwise just diff-and-shift normally.
79
+ *
80
+ * This is what `useMentionComposer` (Phase A2) calls from its
81
+ * ref-buffered onChangeText reconciliation step; it also imperatively
82
+ * calls the native `setSelection(cursor, cursor)` after, which is RN-only
83
+ * and stays out of this pure function.
84
+ */
85
+ export declare function applyTextChange(previousText: string, mentions: Mention[], nextTextRaw: string): ApplyTextChangeResult;
86
+ //# sourceMappingURL=mentionRange.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mentionRange.d.ts","sourceRoot":"","sources":["../../../../../src/utils/mentions/mentionRange.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAGlE,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAKD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,OAAO,EAAE,EACnB,aAAa,EAAE,aAAa,EAC5B,QAAQ,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAChD,mBAAmB,CA2BrB;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,WAAW,EAAE,OAAO,EAAE,CAAC;CACxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,YAAY,EACZ,QAAQ,EACR,QAAQ,GACT,EAAE;IACD,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB,GAAG,uBAAuB,CAsB1B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EACrC,QAAQ,EAAE,OAAO,EAAE,GAClB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAkBhC;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAC7B,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,OAAO,EAAE,EACnB,WAAW,EAAE,MAAM,GAClB,qBAAqB,CAqCvB"}
@@ -0,0 +1,29 @@
1
+ import type { Mention } from '../../types/mention';
2
+ export type Segment = {
3
+ type: 'text';
4
+ text: string;
5
+ start: number;
6
+ end: number;
7
+ } | {
8
+ type: 'mention';
9
+ mention: Mention;
10
+ };
11
+ /**
12
+ * Converts edit-time state (flat string + Mention[] ranges) into an
13
+ * ordered list of plain-text runs and mention tokens — the shape both the
14
+ * composer's highlight renderer (Phase A3) and the sent-message bubble
15
+ * renderer (Phase B3) need to produce their styled `<Text>` children.
16
+ * Shared by both so the segment-splitting logic exists exactly once.
17
+ *
18
+ * O(m log m) sort + O(n) slicing (m = mention count, n = text length) —
19
+ * trivial at the sizes this feature targets (≤50 mentions, ≤2000 chars).
20
+ *
21
+ * Precondition: `mentions` is non-overlapping, guaranteed by Phase A1's own
22
+ * pipeline (insertMention/reconcileMentions never produce overlaps). The
23
+ * `m.start < cursor` skip below is a defensive backstop for
24
+ * externally-sourced data (a message loaded for editing, or an `ex` blob
25
+ * serialized by an older app version) — not a correctness dependency for
26
+ * the live composer.
27
+ */
28
+ export declare function tokenize(text: string, mentions: Mention[]): Segment[];
29
+ //# sourceMappingURL=mentionTokenize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mentionTokenize.d.ts","sourceRoot":"","sources":["../../../../../src/utils/mentions/mentionTokenize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAEnD,MAAM,MAAM,OAAO,GACf;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAE1C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CA6BrE"}
@@ -1 +1 @@
1
- {"version":3,"file":"messageUtils.d.ts","sourceRoot":"","sources":["../../../../src/utils/messageUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,WAAW,EAEX,KAAK,gBAAgB,EACrB,KAAK,WAAW,EACjB,MAAM,+BAA+B,CAAC;AAIvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,eAAO,MAAM,gBAAgB,GAAI,SAAS,WAAW,KAAG,OACL,CAAC;AAKpD,eAAO,MAAM,uBAAuB,EAAE,OAAO,CAC3C,MAAM,CAAC,WAAW,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,CAqEjE,CAAC;AAMF,eAAO,MAAM,wBAAwB,EAEpB,WAAW,EAAE,CAAC;AA2B/B,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAQD,eAAO,MAAM,uBAAuB,GAClC,SAAS,WAAW,KACnB,wBAAwB,GAAG,SA8D7B,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,SAAS,WAAW,KAAG,MAYzD,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,UAAU,WAAW,KAAG,MAyFzD,CAAC;AAEF,eAAO,MAAM,0BAA0B,GACrC,UAAU,WAAW,KACpB,SAUF,CAAC;AAEF,eAAO,MAAM,sBAAsB,GACjC,UAAU,WAAW,KACpB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,SAAS,CAAA;CAGrC,CAAC;AAEH,MAAM,MAAM,sBAAsB,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAEzE,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,sBAAsB,CAAC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAmBD,eAAO,MAAM,uBAAuB,GAClC,SAAS,WAAW,KACnB,oBA0DF,CAAC;AAKF,eAAO,MAAM,6BAA6B,GACxC,SAAS,WAAW,KACnB,MAAM,GAAG,SAaX,CAAC;AAEF,eAAO,MAAM,aAAa,GACxB,SAAS,WAAW,EACpB,gBAAgB,MAAM,EACtB,eAAe,gBAAgB,KAC9B,MAgBF,CAAC;AAEF,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,WAAW,GAAG,SAAS,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,6BAA6B,CAAC,EAAE,OAAO,CAAC;CACzC;AAED,eAAO,MAAM,kBAAkB,GAAI,0EAKhC,wBAAwB,KAAG,MAyB7B,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,UAAU,WAAW,EACrB,gBAAgB,MAAM,EACtB,eAAe,gBAAgB,EAC/B,SAAS,OAAO,KACf,MAOF,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,KAAG,YAAY,GAAG,SAS7D,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,EAAE,WAAW,MAAM,KAAG,MAUhE,CAAC"}
1
+ {"version":3,"file":"messageUtils.d.ts","sourceRoot":"","sources":["../../../../src/utils/messageUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,WAAW,EAEX,KAAK,gBAAgB,EACrB,KAAK,WAAW,EACjB,MAAM,+BAA+B,CAAC;AAIvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,eAAO,MAAM,gBAAgB,GAAI,SAAS,WAAW,KAAG,OACL,CAAC;AAKpD,eAAO,MAAM,uBAAuB,EAAE,OAAO,CAC3C,MAAM,CAAC,WAAW,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,CAqEjE,CAAC;AAMF,eAAO,MAAM,wBAAwB,EAEpB,WAAW,EAAE,CAAC;AA2B/B,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAQD,eAAO,MAAM,uBAAuB,GAClC,SAAS,WAAW,KACnB,wBAAwB,GAAG,SA8D7B,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,SAAS,WAAW,KAAG,MAYzD,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,UAAU,WAAW,KAAG,MA8FzD,CAAC;AAEF,eAAO,MAAM,0BAA0B,GACrC,UAAU,WAAW,KACpB,SAUF,CAAC;AAEF,eAAO,MAAM,sBAAsB,GACjC,UAAU,WAAW,KACpB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,SAAS,CAAA;CAGrC,CAAC;AAEH,MAAM,MAAM,sBAAsB,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAEzE,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,sBAAsB,CAAC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAmBD,eAAO,MAAM,uBAAuB,GAClC,SAAS,WAAW,KACnB,oBA0DF,CAAC;AAKF,eAAO,MAAM,6BAA6B,GACxC,SAAS,WAAW,KACnB,MAAM,GAAG,SAaX,CAAC;AAEF,eAAO,MAAM,aAAa,GACxB,SAAS,WAAW,EACpB,gBAAgB,MAAM,EACtB,eAAe,gBAAgB,KAC9B,MAgBF,CAAC;AAEF,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,WAAW,GAAG,SAAS,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,6BAA6B,CAAC,EAAE,OAAO,CAAC;CACzC;AAED,eAAO,MAAM,kBAAkB,GAAI,0EAKhC,wBAAwB,KAAG,MAyB7B,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,UAAU,WAAW,EACrB,gBAAgB,MAAM,EACtB,eAAe,gBAAgB,EAC/B,SAAS,OAAO,KACf,MAOF,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,KAAG,YAAY,GAAG,SAS7D,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,EAAE,WAAW,MAAM,KAAG,MAUhE,CAAC"}
@@ -1,4 +1,16 @@
1
1
  export declare function extractUrls(text: string): string[];
2
+ export interface TextUrlPart {
3
+ text: string;
4
+ isUrl: boolean;
5
+ }
6
+ /**
7
+ * Splits `text` into an ordered run of plain/URL parts — pure data, no
8
+ * JSX, so both LinkMessage (a whole message that's just text+URLs) and
9
+ * TextMessage (a mention message whose plain-text *segments* may also
10
+ * contain a URL) can render URL styling from the same source instead of
11
+ * two divergent implementations.
12
+ */
13
+ export declare function splitTextByUrls(text: string, urls: string[]): TextUrlPart[];
2
14
  export declare function normalizeUrl(url: string): string;
3
15
  /**
4
16
  * The resource-center API's `urlMetadata` doesn't include a separate domain field
@@ -1 +1 @@
1
- {"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../../../src/utils/url.ts"],"names":[],"mappings":"AA+BA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAElD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,UAKvC;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGjD"}
1
+ {"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../../../src/utils/url.ts"],"names":[],"mappings":"AA+BA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAElD;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,WAAW,EAAE,CAgB3E;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,UAKvC;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGjD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@droppii-org/chat-mobile",
3
- "version": "0.2.81",
3
+ "version": "0.2.83",
4
4
  "description": "Droppii chat mobile",
5
5
  "keywords": [
6
6
  "react-native",
@@ -0,0 +1,28 @@
1
+ import { memo } from 'react';
2
+ import { KLabel, KColors } from '@droppii/libs';
3
+ import { GroupAtType } from '@droppii/openim-rn-client-sdk';
4
+
5
+ interface MentionBadgeProps {
6
+ groupAtType?: GroupAtType;
7
+ }
8
+
9
+ // Figma (node 15177-302069): "@" renders as a standalone bold glyph, no
10
+ // background — same color token as the composer/bubble mention highlight
11
+ // (KColors.palette.primary.w400), not a new one-off value.
12
+ export const MentionBadge = memo(({ groupAtType }: MentionBadgeProps) => {
13
+ if (groupAtType === undefined || groupAtType === GroupAtType.AtNormal) {
14
+ return null;
15
+ }
16
+
17
+ return (
18
+ <KLabel.Text
19
+ typo="TextNmBold"
20
+ color={KColors.palette.primary.w400}
21
+ marginL="0.5rem"
22
+ >
23
+ @
24
+ </KLabel.Text>
25
+ );
26
+ });
27
+
28
+ MentionBadge.displayName = 'MentionBadge';
@@ -6,6 +6,7 @@ import { KContainer, KImage, KLabel, KColors } from '@droppii/libs';
6
6
  import { AvatarSection } from './AvatarSection';
7
7
  import { NamePrefixIcon } from './NamePrefixIcon';
8
8
  import { UnreadBadge } from './UnreadBadge';
9
+ import { MentionBadge } from './MentionBadge';
9
10
  import { formatTimestamp } from '../../utils/dateTimeUtils';
10
11
  import {
11
12
  getLastMessageText,
@@ -44,6 +45,7 @@ const ThreadCard = memo(({ item, onPress }: ThreadCardProps) => {
44
45
  latestMsgSendTime,
45
46
  latestMsg,
46
47
  groupID,
48
+ groupAtType,
47
49
  } = conversation || {};
48
50
 
49
51
  const { data: isJoinedGroup } = useIsJoinedGroup(groupID);
@@ -183,6 +185,7 @@ const ThreadCard = memo(({ item, onPress }: ThreadCardProps) => {
183
185
  />
184
186
  </KContainer.View>
185
187
  </KContainer.VisibleView>
188
+ <MentionBadge groupAtType={groupAtType} />
186
189
  <UnreadBadge count={unreadCount} />
187
190
  </KContainer.View>
188
191
  </KContainer.View>
@@ -0,0 +1,92 @@
1
+ import { memo } from 'react';
2
+ import { StyleSheet } from 'react-native';
3
+ import {
4
+ KContainer,
5
+ KImage,
6
+ KLabel,
7
+ KColors,
8
+ KSpacingValue,
9
+ } from '@droppii/libs';
10
+ import type { IUrlMetadata } from '../../../types/common';
11
+
12
+ interface LinkPreviewCardProps {
13
+ metadata: IUrlMetadata;
14
+ onPressUrl?: (url: string) => void;
15
+ }
16
+
17
+ /**
18
+ * The rich link-preview card (thumbnail/title/url/description) — shared by
19
+ * LinkMessage (message.urlTextElem, a plain URL message) and TextMessage
20
+ * (mention+URL combo: message.ex.urlMetadata attached to an AtTextMessage,
21
+ * since OpenIM's AtTextMessage/UrlTextMessage content types are mutually
22
+ * exclusive — see useSendMessage.ts's doc comment for why the mention wins
23
+ * the content type and the URL card rides along in `ex` instead).
24
+ */
25
+ export const LinkPreviewCard = memo(
26
+ ({ metadata, onPressUrl }: LinkPreviewCardProps) => (
27
+ <KContainer.Touchable
28
+ style={styles.card}
29
+ onPress={() => metadata.url && onPressUrl?.(metadata.url)}
30
+ >
31
+ {!!metadata.image && (
32
+ <KContainer.View style={styles.cardThumb}>
33
+ <KImage.Base
34
+ uri={metadata.image}
35
+ style={StyleSheet.absoluteFill}
36
+ resizeMode="cover"
37
+ />
38
+ </KContainer.View>
39
+ )}
40
+ <KContainer.View style={styles.cardBody}>
41
+ {!!metadata.title && (
42
+ <KLabel.Text
43
+ typo="TextNmMedium"
44
+ color={KColors.palette.gray.w900}
45
+ numberOfLines={1}
46
+ >
47
+ {metadata.title}
48
+ </KLabel.Text>
49
+ )}
50
+ {!!metadata.url && (
51
+ <KLabel.Text
52
+ typo="TextXsMedium"
53
+ color={KColors.palette.primary.w400}
54
+ numberOfLines={1}
55
+ >
56
+ {metadata.url}
57
+ </KLabel.Text>
58
+ )}
59
+ {!!metadata.description && (
60
+ <KLabel.Text
61
+ typo="TextXsNormal"
62
+ color={KColors.gray.normal}
63
+ numberOfLines={2}
64
+ >
65
+ {metadata.description}
66
+ </KLabel.Text>
67
+ )}
68
+ </KContainer.View>
69
+ </KContainer.Touchable>
70
+ )
71
+ );
72
+
73
+ LinkPreviewCard.displayName = 'LinkPreviewCard';
74
+
75
+ const styles = StyleSheet.create({
76
+ card: {
77
+ borderRadius: KSpacingValue['0.75rem'],
78
+ borderWidth: 1,
79
+ borderColor: 'rgba(57,62,64,0.1)',
80
+ backgroundColor: KColors.white,
81
+ overflow: 'hidden',
82
+ },
83
+ cardThumb: {
84
+ width: '100%',
85
+ aspectRatio: 16 / 9,
86
+ },
87
+ cardBody: {
88
+ paddingHorizontal: KSpacingValue['0.75rem'],
89
+ paddingVertical: KSpacingValue['0.5rem'],
90
+ gap: 2,
91
+ },
92
+ });