@cloudflare/realtimekit-ui 1.1.0-staging.5 → 1.1.0-staging.7
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.
- package/dist/browser.js +1 -1
- package/dist/cjs/rtk-avatar_24.cjs.entry.js +186 -175
- package/dist/cjs/rtk-chat-toggle.cjs.entry.js +2 -2
- package/dist/cjs/rtk-notifications.cjs.entry.js +4 -1
- package/dist/collection/components/rtk-chat-messages-ui-paginated/rtk-chat-messages-ui-paginated.js +2 -2
- package/dist/collection/components/rtk-chat-toggle/rtk-chat-toggle.js +2 -2
- package/dist/collection/components/rtk-notifications/rtk-notifications.js +4 -1
- package/dist/collection/components/rtk-paginated-list/rtk-paginated-list.js +184 -173
- package/dist/components/{p-32c6e86d.js → p-1d16490e.js} +2 -2
- package/dist/components/{p-ae376177.js → p-7be71567.js} +3 -3
- package/dist/components/{p-0d472019.js → p-7f8d9afc.js} +184 -173
- package/dist/components/rtk-chat-messages-ui-paginated.js +1 -1
- package/dist/components/rtk-chat-search-results.js +1 -1
- package/dist/components/rtk-chat-toggle.js +2 -2
- package/dist/components/rtk-chat.js +1 -1
- package/dist/components/rtk-meeting.js +3 -3
- package/dist/components/rtk-notifications.js +4 -1
- package/dist/components/rtk-paginated-list.js +1 -1
- package/dist/docs/docs-components.json +1 -1
- package/dist/esm/loader.js +191 -177
- package/dist/esm/rtk-avatar_24.entry.js +186 -175
- package/dist/esm/rtk-chat-toggle.entry.js +2 -2
- package/dist/esm/rtk-notifications.entry.js +4 -1
- package/dist/realtimekit-ui/p-25c13ff8.entry.js +1 -0
- package/dist/realtimekit-ui/p-342b4926.entry.js +1 -0
- package/dist/realtimekit-ui/{p-f457ae6f.entry.js → p-ec5ed8a4.entry.js} +1 -1
- package/dist/realtimekit-ui/realtimekit-ui.esm.js +1 -1
- package/dist/types/components/rtk-paginated-list/rtk-paginated-list.d.ts +32 -46
- package/package.json +1 -1
- package/dist/realtimekit-ui/p-83db4de1.entry.js +0 -1
- package/dist/realtimekit-ui/p-a859d883.entry.js +0 -1
|
@@ -1044,7 +1044,7 @@ const RtkChatMessagesUiPaginated = class {
|
|
|
1044
1044
|
}
|
|
1045
1045
|
const isSelf = message.userId === this.meeting.self.userId;
|
|
1046
1046
|
const viewType = isSelf ? 'outgoing' : 'incoming';
|
|
1047
|
-
return (h("div", null, h("div", { class: "message-wrapper" }, h("rtk-message-view", { pinned: message.pinned, time: message.time, actions: this.getMessageActions(message), authorName: message.displayName, isSelf: isSelf, avatarUrl: displayPicture, hideAuthorName: isContinued, viewType: viewType, variant: "bubble", onAction: (event) => this.onMessageActionHandler(event.detail, message) }, h("div", null, h("div", { class: "body" }, message.type === 'text' && (h("rtk-text-message-view", { text: message.message, isMarkdown: true })), message.type === 'file' && (h("rtk-file-message-view", { name: message.name, url: message.link, size: message.size })), message.type === 'image' && (h("rtk-image-message-view", { url: message.link, onPreview: () => {
|
|
1047
|
+
return (h("div", null, h("div", { class: "message-wrapper", id: message.id }, h("rtk-message-view", { pinned: message.pinned, time: message.time, actions: this.getMessageActions(message), authorName: message.displayName, isSelf: isSelf, avatarUrl: displayPicture, hideAuthorName: isContinued, viewType: viewType, variant: "bubble", onAction: (event) => this.onMessageActionHandler(event.detail, message) }, h("div", null, h("div", { class: "body" }, message.type === 'text' && (h("rtk-text-message-view", { text: message.message, isMarkdown: true })), message.type === 'file' && (h("rtk-file-message-view", { name: message.name, url: message.link, size: message.size })), message.type === 'image' && (h("rtk-image-message-view", { url: message.link, onPreview: () => {
|
|
1048
1048
|
this.stateUpdate.emit({ image: message });
|
|
1049
1049
|
} }))))))));
|
|
1050
1050
|
};
|
|
@@ -1092,7 +1092,7 @@ const RtkChatMessagesUiPaginated = class {
|
|
|
1092
1092
|
this.lastReadMessageIndex = -1;
|
|
1093
1093
|
}
|
|
1094
1094
|
render() {
|
|
1095
|
-
return (h(Host, { key: '
|
|
1095
|
+
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' }))));
|
|
1096
1096
|
}
|
|
1097
1097
|
get host() { return getElement(this); }
|
|
1098
1098
|
static get watchers() { return {
|
|
@@ -2054,32 +2054,30 @@ const rtkPaginatedListCss = ".scrollbar{scrollbar-width:thin;scrollbar-color:var
|
|
|
2054
2054
|
const RtkPaginatedListStyle0 = rtkPaginatedListCss;
|
|
2055
2055
|
|
|
2056
2056
|
/**
|
|
2057
|
-
*
|
|
2057
|
+
* NOTE(ikabra): INFINITE SCROLL IMPLEMENTATION:
|
|
2058
2058
|
*
|
|
2059
|
-
*
|
|
2060
|
-
*
|
|
2059
|
+
* Uses scrollend listener for 2way scrolling.
|
|
2060
|
+
* Empty divs ($topRef, $bottomRef) act as scroll triggers to fetch new messages.
|
|
2061
2061
|
*
|
|
2062
|
-
*
|
|
2063
|
-
*
|
|
2064
|
-
*
|
|
2062
|
+
* UPWARD SCROLLING:
|
|
2063
|
+
* - Fetch top anchor (element currently visible to the user near top)
|
|
2064
|
+
* - Fetch older messages, push to end of 2D array
|
|
2065
|
+
* - When exceeding pagesAllowed, delete pages and scroll back to anchor
|
|
2065
2066
|
*
|
|
2066
|
-
*
|
|
2067
|
-
*
|
|
2067
|
+
* DOWNWARD SCROLLING:
|
|
2068
|
+
* - Fetch bottom anchor (element currently visible to the user near bottom)
|
|
2069
|
+
* - Fetch new page, insert at the start
|
|
2070
|
+
* - Update timestamps & firstEmptyIndex, then rerender
|
|
2071
|
+
* - When exceeding pagesAllowed, delete pages and scroll back to anchor
|
|
2068
2072
|
*
|
|
2069
|
-
*
|
|
2070
|
-
*
|
|
2071
|
-
*
|
|
2072
|
-
* if length exceeds pagesAllowed, we free up the pages and keep the first empty index in memory (firstEmptyIndex).
|
|
2073
|
+
* ADDING NEW NODES:
|
|
2074
|
+
* - If no pages exist, load old page
|
|
2075
|
+
* - If on 1st page, append messages till page size is full and then load new page
|
|
2073
2076
|
*
|
|
2074
|
-
*
|
|
2075
|
-
* If
|
|
2076
|
-
*
|
|
2077
|
-
*
|
|
2078
|
-
* If we have exceeded our page allowance we delete old pages.
|
|
2079
|
-
*
|
|
2080
|
-
* In this case deleting pages is okay as we are not relying on the index of dom elements to detect page end.
|
|
2081
|
-
*
|
|
2082
|
-
* This also simplifies the code because when a user scrolls up we do not need to manage a lastEmptyIndex.
|
|
2077
|
+
* DELETE NODE:
|
|
2078
|
+
* - If deleting the only available node, reset to initial state
|
|
2079
|
+
* - If page is empty, delete it
|
|
2080
|
+
* - Update timestamp curors
|
|
2083
2081
|
*/
|
|
2084
2082
|
var __decorate$2 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
2085
2083
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -2094,43 +2092,27 @@ var __decorate$2 = (undefined && undefined.__decorate) || function (decorators,
|
|
|
2094
2092
|
const RtkPaginatedList = class {
|
|
2095
2093
|
constructor(hostRef) {
|
|
2096
2094
|
registerInstance(this, hostRef);
|
|
2097
|
-
/**
|
|
2098
|
-
* when scrolling up, we can't remove pages as intersectionObserver relies on
|
|
2099
|
-
* the index of dom elements to stay stable.
|
|
2100
|
-
* So, instead we free up the pages and keep the last empty index in memory.
|
|
2101
|
-
*/
|
|
2102
|
-
this.firstEmptyIndex = -1;
|
|
2103
|
-
this.maxTS = 0;
|
|
2104
2095
|
// the length of pages will always be pageSize + 2
|
|
2105
2096
|
this.pages = [];
|
|
2097
|
+
// Controls whether to keep auto-scrolling when a new page load.
|
|
2098
|
+
this.shouldScrollToBottom = false;
|
|
2099
|
+
// Shows "scroll to bottom" button when new nodes arrive and autoscroll is off.
|
|
2100
|
+
this.showNewMessagesCTR = false;
|
|
2106
2101
|
/** label to show when empty */
|
|
2107
2102
|
this.emptyListLabel = null;
|
|
2108
|
-
this.rerenderBoolean = false;
|
|
2109
|
-
this.showEmptyListLabel = false;
|
|
2110
2103
|
/** Icon pack */
|
|
2111
2104
|
this.iconPack = defaultIconPack;
|
|
2112
2105
|
/** Language */
|
|
2113
2106
|
this.t = useLanguage();
|
|
2107
|
+
this.rerenderBoolean = false;
|
|
2108
|
+
this.showEmptyListLabel = false;
|
|
2114
2109
|
this.isLoading = false;
|
|
2115
2110
|
this.isLoadingTop = false;
|
|
2116
2111
|
this.isLoadingBottom = false;
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
* */
|
|
2122
|
-
this.shouldScrollToBottom = false;
|
|
2123
|
-
/** UI Indicator for the "scroll to bottom" button.
|
|
2124
|
-
* Toggles on when a new node is added and autoscroll is disabled.
|
|
2125
|
-
* Toggles off when all nodes are loaded */
|
|
2126
|
-
this.showNewMessagesCTR = false;
|
|
2127
|
-
this.observe = (el) => {
|
|
2128
|
-
if (!el)
|
|
2129
|
-
return;
|
|
2130
|
-
this.intersectionObserver.observe(el);
|
|
2131
|
-
};
|
|
2132
|
-
this.isAtBottom = () => {
|
|
2133
|
-
const rect = this.$bottomRef.getBoundingClientRect();
|
|
2112
|
+
// Tells us if we need to scroll to a specific anchor after a rerender
|
|
2113
|
+
this.pendingScrollAnchor = null;
|
|
2114
|
+
this.isInView = (el) => {
|
|
2115
|
+
const rect = el.getBoundingClientRect();
|
|
2134
2116
|
return rect.top >= 0 && rect.bottom <= window.innerHeight;
|
|
2135
2117
|
};
|
|
2136
2118
|
}
|
|
@@ -2139,10 +2121,12 @@ const RtkPaginatedList = class {
|
|
|
2139
2121
|
* @param {DataNode} node - The data node to add to the beginning of the list
|
|
2140
2122
|
*/
|
|
2141
2123
|
async onNewNode(node) {
|
|
2142
|
-
//
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2124
|
+
// if there are no pages, load the first page
|
|
2125
|
+
if (this.pages.length < 1) {
|
|
2126
|
+
this.oldTS = node.timeMs + 1;
|
|
2127
|
+
this.loadPrevPage();
|
|
2128
|
+
}
|
|
2129
|
+
else {
|
|
2146
2130
|
// append messages to the page if page has not reached full capacity
|
|
2147
2131
|
if (this.pages[0].length < this.pageSize) {
|
|
2148
2132
|
this.pages[0].unshift(node);
|
|
@@ -2154,49 +2138,40 @@ const RtkPaginatedList = class {
|
|
|
2154
2138
|
this.loadNextPage();
|
|
2155
2139
|
}
|
|
2156
2140
|
}
|
|
2157
|
-
// If autoscroll is enabled,
|
|
2141
|
+
// If autoscroll is enabled, scroll to the bottom
|
|
2158
2142
|
if (this.autoScroll) {
|
|
2159
2143
|
this.shouldScrollToBottom = true;
|
|
2160
2144
|
this.scrollToBottom();
|
|
2161
2145
|
}
|
|
2162
|
-
else {
|
|
2163
|
-
this.showNewMessagesCTR = true;
|
|
2164
|
-
}
|
|
2165
|
-
}
|
|
2166
|
-
// this method is called recursively based on shouldScrollToBottom (see scrollEnd listener)
|
|
2167
|
-
scrollToBottom() {
|
|
2168
|
-
this.$bottomRef.scrollIntoView({ behavior: 'smooth' });
|
|
2169
2146
|
}
|
|
2170
2147
|
/**
|
|
2171
2148
|
* Deletes a node anywhere from the list
|
|
2172
2149
|
* @param {string} id - The id of the node to delete
|
|
2173
2150
|
* */
|
|
2174
2151
|
async onNodeDelete(id) {
|
|
2175
|
-
|
|
2176
|
-
|
|
2152
|
+
var _a, _b;
|
|
2153
|
+
let didDelete = false;
|
|
2154
|
+
for (let i = this.pages.length - 1; i >= 0; i--) {
|
|
2177
2155
|
const index = this.pages[i].findIndex((node) => node.id === id);
|
|
2178
|
-
// message
|
|
2179
|
-
if (index
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
this.rerender();
|
|
2198
|
-
}
|
|
2199
|
-
}
|
|
2156
|
+
// if message not found, move on
|
|
2157
|
+
if (index === -1)
|
|
2158
|
+
continue;
|
|
2159
|
+
// delete message
|
|
2160
|
+
this.pages[i].splice(index, 1);
|
|
2161
|
+
// if page is empty, delete it
|
|
2162
|
+
if (this.pages[i].length === 0)
|
|
2163
|
+
this.pages.splice(i, 1);
|
|
2164
|
+
didDelete = true;
|
|
2165
|
+
break;
|
|
2166
|
+
}
|
|
2167
|
+
if (!didDelete)
|
|
2168
|
+
return;
|
|
2169
|
+
// update timestamps
|
|
2170
|
+
const firstPage = this.pages[0];
|
|
2171
|
+
const lastPage = this.pages[this.pages.length - 1];
|
|
2172
|
+
this.newTS = (_a = firstPage === null || firstPage === void 0 ? void 0 : firstPage[0]) === null || _a === void 0 ? void 0 : _a.timeMs;
|
|
2173
|
+
this.oldTS = (_b = lastPage === null || lastPage === void 0 ? void 0 : lastPage[lastPage.length - 1]) === null || _b === void 0 ? void 0 : _b.timeMs;
|
|
2174
|
+
this.rerender();
|
|
2200
2175
|
}
|
|
2201
2176
|
/**
|
|
2202
2177
|
* Updates a new node anywhere in the list
|
|
@@ -2204,146 +2179,182 @@ const RtkPaginatedList = class {
|
|
|
2204
2179
|
* @param {DataNode} _node - The updated data node
|
|
2205
2180
|
* */
|
|
2206
2181
|
async onNodeUpdate(_id, _node) { }
|
|
2207
|
-
rerender() {
|
|
2208
|
-
this.rerenderBoolean = !this.rerenderBoolean;
|
|
2209
|
-
}
|
|
2210
2182
|
connectedCallback() {
|
|
2211
2183
|
this.rerender = debounce(this.rerender.bind(this), 50, { maxWait: 200 });
|
|
2212
|
-
this.intersectionObserver = new IntersectionObserver((entries) => {
|
|
2213
|
-
writeTask(async () => {
|
|
2214
|
-
for (const entry of entries) {
|
|
2215
|
-
if (entry.target.id === 'top-scroll' && entry.isIntersecting) {
|
|
2216
|
-
this.isLoadingTop = true;
|
|
2217
|
-
await this.loadPrevPage();
|
|
2218
|
-
this.isLoadingTop = false;
|
|
2219
|
-
}
|
|
2220
|
-
}
|
|
2221
|
-
});
|
|
2222
|
-
});
|
|
2223
2184
|
}
|
|
2224
2185
|
componentDidLoad() {
|
|
2225
|
-
|
|
2186
|
+
// initial load
|
|
2187
|
+
this.loadPrevPage();
|
|
2226
2188
|
if (this.$containerRef) {
|
|
2227
2189
|
this.$containerRef.onscrollend = async () => {
|
|
2228
|
-
|
|
2229
|
-
* Load new page if:
|
|
2230
|
-
* if there are nodes to load at the bottom (maxTS > newTS)
|
|
2231
|
-
* or if there are pages to fill at the bottom (firstEmptyIndex > -1)
|
|
2232
|
-
*/
|
|
2233
|
-
if (this.isAtBottom() && (this.maxTS > this.newTS || this.firstEmptyIndex > -1)) {
|
|
2234
|
-
this.isLoadingBottom = true;
|
|
2190
|
+
if (this.isInView(this.$bottomRef)) {
|
|
2235
2191
|
await this.loadNextPage();
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2192
|
+
}
|
|
2193
|
+
else if (this.isInView(this.$topRef)) {
|
|
2194
|
+
this.showNewMessagesCTR = true;
|
|
2195
|
+
await this.loadPrevPage();
|
|
2239
2196
|
}
|
|
2240
2197
|
};
|
|
2241
2198
|
}
|
|
2242
2199
|
}
|
|
2200
|
+
componentDidRender() {
|
|
2201
|
+
if (!this.pendingScrollAnchor)
|
|
2202
|
+
return;
|
|
2203
|
+
const anchor = this.pendingScrollAnchor;
|
|
2204
|
+
this.pendingScrollAnchor = null;
|
|
2205
|
+
this.restoreScrollToAnchor(anchor);
|
|
2206
|
+
}
|
|
2243
2207
|
async loadPrevPage() {
|
|
2244
2208
|
if (this.isLoading)
|
|
2245
2209
|
return;
|
|
2246
|
-
|
|
2247
|
-
* NOTE(ikabra): this case also runs on initial load
|
|
2248
|
-
* if scrolling up ->
|
|
2249
|
-
* fetch older messages and push to the end of the array
|
|
2250
|
-
* cleanup 1st non empty page from the array if length exceeds pagesAllowed
|
|
2251
|
-
*/
|
|
2210
|
+
const scrollAnchor = this.getScrollAnchor('top');
|
|
2252
2211
|
// if no old timestamp, it means we are at initial state
|
|
2253
2212
|
if (!this.oldTS)
|
|
2254
2213
|
this.oldTS = new Date().getTime();
|
|
2255
2214
|
// load data
|
|
2256
2215
|
this.isLoading = true;
|
|
2216
|
+
this.isLoadingTop = true;
|
|
2257
2217
|
const data = await this.fetchData(this.oldTS - 1, this.pageSize, true);
|
|
2258
2218
|
this.isLoading = false;
|
|
2219
|
+
this.isLoadingTop = false;
|
|
2259
2220
|
// no more old messages to show, we are at the top of the page
|
|
2260
2221
|
if (!data.length)
|
|
2261
2222
|
return;
|
|
2262
2223
|
// add old data to the end of the array
|
|
2263
2224
|
this.pages.push(data);
|
|
2264
2225
|
// clear old pages when we reach the limit
|
|
2265
|
-
if (this.pages.length > this.pagesAllowed)
|
|
2266
|
-
this.pages
|
|
2267
|
-
|
|
2268
|
-
* find last non empty page in range (this.pages.length, this.firstEmptyIndex)
|
|
2269
|
-
* we are doing this because any of the middle pages in the currently rendered pages
|
|
2270
|
-
* could be empty as we allow deleting messages.
|
|
2271
|
-
* This helps us set the first empty index correctly.
|
|
2272
|
-
*/
|
|
2273
|
-
for (let i = this.firstEmptyIndex + 1; i < this.pages.length; i++) {
|
|
2274
|
-
if (this.pages[i].length > 0)
|
|
2275
|
-
break;
|
|
2276
|
-
this.firstEmptyIndex = i;
|
|
2277
|
-
}
|
|
2278
|
-
}
|
|
2279
|
-
// update the old timestamp
|
|
2226
|
+
if (this.pages.length > this.pagesAllowed)
|
|
2227
|
+
this.pages.shift();
|
|
2228
|
+
// update timestamps
|
|
2280
2229
|
const lastPage = this.pages[this.pages.length - 1];
|
|
2281
2230
|
this.oldTS = lastPage[lastPage.length - 1].timeMs;
|
|
2282
|
-
|
|
2283
|
-
this.newTS = this.pages[this.firstEmptyIndex + 1][0].timeMs;
|
|
2231
|
+
this.newTS = this.pages[0][0].timeMs;
|
|
2284
2232
|
this.rerender();
|
|
2233
|
+
// fix scroll position
|
|
2234
|
+
if (scrollAnchor)
|
|
2235
|
+
this.pendingScrollAnchor = scrollAnchor;
|
|
2285
2236
|
}
|
|
2286
2237
|
async loadNextPage() {
|
|
2287
2238
|
if (this.isLoading)
|
|
2288
2239
|
return;
|
|
2289
|
-
//
|
|
2240
|
+
// Do nothing. New timestamp needs to be assigned by loadPrevPage method
|
|
2290
2241
|
if (!this.newTS) {
|
|
2291
2242
|
this.showNewMessagesCTR = false;
|
|
2292
2243
|
this.shouldScrollToBottom = false;
|
|
2293
2244
|
return;
|
|
2294
2245
|
}
|
|
2295
|
-
//
|
|
2246
|
+
// for autoscroll or scroll to bottom button
|
|
2247
|
+
const maxAutoLoads = 200;
|
|
2248
|
+
let loads = 0;
|
|
2249
|
+
let prevNewTS = this.newTS;
|
|
2296
2250
|
this.isLoading = true;
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
this.
|
|
2302
|
-
this.
|
|
2303
|
-
//
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
this.pages[this.firstEmptyIndex] = data.reverse();
|
|
2311
|
-
}
|
|
2312
|
-
else {
|
|
2313
|
-
// when already at the bottom and loading messages in realtime
|
|
2251
|
+
this.isLoadingBottom = true;
|
|
2252
|
+
while (loads < maxAutoLoads) {
|
|
2253
|
+
const scrollAnchor = this.getScrollAnchor('bottom');
|
|
2254
|
+
const data = await this.fetchData(this.newTS + 1, this.pageSize, false);
|
|
2255
|
+
this.isLoading = false;
|
|
2256
|
+
this.isLoadingBottom = false;
|
|
2257
|
+
// no more new messages to load
|
|
2258
|
+
if (!data.length) {
|
|
2259
|
+
this.showNewMessagesCTR = false;
|
|
2260
|
+
this.shouldScrollToBottom = false;
|
|
2261
|
+
break;
|
|
2262
|
+
}
|
|
2263
|
+
// load new messages and append to the start
|
|
2314
2264
|
this.pages.unshift(data.reverse());
|
|
2265
|
+
// remove pages if out of bounds
|
|
2266
|
+
if (this.pages.length > this.pagesAllowed)
|
|
2267
|
+
this.pages.pop();
|
|
2268
|
+
// update timestamps
|
|
2269
|
+
const lastPage = this.pages[this.pages.length - 1];
|
|
2270
|
+
this.oldTS = lastPage[lastPage.length - 1].timeMs;
|
|
2271
|
+
this.newTS = this.pages[0][0].timeMs;
|
|
2272
|
+
this.rerender();
|
|
2273
|
+
this.pendingScrollAnchor = scrollAnchor;
|
|
2274
|
+
if (!this.shouldScrollToBottom)
|
|
2275
|
+
break;
|
|
2276
|
+
// if should scroll to bottom then retrigger
|
|
2277
|
+
await this.waitForNextFrame();
|
|
2278
|
+
this.scrollToBottom();
|
|
2279
|
+
await this.waitForNextFrame();
|
|
2280
|
+
// if no new messages, break
|
|
2281
|
+
if (this.newTS === prevNewTS)
|
|
2282
|
+
break;
|
|
2283
|
+
prevNewTS = this.newTS;
|
|
2284
|
+
loads++;
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
// Find the element that is closest to the top/bottom of the container
|
|
2288
|
+
getScrollAnchor(edge = 'top') {
|
|
2289
|
+
if (!this.$containerRef)
|
|
2290
|
+
return null;
|
|
2291
|
+
const containerRect = this.$containerRef.getBoundingClientRect();
|
|
2292
|
+
const candidates = Array.from(this.$containerRef.querySelectorAll('[id]')).filter((el) => el.id !== 'top-scroll' && el.id !== 'bottom-scroll');
|
|
2293
|
+
let best = null;
|
|
2294
|
+
for (const el of candidates) {
|
|
2295
|
+
const rect = el.getBoundingClientRect();
|
|
2296
|
+
const isVisibleInContainer = rect.bottom > containerRect.top && rect.top < containerRect.bottom;
|
|
2297
|
+
if (!isVisibleInContainer)
|
|
2298
|
+
continue;
|
|
2299
|
+
if (edge === 'top') {
|
|
2300
|
+
const offsetTop = rect.top - containerRect.top;
|
|
2301
|
+
if (best == null || (best.edge === 'top' && offsetTop < best.offsetTop)) {
|
|
2302
|
+
best = { id: el.id, edge: 'top', offsetTop };
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
else {
|
|
2306
|
+
const offsetBottom = containerRect.bottom - rect.bottom;
|
|
2307
|
+
if (best == null || (best.edge === 'bottom' && offsetBottom < best.offsetBottom)) {
|
|
2308
|
+
best = { id: el.id, edge: 'bottom', offsetBottom };
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2315
2311
|
}
|
|
2316
|
-
|
|
2317
|
-
|
|
2312
|
+
return best;
|
|
2313
|
+
}
|
|
2314
|
+
//instant scroll to anchor to make sure we are at the same position after a rerender
|
|
2315
|
+
restoreScrollToAnchor(anchor) {
|
|
2316
|
+
if (!this.$containerRef)
|
|
2317
|
+
return;
|
|
2318
|
+
// make element id safe to use inside a CSS selector
|
|
2319
|
+
const escapeId = (id) => {
|
|
2320
|
+
var _a;
|
|
2321
|
+
const cssEscape = (_a = globalThis.CSS) === null || _a === void 0 ? void 0 : _a.escape;
|
|
2322
|
+
return typeof cssEscape === 'function'
|
|
2323
|
+
? cssEscape(id)
|
|
2324
|
+
: id.replace(/[^a-zA-Z0-9_-]/g, '\\$&');
|
|
2325
|
+
};
|
|
2326
|
+
const el = this.$containerRef.querySelector(`#${escapeId(anchor.id)}`);
|
|
2327
|
+
if (!el)
|
|
2328
|
+
return;
|
|
2329
|
+
const containerRect = this.$containerRef.getBoundingClientRect();
|
|
2330
|
+
const rect = el.getBoundingClientRect();
|
|
2331
|
+
if (anchor.edge === 'top') {
|
|
2332
|
+
const newOffsetTop = rect.top - containerRect.top;
|
|
2333
|
+
this.$containerRef.scrollTop += newOffsetTop - anchor.offsetTop;
|
|
2318
2334
|
}
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
for (let i = this.pages.length - 1; i > this.firstEmptyIndex; i--) {
|
|
2323
|
-
if (this.pages[i].length > 0)
|
|
2324
|
-
break;
|
|
2325
|
-
// if page is empty, remove it
|
|
2326
|
-
this.pages.pop();
|
|
2335
|
+
else {
|
|
2336
|
+
const newOffsetBottom = containerRect.bottom - rect.bottom;
|
|
2337
|
+
this.$containerRef.scrollTop += anchor.offsetBottom - newOffsetBottom;
|
|
2327
2338
|
}
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2339
|
+
}
|
|
2340
|
+
// this method is called recursively based on shouldScrollToBottom (see loadNextPage)
|
|
2341
|
+
scrollToBottom() {
|
|
2342
|
+
this.$bottomRef.scrollIntoView({ behavior: 'smooth' });
|
|
2343
|
+
}
|
|
2344
|
+
waitForNextFrame() {
|
|
2345
|
+
return new Promise((resolve) => requestAnimationFrame(() => resolve()));
|
|
2346
|
+
}
|
|
2347
|
+
rerender() {
|
|
2348
|
+
this.rerenderBoolean = !this.rerenderBoolean;
|
|
2337
2349
|
}
|
|
2338
2350
|
render() {
|
|
2339
2351
|
/**
|
|
2340
|
-
* div.container is flex=column-
|
|
2341
|
-
* which is why div#bottom-scroll comes before div#top-scroll
|
|
2352
|
+
* div.container is flex=column-reversewhich is why div#bottom-scroll comes before div#top-scroll
|
|
2342
2353
|
*/
|
|
2343
|
-
return (h(Host, { key: '
|
|
2354
|
+
return (h(Host, { key: 'e0f806cccdcba162d0c834476863b34630cb1a1e' }, h("div", { key: '6d54d50ed703a59df8d26399499533e3cb0d70fe', class: "scrollbar container", part: "container", ref: (el) => (this.$containerRef = el) }, h("div", { key: '4e9fcbed725fb55d9fbb67eca94b0e770662d51b', class: { 'show-new-messages-ctr': true, active: this.showNewMessagesCTR } }, h("rtk-button", { key: '7db0236d35db3fb9856fea7a4f62c1fbd421829e', class: "show-new-messages", kind: "icon", variant: "secondary", part: "show-new-messages", onClick: () => {
|
|
2344
2355
|
this.shouldScrollToBottom = true;
|
|
2345
2356
|
this.scrollToBottom();
|
|
2346
|
-
} }, h("rtk-icon", { key: '
|
|
2357
|
+
} }, h("rtk-icon", { key: '6e247e86029560601080e0b4d6dcfccbd90fcdd6', icon: this.iconPack.chevron_down }))), h("div", { key: '01d1ba7eacc67ece70b805fb2dfb493cdf1c9d23', class: "smallest-dom-element", id: "bottom-scroll", ref: (el) => (this.$bottomRef = el) }), this.isLoadingBottom && this.pages.length > 0 && h("rtk-spinner", { key: '24391bfc34914675cbfd0c287332bfdf3f5f5000', size: "sm" }), this.isLoading && this.pages.length < 1 && h("rtk-spinner", { key: 'e6c2cb44fce52ca54c9f1e543d91a29648745408', size: "lg" }), !this.isLoading && this.pages.flat().length === 0 ? (h("div", { class: "empty-list" }, this.t('list.empty'))) : (h("div", { class: "page-wrapper" }, this.pages.map((page, pageIndex) => (h("div", { class: "page", "data-page-index": pageIndex }, this.createNodes([...page].reverse())))))), this.isLoadingTop && this.pages.length > 0 && h("rtk-spinner", { key: 'd90a15494bcd7ec6c9c7ffc5e9a55054252a4258', size: "sm" }), h("div", { key: '2a65f98c3c4e6f2510c6cf1b4e2bcf1e607a7552', class: "smallest-dom-element", id: "top-scroll", ref: (el) => (this.$topRef = el) }))));
|
|
2347
2358
|
}
|
|
2348
2359
|
};
|
|
2349
2360
|
__decorate$2([
|
|
@@ -90,9 +90,9 @@ const RtkChatToggle = class {
|
|
|
90
90
|
const { messages } = await chat.getMessages(new Date().getTime(), this.pageSize, true);
|
|
91
91
|
const meetingStartedTimeMs = (_b = (_a = this.meeting.meta) === null || _a === void 0 ? void 0 : _a.meetingStartedTimestamp.getTime()) !== null && _b !== void 0 ? _b : 0;
|
|
92
92
|
const newMessages = messages.filter((m) => m.timeMs > meetingStartedTimeMs);
|
|
93
|
-
if (newMessages.length ===
|
|
93
|
+
if (newMessages.length === this.pageSize && newMessages.length > 0) {
|
|
94
94
|
// all messages are new, so we can't know the exact count, but we know there are at least pageSize - 1 new messages
|
|
95
|
-
this.unreadMessageCount =
|
|
95
|
+
this.unreadMessageCount = newMessages.length;
|
|
96
96
|
}
|
|
97
97
|
else {
|
|
98
98
|
this.unreadMessageCount = newMessages.length;
|
|
@@ -228,7 +228,10 @@ const RtkNotifications = class {
|
|
|
228
228
|
this.waitlistedParticipantLeftListener = (participant) => {
|
|
229
229
|
this.remove(`${participant.id}-joined-waitlist`);
|
|
230
230
|
};
|
|
231
|
-
this.chatUpdateListener = ({ message }) => {
|
|
231
|
+
this.chatUpdateListener = ({ message, action }) => {
|
|
232
|
+
// NOTE(ikabra): we only want notifications for new messages
|
|
233
|
+
if (action !== 'add')
|
|
234
|
+
return;
|
|
232
235
|
const parsedMessage = parseMessageForTarget(message);
|
|
233
236
|
if (parsedMessage != null) {
|
|
234
237
|
if (parsedMessage.userId === meeting.self.userId) {
|