@droppii-org/chat-mobile 0.2.51 → 0.2.52

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 (134) 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/core/notification/pushToken.js +7 -7
  11. package/lib/module/core/notification/pushToken.js.map +1 -1
  12. package/lib/module/hooks/query-keys.js +4 -0
  13. package/lib/module/hooks/query-keys.js.map +1 -1
  14. package/lib/module/hooks/resourceCenter/useThreadResources.js +116 -0
  15. package/lib/module/hooks/resourceCenter/useThreadResources.js.map +1 -0
  16. package/lib/module/hooks/useImageAttachment.js +0 -2
  17. package/lib/module/hooks/useImageAttachment.js.map +1 -1
  18. package/lib/module/hooks/useMediaViewer.js +4 -1
  19. package/lib/module/hooks/useMediaViewer.js.map +1 -1
  20. package/lib/module/hooks/useSendAttachment.js +4 -1
  21. package/lib/module/hooks/useSendAttachment.js.map +1 -1
  22. package/lib/module/index.js +1 -0
  23. package/lib/module/index.js.map +1 -1
  24. package/lib/module/screens/MediaView/index.js +104 -42
  25. package/lib/module/screens/MediaView/index.js.map +1 -1
  26. package/lib/module/screens/ResourceCenter/FileResourceRow.js +80 -0
  27. package/lib/module/screens/ResourceCenter/FileResourceRow.js.map +1 -0
  28. package/lib/module/screens/ResourceCenter/GridRenderer.js +83 -0
  29. package/lib/module/screens/ResourceCenter/GridRenderer.js.map +1 -0
  30. package/lib/module/screens/ResourceCenter/GridThumbnail.js +97 -0
  31. package/lib/module/screens/ResourceCenter/GridThumbnail.js.map +1 -0
  32. package/lib/module/screens/ResourceCenter/LinkResourceRow.js +81 -0
  33. package/lib/module/screens/ResourceCenter/LinkResourceRow.js.map +1 -0
  34. package/lib/module/screens/ResourceCenter/ListRenderer.js +60 -0
  35. package/lib/module/screens/ResourceCenter/ListRenderer.js.map +1 -0
  36. package/lib/module/screens/ResourceCenter/ResourceCenterTabBar.js +75 -0
  37. package/lib/module/screens/ResourceCenter/ResourceCenterTabBar.js.map +1 -0
  38. package/lib/module/screens/ResourceCenter/ResourceCenterTabScene.js +122 -0
  39. package/lib/module/screens/ResourceCenter/ResourceCenterTabScene.js.map +1 -0
  40. package/lib/module/screens/ResourceCenter/ResourceEmptyState.js +36 -0
  41. package/lib/module/screens/ResourceCenter/ResourceEmptyState.js.map +1 -0
  42. package/lib/module/screens/ResourceCenter/index.js +104 -0
  43. package/lib/module/screens/ResourceCenter/index.js.map +1 -0
  44. package/lib/module/screens/ResourceCenter/types.js +25 -0
  45. package/lib/module/screens/ResourceCenter/types.js.map +1 -0
  46. package/lib/module/services/attachmentOrchestrator.js +19 -4
  47. package/lib/module/services/attachmentOrchestrator.js.map +1 -1
  48. package/lib/module/services/resourceCenter.js +102 -0
  49. package/lib/module/services/resourceCenter.js.map +1 -0
  50. package/lib/module/translation/resources/i18n.js +8 -1
  51. package/lib/module/translation/resources/i18n.js.map +1 -1
  52. package/lib/module/types/attachment.js +2 -0
  53. package/lib/module/types/chat.js +20 -0
  54. package/lib/module/types/chat.js.map +1 -1
  55. package/lib/module/utils/url.js +11 -0
  56. package/lib/module/utils/url.js.map +1 -1
  57. package/lib/typescript/src/assets/images/index.d.ts +1 -0
  58. package/lib/typescript/src/assets/images/index.d.ts.map +1 -1
  59. package/lib/typescript/src/components/MediaViewerModal.d.ts +3 -1
  60. package/lib/typescript/src/components/MediaViewerModal.d.ts.map +1 -1
  61. package/lib/typescript/src/components/MergedImageGrid.d.ts.map +1 -1
  62. package/lib/typescript/src/config/api-endpoints.d.ts +1 -0
  63. package/lib/typescript/src/config/api-endpoints.d.ts.map +1 -1
  64. package/lib/typescript/src/hooks/query-keys.d.ts +4 -0
  65. package/lib/typescript/src/hooks/query-keys.d.ts.map +1 -1
  66. package/lib/typescript/src/hooks/resourceCenter/useThreadResources.d.ts +21 -0
  67. package/lib/typescript/src/hooks/resourceCenter/useThreadResources.d.ts.map +1 -0
  68. package/lib/typescript/src/hooks/useImageAttachment.d.ts.map +1 -1
  69. package/lib/typescript/src/hooks/useMediaViewer.d.ts +5 -1
  70. package/lib/typescript/src/hooks/useMediaViewer.d.ts.map +1 -1
  71. package/lib/typescript/src/hooks/useSendAttachment.d.ts.map +1 -1
  72. package/lib/typescript/src/index.d.ts +2 -0
  73. package/lib/typescript/src/index.d.ts.map +1 -1
  74. package/lib/typescript/src/screens/MediaView/index.d.ts.map +1 -1
  75. package/lib/typescript/src/screens/ResourceCenter/FileResourceRow.d.ts +7 -0
  76. package/lib/typescript/src/screens/ResourceCenter/FileResourceRow.d.ts.map +1 -0
  77. package/lib/typescript/src/screens/ResourceCenter/GridRenderer.d.ts +14 -0
  78. package/lib/typescript/src/screens/ResourceCenter/GridRenderer.d.ts.map +1 -0
  79. package/lib/typescript/src/screens/ResourceCenter/GridThumbnail.d.ts +10 -0
  80. package/lib/typescript/src/screens/ResourceCenter/GridThumbnail.d.ts.map +1 -0
  81. package/lib/typescript/src/screens/ResourceCenter/LinkResourceRow.d.ts +8 -0
  82. package/lib/typescript/src/screens/ResourceCenter/LinkResourceRow.d.ts.map +1 -0
  83. package/lib/typescript/src/screens/ResourceCenter/ListRenderer.d.ts +16 -0
  84. package/lib/typescript/src/screens/ResourceCenter/ListRenderer.d.ts.map +1 -0
  85. package/lib/typescript/src/screens/ResourceCenter/ResourceCenterTabBar.d.ts +3 -0
  86. package/lib/typescript/src/screens/ResourceCenter/ResourceCenterTabBar.d.ts.map +1 -0
  87. package/lib/typescript/src/screens/ResourceCenter/ResourceCenterTabScene.d.ts +16 -0
  88. package/lib/typescript/src/screens/ResourceCenter/ResourceCenterTabScene.d.ts.map +1 -0
  89. package/lib/typescript/src/screens/ResourceCenter/ResourceEmptyState.d.ts +6 -0
  90. package/lib/typescript/src/screens/ResourceCenter/ResourceEmptyState.d.ts.map +1 -0
  91. package/lib/typescript/src/screens/ResourceCenter/index.d.ts +4 -0
  92. package/lib/typescript/src/screens/ResourceCenter/index.d.ts.map +1 -0
  93. package/lib/typescript/src/screens/ResourceCenter/types.d.ts +28 -0
  94. package/lib/typescript/src/screens/ResourceCenter/types.d.ts.map +1 -0
  95. package/lib/typescript/src/services/attachmentOrchestrator.d.ts.map +1 -1
  96. package/lib/typescript/src/services/resourceCenter.d.ts +9 -0
  97. package/lib/typescript/src/services/resourceCenter.d.ts.map +1 -0
  98. package/lib/typescript/src/translation/resources/i18n.d.ts.map +1 -1
  99. package/lib/typescript/src/types/attachment.d.ts +2 -0
  100. package/lib/typescript/src/types/attachment.d.ts.map +1 -1
  101. package/lib/typescript/src/types/chat.d.ts +58 -0
  102. package/lib/typescript/src/types/chat.d.ts.map +1 -1
  103. package/lib/typescript/src/utils/url.d.ts +7 -0
  104. package/lib/typescript/src/utils/url.d.ts.map +1 -1
  105. package/package.json +1 -1
  106. package/src/assets/images/index.ts +1 -0
  107. package/src/assets/images/resource-center-empty-box.png +0 -0
  108. package/src/components/MediaViewerModal.tsx +8 -3
  109. package/src/components/MergedImageGrid.tsx +1 -0
  110. package/src/config/api-endpoints.ts +2 -0
  111. package/src/core/notification/pushToken.ts +7 -7
  112. package/src/hooks/query-keys.ts +6 -0
  113. package/src/hooks/resourceCenter/useThreadResources.ts +124 -0
  114. package/src/hooks/useImageAttachment.ts +1 -2
  115. package/src/hooks/useMediaViewer.ts +21 -7
  116. package/src/hooks/useSendAttachment.ts +4 -1
  117. package/src/index.tsx +2 -0
  118. package/src/screens/MediaView/index.tsx +117 -54
  119. package/src/screens/ResourceCenter/FileResourceRow.tsx +99 -0
  120. package/src/screens/ResourceCenter/GridRenderer.tsx +104 -0
  121. package/src/screens/ResourceCenter/GridThumbnail.tsx +98 -0
  122. package/src/screens/ResourceCenter/LinkResourceRow.tsx +96 -0
  123. package/src/screens/ResourceCenter/ListRenderer.tsx +81 -0
  124. package/src/screens/ResourceCenter/ResourceCenterTabBar.tsx +89 -0
  125. package/src/screens/ResourceCenter/ResourceCenterTabScene.tsx +142 -0
  126. package/src/screens/ResourceCenter/ResourceEmptyState.tsx +39 -0
  127. package/src/screens/ResourceCenter/index.tsx +129 -0
  128. package/src/screens/ResourceCenter/types.ts +56 -0
  129. package/src/services/attachmentOrchestrator.ts +15 -2
  130. package/src/services/resourceCenter.ts +137 -0
  131. package/src/translation/resources/i18n.ts +7 -0
  132. package/src/types/attachment.ts +3 -0
  133. package/src/types/chat.ts +73 -0
  134. 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,13 @@ 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',
96
103
  };
97
104
 
98
105
  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
@@ -242,3 +242,76 @@ export type CustomElemOrderData = {
242
242
  export type CustomMessagePayload =
243
243
  | { type: typeof CustomMessageType.Order; data: CustomElemOrderData }
244
244
  | { type: typeof CustomMessageType.Product; data: Record<string, unknown> };
245
+
246
+ export enum DThreadResourceContentType {
247
+ IMAGE = 102,
248
+ VIDEO = 104,
249
+ FILE = 105,
250
+ URL_TEXT = 160,
251
+ }
252
+
253
+ export interface DThreadResourceRequest {
254
+ // ⚠️ U4c: confirmed working for BOTH shapes — a bot/group thread using the
255
+ // groupID-shaped value (`bot_<uuid>_<uuid>`, chatLog.groupID echoes it back,
256
+ // chatLog.recvID empty), AND a plain 1-1 thread using the peer's userID
257
+ // (matches this repo's own `getReceiverId()` convention exactly — chatLog.recvID
258
+ // echoes it back, chatLog.groupID empty). U4c fully resolved.
259
+ recvID: string;
260
+ contentType: DThreadResourceContentType;
261
+ page: number;
262
+ pageSize: number;
263
+ applicationType: DChatApplicationType;
264
+ // Added by BE 2026-07-28 — required. `GROUP` for group/bot threads (matches
265
+ // whichever branch resolved `recvID` to a `groupID`), `SINGLE` for 1-1
266
+ // threads (`recvID` resolved to the peer's `userID`). NOTE: BE's literal
267
+ // value here is `SINGLE`, not `USER` — do not confuse with
268
+ // `DConversationPeer.type`'s unrelated `'USER' | 'GROUP'` union above.
269
+ chatType: 'SINGLE' | 'GROUP';
270
+ }
271
+
272
+ /**
273
+ * Fully verified 2026-07-26 against `POST chat-service/v1/app/messages/resources`
274
+ * (staging) for all 4 content types (IMAGE/VIDEO/FILE/URL_TEXT) — see
275
+ * `ResourceCenterService.parseThreadResourceItem` for the exact raw-row → this-shape
276
+ * mapping. All field names matched the original OpenIM-elem-based assumptions exactly.
277
+ */
278
+ export interface DThreadResource {
279
+ id: string; // chatLog.clientMsgID
280
+ contentType: DThreadResourceContentType;
281
+ isRevoked: boolean; // BE does NOT pre-filter revoked items, client must (U5, confirmed)
282
+ sourceUrl?: string; // IMAGE: content.bigPicture.url · VIDEO: content.videoUrl · FILE: content.sourceUrl
283
+ thumbnailUrl?: string; // IMAGE: content.snapshotPicture.url (640x640 resized) · VIDEO: content.snapshotUrl
284
+ fileName?: string; // FILE: content.fileName
285
+ fileSize?: number; // FILE: content.fileSize
286
+ mimeType?: string; // FILE: content.fileType
287
+ linkUrl?: string; // URL_TEXT: ex.urlMetadata.url
288
+ linkTitle?: string; // URL_TEXT: ex.urlMetadata.title
289
+ linkDomain?: string; // URL_TEXT: NOT provided by BE — derived client-side via extractDomain()
290
+ linkImageUrl?: string; // URL_TEXT: ex.urlMetadata.image
291
+ linkDescription?: string; // URL_TEXT: ex.urlMetadata.description (resolves U8)
292
+ duration?: number; // VIDEO: content.duration (milliseconds)
293
+ createdAt?: number; // chatLog.sendTime (ms)
294
+ }
295
+
296
+ /**
297
+ * `BaseResponse<DThreadResourceListResponse>.data` is a plain array of raw rows
298
+ * (`{ chatLog, isRevoked, tag, displayTag }`), NOT `{ items: [...] }` — corrected
299
+ * after fetching a real response (initial assumption was wrong).
300
+ */
301
+ export type DThreadResourceListResponse = DThreadResourceRawRow[];
302
+
303
+ export interface DThreadResourceRawRow {
304
+ chatLog: {
305
+ clientMsgID: string;
306
+ serverMsgID: string;
307
+ contentType: number;
308
+ content: string; // JSON string — shape depends on contentType, parse per-type
309
+ ex: string; // JSON string — e.g. `{ applicationType, urlMetadata? }` for URL_TEXT
310
+ sendTime: number;
311
+ createTime: number;
312
+ isRevoked: boolean;
313
+ };
314
+ isRevoked: boolean;
315
+ tag: unknown;
316
+ displayTag: unknown;
317
+ }
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
+ }