@cloudflare/realtimekit-ui 1.1.0-staging.7 → 1.1.0-staging.9

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.
@@ -507,6 +507,13 @@ const RtkChat = class {
507
507
  const message = event.detail;
508
508
  this.meeting.chat.deleteMessage(message.id);
509
509
  };
510
+ this.onMessageEdit = (event) => {
511
+ const message = event.detail;
512
+ if (message.type !== 'text')
513
+ return;
514
+ this.replyMessage = null;
515
+ this.editingMessage = message;
516
+ };
510
517
  this.getPrivateChatRecipients = () => {
511
518
  const participants = this.getFilteredParticipants().map((participant) => {
512
519
  const key = chat.generateChatGroupKey([participant.userId, this.meeting.self.userId]);
@@ -691,14 +698,21 @@ const RtkChat = class {
691
698
  const uiProps = { iconPack: this.iconPack, t: this.t, size: this.size };
692
699
  const message = this.editingMessage ? this.editingMessage.message : '';
693
700
  const quotedMessage = this.replyMessage ? this.replyMessage.message : '';
694
- return (index$1.h("rtk-chat-composer-view", Object.assign({ message: message, storageKey: (_a = this.selectedChannelId) !== null && _a !== void 0 ? _a : `draft-${this.selectedChannelId}`, quotedMessage: quotedMessage, isEditing: !!this.editingMessage, canSendTextMessage: this.isTextMessagingAllowed(), canSendFiles: this.isFileMessagingAllowed(), disableEmojiPicker: this.overrides.disableEmojiPicker, maxLength: this.meeting.chat.maxTextLimit, rateLimits: this.meeting.chat.rateLimits, inputTextPlaceholder: this.t('chat.message_placeholder'), onNewMessage: this.onNewMessageHandler, onEditMessage: this.onEditMessageHandler, onEditCancel: this.onEditCancel, onQuotedMessageDismiss: this.onQuotedMessageDismiss }, uiProps), index$1.h("slot", { name: "chat-addon", slot: "chat-addon" })));
701
+ const draftStorageKey = this.selectedChannelId
702
+ ? `rtk-chat-draft-${this.selectedChannelId}`
703
+ : 'rtk-chat-draft';
704
+ const editStorageKey = this.editingMessage
705
+ ? `rtk-chat-edit-${(_a = this.selectedChannelId) !== null && _a !== void 0 ? _a : 'no-channel'}-${this.editingMessage.id}`
706
+ : 'rtk-chat-edit';
707
+ const storageKey = this.editingMessage ? editStorageKey : draftStorageKey;
708
+ return (index$1.h("rtk-chat-composer-view", Object.assign({ message: message, storageKey: storageKey, quotedMessage: quotedMessage, isEditing: !!this.editingMessage, canSendTextMessage: this.isTextMessagingAllowed(), canSendFiles: this.isFileMessagingAllowed(), disableEmojiPicker: this.overrides.disableEmojiPicker, maxLength: this.meeting.chat.maxTextLimit, rateLimits: this.meeting.chat.rateLimits, inputTextPlaceholder: this.t('chat.message_placeholder'), onNewMessage: this.onNewMessageHandler, onEditMessage: this.onEditMessageHandler, onEditCancel: this.onEditCancel, onQuotedMessageDismiss: this.onQuotedMessageDismiss }, uiProps), index$1.h("slot", { name: "chat-addon", slot: "chat-addon" })));
695
709
  }
696
710
  render() {
697
711
  var _a;
698
712
  if (!this.meeting) {
699
713
  return null;
700
714
  }
701
- return (index$1.h(index$1.Host, null, index$1.h("div", { class: "chat-container" }, index$1.h("div", { class: "chat" }, this.isFileMessagingAllowed() && (index$1.h("div", { id: "dropzone", class: { active: this.dropzoneActivated }, part: "dropzone" }, index$1.h("rtk-icon", { icon: this.iconPack.attach }), index$1.h("p", null, this.t('chat.send_attachment')))), this.renderPinnedMessagesHeader(), this.isPrivateChatSupported() && (index$1.h("rtk-channel-selector-view", { channels: this.getPrivateChatRecipients(), selectedChannelId: ((_a = this.selectedParticipant) === null || _a === void 0 ? void 0 : _a.userId) || 'everyone', onChannelChange: this.updateRecipients, t: this.t, viewAs: "dropdown" })), index$1.h("rtk-chat-messages-ui-paginated", { meeting: this.meeting, onPinMessage: this.onPinMessage, onDeleteMessage: this.onDeleteMessage, size: this.size, iconPack: this.iconPack, t: this.t }), this.renderComposerUI()))));
715
+ return (index$1.h(index$1.Host, null, index$1.h("div", { class: "chat-container" }, index$1.h("div", { class: "chat" }, this.isFileMessagingAllowed() && (index$1.h("div", { id: "dropzone", class: { active: this.dropzoneActivated }, part: "dropzone" }, index$1.h("rtk-icon", { icon: this.iconPack.attach }), index$1.h("p", null, this.t('chat.send_attachment')))), this.renderPinnedMessagesHeader(), this.isPrivateChatSupported() && (index$1.h("rtk-channel-selector-view", { channels: this.getPrivateChatRecipients(), selectedChannelId: ((_a = this.selectedParticipant) === null || _a === void 0 ? void 0 : _a.userId) || 'everyone', onChannelChange: this.updateRecipients, t: this.t, viewAs: "dropdown" })), index$1.h("rtk-chat-messages-ui-paginated", { meeting: this.meeting, onPinMessage: this.onPinMessage, onEditMessage: this.onMessageEdit, onDeleteMessage: this.onDeleteMessage, size: this.size, iconPack: this.iconPack, t: this.t }), this.renderComposerUI()))));
702
716
  }
703
717
  get host() { return index$1.getElement(this); }
704
718
  static get watchers() { return {
@@ -941,6 +955,7 @@ const RtkChatMessagesUiPaginated = class {
941
955
  index$1.registerInstance(this, hostRef);
942
956
  this.editMessageInit = index$1.createEvent(this, "editMessageInit", 7);
943
957
  this.onPinMessage = index$1.createEvent(this, "pinMessage", 7);
958
+ this.onEditMessage = index$1.createEvent(this, "editMessage", 7);
944
959
  this.onDeleteMessage = index$1.createEvent(this, "deleteMessage", 7);
945
960
  this.stateUpdate = index$1.createEvent(this, "rtkStateUpdate", 7);
946
961
  /** Icon pack */
@@ -991,22 +1006,18 @@ const RtkChatMessagesUiPaginated = class {
991
1006
  };
992
1007
  this.getMessageActions = (message) => {
993
1008
  const actions = [];
994
- // const isSelf = this.meeting.self.userId === message.userId;
995
- // const chatMessagePermissions = this.meeting.self.permissions?.chatMessage;
996
- // const canEdit =
997
- // chatMessagePermissions === undefined
998
- // ? isSelf
999
- // : chatMessagePermissions.canEdit === 'ALL' ||
1000
- // (chatMessagePermissions.canEdit === 'SELF' && isSelf);
1001
- const canDelete = message.userId === this.meeting.self.userId;
1002
- if (this.meeting.self.permissions.pinParticipant) {
1009
+ const messageBelongsToSelf = message.userId === this.meeting.self.userId;
1010
+ actions.push({
1011
+ id: 'pin_message',
1012
+ label: message.pinned ? this.t('unpin') : this.t('pin'),
1013
+ icon: this.iconPack.pin,
1014
+ });
1015
+ if (messageBelongsToSelf) {
1003
1016
  actions.push({
1004
- id: 'pin_message',
1005
- label: message.pinned ? this.t('unpin') : this.t('pin'),
1006
- icon: this.iconPack.pin,
1017
+ id: 'edit_message',
1018
+ label: this.t('chat.edit_msg'),
1019
+ icon: this.iconPack.edit,
1007
1020
  });
1008
- }
1009
- if (canDelete) {
1010
1021
  actions.push({
1011
1022
  id: 'delete_message',
1012
1023
  label: this.t('chat.delete_msg'),
@@ -1020,6 +1031,9 @@ const RtkChatMessagesUiPaginated = class {
1020
1031
  case 'pin_message':
1021
1032
  this.onPinMessage.emit(message);
1022
1033
  break;
1034
+ case 'edit_message':
1035
+ this.onEditMessage.emit(message);
1036
+ break;
1023
1037
  case 'delete_message':
1024
1038
  this.onDeleteMessage.emit(message);
1025
1039
  break;
@@ -1096,7 +1110,7 @@ const RtkChatMessagesUiPaginated = class {
1096
1110
  this.lastReadMessageIndex = -1;
1097
1111
  }
1098
1112
  render() {
1099
- return (index$1.h(index$1.Host, { key: 'c710da6e2fda420146905a2ed75d3444dd6d2c0b' }, index$1.h("rtk-paginated-list", { key: '51a36437e38e9c0242cca34bfda39f6d8309bee3', ref: (el) => (this.$paginatedListRef = el), pageSize: this.pageSize, pagesAllowed: 3, fetchData: this.getChatMessages, createNodes: this.createChatNodes, selectedItemId: this.selectedChannelId, emptyListLabel: this.t('chat.empty_channel') }, index$1.h("slot", { key: '69b54a41263510b425ce3e39af055321c4e2deb8' }))));
1113
+ return (index$1.h(index$1.Host, { key: '55b594d1fad2c164a70b71e297f63273ece0bc4f' }, index$1.h("rtk-paginated-list", { key: '1554ee896643feec72a02672f156f95c988813ce', ref: (el) => (this.$paginatedListRef = el), pageSize: this.pageSize, pagesAllowed: 3, fetchData: this.getChatMessages, createNodes: this.createChatNodes, selectedItemId: this.selectedChannelId, emptyListLabel: this.t('chat.empty_channel') }, index$1.h("slot", { key: '03aeb2069b87cb217b9759cabd3835c9bac81f11' }))));
1100
1114
  }
1101
1115
  get host() { return index$1.getElement(this); }
1102
1116
  static get watchers() { return {
@@ -2125,12 +2139,18 @@ const RtkPaginatedList = class {
2125
2139
  * @param {DataNode} node - The data node to add to the beginning of the list
2126
2140
  */
2127
2141
  async onNewNode(node) {
2128
- // if there are no pages, load the first page
2142
+ // if there are no pages, append to the first page
2129
2143
  if (this.pages.length < 1) {
2130
- this.oldTS = node.timeMs + 1;
2131
- this.loadPrevPage();
2144
+ this.oldTS = node.timeMs;
2145
+ this.pages.unshift([node]);
2146
+ this.newTS = node.timeMs;
2147
+ this.maxTS = node.timeMs;
2148
+ this.rerender();
2149
+ if (this.autoScroll)
2150
+ this.$bottomRef.scrollIntoView({ behavior: 'smooth' });
2132
2151
  }
2133
- else {
2152
+ else if (this.maxTS === this.newTS) {
2153
+ this.maxTS = node.timeMs;
2134
2154
  // append messages to the page if page has not reached full capacity
2135
2155
  if (this.pages[0].length < this.pageSize) {
2136
2156
  this.pages[0].unshift(node);
@@ -2139,14 +2159,25 @@ const RtkPaginatedList = class {
2139
2159
  }
2140
2160
  else {
2141
2161
  // if page is at full capacity, load next page
2142
- this.loadNextPage();
2162
+ this.pages.unshift([node]);
2163
+ this.newTS = node.timeMs;
2164
+ // remove pages if out of bounds
2165
+ if (this.pages.length > this.pagesAllowed)
2166
+ this.pages.pop();
2167
+ // update timestamps
2168
+ const lastPage = this.pages[this.pages.length - 1];
2169
+ this.oldTS = lastPage[lastPage.length - 1].timeMs;
2170
+ this.newTS = this.pages[0][0].timeMs;
2171
+ this.rerender();
2143
2172
  }
2173
+ if (this.autoScroll)
2174
+ this.$bottomRef.scrollIntoView({ behavior: 'smooth' });
2144
2175
  }
2145
- // If autoscroll is enabled, scroll to the bottom
2146
- if (this.autoScroll) {
2147
- this.shouldScrollToBottom = true;
2148
- this.scrollToBottom();
2176
+ else {
2177
+ if (this.autoScroll)
2178
+ this.scrollToBottom();
2149
2179
  }
2180
+ this.pendingScrollAnchor = null;
2150
2181
  }
2151
2182
  /**
2152
2183
  * Deletes a node anywhere from the list
@@ -2154,7 +2185,6 @@ const RtkPaginatedList = class {
2154
2185
  * */
2155
2186
  async onNodeDelete(id) {
2156
2187
  var _a, _b;
2157
- let didDelete = false;
2158
2188
  for (let i = this.pages.length - 1; i >= 0; i--) {
2159
2189
  const index = this.pages[i].findIndex((node) => node.id === id);
2160
2190
  // if message not found, move on
@@ -2165,24 +2195,35 @@ const RtkPaginatedList = class {
2165
2195
  // if page is empty, delete it
2166
2196
  if (this.pages[i].length === 0)
2167
2197
  this.pages.splice(i, 1);
2168
- didDelete = true;
2198
+ // update timestamps
2199
+ const firstPage = this.pages[0];
2200
+ const lastPage = this.pages[this.pages.length - 1];
2201
+ this.newTS = (_a = firstPage === null || firstPage === void 0 ? void 0 : firstPage[0]) === null || _a === void 0 ? void 0 : _a.timeMs;
2202
+ this.oldTS = (_b = lastPage === null || lastPage === void 0 ? void 0 : lastPage[lastPage.length - 1]) === null || _b === void 0 ? void 0 : _b.timeMs;
2203
+ // if I have deleted the latest message, update maxTS
2204
+ if (index === 0 && i === 0)
2205
+ this.maxTS = this.newTS;
2206
+ this.rerender();
2169
2207
  break;
2170
2208
  }
2171
- if (!didDelete)
2172
- return;
2173
- // update timestamps
2174
- const firstPage = this.pages[0];
2175
- const lastPage = this.pages[this.pages.length - 1];
2176
- this.newTS = (_a = firstPage === null || firstPage === void 0 ? void 0 : firstPage[0]) === null || _a === void 0 ? void 0 : _a.timeMs;
2177
- this.oldTS = (_b = lastPage === null || lastPage === void 0 ? void 0 : lastPage[lastPage.length - 1]) === null || _b === void 0 ? void 0 : _b.timeMs;
2178
- this.rerender();
2179
2209
  }
2180
2210
  /**
2181
2211
  * Updates a new node anywhere in the list
2182
- * @param {string} _id - The id of the node to update
2183
- * @param {DataNode} _node - The updated data node
2212
+ * @param {string} id - The id of the node to update
2213
+ * @param {DataNode} node - The updated data node
2184
2214
  * */
2185
- async onNodeUpdate(_id, _node) { }
2215
+ async onNodeUpdate(id, node) {
2216
+ for (let i = this.pages.length - 1; i >= 0; i--) {
2217
+ const index = this.pages[i].findIndex((node) => node.id === id);
2218
+ // if message not found, move on
2219
+ if (index === -1)
2220
+ continue;
2221
+ // edit message
2222
+ this.pages[i][index] = node;
2223
+ this.rerender();
2224
+ break;
2225
+ }
2226
+ }
2186
2227
  connectedCallback() {
2187
2228
  this.rerender = debounce.debounce(this.rerender.bind(this), 50, { maxWait: 200 });
2188
2229
  }
@@ -2191,6 +2232,10 @@ const RtkPaginatedList = class {
2191
2232
  this.loadPrevPage();
2192
2233
  if (this.$containerRef) {
2193
2234
  this.$containerRef.onscrollend = async () => {
2235
+ // do not do anything if we are scrolling to bottom
2236
+ if (this.shouldScrollToBottom)
2237
+ return;
2238
+ // handle top and bottom scroll
2194
2239
  if (this.isInView(this.$bottomRef)) {
2195
2240
  await this.loadNextPage();
2196
2241
  }
@@ -2233,6 +2278,8 @@ const RtkPaginatedList = class {
2233
2278
  const lastPage = this.pages[this.pages.length - 1];
2234
2279
  this.oldTS = lastPage[lastPage.length - 1].timeMs;
2235
2280
  this.newTS = this.pages[0][0].timeMs;
2281
+ if (!this.maxTS)
2282
+ this.maxTS = this.newTS;
2236
2283
  this.rerender();
2237
2284
  // fix scroll position
2238
2285
  if (scrollAnchor)
@@ -2240,53 +2287,47 @@ const RtkPaginatedList = class {
2240
2287
  }
2241
2288
  async loadNextPage() {
2242
2289
  if (this.isLoading)
2243
- return;
2290
+ return [];
2244
2291
  // Do nothing. New timestamp needs to be assigned by loadPrevPage method
2245
2292
  if (!this.newTS) {
2246
2293
  this.showNewMessagesCTR = false;
2247
- this.shouldScrollToBottom = false;
2248
- return;
2294
+ return [];
2249
2295
  }
2250
- // for autoscroll or scroll to bottom button
2251
- const maxAutoLoads = 200;
2252
- let loads = 0;
2253
- let prevNewTS = this.newTS;
2254
2296
  this.isLoading = true;
2255
2297
  this.isLoadingBottom = true;
2256
- while (loads < maxAutoLoads) {
2257
- const scrollAnchor = this.getScrollAnchor('bottom');
2258
- const data = await this.fetchData(this.newTS + 1, this.pageSize, false);
2259
- this.isLoading = false;
2260
- this.isLoadingBottom = false;
2261
- // no more new messages to load
2262
- if (!data.length) {
2263
- this.showNewMessagesCTR = false;
2264
- this.shouldScrollToBottom = false;
2265
- break;
2266
- }
2267
- // load new messages and append to the start
2268
- this.pages.unshift(data.reverse());
2269
- // remove pages if out of bounds
2270
- if (this.pages.length > this.pagesAllowed)
2271
- this.pages.pop();
2272
- // update timestamps
2273
- const lastPage = this.pages[this.pages.length - 1];
2274
- this.oldTS = lastPage[lastPage.length - 1].timeMs;
2275
- this.newTS = this.pages[0][0].timeMs;
2276
- this.rerender();
2277
- this.pendingScrollAnchor = scrollAnchor;
2278
- if (!this.shouldScrollToBottom)
2279
- break;
2280
- // if should scroll to bottom then retrigger
2281
- await this.waitForNextFrame();
2282
- this.scrollToBottom();
2283
- await this.waitForNextFrame();
2284
- // if no new messages, break
2285
- if (this.newTS === prevNewTS)
2286
- break;
2287
- prevNewTS = this.newTS;
2288
- loads++;
2298
+ const scrollAnchor = this.getScrollAnchor('bottom');
2299
+ const data = await this.fetchData(this.newTS + 1, this.pageSize, false);
2300
+ this.isLoading = false;
2301
+ this.isLoadingBottom = false;
2302
+ // no more new messages to load
2303
+ if (!data.length) {
2304
+ this.maxTS = this.newTS;
2305
+ this.showNewMessagesCTR = false;
2306
+ return [];
2307
+ }
2308
+ // load new messages and append to the start
2309
+ const incoming = [...data].reverse();
2310
+ if (this.pages.length === 0)
2311
+ this.pages.unshift([]);
2312
+ const firstPage = this.pages[0];
2313
+ const spaceInFirstPage = this.pageSize - firstPage.length;
2314
+ if (spaceInFirstPage > 0) {
2315
+ const toFill = incoming.splice(0, spaceInFirstPage);
2316
+ firstPage.unshift(...toFill);
2317
+ }
2318
+ while (incoming.length > 0) {
2319
+ this.pages.unshift(incoming.splice(0, this.pageSize));
2289
2320
  }
2321
+ // remove pages if out of bounds
2322
+ if (this.pages.length > this.pagesAllowed)
2323
+ this.pages.pop();
2324
+ // update timestamps
2325
+ const lastPage = this.pages[this.pages.length - 1];
2326
+ this.oldTS = lastPage[lastPage.length - 1].timeMs;
2327
+ this.newTS = this.pages[0][0].timeMs;
2328
+ this.rerender();
2329
+ this.pendingScrollAnchor = scrollAnchor;
2330
+ return data;
2290
2331
  }
2291
2332
  // Find the element that is closest to the top/bottom of the container
2292
2333
  getScrollAnchor(edge = 'top') {
@@ -2342,11 +2383,14 @@ const RtkPaginatedList = class {
2342
2383
  }
2343
2384
  }
2344
2385
  // this method is called recursively based on shouldScrollToBottom (see loadNextPage)
2345
- scrollToBottom() {
2346
- this.$bottomRef.scrollIntoView({ behavior: 'smooth' });
2347
- }
2348
- waitForNextFrame() {
2349
- return new Promise((resolve) => requestAnimationFrame(() => resolve()));
2386
+ async scrollToBottom() {
2387
+ this.shouldScrollToBottom = true;
2388
+ while (this.shouldScrollToBottom) {
2389
+ const response = await this.loadNextPage();
2390
+ this.$bottomRef.scrollIntoView({ behavior: 'smooth' });
2391
+ if (response.length === 0)
2392
+ this.shouldScrollToBottom = false;
2393
+ }
2350
2394
  }
2351
2395
  rerender() {
2352
2396
  this.rerenderBoolean = !this.rerenderBoolean;
@@ -2355,10 +2399,9 @@ const RtkPaginatedList = class {
2355
2399
  /**
2356
2400
  * div.container is flex=column-reversewhich is why div#bottom-scroll comes before div#top-scroll
2357
2401
  */
2358
- return (index$1.h(index$1.Host, { key: 'e0f806cccdcba162d0c834476863b34630cb1a1e' }, index$1.h("div", { key: '6d54d50ed703a59df8d26399499533e3cb0d70fe', class: "scrollbar container", part: "container", ref: (el) => (this.$containerRef = el) }, index$1.h("div", { key: '4e9fcbed725fb55d9fbb67eca94b0e770662d51b', class: { 'show-new-messages-ctr': true, active: this.showNewMessagesCTR } }, index$1.h("rtk-button", { key: '7db0236d35db3fb9856fea7a4f62c1fbd421829e', class: "show-new-messages", kind: "icon", variant: "secondary", part: "show-new-messages", onClick: () => {
2359
- this.shouldScrollToBottom = true;
2402
+ return (index$1.h(index$1.Host, { key: '3e7a77a4254ea0c75513edeaf72a5a77cee0e913' }, index$1.h("div", { key: 'f61e72e7a447048fe17ed8321a077841f991bfc5', class: "scrollbar container", part: "container", ref: (el) => (this.$containerRef = el) }, index$1.h("div", { key: '9efd0543b48b5ad5e147a763d258f7ef1a5024c5', class: { 'show-new-messages-ctr': true, active: this.showNewMessagesCTR } }, index$1.h("rtk-button", { key: '4b9bfda38538ceb89f31f317ddcb15d24f75ae8e', class: "show-new-messages", kind: "icon", variant: "secondary", part: "show-new-messages", onClick: () => {
2360
2403
  this.scrollToBottom();
2361
- } }, index$1.h("rtk-icon", { key: '6e247e86029560601080e0b4d6dcfccbd90fcdd6', icon: this.iconPack.chevron_down }))), index$1.h("div", { key: '01d1ba7eacc67ece70b805fb2dfb493cdf1c9d23', class: "smallest-dom-element", id: "bottom-scroll", ref: (el) => (this.$bottomRef = el) }), this.isLoadingBottom && this.pages.length > 0 && index$1.h("rtk-spinner", { key: '24391bfc34914675cbfd0c287332bfdf3f5f5000', size: "sm" }), this.isLoading && this.pages.length < 1 && index$1.h("rtk-spinner", { key: 'e6c2cb44fce52ca54c9f1e543d91a29648745408', size: "lg" }), !this.isLoading && this.pages.flat().length === 0 ? (index$1.h("div", { class: "empty-list" }, this.t('list.empty'))) : (index$1.h("div", { class: "page-wrapper" }, this.pages.map((page, pageIndex) => (index$1.h("div", { class: "page", "data-page-index": pageIndex }, this.createNodes([...page].reverse())))))), this.isLoadingTop && this.pages.length > 0 && index$1.h("rtk-spinner", { key: 'd90a15494bcd7ec6c9c7ffc5e9a55054252a4258', size: "sm" }), index$1.h("div", { key: '2a65f98c3c4e6f2510c6cf1b4e2bcf1e607a7552', class: "smallest-dom-element", id: "top-scroll", ref: (el) => (this.$topRef = el) }))));
2404
+ } }, index$1.h("rtk-icon", { key: 'fbcbc895940c8046916a2382806fb403e83a0a53', icon: this.iconPack.chevron_down }))), index$1.h("div", { key: 'fe2e51385216cba1905c75db783f037953e4831e', class: "smallest-dom-element", id: "bottom-scroll", ref: (el) => (this.$bottomRef = el) }), this.isLoadingBottom && this.pages.length > 0 && index$1.h("rtk-spinner", { key: '548899e797bbf139526208df3c7696b5859cc884', size: "sm" }), this.isLoading && this.pages.length < 1 && index$1.h("rtk-spinner", { key: '6353d5970e284369c274c28fc3c774d03fd555cc', size: "lg" }), !this.isLoading && this.pages.flat().length === 0 ? (index$1.h("div", { class: "empty-list" }, this.t('list.empty'))) : (index$1.h("div", { class: "page-wrapper" }, this.pages.map((page, pageIndex) => (index$1.h("div", { class: "page", "data-page-index": pageIndex }, this.createNodes([...page].reverse())))))), this.isLoadingTop && this.pages.length > 0 && index$1.h("rtk-spinner", { key: 'ff7b4e80f27610c0b73b63ea32fa5331b0cf4630', size: "sm" }), index$1.h("div", { key: '8729bf082cde39f7b154fa5816a70b6fb2c7f7df', class: "smallest-dom-element", id: "top-scroll", ref: (el) => (this.$topRef = el) }))));
2362
2405
  }
2363
2406
  };
2364
2407
  __decorate$2([
@@ -283,6 +283,13 @@ export class RtkChat {
283
283
  const message = event.detail;
284
284
  this.meeting.chat.deleteMessage(message.id);
285
285
  };
286
+ this.onMessageEdit = (event) => {
287
+ const message = event.detail;
288
+ if (message.type !== 'text')
289
+ return;
290
+ this.replyMessage = null;
291
+ this.editingMessage = message;
292
+ };
286
293
  this.getPrivateChatRecipients = () => {
287
294
  const participants = this.getFilteredParticipants().map((participant) => {
288
295
  const key = generateChatGroupKey([participant.userId, this.meeting.self.userId]);
@@ -467,14 +474,21 @@ export class RtkChat {
467
474
  const uiProps = { iconPack: this.iconPack, t: this.t, size: this.size };
468
475
  const message = this.editingMessage ? this.editingMessage.message : '';
469
476
  const quotedMessage = this.replyMessage ? this.replyMessage.message : '';
470
- return (h("rtk-chat-composer-view", Object.assign({ message: message, storageKey: (_a = this.selectedChannelId) !== null && _a !== void 0 ? _a : `draft-${this.selectedChannelId}`, quotedMessage: quotedMessage, isEditing: !!this.editingMessage, canSendTextMessage: this.isTextMessagingAllowed(), canSendFiles: this.isFileMessagingAllowed(), disableEmojiPicker: this.overrides.disableEmojiPicker, maxLength: this.meeting.chat.maxTextLimit, rateLimits: this.meeting.chat.rateLimits, inputTextPlaceholder: this.t('chat.message_placeholder'), onNewMessage: this.onNewMessageHandler, onEditMessage: this.onEditMessageHandler, onEditCancel: this.onEditCancel, onQuotedMessageDismiss: this.onQuotedMessageDismiss }, uiProps), h("slot", { name: "chat-addon", slot: "chat-addon" })));
477
+ const draftStorageKey = this.selectedChannelId
478
+ ? `rtk-chat-draft-${this.selectedChannelId}`
479
+ : 'rtk-chat-draft';
480
+ const editStorageKey = this.editingMessage
481
+ ? `rtk-chat-edit-${(_a = this.selectedChannelId) !== null && _a !== void 0 ? _a : 'no-channel'}-${this.editingMessage.id}`
482
+ : 'rtk-chat-edit';
483
+ const storageKey = this.editingMessage ? editStorageKey : draftStorageKey;
484
+ return (h("rtk-chat-composer-view", Object.assign({ message: message, storageKey: storageKey, quotedMessage: quotedMessage, isEditing: !!this.editingMessage, canSendTextMessage: this.isTextMessagingAllowed(), canSendFiles: this.isFileMessagingAllowed(), disableEmojiPicker: this.overrides.disableEmojiPicker, maxLength: this.meeting.chat.maxTextLimit, rateLimits: this.meeting.chat.rateLimits, inputTextPlaceholder: this.t('chat.message_placeholder'), onNewMessage: this.onNewMessageHandler, onEditMessage: this.onEditMessageHandler, onEditCancel: this.onEditCancel, onQuotedMessageDismiss: this.onQuotedMessageDismiss }, uiProps), h("slot", { name: "chat-addon", slot: "chat-addon" })));
471
485
  }
472
486
  render() {
473
487
  var _a;
474
488
  if (!this.meeting) {
475
489
  return null;
476
490
  }
477
- return (h(Host, null, h("div", { class: "chat-container" }, h("div", { class: "chat" }, this.isFileMessagingAllowed() && (h("div", { id: "dropzone", class: { active: this.dropzoneActivated }, part: "dropzone" }, h("rtk-icon", { icon: this.iconPack.attach }), h("p", null, this.t('chat.send_attachment')))), this.renderPinnedMessagesHeader(), this.isPrivateChatSupported() && (h("rtk-channel-selector-view", { channels: this.getPrivateChatRecipients(), selectedChannelId: ((_a = this.selectedParticipant) === null || _a === void 0 ? void 0 : _a.userId) || 'everyone', onChannelChange: this.updateRecipients, t: this.t, viewAs: "dropdown" })), h("rtk-chat-messages-ui-paginated", { meeting: this.meeting, onPinMessage: this.onPinMessage, onDeleteMessage: this.onDeleteMessage, size: this.size, iconPack: this.iconPack, t: this.t }), this.renderComposerUI()))));
491
+ return (h(Host, null, h("div", { class: "chat-container" }, h("div", { class: "chat" }, this.isFileMessagingAllowed() && (h("div", { id: "dropzone", class: { active: this.dropzoneActivated }, part: "dropzone" }, h("rtk-icon", { icon: this.iconPack.attach }), h("p", null, this.t('chat.send_attachment')))), this.renderPinnedMessagesHeader(), this.isPrivateChatSupported() && (h("rtk-channel-selector-view", { channels: this.getPrivateChatRecipients(), selectedChannelId: ((_a = this.selectedParticipant) === null || _a === void 0 ? void 0 : _a.userId) || 'everyone', onChannelChange: this.updateRecipients, t: this.t, viewAs: "dropdown" })), h("rtk-chat-messages-ui-paginated", { meeting: this.meeting, onPinMessage: this.onPinMessage, onEditMessage: this.onMessageEdit, onDeleteMessage: this.onDeleteMessage, size: this.size, iconPack: this.iconPack, t: this.t }), this.renderComposerUI()))));
478
492
  }
479
493
  static get is() { return "rtk-chat"; }
480
494
  static get encapsulation() { return "shadow"; }
@@ -62,22 +62,18 @@ export class RtkChatMessagesUiPaginated {
62
62
  };
63
63
  this.getMessageActions = (message) => {
64
64
  const actions = [];
65
- // const isSelf = this.meeting.self.userId === message.userId;
66
- // const chatMessagePermissions = this.meeting.self.permissions?.chatMessage;
67
- // const canEdit =
68
- // chatMessagePermissions === undefined
69
- // ? isSelf
70
- // : chatMessagePermissions.canEdit === 'ALL' ||
71
- // (chatMessagePermissions.canEdit === 'SELF' && isSelf);
72
- const canDelete = message.userId === this.meeting.self.userId;
73
- if (this.meeting.self.permissions.pinParticipant) {
65
+ const messageBelongsToSelf = message.userId === this.meeting.self.userId;
66
+ actions.push({
67
+ id: 'pin_message',
68
+ label: message.pinned ? this.t('unpin') : this.t('pin'),
69
+ icon: this.iconPack.pin,
70
+ });
71
+ if (messageBelongsToSelf) {
74
72
  actions.push({
75
- id: 'pin_message',
76
- label: message.pinned ? this.t('unpin') : this.t('pin'),
77
- icon: this.iconPack.pin,
73
+ id: 'edit_message',
74
+ label: this.t('chat.edit_msg'),
75
+ icon: this.iconPack.edit,
78
76
  });
79
- }
80
- if (canDelete) {
81
77
  actions.push({
82
78
  id: 'delete_message',
83
79
  label: this.t('chat.delete_msg'),
@@ -91,6 +87,9 @@ export class RtkChatMessagesUiPaginated {
91
87
  case 'pin_message':
92
88
  this.onPinMessage.emit(message);
93
89
  break;
90
+ case 'edit_message':
91
+ this.onEditMessage.emit(message);
92
+ break;
94
93
  case 'delete_message':
95
94
  this.onDeleteMessage.emit(message);
96
95
  break;
@@ -169,7 +168,7 @@ export class RtkChatMessagesUiPaginated {
169
168
  this.lastReadMessageIndex = -1;
170
169
  }
171
170
  render() {
172
- return (h(Host, { key: 'c710da6e2fda420146905a2ed75d3444dd6d2c0b' }, h("rtk-paginated-list", { key: '51a36437e38e9c0242cca34bfda39f6d8309bee3', ref: (el) => (this.$paginatedListRef = el), pageSize: this.pageSize, pagesAllowed: 3, fetchData: this.getChatMessages, createNodes: this.createChatNodes, selectedItemId: this.selectedChannelId, emptyListLabel: this.t('chat.empty_channel') }, h("slot", { key: '69b54a41263510b425ce3e39af055321c4e2deb8' }))));
171
+ return (h(Host, { key: '55b594d1fad2c164a70b71e297f63273ece0bc4f' }, h("rtk-paginated-list", { key: '1554ee896643feec72a02672f156f95c988813ce', ref: (el) => (this.$paginatedListRef = el), pageSize: this.pageSize, pagesAllowed: 3, fetchData: this.getChatMessages, createNodes: this.createChatNodes, selectedItemId: this.selectedChannelId, emptyListLabel: this.t('chat.empty_channel') }, h("slot", { key: '03aeb2069b87cb217b9759cabd3835c9bac81f11' }))));
173
172
  }
174
173
  static get is() { return "rtk-chat-messages-ui-paginated"; }
175
174
  static get encapsulation() { return "shadow"; }
@@ -394,6 +393,27 @@ export class RtkChatMessagesUiPaginated {
394
393
  }
395
394
  }
396
395
  }
396
+ }, {
397
+ "method": "onEditMessage",
398
+ "name": "editMessage",
399
+ "bubbles": true,
400
+ "cancelable": true,
401
+ "composed": true,
402
+ "docs": {
403
+ "tags": [],
404
+ "text": "Event emitted when a message is edited"
405
+ },
406
+ "complexType": {
407
+ "original": "Message",
408
+ "resolved": "CustomMessage | FileMessage | ImageMessage | TextMessage",
409
+ "references": {
410
+ "Message": {
411
+ "location": "import",
412
+ "path": "@cloudflare/realtimekit",
413
+ "id": "node_modules::Message"
414
+ }
415
+ }
416
+ }
397
417
  }, {
398
418
  "method": "onDeleteMessage",
399
419
  "name": "deleteMessage",