@repzo/widget-react-native 1.0.0 → 1.0.4

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 (153) hide show
  1. package/README.md +167 -74
  2. package/lib/commonjs/api/client.js +3 -1
  3. package/lib/commonjs/api/client.js.map +1 -1
  4. package/lib/commonjs/api/realtime.js +2 -0
  5. package/lib/commonjs/api/realtime.js.map +1 -1
  6. package/lib/commonjs/attachments/pickers.js +493 -0
  7. package/lib/commonjs/attachments/pickers.js.map +1 -0
  8. package/lib/commonjs/i18n.js +45 -0
  9. package/lib/commonjs/i18n.js.map +1 -1
  10. package/lib/commonjs/index.js.map +1 -1
  11. package/lib/commonjs/locales.js +21 -0
  12. package/lib/commonjs/locales.js.map +1 -1
  13. package/lib/commonjs/state/widget-store.js +256 -18
  14. package/lib/commonjs/state/widget-store.js.map +1 -1
  15. package/lib/commonjs/types.js +16 -0
  16. package/lib/commonjs/types.js.map +1 -1
  17. package/lib/commonjs/ui/WidgetProvider.js +70 -10
  18. package/lib/commonjs/ui/WidgetProvider.js.map +1 -1
  19. package/lib/commonjs/ui/components/AttachmentChips.js +135 -0
  20. package/lib/commonjs/ui/components/AttachmentChips.js.map +1 -0
  21. package/lib/commonjs/ui/components/AttachmentMenu.js +125 -0
  22. package/lib/commonjs/ui/components/AttachmentMenu.js.map +1 -0
  23. package/lib/commonjs/ui/components/ImageViewer.js +181 -0
  24. package/lib/commonjs/ui/components/ImageViewer.js.map +1 -0
  25. package/lib/commonjs/ui/components/InAppNotification.js +210 -0
  26. package/lib/commonjs/ui/components/InAppNotification.js.map +1 -0
  27. package/lib/commonjs/ui/components/TeamAvatars.js +63 -0
  28. package/lib/commonjs/ui/components/TeamAvatars.js.map +1 -0
  29. package/lib/commonjs/ui/components/icons.js +50 -0
  30. package/lib/commonjs/ui/components/icons.js.map +1 -1
  31. package/lib/commonjs/ui/screens/chat/ChatView.js +303 -24
  32. package/lib/commonjs/ui/screens/chat/ChatView.js.map +1 -1
  33. package/lib/commonjs/ui/screens/chat/ChatView.styles.js +70 -0
  34. package/lib/commonjs/ui/screens/chat/ChatView.styles.js.map +1 -1
  35. package/lib/module/api/client.js +3 -1
  36. package/lib/module/api/client.js.map +1 -1
  37. package/lib/module/api/realtime.js +2 -0
  38. package/lib/module/api/realtime.js.map +1 -1
  39. package/lib/module/attachments/pickers.js +479 -0
  40. package/lib/module/attachments/pickers.js.map +1 -0
  41. package/lib/module/i18n.js +44 -0
  42. package/lib/module/i18n.js.map +1 -1
  43. package/lib/module/index.js.map +1 -1
  44. package/lib/module/locales.js +21 -0
  45. package/lib/module/locales.js.map +1 -1
  46. package/lib/module/state/widget-store.js +256 -18
  47. package/lib/module/state/widget-store.js.map +1 -1
  48. package/lib/module/types.js +16 -0
  49. package/lib/module/types.js.map +1 -1
  50. package/lib/module/ui/WidgetProvider.js +71 -11
  51. package/lib/module/ui/WidgetProvider.js.map +1 -1
  52. package/lib/module/ui/components/AttachmentChips.js +131 -0
  53. package/lib/module/ui/components/AttachmentChips.js.map +1 -0
  54. package/lib/module/ui/components/AttachmentMenu.js +120 -0
  55. package/lib/module/ui/components/AttachmentMenu.js.map +1 -0
  56. package/lib/module/ui/components/ImageViewer.js +177 -0
  57. package/lib/module/ui/components/ImageViewer.js.map +1 -0
  58. package/lib/module/ui/components/InAppNotification.js +208 -0
  59. package/lib/module/ui/components/InAppNotification.js.map +1 -0
  60. package/lib/module/ui/components/TeamAvatars.js +59 -0
  61. package/lib/module/ui/components/TeamAvatars.js.map +1 -0
  62. package/lib/module/ui/components/icons.js +49 -0
  63. package/lib/module/ui/components/icons.js.map +1 -1
  64. package/lib/module/ui/screens/chat/ChatView.js +306 -27
  65. package/lib/module/ui/screens/chat/ChatView.js.map +1 -1
  66. package/lib/module/ui/screens/chat/ChatView.styles.js +70 -0
  67. package/lib/module/ui/screens/chat/ChatView.styles.js.map +1 -1
  68. package/lib/typescript/commonjs/api/client.d.ts +8 -2
  69. package/lib/typescript/commonjs/api/client.d.ts.map +1 -1
  70. package/lib/typescript/commonjs/api/realtime.d.ts.map +1 -1
  71. package/lib/typescript/commonjs/attachments/pickers.d.ts +81 -0
  72. package/lib/typescript/commonjs/attachments/pickers.d.ts.map +1 -0
  73. package/lib/typescript/commonjs/i18n.d.ts +23 -0
  74. package/lib/typescript/commonjs/i18n.d.ts.map +1 -1
  75. package/lib/typescript/commonjs/index.d.ts +1 -1
  76. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  77. package/lib/typescript/commonjs/locales.d.ts.map +1 -1
  78. package/lib/typescript/commonjs/state/widget-store.d.ts +40 -2
  79. package/lib/typescript/commonjs/state/widget-store.d.ts.map +1 -1
  80. package/lib/typescript/commonjs/types.d.ts +88 -1
  81. package/lib/typescript/commonjs/types.d.ts.map +1 -1
  82. package/lib/typescript/commonjs/ui/WidgetProvider.d.ts.map +1 -1
  83. package/lib/typescript/commonjs/ui/components/AttachmentChips.d.ts +16 -0
  84. package/lib/typescript/commonjs/ui/components/AttachmentChips.d.ts.map +1 -0
  85. package/lib/typescript/commonjs/ui/components/AttachmentMenu.d.ts +28 -0
  86. package/lib/typescript/commonjs/ui/components/AttachmentMenu.d.ts.map +1 -0
  87. package/lib/typescript/commonjs/ui/components/ImageViewer.d.ts +23 -0
  88. package/lib/typescript/commonjs/ui/components/ImageViewer.d.ts.map +1 -0
  89. package/lib/typescript/commonjs/ui/components/InAppNotification.d.ts +28 -0
  90. package/lib/typescript/commonjs/ui/components/InAppNotification.d.ts.map +1 -0
  91. package/lib/typescript/commonjs/ui/components/TeamAvatars.d.ts +17 -0
  92. package/lib/typescript/commonjs/ui/components/TeamAvatars.d.ts.map +1 -0
  93. package/lib/typescript/commonjs/ui/components/icons.d.ts +2 -0
  94. package/lib/typescript/commonjs/ui/components/icons.d.ts.map +1 -1
  95. package/lib/typescript/commonjs/ui/screens/chat/ChatView.d.ts +3 -2
  96. package/lib/typescript/commonjs/ui/screens/chat/ChatView.d.ts.map +1 -1
  97. package/lib/typescript/commonjs/ui/screens/chat/ChatView.styles.d.ts +69 -0
  98. package/lib/typescript/commonjs/ui/screens/chat/ChatView.styles.d.ts.map +1 -1
  99. package/lib/typescript/commonjs/ui/screens/chat/ChatView.types.d.ts +41 -2
  100. package/lib/typescript/commonjs/ui/screens/chat/ChatView.types.d.ts.map +1 -1
  101. package/lib/typescript/module/api/client.d.ts +8 -2
  102. package/lib/typescript/module/api/client.d.ts.map +1 -1
  103. package/lib/typescript/module/api/realtime.d.ts.map +1 -1
  104. package/lib/typescript/module/attachments/pickers.d.ts +81 -0
  105. package/lib/typescript/module/attachments/pickers.d.ts.map +1 -0
  106. package/lib/typescript/module/i18n.d.ts +23 -0
  107. package/lib/typescript/module/i18n.d.ts.map +1 -1
  108. package/lib/typescript/module/index.d.ts +1 -1
  109. package/lib/typescript/module/index.d.ts.map +1 -1
  110. package/lib/typescript/module/locales.d.ts.map +1 -1
  111. package/lib/typescript/module/state/widget-store.d.ts +40 -2
  112. package/lib/typescript/module/state/widget-store.d.ts.map +1 -1
  113. package/lib/typescript/module/types.d.ts +88 -1
  114. package/lib/typescript/module/types.d.ts.map +1 -1
  115. package/lib/typescript/module/ui/WidgetProvider.d.ts.map +1 -1
  116. package/lib/typescript/module/ui/components/AttachmentChips.d.ts +16 -0
  117. package/lib/typescript/module/ui/components/AttachmentChips.d.ts.map +1 -0
  118. package/lib/typescript/module/ui/components/AttachmentMenu.d.ts +28 -0
  119. package/lib/typescript/module/ui/components/AttachmentMenu.d.ts.map +1 -0
  120. package/lib/typescript/module/ui/components/ImageViewer.d.ts +23 -0
  121. package/lib/typescript/module/ui/components/ImageViewer.d.ts.map +1 -0
  122. package/lib/typescript/module/ui/components/InAppNotification.d.ts +28 -0
  123. package/lib/typescript/module/ui/components/InAppNotification.d.ts.map +1 -0
  124. package/lib/typescript/module/ui/components/TeamAvatars.d.ts +17 -0
  125. package/lib/typescript/module/ui/components/TeamAvatars.d.ts.map +1 -0
  126. package/lib/typescript/module/ui/components/icons.d.ts +2 -0
  127. package/lib/typescript/module/ui/components/icons.d.ts.map +1 -1
  128. package/lib/typescript/module/ui/screens/chat/ChatView.d.ts +3 -2
  129. package/lib/typescript/module/ui/screens/chat/ChatView.d.ts.map +1 -1
  130. package/lib/typescript/module/ui/screens/chat/ChatView.styles.d.ts +69 -0
  131. package/lib/typescript/module/ui/screens/chat/ChatView.styles.d.ts.map +1 -1
  132. package/lib/typescript/module/ui/screens/chat/ChatView.types.d.ts +41 -2
  133. package/lib/typescript/module/ui/screens/chat/ChatView.types.d.ts.map +1 -1
  134. package/metro.js +17 -32
  135. package/package.json +18 -1
  136. package/src/api/client.ts +5 -3
  137. package/src/api/realtime.ts +2 -0
  138. package/src/attachments/pickers.ts +515 -0
  139. package/src/i18n.ts +48 -0
  140. package/src/index.tsx +4 -0
  141. package/src/locales.ts +21 -0
  142. package/src/state/widget-store.ts +256 -19
  143. package/src/types.ts +95 -0
  144. package/src/ui/WidgetProvider.tsx +90 -9
  145. package/src/ui/components/AttachmentChips.tsx +135 -0
  146. package/src/ui/components/AttachmentMenu.tsx +132 -0
  147. package/src/ui/components/ImageViewer.tsx +178 -0
  148. package/src/ui/components/InAppNotification.tsx +225 -0
  149. package/src/ui/components/TeamAvatars.tsx +58 -0
  150. package/src/ui/components/icons.tsx +37 -0
  151. package/src/ui/screens/chat/ChatView.styles.ts +70 -0
  152. package/src/ui/screens/chat/ChatView.tsx +345 -35
  153. package/src/ui/screens/chat/ChatView.types.ts +51 -2
@@ -19,6 +19,18 @@ const ar = {
19
19
  emptyConversation: 'أرسل رسالة لبدء المحادثة',
20
20
  messagePlaceholder: 'اكتب رسالة...',
21
21
  attachFile: 'إرفاق ملف',
22
+ attachPhotoLibrary: 'مكتبة الصور',
23
+ attachTakePhoto: 'التقاط صورة',
24
+ attachChooseFile: 'ملف',
25
+ removeAttachment: 'إزالة {name}',
26
+ attachmentTooLarge: 'الملف كبير جدًا (الحد الأقصى {max})',
27
+ attachmentTooMany: 'يمكنك إرفاق {count} ملفات كحد أقصى',
28
+ attachmentsTooLargeTogether: 'المرفقات كبيرة جدًا لإرسالها معًا — أرسل بعضها في رسالة أخرى',
29
+ attachmentFailed: 'تعذر إرفاق هذا الملف',
30
+ imageLoadFailed: 'تعذر تحميل هذه الصورة',
31
+ openImageExternally: 'فتح الصورة',
32
+ cameraPermissionDenied: 'اسمح بالوصول إلى الكاميرا من الإعدادات لالتقاط صورة',
33
+ cancel: 'إلغاء',
22
34
  failedToSend: 'تعذر الإرسال',
23
35
  thinking: 'يكتب',
24
36
  messages: 'الرسائل',
@@ -55,6 +67,11 @@ const ar = {
55
67
  today: 'اليوم',
56
68
  yesterday: 'أمس',
57
69
  conversationUpdated: 'تم تحديث المحادثة',
70
+ messageDeleted: 'تم حذف هذه الرسالة',
71
+ messageSent: 'تم الإرسال',
72
+ messageSeen: 'تمت المشاهدة',
73
+ waitingForTeammate: 'بانتظار أحد أعضاء الفريق',
74
+ joinedConversation: 'انضم إلى المحادثة',
58
75
  procedureSend: 'إرسال التفاصيل',
59
76
  procedureSent: 'تم الإرسال',
60
77
  fieldRequired: '{field} مطلوب',
@@ -63,6 +80,10 @@ const ar = {
63
80
  outboundTitle: 'تحديث جديد',
64
81
  outboundCta: 'اعرف المزيد',
65
82
  dismiss: 'تجاهل',
83
+ notificationReplyFrom: 'رد من {name}',
84
+ notificationNewReply: 'رد جديد',
85
+ notificationAttachment: 'أرسل مرفقًا',
86
+ notificationOpenHint: 'يفتح المحادثة',
66
87
  blockService: 'خدمة',
67
88
  blockProduct: 'منتج',
68
89
  blockChooseTime: 'اختر وقتًا',
@@ -1 +1 @@
1
- {"version":3,"names":["ar","back","loading","loadEarlier","emptyConversation","messagePlaceholder","attachFile","failedToSend","thinking","messages","noMessages","historyHidden","startBelow","preChatTitle","preChatSubtitle","preChatSkip","preChatSubmit","viewPreviousConversations","homeRecentTitle","homeViewAllCount","homeNoConversations","homeStatusOnline","homeStatusAway","unreadBadge","team","home","close","retry","connectionError","connecting","chatUnavailable","identityVerificationError","widgetCrashed","timeJustNow","timeMinutes","timeHours","timeDays","timeMonths","timeYears","today","yesterday","conversationUpdated","procedureSend","procedureSent","fieldRequired","invalidEmail","working","outboundTitle","outboundCta","dismiss","blockService","blockProduct","blockChooseTime","blockProfessional","blockPriceOffer","BUNDLED_TRANSLATIONS","baseLanguage","locale","split","toLowerCase"],"sourceRoot":"../../src","sources":["locales.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAMA,MAAMA,EAAY,GAAG;EACnBC,IAAI,EAAE,MAAM;EACZC,OAAO,EAAE,iBAAiB;EAC1BC,WAAW,EAAE,uBAAuB;EACpCC,iBAAiB,EAAE,0BAA0B;EAC7CC,kBAAkB,EAAE,eAAe;EACnCC,UAAU,EAAE,WAAW;EACvBC,YAAY,EAAE,cAAc;EAC5BC,QAAQ,EAAE,MAAM;EAChBC,QAAQ,EAAE,SAAS;EACnBC,UAAU,EAAE,mBAAmB;EAC/BC,aAAa,EAAE,oBAAoB;EACnCC,UAAU,EAAE,uBAAuB;EACnCC,YAAY,EAAE,aAAa;EAC3BC,eAAe,EAAE,mDAAmD;EACpEC,WAAW,EAAE,MAAM;EACnBC,aAAa,EAAE,eAAe;EAC9BC,yBAAyB,EAAE,uBAAuB;EAClDC,eAAe,EAAE,QAAQ;EACzBC,gBAAgB,EAAE,oBAAoB;EACtCC,mBAAmB,EAAE,oBAAoB;EACzCC,gBAAgB,EAAE,YAAY;EAC9BC,cAAc,EAAE,gBAAgB;EAChCC,WAAW,EAAE,oBAAoB;EACjCC,IAAI,EAAE,QAAQ;EACdC,IAAI,EAAE,UAAU;EAChBC,KAAK,EAAE,OAAO;EACdC,KAAK,EAAE,gBAAgB;EACvBC,eAAe,EAAE,8CAA8C;EAC/DC,UAAU,EAAE,qBAAqB;EACjCC,eAAe,EAAE,4BAA4B;EAC7CC,yBAAyB,EAAE,mDAAmD;EAC9EC,aAAa,EAAE,aAAa;EAC5B;EACAC,WAAW,EAAE,MAAM;EACnBC,WAAW,EAAE,WAAW;EACxBC,SAAS,EAAE,WAAW;EACtBC,QAAQ,EAAE,WAAW;EACrBC,UAAU,EAAE,aAAa;EACzBC,SAAS,EAAE,aAAa;EACxBC,KAAK,EAAE,OAAO;EACdC,SAAS,EAAE,KAAK;EAChBC,mBAAmB,EAAE,mBAAmB;EACxCC,aAAa,EAAE,gBAAgB;EAC/BC,aAAa,EAAE,YAAY;EAC3BC,aAAa,EAAE,eAAe;EAC9BC,YAAY,EAAE,+BAA+B;EAC7CC,OAAO,EAAE,eAAe;EACxBC,aAAa,EAAE,YAAY;EAC3BC,WAAW,EAAE,aAAa;EAC1BC,OAAO,EAAE,OAAO;EAChBC,YAAY,EAAE,MAAM;EACpBC,YAAY,EAAE,MAAM;EACpBC,eAAe,EAAE,YAAY;EAC7BC,iBAAiB,EAAE,QAAQ;EAC3BC,eAAe,EAAE;AACnB,CAAC;AAED,OAAO,MAAMC,oBAA8C,GAAG;EAAEvD;AAAG,CAAC;;AAEpE;AACA,OAAO,SAASwD,YAAYA,CAACC,MAAsB,EAAU;EAC3D,OAAO,CAACA,MAAM,IAAI,EAAE,EAAEC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAEC,WAAW,CAAC,CAAC,IAAI,EAAE;AAC7D","ignoreList":[]}
1
+ {"version":3,"names":["ar","back","loading","loadEarlier","emptyConversation","messagePlaceholder","attachFile","attachPhotoLibrary","attachTakePhoto","attachChooseFile","removeAttachment","attachmentTooLarge","attachmentTooMany","attachmentsTooLargeTogether","attachmentFailed","imageLoadFailed","openImageExternally","cameraPermissionDenied","cancel","failedToSend","thinking","messages","noMessages","historyHidden","startBelow","preChatTitle","preChatSubtitle","preChatSkip","preChatSubmit","viewPreviousConversations","homeRecentTitle","homeViewAllCount","homeNoConversations","homeStatusOnline","homeStatusAway","unreadBadge","team","home","close","retry","connectionError","connecting","chatUnavailable","identityVerificationError","widgetCrashed","timeJustNow","timeMinutes","timeHours","timeDays","timeMonths","timeYears","today","yesterday","conversationUpdated","messageDeleted","messageSent","messageSeen","waitingForTeammate","joinedConversation","procedureSend","procedureSent","fieldRequired","invalidEmail","working","outboundTitle","outboundCta","dismiss","notificationReplyFrom","notificationNewReply","notificationAttachment","notificationOpenHint","blockService","blockProduct","blockChooseTime","blockProfessional","blockPriceOffer","BUNDLED_TRANSLATIONS","baseLanguage","locale","split","toLowerCase"],"sourceRoot":"../../src","sources":["locales.ts"],"mappings":"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","ignoreList":[]}
@@ -17,9 +17,10 @@ import { getDeviceId, rotateDeviceId } from "../device-id.js";
17
17
  import { EventEmitter, RepzoWidgetEvents } from "../events.js";
18
18
  import { PushManager } from "../push/push-notifications.js";
19
19
  import { CONFIG_KEY, LEGACY_CONFIG_KEY, SecureStorage, Storage, UNREAD_KEY, USER_KEY } from "../storage.js";
20
+ import { setAttachmentOptions } from "../attachments/pickers.js";
20
21
  import { DEFAULT_BASE_URL } from "../types.js";
21
22
  import { setOpenUrlHandler } from "../ui/utils/safe-url.js";
22
- const SDK_VERSION = '@repzo/widget-react-native@1.0.0';
23
+ const SDK_VERSION = '@repzo/widget-react-native@1.0.4';
23
24
 
24
25
  /** The platform this session reports — mobile sessions get the Mobile SDKs overrides. */
25
26
  function sessionPlatform() {
@@ -67,7 +68,8 @@ const INITIAL_STATE = {
67
68
  locale: 'en',
68
69
  prefillText: '',
69
70
  outboundMessage: null,
70
- visitorProfile: {}
71
+ visitorProfile: {},
72
+ inAppNotification: null
71
73
  };
72
74
 
73
75
  /** Everything in state that belongs to the signed-in visitor. */
@@ -86,7 +88,8 @@ const USER_SCOPED_STATE = {
86
88
  unreadByConversation: {},
87
89
  prefillText: '',
88
90
  outboundMessage: null,
89
- visitorProfile: {}
91
+ visitorProfile: {},
92
+ inAppNotification: null
90
93
  };
91
94
  export class WidgetStore {
92
95
  client = new WidgetApiClient();
@@ -100,6 +103,12 @@ export class WidgetStore {
100
103
  typingTimer = null;
101
104
  pendingAgentByConversation = {};
102
105
  bootConfig = null;
106
+
107
+ /** `boot({ inAppNotifications })` — the card over the app for new replies. */
108
+ inAppNotificationsEnabled = true;
109
+
110
+ /** The launch card (see showUnreadOnLaunch) is offered once per visitor session. */
111
+ launchCardOffered = false;
103
112
  bootPromise = null;
104
113
 
105
114
  /** Identity key of the in-flight boot — a boot for someone else is not deduped. */
@@ -116,6 +125,8 @@ export class WidgetStore {
116
125
  lastTypingPingAt = 0;
117
126
  visitorTypingActive = false;
118
127
  seenMessageIds = [];
128
+ /** Statuses (e.g. "read") that arrived for an id before a send response revealed it. */
129
+ earlyStatuses = new Map();
119
130
 
120
131
  /**
121
132
  * Identity generation. Bumped whenever boot/login/logout is requested; every
@@ -184,6 +195,9 @@ export class WidgetStore {
184
195
  };
185
196
  };
186
197
  getState = () => this.state;
198
+
199
+ /** Composer identity: explicit opens reset it; a draft receiving its server ID does not. */
200
+ getThreadGeneration = () => this.threadGeneration;
187
201
  setState(patch) {
188
202
  const previous = this.state;
189
203
  const resolved = typeof patch === 'function' ? patch(previous) : patch;
@@ -194,6 +208,13 @@ export class WidgetStore {
194
208
  // The total is always derived from the per-thread map, so no surface can
195
209
  // zero the badge while threads stay unread.
196
210
  next.unreadCount = sumUnread(next.unreadByConversation);
211
+ // The in-app card only lives while the messenger is closed and its thread
212
+ // is unread — opening the widget, reading the thread (here or on another
213
+ // device) or logging out all take it down without each path clearing it.
214
+ const card = next.inAppNotification;
215
+ if (card && (next.isOpen || !next.unreadByConversation[card.conversationId])) {
216
+ next.inAppNotification = null;
217
+ }
197
218
  this.state = next;
198
219
  if (next.unreadCount !== previous.unreadCount) {
199
220
  this.events.emit(RepzoWidgetEvents.UnreadCountDidChange, {
@@ -237,6 +258,10 @@ export class WidgetStore {
237
258
  }
238
259
  const baseUrl = assertSecureBaseUrl(options.baseUrl?.trim() || DEFAULT_BASE_URL);
239
260
  if (options.onOpenUrl !== undefined) setOpenUrlHandler(options.onOpenUrl ?? null);
261
+ if (options.attachments !== undefined) setAttachmentOptions(options.attachments ?? null);
262
+ if (options.inAppNotifications !== undefined) {
263
+ this.inAppNotificationsEnabled = options.inAppNotifications !== false;
264
+ }
240
265
  const sameTarget = this.bootConfig?.baseUrl === baseUrl && this.bootConfig?.channelAccountId === options.channelAccountId;
241
266
  const userKey = options.user ? identityKey(options.user) : null;
242
267
  if (sameTarget && this.bootPromise && (!userKey || userKey === this.bootingUserKey)) {
@@ -441,8 +466,12 @@ export class WidgetStore {
441
466
  if (this.pendingConversationId) {
442
467
  const conversationId = this.pendingConversationId;
443
468
  this.pendingConversationId = null;
469
+ // Launched into a thread (push tap): that is this session's launch, so
470
+ // a later silent re-init must not offer the card either.
471
+ this.launchCardOffered = true;
444
472
  void this.openConversation(conversationId);
445
473
  } else {
474
+ this.showUnreadOnLaunch();
446
475
  // A re-init after a 401 means the stream was down: catch the open
447
476
  // thread up (a no-op when nothing is on screen).
448
477
  void this.refreshOpenThread();
@@ -656,6 +685,7 @@ export class WidgetStore {
656
685
  this.seenMessageIds = [];
657
686
  this.visitorTypingActive = false;
658
687
  this.lastTypingPingAt = 0;
688
+ this.launchCardOffered = false;
659
689
  this.setState({
660
690
  ...USER_SCOPED_STATE
661
691
  });
@@ -794,7 +824,7 @@ export class WidgetStore {
794
824
  }
795
825
  }
796
826
  handleRealtimeEvent(event, data) {
797
- if ((event === 'message.new' || event === 'conversation.event') && this.shouldHoldForDraft(data?.conversationId)) {
827
+ if ((event === 'message.new' || event === 'message.deleted' || event === 'message.status' || event === 'conversation.event') && this.shouldHoldForDraft(data?.conversationId)) {
798
828
  this.draftEvents.push({
799
829
  event,
800
830
  data
@@ -818,13 +848,20 @@ export class WidgetStore {
818
848
  case 'message.new':
819
849
  this.handleNewMessage(data);
820
850
  break;
851
+ case 'message.deleted':
852
+ this.handleDeletedMessage(data);
853
+ break;
854
+ case 'message.status':
855
+ this.handleMessageStatus(data);
856
+ break;
821
857
  case 'conversation.updated':
822
858
  this.rememberConversationAgent(data);
823
859
  this.setState(current => ({
824
860
  conversations: current.conversations.map(conversation => conversation.id === data?.conversationId ? {
825
861
  ...conversation,
826
862
  agentName: data.agentName === undefined ? conversation.agentName : data.agentName,
827
- agentAvatar: data.agentAvatar === undefined ? conversation.agentAvatar : this.client.resolveUrl(data.agentAvatar)
863
+ agentAvatar: data.agentAvatar === undefined ? conversation.agentAvatar : this.client.resolveUrl(data.agentAvatar),
864
+ agentOnline: data.agentOnline === undefined ? conversation.agentOnline : data.agentOnline
828
865
  } : conversation)
829
866
  }));
830
867
  break;
@@ -922,6 +959,50 @@ export class WidgetStore {
922
959
  if (this.seenMessageIds.length > SEEN_MESSAGE_LIMIT) this.seenMessageIds.shift();
923
960
  return false;
924
961
  }
962
+
963
+ /**
964
+ * A teammate deleted a message: the visitor sees "This message was deleted"
965
+ * in its place (they can't delete anything themselves), and the list is
966
+ * refreshed so its preview stops showing the text. The id stays in
967
+ * `seenMessageIds`, so a replayed `message.new` can't restore the content.
968
+ */
969
+ /**
970
+ * A teammate opened the conversation: the visitor's messages are now Seen.
971
+ * The ids are also remembered, because the event can arrive before the send
972
+ * response swaps a pending bubble's temporary id for the server id.
973
+ */
974
+ handleMessageStatus(data) {
975
+ const ids = Array.isArray(data?.messageIds) ? data.messageIds : [];
976
+ const status = typeof data?.status === 'string' ? data.status : undefined;
977
+ if (!status || ids.length === 0) return;
978
+ for (const id of ids) this.earlyStatuses.set(id, status);
979
+ if (this.earlyStatuses.size > SEEN_MESSAGE_LIMIT) {
980
+ this.earlyStatuses.delete(this.earlyStatuses.keys().next().value);
981
+ }
982
+ if (!this.isThreadOnScreen(data?.conversationId)) return;
983
+ this.setState(current => ({
984
+ messages: current.messages.map(message => ids.includes(message.id) ? {
985
+ ...message,
986
+ status
987
+ } : message)
988
+ }));
989
+ }
990
+ handleDeletedMessage(data) {
991
+ const messageId = data?.messageId;
992
+ if (!messageId) return;
993
+ if (!this.seenMessageIds.includes(messageId)) this.isReplayedMessage(messageId);
994
+ if (this.isThreadOnScreen(data?.conversationId)) {
995
+ this.setState(current => ({
996
+ messages: current.messages.map(message => message.id === messageId ? asDeletedPlaceholder(message, new Date().toISOString()) : message)
997
+ }));
998
+ }
999
+ if (this.state.inAppNotification?.messageId === messageId) {
1000
+ this.setState({
1001
+ inAppNotification: null
1002
+ });
1003
+ }
1004
+ void this.refreshConversations();
1005
+ }
925
1006
  handleNewMessage(data) {
926
1007
  if (this.isReplayedMessage(data?.messageId)) return;
927
1008
 
@@ -937,7 +1018,7 @@ export class WidgetStore {
937
1018
  body: data?.body || null,
938
1019
  bodyPlain: data?.bodyPlain || null,
939
1020
  attachments: this.hydrateAttachments(data?.attachments, messageId),
940
- metadata: data?.metadata || null,
1021
+ metadata: this.hydrateMetadata(data?.metadata || null),
941
1022
  status: 'delivered',
942
1023
  createdAt: data?.createdAt || new Date().toISOString()
943
1024
  };
@@ -962,7 +1043,7 @@ export class WidgetStore {
962
1043
  const alreadyPresent = current.messages.some(existing => existing.id === message.id);
963
1044
  return {
964
1045
  conversations,
965
- messages: alreadyPresent ? current.messages : [...current.messages, message],
1046
+ messages: alreadyPresent ? adoptServerAttachments(current.messages, [message]) : [...current.messages, message],
966
1047
  agentTyping: clearsTyping ? false : current.agentTyping
967
1048
  };
968
1049
  }
@@ -975,9 +1056,25 @@ export class WidgetStore {
975
1056
  };
976
1057
  this.persistUnread(unreadByConversation);
977
1058
  countedAsUnread = true;
1059
+ if (!this.inAppNotificationsEnabled || current.isOpen) {
1060
+ return {
1061
+ conversations,
1062
+ unreadByConversation
1063
+ };
1064
+ }
1065
+ // Newest reply wins: one card, not a stack, like the launcher badge.
1066
+ const conversation = conversations.find(item => item.id === conversationId);
978
1067
  return {
979
1068
  conversations,
980
- unreadByConversation
1069
+ unreadByConversation,
1070
+ inAppNotification: {
1071
+ conversationId,
1072
+ messageId,
1073
+ senderName: message.senderName || conversation?.agentName || null,
1074
+ senderAvatar: conversation?.agentAvatar ?? null,
1075
+ preview: (message.bodyPlain ?? '').trim(),
1076
+ hasAttachments: (message.attachments?.length ?? 0) > 0
1077
+ }
981
1078
  };
982
1079
  });
983
1080
 
@@ -1215,6 +1312,44 @@ export class WidgetStore {
1215
1312
  prefillText: prefill
1216
1313
  });
1217
1314
  }
1315
+
1316
+ /**
1317
+ * Replies that arrived while the app was closed get the same card a live
1318
+ * reply would, once per launch — so a visitor without push (not set up on
1319
+ * the channel, or permission denied) still sees them. Only the newest unread
1320
+ * thread; silent re-inits and foreground returns never re-offer it.
1321
+ */
1322
+ showUnreadOnLaunch() {
1323
+ if (this.launchCardOffered || !this.inAppNotificationsEnabled) return;
1324
+ this.launchCardOffered = true;
1325
+ const {
1326
+ isOpen,
1327
+ inAppNotification,
1328
+ conversations,
1329
+ unreadByConversation
1330
+ } = this.state;
1331
+ if (isOpen || inAppNotification) return;
1332
+ const time = conversation => Date.parse(conversation.lastMessageAt ?? conversation.createdAt) || 0;
1333
+ const newest = conversations.filter(conversation => (unreadByConversation[conversation.id] ?? 0) > 0).sort((a, b) => time(b) - time(a))[0];
1334
+ if (!newest) return;
1335
+ this.setState({
1336
+ inAppNotification: {
1337
+ conversationId: newest.id,
1338
+ messageId: `launch-${newest.id}`,
1339
+ senderName: newest.agentName,
1340
+ senderAvatar: newest.agentAvatar,
1341
+ preview: (newest.snippet ?? '').trim(),
1342
+ hasAttachments: false
1343
+ }
1344
+ });
1345
+ }
1346
+
1347
+ /** The visitor swiped the in-app card away; the thread stays unread. */
1348
+ dismissInAppNotification() {
1349
+ if (this.state.inAppNotification) this.setState({
1350
+ inAppNotification: null
1351
+ });
1352
+ }
1218
1353
  dismissOutboundMessage() {
1219
1354
  const outbound = this.state.outboundMessage;
1220
1355
  if (outbound?.id) this.track('widget_outbound_dismissed', {
@@ -1294,9 +1429,10 @@ export class WidgetStore {
1294
1429
  // Live replies and optimistic sends may have arrived since this page
1295
1430
  // was requested. Keep them, preferring the live copy for shared ids.
1296
1431
  const liveIds = new Set(current.messages.map(message => message.id));
1297
- const history = this.hydrateMessages(result.data).filter(message => !liveIds.has(message.id));
1432
+ const page = this.hydrateMessages(result.data);
1433
+ const history = page.filter(message => !liveIds.has(message.id));
1298
1434
  return {
1299
- messages: sortByCreatedAt([...history, ...current.messages]),
1435
+ messages: sortByCreatedAt([...history, ...adoptServerAttachments(current.messages, page)]),
1300
1436
  messagesLoading: false,
1301
1437
  hasMoreMessages: result.meta.hasMore,
1302
1438
  messageCursor: result.meta.nextCursor
@@ -1366,7 +1502,10 @@ export class WidgetStore {
1366
1502
  this.setState(current => {
1367
1503
  const known = new Set(current.messages.map(message => message.id));
1368
1504
  const missing = fresh.filter(message => !known.has(message.id));
1369
- if (missing.length === 0) return {};
1505
+ const upgraded = adoptServerStatuses(adoptServerDeletions(adoptServerAttachments(current.messages, fresh), fresh), fresh);
1506
+ if (missing.length === 0) return upgraded === current.messages ? {} : {
1507
+ messages: upgraded
1508
+ };
1370
1509
  const local = current.messages.filter(isLocalMessage);
1371
1510
  const hasServerMessages = local.length < current.messages.length;
1372
1511
  // No overlap means the gap is longer than a page: restart from the
@@ -1379,7 +1518,7 @@ export class WidgetStore {
1379
1518
  };
1380
1519
  }
1381
1520
  return {
1382
- messages: sortByCreatedAt([...current.messages, ...missing])
1521
+ messages: sortByCreatedAt([...upgraded, ...missing])
1383
1522
  };
1384
1523
  });
1385
1524
  // The server just moved the read cursor — mirror it locally.
@@ -1397,9 +1536,26 @@ export class WidgetStore {
1397
1536
  hydrateMessages(page) {
1398
1537
  return page.map(message => ({
1399
1538
  ...message,
1400
- attachments: this.hydrateAttachments(message.attachments, message.id)
1539
+ attachments: this.hydrateAttachments(message.attachments, message.id),
1540
+ metadata: this.hydrateMetadata(message.metadata)
1401
1541
  })).reverse();
1402
1542
  }
1543
+
1544
+ /** The "joined" line's avatar can be a server-relative media path. */
1545
+ hydrateMetadata(metadata) {
1546
+ if (!metadata || typeof metadata.agentAvatar !== 'string') return metadata ?? null;
1547
+ return {
1548
+ ...metadata,
1549
+ agentAvatar: this.client.resolveUrl(metadata.agentAvatar)
1550
+ };
1551
+ }
1552
+
1553
+ /**
1554
+ * Rejects when the message wasn't sent, so the composer can keep
1555
+ * attachments for a retry. A text-only bubble is marked failed; one with
1556
+ * attachments is removed instead — the composer's chips are the retry, and
1557
+ * keeping both would leave a duplicate after a successful resend.
1558
+ */
1403
1559
  async sendMessage(body, attachments = [], agentUiAction) {
1404
1560
  const trimmed = body.trim();
1405
1561
  if (!trimmed && attachments.length === 0) return;
@@ -1416,7 +1572,16 @@ export class WidgetStore {
1416
1572
  senderName: visitorProfile.name || 'You',
1417
1573
  body: trimmed ? `<p>${trimmed}</p>` : '',
1418
1574
  bodyPlain: trimmed,
1419
- attachments: attachments.length > 0 ? attachments : null,
1575
+ // The local preview, never a data URL: the bubble shows up at once and
1576
+ // state never holds megabytes of base64. Without a local file the
1577
+ // bubble shows the name until the server copy replaces it.
1578
+ attachments: attachments.length > 0 ? attachments.map((attachment, index) => ({
1579
+ id: `${tempId}-${index}`,
1580
+ name: attachment.name,
1581
+ url: attachment.previewUrl && !/^data:/i.test(attachment.previewUrl) ? attachment.previewUrl : '',
1582
+ contentType: attachment.contentType,
1583
+ size: attachment.size
1584
+ })) : null,
1420
1585
  metadata: null,
1421
1586
  status: 'pending',
1422
1587
  createdAt: new Date().toISOString()
@@ -1430,7 +1595,7 @@ export class WidgetStore {
1430
1595
  attachmentCount: attachments.length
1431
1596
  });
1432
1597
  const markFailed = () => this.setState(current => ({
1433
- messages: current.messages.map(message => message.id === tempId ? {
1598
+ messages: attachments.length > 0 ? current.messages.filter(message => message.id !== tempId) : current.messages.map(message => message.id === tempId ? {
1434
1599
  ...message,
1435
1600
  status: 'failed'
1436
1601
  } : message)
@@ -1455,7 +1620,7 @@ export class WidgetStore {
1455
1620
  // don't open a thread nobody is looking at.
1456
1621
  if (!conversationId && this.threadGeneration !== generation) {
1457
1622
  markFailed();
1458
- return;
1623
+ throw new Error('The conversation this message was queued for was never created');
1459
1624
  }
1460
1625
  }
1461
1626
  let createdId = null;
@@ -1464,7 +1629,17 @@ export class WidgetStore {
1464
1629
  conversationId: conversationId || undefined,
1465
1630
  forceNewConversation: !conversationId,
1466
1631
  body: trimmed,
1467
- attachments: attachments.length > 0 ? attachments : undefined,
1632
+ attachments: attachments.length > 0 ? attachments.map(({
1633
+ name,
1634
+ url,
1635
+ contentType,
1636
+ size
1637
+ }) => ({
1638
+ name,
1639
+ url,
1640
+ contentType,
1641
+ size
1642
+ })) : undefined,
1468
1643
  visitorName: visitorProfile.name,
1469
1644
  visitorEmail: visitorProfile.email,
1470
1645
  agentUiAction
@@ -1478,7 +1653,7 @@ export class WidgetStore {
1478
1653
  const messages = delivered ? current.messages.filter(message => message.id !== tempId) : current.messages.map(message => message.id === tempId ? {
1479
1654
  ...message,
1480
1655
  id: result.messageId,
1481
- status: 'sent'
1656
+ status: this.earlyStatuses.get(result.messageId) ?? 'sent'
1482
1657
  } : message);
1483
1658
  if (!createdId) return {
1484
1659
  messages
@@ -1612,6 +1787,69 @@ function isLocalMessage(message) {
1612
1787
  return message.id.startsWith('temp-');
1613
1788
  }
1614
1789
 
1790
+ /**
1791
+ * A sent visitor message keeps its local previews (a `file://` URI, or no URL
1792
+ * at all) until the server copy shows up — the stream echo or a history page.
1793
+ * Its attachments, the only ones that can be opened, then replace them.
1794
+ */
1795
+ /** A "Seen" missed while the stream was down shows up on the next re-read. */
1796
+ function adoptServerStatuses(messages, server) {
1797
+ const seen = new Set(server.filter(message => message.status === 'read').map(message => message.id));
1798
+ if (seen.size === 0) return messages;
1799
+ let changed = false;
1800
+ const next = messages.map(message => {
1801
+ if (!seen.has(message.id) || message.status === 'read') return message;
1802
+ changed = true;
1803
+ return {
1804
+ ...message,
1805
+ status: 'read'
1806
+ };
1807
+ });
1808
+ return changed ? next : messages;
1809
+ }
1810
+
1811
+ /** Keeps who/when, drops everything the visitor could read. */
1812
+ function asDeletedPlaceholder(message, deletedAt) {
1813
+ return {
1814
+ ...message,
1815
+ body: null,
1816
+ bodyPlain: null,
1817
+ attachments: null,
1818
+ metadata: null,
1819
+ deletedAt
1820
+ };
1821
+ }
1822
+
1823
+ /** A deletion missed while the stream was down shows up on the next re-read. */
1824
+ function adoptServerDeletions(messages, server) {
1825
+ const deleted = new Map(server.filter(message => message.deletedAt).map(message => [message.id, message.deletedAt]));
1826
+ if (deleted.size === 0) return messages;
1827
+ let changed = false;
1828
+ const next = messages.map(message => {
1829
+ const deletedAt = deleted.get(message.id);
1830
+ if (!deletedAt || message.deletedAt) return message;
1831
+ changed = true;
1832
+ return asDeletedPlaceholder(message, deletedAt);
1833
+ });
1834
+ return changed ? next : messages;
1835
+ }
1836
+ function adoptServerAttachments(messages, server) {
1837
+ const serverCopies = new Map(server.filter(message => message.attachments?.length).map(message => [message.id, message]));
1838
+ if (serverCopies.size === 0) return messages;
1839
+ let changed = false;
1840
+ const next = messages.map(message => {
1841
+ const copy = serverCopies.get(message.id);
1842
+ const hasLocal = message.attachments?.some(attachment => !/^https?:\/\//i.test(attachment.url));
1843
+ if (!copy || !hasLocal) return message;
1844
+ changed = true;
1845
+ return {
1846
+ ...message,
1847
+ attachments: copy.attachments
1848
+ };
1849
+ });
1850
+ return changed ? next : messages;
1851
+ }
1852
+
1615
1853
  /** Stable, so same-instant rows keep their order; bad dates sort as oldest. */
1616
1854
  function sortByCreatedAt(messages) {
1617
1855
  const time = message => Date.parse(message.createdAt) || 0;