@droppii-org/chat-mobile 0.2.51 → 0.2.53

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 (162) hide show
  1. package/lib/module/assets/images/index.js +2 -1
  2. package/lib/module/assets/images/index.js.map +1 -1
  3. package/lib/module/assets/images/resource-center-empty-box.png +0 -0
  4. package/lib/module/components/MediaViewerModal.js +2 -1
  5. package/lib/module/components/MediaViewerModal.js.map +1 -1
  6. package/lib/module/components/MergedImageGrid.js +1 -0
  7. package/lib/module/components/MergedImageGrid.js.map +1 -1
  8. package/lib/module/config/api-endpoints.js +2 -1
  9. package/lib/module/config/api-endpoints.js.map +1 -1
  10. package/lib/module/context/ChatDetailContext.js +2 -1
  11. package/lib/module/context/ChatDetailContext.js.map +1 -1
  12. package/lib/module/core/notification/pushToken.js +7 -7
  13. package/lib/module/core/notification/pushToken.js.map +1 -1
  14. package/lib/module/hooks/flows/useFlow.js +10 -0
  15. package/lib/module/hooks/flows/useFlow.js.map +1 -1
  16. package/lib/module/hooks/query-keys.js +5 -0
  17. package/lib/module/hooks/query-keys.js.map +1 -1
  18. package/lib/module/hooks/resourceCenter/useThreadResources.js +116 -0
  19. package/lib/module/hooks/resourceCenter/useThreadResources.js.map +1 -0
  20. package/lib/module/hooks/useImageAttachment.js +0 -2
  21. package/lib/module/hooks/useImageAttachment.js.map +1 -1
  22. package/lib/module/hooks/useMediaViewer.js +4 -1
  23. package/lib/module/hooks/useMediaViewer.js.map +1 -1
  24. package/lib/module/hooks/useSendAttachment.js +4 -1
  25. package/lib/module/hooks/useSendAttachment.js.map +1 -1
  26. package/lib/module/index.js +1 -0
  27. package/lib/module/index.js.map +1 -1
  28. package/lib/module/screens/MediaView/index.js +104 -42
  29. package/lib/module/screens/MediaView/index.js.map +1 -1
  30. package/lib/module/screens/ResourceCenter/FileResourceRow.js +80 -0
  31. package/lib/module/screens/ResourceCenter/FileResourceRow.js.map +1 -0
  32. package/lib/module/screens/ResourceCenter/GridRenderer.js +83 -0
  33. package/lib/module/screens/ResourceCenter/GridRenderer.js.map +1 -0
  34. package/lib/module/screens/ResourceCenter/GridThumbnail.js +97 -0
  35. package/lib/module/screens/ResourceCenter/GridThumbnail.js.map +1 -0
  36. package/lib/module/screens/ResourceCenter/LinkResourceRow.js +81 -0
  37. package/lib/module/screens/ResourceCenter/LinkResourceRow.js.map +1 -0
  38. package/lib/module/screens/ResourceCenter/ListRenderer.js +60 -0
  39. package/lib/module/screens/ResourceCenter/ListRenderer.js.map +1 -0
  40. package/lib/module/screens/ResourceCenter/ResourceCenterTabBar.js +75 -0
  41. package/lib/module/screens/ResourceCenter/ResourceCenterTabBar.js.map +1 -0
  42. package/lib/module/screens/ResourceCenter/ResourceCenterTabScene.js +122 -0
  43. package/lib/module/screens/ResourceCenter/ResourceCenterTabScene.js.map +1 -0
  44. package/lib/module/screens/ResourceCenter/ResourceEmptyState.js +36 -0
  45. package/lib/module/screens/ResourceCenter/ResourceEmptyState.js.map +1 -0
  46. package/lib/module/screens/ResourceCenter/index.js +104 -0
  47. package/lib/module/screens/ResourceCenter/index.js.map +1 -0
  48. package/lib/module/screens/ResourceCenter/types.js +25 -0
  49. package/lib/module/screens/ResourceCenter/types.js.map +1 -0
  50. package/lib/module/screens/chat-detail/BotMenuSheet.js +73 -0
  51. package/lib/module/screens/chat-detail/BotMenuSheet.js.map +1 -0
  52. package/lib/module/screens/chat-detail/ChatComposer.js +78 -3
  53. package/lib/module/screens/chat-detail/ChatComposer.js.map +1 -1
  54. package/lib/module/screens/chat-detail/components/ChatMessageInput.js +11 -6
  55. package/lib/module/screens/chat-detail/components/ChatMessageInput.js.map +1 -1
  56. package/lib/module/services/attachmentOrchestrator.js +19 -4
  57. package/lib/module/services/attachmentOrchestrator.js.map +1 -1
  58. package/lib/module/services/endpoints.js +1 -0
  59. package/lib/module/services/endpoints.js.map +1 -1
  60. package/lib/module/services/resourceCenter.js +102 -0
  61. package/lib/module/services/resourceCenter.js.map +1 -0
  62. package/lib/module/translation/resources/i18n.js +13 -1
  63. package/lib/module/translation/resources/i18n.js.map +1 -1
  64. package/lib/module/types/attachment.js +2 -0
  65. package/lib/module/types/chat.js +20 -0
  66. package/lib/module/types/chat.js.map +1 -1
  67. package/lib/module/utils/url.js +11 -0
  68. package/lib/module/utils/url.js.map +1 -1
  69. package/lib/typescript/src/assets/images/index.d.ts +1 -0
  70. package/lib/typescript/src/assets/images/index.d.ts.map +1 -1
  71. package/lib/typescript/src/components/MediaViewerModal.d.ts +3 -1
  72. package/lib/typescript/src/components/MediaViewerModal.d.ts.map +1 -1
  73. package/lib/typescript/src/components/MergedImageGrid.d.ts.map +1 -1
  74. package/lib/typescript/src/config/api-endpoints.d.ts +1 -0
  75. package/lib/typescript/src/config/api-endpoints.d.ts.map +1 -1
  76. package/lib/typescript/src/context/ChatDetailContext.d.ts.map +1 -1
  77. package/lib/typescript/src/hooks/flows/useFlow.d.ts +1 -0
  78. package/lib/typescript/src/hooks/flows/useFlow.d.ts.map +1 -1
  79. package/lib/typescript/src/hooks/query-keys.d.ts +5 -0
  80. package/lib/typescript/src/hooks/query-keys.d.ts.map +1 -1
  81. package/lib/typescript/src/hooks/resourceCenter/useThreadResources.d.ts +21 -0
  82. package/lib/typescript/src/hooks/resourceCenter/useThreadResources.d.ts.map +1 -0
  83. package/lib/typescript/src/hooks/useImageAttachment.d.ts.map +1 -1
  84. package/lib/typescript/src/hooks/useMediaViewer.d.ts +5 -1
  85. package/lib/typescript/src/hooks/useMediaViewer.d.ts.map +1 -1
  86. package/lib/typescript/src/hooks/useSendAttachment.d.ts.map +1 -1
  87. package/lib/typescript/src/index.d.ts +2 -0
  88. package/lib/typescript/src/index.d.ts.map +1 -1
  89. package/lib/typescript/src/screens/MediaView/index.d.ts.map +1 -1
  90. package/lib/typescript/src/screens/ResourceCenter/FileResourceRow.d.ts +7 -0
  91. package/lib/typescript/src/screens/ResourceCenter/FileResourceRow.d.ts.map +1 -0
  92. package/lib/typescript/src/screens/ResourceCenter/GridRenderer.d.ts +14 -0
  93. package/lib/typescript/src/screens/ResourceCenter/GridRenderer.d.ts.map +1 -0
  94. package/lib/typescript/src/screens/ResourceCenter/GridThumbnail.d.ts +10 -0
  95. package/lib/typescript/src/screens/ResourceCenter/GridThumbnail.d.ts.map +1 -0
  96. package/lib/typescript/src/screens/ResourceCenter/LinkResourceRow.d.ts +8 -0
  97. package/lib/typescript/src/screens/ResourceCenter/LinkResourceRow.d.ts.map +1 -0
  98. package/lib/typescript/src/screens/ResourceCenter/ListRenderer.d.ts +16 -0
  99. package/lib/typescript/src/screens/ResourceCenter/ListRenderer.d.ts.map +1 -0
  100. package/lib/typescript/src/screens/ResourceCenter/ResourceCenterTabBar.d.ts +3 -0
  101. package/lib/typescript/src/screens/ResourceCenter/ResourceCenterTabBar.d.ts.map +1 -0
  102. package/lib/typescript/src/screens/ResourceCenter/ResourceCenterTabScene.d.ts +16 -0
  103. package/lib/typescript/src/screens/ResourceCenter/ResourceCenterTabScene.d.ts.map +1 -0
  104. package/lib/typescript/src/screens/ResourceCenter/ResourceEmptyState.d.ts +6 -0
  105. package/lib/typescript/src/screens/ResourceCenter/ResourceEmptyState.d.ts.map +1 -0
  106. package/lib/typescript/src/screens/ResourceCenter/index.d.ts +4 -0
  107. package/lib/typescript/src/screens/ResourceCenter/index.d.ts.map +1 -0
  108. package/lib/typescript/src/screens/ResourceCenter/types.d.ts +28 -0
  109. package/lib/typescript/src/screens/ResourceCenter/types.d.ts.map +1 -0
  110. package/lib/typescript/src/screens/chat-detail/BotMenuSheet.d.ts +7 -0
  111. package/lib/typescript/src/screens/chat-detail/BotMenuSheet.d.ts.map +1 -0
  112. package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts.map +1 -1
  113. package/lib/typescript/src/screens/chat-detail/components/ChatMessageInput.d.ts +2 -1
  114. package/lib/typescript/src/screens/chat-detail/components/ChatMessageInput.d.ts.map +1 -1
  115. package/lib/typescript/src/services/attachmentOrchestrator.d.ts.map +1 -1
  116. package/lib/typescript/src/services/endpoints.d.ts +1 -0
  117. package/lib/typescript/src/services/endpoints.d.ts.map +1 -1
  118. package/lib/typescript/src/services/resourceCenter.d.ts +9 -0
  119. package/lib/typescript/src/services/resourceCenter.d.ts.map +1 -0
  120. package/lib/typescript/src/translation/resources/i18n.d.ts.map +1 -1
  121. package/lib/typescript/src/types/attachment.d.ts +2 -0
  122. package/lib/typescript/src/types/attachment.d.ts.map +1 -1
  123. package/lib/typescript/src/types/chat.d.ts +60 -1
  124. package/lib/typescript/src/types/chat.d.ts.map +1 -1
  125. package/lib/typescript/src/utils/url.d.ts +7 -0
  126. package/lib/typescript/src/utils/url.d.ts.map +1 -1
  127. package/package.json +1 -1
  128. package/src/assets/images/index.ts +1 -0
  129. package/src/assets/images/resource-center-empty-box.png +0 -0
  130. package/src/components/MediaViewerModal.tsx +8 -3
  131. package/src/components/MergedImageGrid.tsx +1 -0
  132. package/src/config/api-endpoints.ts +2 -0
  133. package/src/context/ChatDetailContext.tsx +1 -0
  134. package/src/core/notification/pushToken.ts +7 -7
  135. package/src/hooks/flows/useFlow.ts +14 -0
  136. package/src/hooks/query-keys.ts +10 -0
  137. package/src/hooks/resourceCenter/useThreadResources.ts +124 -0
  138. package/src/hooks/useImageAttachment.ts +1 -2
  139. package/src/hooks/useMediaViewer.ts +21 -7
  140. package/src/hooks/useSendAttachment.ts +4 -1
  141. package/src/index.tsx +2 -0
  142. package/src/screens/MediaView/index.tsx +117 -54
  143. package/src/screens/ResourceCenter/FileResourceRow.tsx +99 -0
  144. package/src/screens/ResourceCenter/GridRenderer.tsx +104 -0
  145. package/src/screens/ResourceCenter/GridThumbnail.tsx +98 -0
  146. package/src/screens/ResourceCenter/LinkResourceRow.tsx +96 -0
  147. package/src/screens/ResourceCenter/ListRenderer.tsx +81 -0
  148. package/src/screens/ResourceCenter/ResourceCenterTabBar.tsx +89 -0
  149. package/src/screens/ResourceCenter/ResourceCenterTabScene.tsx +142 -0
  150. package/src/screens/ResourceCenter/ResourceEmptyState.tsx +39 -0
  151. package/src/screens/ResourceCenter/index.tsx +129 -0
  152. package/src/screens/ResourceCenter/types.ts +56 -0
  153. package/src/screens/chat-detail/BotMenuSheet.tsx +94 -0
  154. package/src/screens/chat-detail/ChatComposer.tsx +100 -3
  155. package/src/screens/chat-detail/components/ChatMessageInput.tsx +11 -7
  156. package/src/services/attachmentOrchestrator.ts +15 -2
  157. package/src/services/endpoints.ts +2 -0
  158. package/src/services/resourceCenter.ts +137 -0
  159. package/src/translation/resources/i18n.ts +13 -0
  160. package/src/types/attachment.ts +3 -0
  161. package/src/types/chat.ts +75 -0
  162. package/src/utils/url.ts +11 -0
@@ -0,0 +1,137 @@
1
+ import { apiInstance } from './apis';
2
+ import { getApiEndpoint } from '../config/api-endpoints';
3
+ import { extractDomain } from '../utils/url';
4
+ import type { BaseResponse } from '../types/auth';
5
+ import {
6
+ DThreadResourceContentType,
7
+ type DThreadResource,
8
+ type DThreadResourceListResponse,
9
+ type DThreadResourceRawRow,
10
+ type DThreadResourceRequest,
11
+ } from '../types/chat';
12
+
13
+ interface RawPicture {
14
+ url?: string;
15
+ }
16
+
17
+ interface RawPictureContent {
18
+ bigPicture?: RawPicture;
19
+ snapshotPicture?: RawPicture;
20
+ }
21
+
22
+ interface RawUrlTextEx {
23
+ urlMetadata?: {
24
+ title?: string;
25
+ description?: string;
26
+ image?: string;
27
+ url?: string;
28
+ };
29
+ }
30
+
31
+ // Confirmed real: `ex.urlMetadata` is sometimes absent entirely (link preview never
32
+ // resolved by BE) — `content.urls[0]` (the raw URL from the message text) is the
33
+ // fallback so the row still has something to display/open.
34
+ interface RawUrlTextContent {
35
+ urls?: string[];
36
+ }
37
+
38
+ // FILE/VIDEO shapes verified 2026-07-26 against a real staging response (a 1-1
39
+ // thread, recvID = peer userID) — matched the originally-assumed OpenIM
40
+ // `fileElem`/`videoElem`-style field names exactly.
41
+ interface RawFileContent {
42
+ fileName?: string;
43
+ fileSize?: number;
44
+ fileType?: string;
45
+ sourceUrl?: string;
46
+ }
47
+
48
+ interface RawVideoContent {
49
+ videoUrl?: string;
50
+ snapshotUrl?: string;
51
+ duration?: number;
52
+ }
53
+
54
+ const safeJsonParse = <T>(raw: string | undefined): T | undefined => {
55
+ if (!raw) return undefined;
56
+ try {
57
+ return JSON.parse(raw) as T;
58
+ } catch {
59
+ return undefined;
60
+ }
61
+ };
62
+
63
+ const parseThreadResourceItem = (
64
+ row: DThreadResourceRawRow
65
+ ): DThreadResource => {
66
+ const { chatLog } = row;
67
+ const contentType = chatLog.contentType as DThreadResourceContentType;
68
+ const base: DThreadResource = {
69
+ id: chatLog.clientMsgID,
70
+ contentType,
71
+ isRevoked: row.isRevoked,
72
+ createdAt: chatLog.sendTime,
73
+ };
74
+
75
+ switch (contentType) {
76
+ case DThreadResourceContentType.IMAGE: {
77
+ const content = safeJsonParse<RawPictureContent>(chatLog.content);
78
+ return {
79
+ ...base,
80
+ sourceUrl: content?.bigPicture?.url,
81
+ thumbnailUrl: content?.snapshotPicture?.url,
82
+ };
83
+ }
84
+ case DThreadResourceContentType.VIDEO: {
85
+ const content = safeJsonParse<RawVideoContent>(chatLog.content);
86
+ return {
87
+ ...base,
88
+ sourceUrl: content?.videoUrl,
89
+ thumbnailUrl: content?.snapshotUrl,
90
+ duration: content?.duration,
91
+ };
92
+ }
93
+ case DThreadResourceContentType.FILE: {
94
+ const content = safeJsonParse<RawFileContent>(chatLog.content);
95
+ return {
96
+ ...base,
97
+ sourceUrl: content?.sourceUrl,
98
+ fileName: content?.fileName,
99
+ fileSize: content?.fileSize,
100
+ mimeType: content?.fileType,
101
+ };
102
+ }
103
+ case DThreadResourceContentType.URL_TEXT: {
104
+ const ex = safeJsonParse<RawUrlTextEx>(chatLog.ex);
105
+ const content = safeJsonParse<RawUrlTextContent>(chatLog.content);
106
+ const metadata = ex?.urlMetadata;
107
+ const linkUrl = metadata?.url ?? content?.urls?.[0];
108
+ return {
109
+ ...base,
110
+ linkUrl,
111
+ linkTitle: metadata?.title,
112
+ linkDescription: metadata?.description,
113
+ linkImageUrl: metadata?.image,
114
+ linkDomain: linkUrl ? extractDomain(linkUrl) : undefined,
115
+ };
116
+ }
117
+ default:
118
+ return base;
119
+ }
120
+ };
121
+
122
+ export namespace ResourceCenterService {
123
+ export const fetchThreadResources = async (
124
+ req: DThreadResourceRequest
125
+ ): Promise<{ items: DThreadResource[]; page: number; hasMore: boolean }> => {
126
+ const res = await apiInstance?.post<
127
+ BaseResponse<DThreadResourceListResponse>
128
+ >(getApiEndpoint('messagesResources'), req);
129
+ const rows = res?.data?.data ?? [];
130
+ const items = rows.map(parseThreadResourceItem);
131
+ // `pageable.totalPages`/`totalElements` come back as `0` on this endpoint even
132
+ // when rows are present (confirmed against staging) — unreliable, don't use it.
133
+ // Item-count-vs-pageSize is the only trustworthy `hasMore` signal available.
134
+ const hasMore = items.length >= req.pageSize;
135
+ return { items, page: req.page, hasMore };
136
+ };
137
+ }
@@ -93,6 +93,19 @@ const i18nKey: Record<string, string> = {
93
93
  'yourself': 'chính mình',
94
94
  'you': 'Bạn',
95
95
  'recent': 'Gần đây',
96
+ 'chat:resource_center_title': 'Tập tin phương tiện',
97
+ 'chat:resource_center_tab_image': 'Ảnh',
98
+ 'chat:resource_center_tab_video': 'Video',
99
+ 'chat:resource_center_tab_file': 'Tập tin',
100
+ 'chat:resource_center_tab_link': 'Liên kết',
101
+ 'chat:resource_center_empty_generic': 'Không tìm thấy tập tin nào',
102
+ 'chat:resource_center_empty_link': 'Không tìm thấy liên kết nào',
103
+ 'chat:bot_menu_title': 'Gợi ý',
104
+ 'chat:end_support_session_title': 'Kết thúc phiên hỗ trợ',
105
+ 'chat:end_support_session_desc':
106
+ 'Bạn đang trong phiên hỗ trợ với hỗ trợ viên, bạn có muốn <bold>kết thúc phiên hỗ trợ</bold> ngay bây giờ?',
107
+ 'chat:end_support_session_primary': 'Kết thúc',
108
+ 'chat:end_support_session_secondary': 'Tiếp tục',
96
109
  };
97
110
 
98
111
  export default {
@@ -1,3 +1,5 @@
1
+ import type { DChatApplicationType } from './chat';
2
+
1
3
  export type AttachmentType = 'image' | 'video' | 'file' | 'audio' | 'document';
2
4
 
3
5
  export interface BaseAttachment {
@@ -70,6 +72,7 @@ export type BatchStrategy = 'individual' | 'merged' | 'grouped';
70
72
 
71
73
  export interface AttachmentSendOptions {
72
74
  strategy: BatchStrategy;
75
+ applicationType?: DChatApplicationType;
73
76
  includeCaption?: string;
74
77
  uploadConcurrency?: number;
75
78
  retryFailedUploads?: boolean;
package/src/types/chat.ts CHANGED
@@ -8,6 +8,7 @@ import type {
8
8
  DFlowContext,
9
9
  IConversationFlow,
10
10
  IConversationFlowStep,
11
+ InputButtonItem,
11
12
  IRunConversationFlowPayload,
12
13
  } from './flows';
13
14
  import type { GetOpenIMTokenResponse } from './auth';
@@ -209,6 +210,7 @@ export interface ChatDetailContextType {
209
210
  conversation?: ConversationItem;
210
211
  quotedMessage?: MessageItem;
211
212
  setQuotedMessage: (item?: MessageItem) => void;
213
+ conversationFlowMainMenu?: InputButtonItem[];
212
214
  }
213
215
 
214
216
  export interface ChatDetailProviderProps extends PropsWithChildren {}
@@ -242,3 +244,76 @@ export type CustomElemOrderData = {
242
244
  export type CustomMessagePayload =
243
245
  | { type: typeof CustomMessageType.Order; data: CustomElemOrderData }
244
246
  | { type: typeof CustomMessageType.Product; data: Record<string, unknown> };
247
+
248
+ export enum DThreadResourceContentType {
249
+ IMAGE = 102,
250
+ VIDEO = 104,
251
+ FILE = 105,
252
+ URL_TEXT = 160,
253
+ }
254
+
255
+ export interface DThreadResourceRequest {
256
+ // ⚠️ U4c: confirmed working for BOTH shapes — a bot/group thread using the
257
+ // groupID-shaped value (`bot_<uuid>_<uuid>`, chatLog.groupID echoes it back,
258
+ // chatLog.recvID empty), AND a plain 1-1 thread using the peer's userID
259
+ // (matches this repo's own `getReceiverId()` convention exactly — chatLog.recvID
260
+ // echoes it back, chatLog.groupID empty). U4c fully resolved.
261
+ recvID: string;
262
+ contentType: DThreadResourceContentType;
263
+ page: number;
264
+ pageSize: number;
265
+ applicationType: DChatApplicationType;
266
+ // Added by BE 2026-07-28 — required. `GROUP` for group/bot threads (matches
267
+ // whichever branch resolved `recvID` to a `groupID`), `SINGLE` for 1-1
268
+ // threads (`recvID` resolved to the peer's `userID`). NOTE: BE's literal
269
+ // value here is `SINGLE`, not `USER` — do not confuse with
270
+ // `DConversationPeer.type`'s unrelated `'USER' | 'GROUP'` union above.
271
+ chatType: 'SINGLE' | 'GROUP';
272
+ }
273
+
274
+ /**
275
+ * Fully verified 2026-07-26 against `POST chat-service/v1/app/messages/resources`
276
+ * (staging) for all 4 content types (IMAGE/VIDEO/FILE/URL_TEXT) — see
277
+ * `ResourceCenterService.parseThreadResourceItem` for the exact raw-row → this-shape
278
+ * mapping. All field names matched the original OpenIM-elem-based assumptions exactly.
279
+ */
280
+ export interface DThreadResource {
281
+ id: string; // chatLog.clientMsgID
282
+ contentType: DThreadResourceContentType;
283
+ isRevoked: boolean; // BE does NOT pre-filter revoked items, client must (U5, confirmed)
284
+ sourceUrl?: string; // IMAGE: content.bigPicture.url · VIDEO: content.videoUrl · FILE: content.sourceUrl
285
+ thumbnailUrl?: string; // IMAGE: content.snapshotPicture.url (640x640 resized) · VIDEO: content.snapshotUrl
286
+ fileName?: string; // FILE: content.fileName
287
+ fileSize?: number; // FILE: content.fileSize
288
+ mimeType?: string; // FILE: content.fileType
289
+ linkUrl?: string; // URL_TEXT: ex.urlMetadata.url
290
+ linkTitle?: string; // URL_TEXT: ex.urlMetadata.title
291
+ linkDomain?: string; // URL_TEXT: NOT provided by BE — derived client-side via extractDomain()
292
+ linkImageUrl?: string; // URL_TEXT: ex.urlMetadata.image
293
+ linkDescription?: string; // URL_TEXT: ex.urlMetadata.description (resolves U8)
294
+ duration?: number; // VIDEO: content.duration (milliseconds)
295
+ createdAt?: number; // chatLog.sendTime (ms)
296
+ }
297
+
298
+ /**
299
+ * `BaseResponse<DThreadResourceListResponse>.data` is a plain array of raw rows
300
+ * (`{ chatLog, isRevoked, tag, displayTag }`), NOT `{ items: [...] }` — corrected
301
+ * after fetching a real response (initial assumption was wrong).
302
+ */
303
+ export type DThreadResourceListResponse = DThreadResourceRawRow[];
304
+
305
+ export interface DThreadResourceRawRow {
306
+ chatLog: {
307
+ clientMsgID: string;
308
+ serverMsgID: string;
309
+ contentType: number;
310
+ content: string; // JSON string — shape depends on contentType, parse per-type
311
+ ex: string; // JSON string — e.g. `{ applicationType, urlMetadata? }` for URL_TEXT
312
+ sendTime: number;
313
+ createTime: number;
314
+ isRevoked: boolean;
315
+ };
316
+ isRevoked: boolean;
317
+ tag: unknown;
318
+ displayTag: unknown;
319
+ }
package/src/utils/url.ts CHANGED
@@ -39,3 +39,14 @@ export function normalizeUrl(url: string) {
39
39
  }
40
40
  return url;
41
41
  }
42
+
43
+ /**
44
+ * The resource-center API's `urlMetadata` doesn't include a separate domain field
45
+ * (confirmed against a real staging response — only `title`/`description`/`image`/
46
+ * `url`) — derive it from the full URL instead of relying on `new URL()` (avoids a
47
+ * dependency on a global `URL` polyfill being present on every RN engine target).
48
+ */
49
+ export function extractDomain(url: string): string {
50
+ const match = url.match(/^(?:https?:\/\/)?(?:www\.)?([^/?#]+)/i);
51
+ return match?.[1] ?? url;
52
+ }