@convokitapp/vue-ui 0.4.1 → 0.5.0
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/CHANGELOG.md +33 -0
- package/PARITY.md +13 -6
- package/README.md +46 -0
- package/dist/index.cjs +147 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -5
- package/dist/index.d.ts +50 -5
- package/dist/index.js +146 -38
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -21,7 +21,7 @@ function createConvoKitUiClient(client) {
|
|
|
21
21
|
getMessages: (options) => client.getMessages(options),
|
|
22
22
|
getMessage: (id) => client.getMessage(id),
|
|
23
23
|
sendMessage: (input) => client.sendMessage(input),
|
|
24
|
-
markConversationRead: (conversationId) => client.markConversationRead(conversationId),
|
|
24
|
+
markConversationRead: (conversationId, options) => client.markConversationRead(conversationId, options),
|
|
25
25
|
sendTyping: (input) => client.sendTyping(input),
|
|
26
26
|
onMessage: (conversationId, handler, onError) => client.realtime.onMessage(conversationId, {
|
|
27
27
|
onEvent: handler,
|
|
@@ -43,9 +43,13 @@ import { AvatarFallback, AvatarImage, AvatarRoot } from "reka-ui";
|
|
|
43
43
|
import { defineComponent, h } from "vue";
|
|
44
44
|
|
|
45
45
|
// src/utils.ts
|
|
46
|
+
import { readThrough } from "@convokitapp/sdk";
|
|
46
47
|
import { clsx } from "clsx";
|
|
47
48
|
import { normalizeClass } from "vue";
|
|
48
49
|
var pendingMessageIdPrefix = "convokit-pending-";
|
|
50
|
+
function compareMessageOrder(left, right) {
|
|
51
|
+
return left.createdAt.getTime() - right.createdAt.getTime() || (left.id < right.id ? -1 : left.id > right.id ? 1 : 0);
|
|
52
|
+
}
|
|
49
53
|
function isConvoKitPendingMessage(message) {
|
|
50
54
|
return message.id.startsWith(pendingMessageIdPrefix);
|
|
51
55
|
}
|
|
@@ -94,13 +98,16 @@ function mergeMessages(current, incoming) {
|
|
|
94
98
|
const leftPending = isConvoKitPendingMessage(left);
|
|
95
99
|
const rightPending = isConvoKitPendingMessage(right);
|
|
96
100
|
if (leftPending !== rightPending) return leftPending ? 1 : -1;
|
|
97
|
-
|
|
98
|
-
return byTime === 0 ? left.id.localeCompare(right.id) : byTime;
|
|
101
|
+
return compareMessageOrder(left, right);
|
|
99
102
|
});
|
|
100
103
|
}
|
|
101
|
-
function readerIdsFor(message, readAtByUserId) {
|
|
104
|
+
function readerIdsFor(message, readAtByUserId, readPositionByUserId = /* @__PURE__ */ new Map()) {
|
|
102
105
|
if (isConvoKitPendingMessage(message)) return /* @__PURE__ */ new Set();
|
|
103
|
-
|
|
106
|
+
const userIds = /* @__PURE__ */ new Set([...readAtByUserId.keys(), ...readPositionByUserId.keys()]);
|
|
107
|
+
return new Set([...userIds].filter((userId) => userId !== message.senderId && readThrough({
|
|
108
|
+
readPosition: readPositionByUserId.get(userId) ?? null,
|
|
109
|
+
lastReadAt: readAtByUserId.get(userId) ?? null
|
|
110
|
+
}, message)));
|
|
104
111
|
}
|
|
105
112
|
function partClass(part, appearance, defaultClass) {
|
|
106
113
|
return cx(!appearance.unstyled && defaultClass, appearance.classNames?.[part]);
|
|
@@ -151,6 +158,7 @@ import { ArrowLeft, LoaderCircle as LoaderCircle2, Paperclip, RefreshCw, Send }
|
|
|
151
158
|
import {
|
|
152
159
|
defineComponent as defineComponent3,
|
|
153
160
|
h as h3,
|
|
161
|
+
onBeforeUnmount,
|
|
154
162
|
ref as ref2,
|
|
155
163
|
watchEffect
|
|
156
164
|
} from "vue";
|
|
@@ -169,8 +177,20 @@ function hasContent(message) {
|
|
|
169
177
|
function newest(current, incoming, incomingComplete = true) {
|
|
170
178
|
return version(current) > version(incoming) || !incomingComplete && version(current) === version(incoming) ? current : incoming;
|
|
171
179
|
}
|
|
172
|
-
|
|
173
|
-
|
|
180
|
+
var compare = compareMessageOrder;
|
|
181
|
+
function positionCursor(position) {
|
|
182
|
+
return { createdAt: position.createdAt, id: position.messageId };
|
|
183
|
+
}
|
|
184
|
+
function readEntry(participant) {
|
|
185
|
+
return { userId: participant.appUserId, readAt: participant.lastReadAt, readPosition: participant.readPosition };
|
|
186
|
+
}
|
|
187
|
+
function acknowledgement() {
|
|
188
|
+
return { inFlight: void 0, followUp: false, suppressed: false, target: void 0, acknowledged: void 0, unacknowledgeable: /* @__PURE__ */ new Set() };
|
|
189
|
+
}
|
|
190
|
+
function isTargetMiss(cause) {
|
|
191
|
+
if (typeof cause !== "object" || cause === null) return false;
|
|
192
|
+
const { code, status } = cause;
|
|
193
|
+
return code === "MESSAGE_NOT_FOUND" || code === void 0 && status === 404;
|
|
174
194
|
}
|
|
175
195
|
function blank(currentUserId = "") {
|
|
176
196
|
return {
|
|
@@ -178,6 +198,7 @@ function blank(currentUserId = "") {
|
|
|
178
198
|
messages: [],
|
|
179
199
|
typingUserIds: /* @__PURE__ */ new Set(),
|
|
180
200
|
readAtByUserId: /* @__PURE__ */ new Map(),
|
|
201
|
+
readPositionByUserId: /* @__PURE__ */ new Map(),
|
|
181
202
|
isInitialLoading: false,
|
|
182
203
|
isLoadingOlder: false,
|
|
183
204
|
isReconciling: false,
|
|
@@ -225,6 +246,9 @@ var ConversationStore = class {
|
|
|
225
246
|
hydrationPool = { running: /* @__PURE__ */ new Set(), queued: /* @__PURE__ */ new Map() };
|
|
226
247
|
// Keep tombstones until an explicit reload/session change, including across refreshes.
|
|
227
248
|
deleted = /* @__PURE__ */ new Set();
|
|
249
|
+
ack = acknowledgement();
|
|
250
|
+
// Visible until the platform reports otherwise; unknown/prerender/no document count as visible.
|
|
251
|
+
visible = true;
|
|
228
252
|
sendRevision;
|
|
229
253
|
activeSend;
|
|
230
254
|
refreshQueued = false;
|
|
@@ -287,6 +311,7 @@ var ConversationStore = class {
|
|
|
287
311
|
this.hydrations.clear();
|
|
288
312
|
this.hydrationPool.queued.clear();
|
|
289
313
|
this.deleted.clear();
|
|
314
|
+
this.ack = acknowledgement();
|
|
290
315
|
this.sendRevision = void 0;
|
|
291
316
|
this.activeSend = void 0;
|
|
292
317
|
this.refreshQueued = false;
|
|
@@ -342,8 +367,8 @@ var ConversationStore = class {
|
|
|
342
367
|
if (!this.alive(generation) || conversationId !== this.room || !id.trim()) return;
|
|
343
368
|
this.removeMessage(id);
|
|
344
369
|
}, report));
|
|
345
|
-
add(() => this.client.onReadReceipt(this.room, ({ userId, readAt }) => {
|
|
346
|
-
if (this.alive(generation)) this.mergeReads([
|
|
370
|
+
add(() => this.client.onReadReceipt(this.room, ({ userId, readAt, readPosition }) => {
|
|
371
|
+
if (this.alive(generation)) this.mergeReads([{ userId, readAt, readPosition }]);
|
|
347
372
|
}, report));
|
|
348
373
|
add(() => this.client.onTyping(this.room, ({ userId, isTyping }) => {
|
|
349
374
|
if (!this.alive(generation) || !userId.trim() || userId === this.user) return;
|
|
@@ -386,9 +411,6 @@ var ConversationStore = class {
|
|
|
386
411
|
this.hydrations.set(message.id, job);
|
|
387
412
|
this.hydrationPool.queued.set(message.id, job);
|
|
388
413
|
this.drainHydration();
|
|
389
|
-
if (type === "insert" && !existing && message.senderId !== this.user && (this.options.markReadOnReceive ?? true)) {
|
|
390
|
-
void this.markRead();
|
|
391
|
-
}
|
|
392
414
|
}
|
|
393
415
|
record(message, insert, revision, complete) {
|
|
394
416
|
const existing = this.state.messages.find((item) => item.id === message.id);
|
|
@@ -398,7 +420,9 @@ var ConversationStore = class {
|
|
|
398
420
|
this.confirmSend(message);
|
|
399
421
|
}
|
|
400
422
|
this.changes.set(message.id, { revision, message, insert, complete });
|
|
401
|
-
if (existing
|
|
423
|
+
if (!existing && !(insert && hasContent(message))) return;
|
|
424
|
+
this.patch({ messages: mergeMessages(this.state.messages, [message]) });
|
|
425
|
+
if (!existing && message.senderId !== this.user && (this.options.markReadOnReceive ?? true)) void this.acknowledge(true);
|
|
402
426
|
}
|
|
403
427
|
confirmSend(message) {
|
|
404
428
|
const send = this.activeSend;
|
|
@@ -410,11 +434,19 @@ var ConversationStore = class {
|
|
|
410
434
|
return this.alive(job.generation) && !this.deleted.has(job.message.id) && this.hydrations.get(job.message.id) === job;
|
|
411
435
|
}
|
|
412
436
|
removeMessage(id) {
|
|
437
|
+
this.forget(id);
|
|
438
|
+
this.patch({ messages: this.state.messages.filter((message) => message.id !== id) });
|
|
439
|
+
}
|
|
440
|
+
/** Tombstone a row learned to be gone; a removed acknowledgement target is re-resolved from what remains. */
|
|
441
|
+
forget(id) {
|
|
413
442
|
this.deleted.add(id);
|
|
414
443
|
this.changes.delete(id);
|
|
415
444
|
this.hydrations.delete(id);
|
|
416
445
|
this.hydrationPool.queued.delete(id);
|
|
417
|
-
|
|
446
|
+
const ack = this.ack;
|
|
447
|
+
if (ack.target !== id && ack.acknowledged?.id !== id) return;
|
|
448
|
+
ack.unacknowledgeable.add(id);
|
|
449
|
+
if (ack.target === id) ack.followUp = true;
|
|
418
450
|
}
|
|
419
451
|
drainHydration() {
|
|
420
452
|
const pool = this.hydrationPool;
|
|
@@ -451,13 +483,23 @@ var ConversationStore = class {
|
|
|
451
483
|
});
|
|
452
484
|
}
|
|
453
485
|
}
|
|
486
|
+
/** Both maps only ever advance: acknowledgement times by time, positions by (createdAt, id). Participants and
|
|
487
|
+
* read events are the only sources; the local user's own read is never written from the device clock.
|
|
488
|
+
*/
|
|
454
489
|
mergeReads(entries) {
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
490
|
+
const readAt = new Map(this.state.readAtByUserId);
|
|
491
|
+
const positions = new Map(this.state.readPositionByUserId);
|
|
492
|
+
for (const entry of entries) {
|
|
493
|
+
const { userId, readPosition } = entry;
|
|
494
|
+
if (!userId.trim()) continue;
|
|
495
|
+
if (entry.readAt && Number.isFinite(entry.readAt.getTime()) && entry.readAt.getTime() > (readAt.get(userId)?.getTime() ?? -Infinity)) {
|
|
496
|
+
readAt.set(userId, entry.readAt);
|
|
497
|
+
}
|
|
498
|
+
if (!readPosition || typeof readPosition.messageId !== "string" || !readPosition.messageId.trim() || !(readPosition.createdAt instanceof Date) || !Number.isFinite(readPosition.createdAt.getTime())) continue;
|
|
499
|
+
const current = positions.get(userId);
|
|
500
|
+
if (!current || compare(positionCursor(readPosition), positionCursor(current)) > 0) positions.set(userId, readPosition);
|
|
459
501
|
}
|
|
460
|
-
this.patch({ readAtByUserId:
|
|
502
|
+
this.patch({ readAtByUserId: readAt, readPositionByUserId: positions });
|
|
461
503
|
}
|
|
462
504
|
validatePage(page, before) {
|
|
463
505
|
if (page.length > this.pageSize) throw new Error("Message page exceeds the requested limit");
|
|
@@ -514,9 +556,9 @@ var ConversationStore = class {
|
|
|
514
556
|
this.validatePage(page);
|
|
515
557
|
this.cursor = page.at(-1);
|
|
516
558
|
this.patch({ conversation, messages: this.overlay(page, revision), hasOlderMessages: page.length === this.pageSize });
|
|
517
|
-
this.mergeReads(conversation.participants.
|
|
559
|
+
this.mergeReads(conversation.participants.map(readEntry));
|
|
518
560
|
this.prune(revision);
|
|
519
|
-
if (this.options.markReadOnLoad ?? true) await this.
|
|
561
|
+
if (this.options.markReadOnLoad ?? true) await this.acknowledge(true);
|
|
520
562
|
} catch (cause) {
|
|
521
563
|
this.fail(cause, generation, true);
|
|
522
564
|
} finally {
|
|
@@ -572,14 +614,9 @@ var ConversationStore = class {
|
|
|
572
614
|
const reconciled = this.overlay(rows, revision);
|
|
573
615
|
const survivingIds = new Set(reconciled.map((message) => message.id));
|
|
574
616
|
const known = this.state.messages.filter((message) => !isConvoKitPendingMessage(message)).map((message) => message.id).concat([...this.changes].filter(([, change]) => change.insert && change.revision <= revision).map(([id]) => id));
|
|
575
|
-
for (const id of known) if (!survivingIds.has(id))
|
|
576
|
-
this.deleted.add(id);
|
|
577
|
-
this.changes.delete(id);
|
|
578
|
-
this.hydrations.delete(id);
|
|
579
|
-
this.hydrationPool.queued.delete(id);
|
|
580
|
-
}
|
|
617
|
+
for (const id of known) if (!survivingIds.has(id)) this.forget(id);
|
|
581
618
|
this.patch({ conversation, messages: reconciled, hasOlderMessages: hasOlder });
|
|
582
|
-
this.mergeReads(conversation.participants.
|
|
619
|
+
this.mergeReads(conversation.participants.map(readEntry));
|
|
583
620
|
this.prune(revision);
|
|
584
621
|
} catch (cause) {
|
|
585
622
|
this.fail(cause, generation, true);
|
|
@@ -614,15 +651,68 @@ var ConversationStore = class {
|
|
|
614
651
|
}
|
|
615
652
|
}
|
|
616
653
|
};
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
654
|
+
/** Acknowledge through the newest rendered row now, regardless of visibility; no request without a target. */
|
|
655
|
+
markRead = () => this.alive() ? this.acknowledge(false) : Promise.resolve();
|
|
656
|
+
/** Automatic acknowledgements wait while hidden and are re-issued (once) on becoming visible. */
|
|
657
|
+
setVisible = (visible) => {
|
|
658
|
+
this.visible = visible;
|
|
659
|
+
if (!visible || !this.ack.suppressed || !this.alive()) return;
|
|
660
|
+
this.ack.suppressed = false;
|
|
661
|
+
void this.acknowledge(true);
|
|
662
|
+
};
|
|
663
|
+
/** The newest non-pending rendered row by (createdAt, id), never by list index and never a raw realtime
|
|
664
|
+
* row; rows the server does not know are skipped, and nothing at or before the accepted target is re-sent.
|
|
665
|
+
*/
|
|
666
|
+
ackTarget(ack) {
|
|
667
|
+
let target;
|
|
668
|
+
for (const message of this.state.messages) {
|
|
669
|
+
if (isConvoKitPendingMessage(message) || ack.unacknowledgeable.has(message.id)) continue;
|
|
670
|
+
if (!target || compare(message, target) > 0) target = { createdAt: message.createdAt, id: message.id };
|
|
671
|
+
}
|
|
672
|
+
return target && (!ack.acknowledged || compare(target, ack.acknowledged) > 0) ? target : void 0;
|
|
673
|
+
}
|
|
674
|
+
/** Resolves when the request this call issued or joined settles; a follow-up is issued, not awaited. */
|
|
675
|
+
acknowledge(automatic) {
|
|
676
|
+
const ack = this.ack;
|
|
677
|
+
if (automatic && !this.visible) {
|
|
678
|
+
ack.suppressed = true;
|
|
679
|
+
return Promise.resolve();
|
|
680
|
+
}
|
|
681
|
+
if (ack.inFlight) {
|
|
682
|
+
ack.followUp = true;
|
|
683
|
+
return ack.inFlight;
|
|
684
|
+
}
|
|
685
|
+
return this.issue(ack, this.generation) ?? Promise.resolve();
|
|
686
|
+
}
|
|
687
|
+
issue(ack, generation) {
|
|
688
|
+
ack.followUp = false;
|
|
689
|
+
const target = this.ackTarget(ack);
|
|
690
|
+
if (!target) return void 0;
|
|
691
|
+
ack.target = target.id;
|
|
692
|
+
ack.inFlight = this.send(ack, generation, target).finally(() => {
|
|
693
|
+
ack.inFlight = void 0;
|
|
694
|
+
ack.target = void 0;
|
|
695
|
+
if (!this.alive(generation) || !ack.followUp) return;
|
|
696
|
+
if (!this.visible) {
|
|
697
|
+
ack.followUp = false;
|
|
698
|
+
ack.suppressed = true;
|
|
699
|
+
} else this.issue(ack, generation);
|
|
700
|
+
});
|
|
701
|
+
return ack.inFlight;
|
|
702
|
+
}
|
|
703
|
+
async send(ack, generation, target) {
|
|
620
704
|
try {
|
|
621
|
-
await this.client.markConversationRead(this.room);
|
|
705
|
+
await this.client.markConversationRead(this.room, { throughMessageId: target.id });
|
|
706
|
+
if (!this.alive(generation)) return;
|
|
707
|
+
if (!ack.acknowledged || compare(target, ack.acknowledged) > 0) ack.acknowledged = target;
|
|
622
708
|
} catch (cause) {
|
|
623
|
-
this.
|
|
709
|
+
if (!this.alive(generation)) return;
|
|
710
|
+
if (isTargetMiss(cause)) {
|
|
711
|
+
ack.unacknowledgeable.add(target.id);
|
|
712
|
+
ack.followUp = true;
|
|
713
|
+
} else this.fail(cause, generation);
|
|
624
714
|
}
|
|
625
|
-
}
|
|
715
|
+
}
|
|
626
716
|
updateTyping = async (isTyping) => {
|
|
627
717
|
if (!this.alive()) return;
|
|
628
718
|
const generation = this.generation;
|
|
@@ -705,7 +795,7 @@ var ConversationStore = class {
|
|
|
705
795
|
}
|
|
706
796
|
}
|
|
707
797
|
};
|
|
708
|
-
readerIdsFor = (message) => readerIdsFor(message, this.state.readAtByUserId);
|
|
798
|
+
readerIdsFor = (message) => readerIdsFor(message, this.state.readAtByUserId, this.state.readPositionByUserId);
|
|
709
799
|
};
|
|
710
800
|
|
|
711
801
|
// src/composables/use-conversation.ts
|
|
@@ -718,6 +808,7 @@ function useConversation(options) {
|
|
|
718
808
|
let store = createStore();
|
|
719
809
|
const snapshot = shallowRef(store.getSnapshot());
|
|
720
810
|
let unsubscribe;
|
|
811
|
+
let visible = true;
|
|
721
812
|
const stop = watch(
|
|
722
813
|
() => [toValue(options.client), toValue(options.client).sessionIdentity, toValue(options.conversationId)],
|
|
723
814
|
() => {
|
|
@@ -728,6 +819,7 @@ function useConversation(options) {
|
|
|
728
819
|
unsubscribe = store.subscribe(() => {
|
|
729
820
|
snapshot.value = store.getSnapshot();
|
|
730
821
|
});
|
|
822
|
+
store.setVisible(visible);
|
|
731
823
|
store.start(options.autoLoad ?? true);
|
|
732
824
|
},
|
|
733
825
|
{ immediate: true, flush: "sync" }
|
|
@@ -747,6 +839,7 @@ function useConversation(options) {
|
|
|
747
839
|
messages: field("messages"),
|
|
748
840
|
typingUserIds: field("typingUserIds"),
|
|
749
841
|
readAtByUserId: field("readAtByUserId"),
|
|
842
|
+
readPositionByUserId: field("readPositionByUserId"),
|
|
750
843
|
isInitialLoading: field("isInitialLoading"),
|
|
751
844
|
isLoadingOlder: field("isLoadingOlder"),
|
|
752
845
|
isReconciling: field("isReconciling"),
|
|
@@ -762,6 +855,10 @@ function useConversation(options) {
|
|
|
762
855
|
sendMessage: (input) => store.sendMessage(input),
|
|
763
856
|
markRead: () => store.markRead(),
|
|
764
857
|
updateTyping: (isTyping) => store.updateTyping(isTyping),
|
|
858
|
+
setVisible: (value) => {
|
|
859
|
+
visible = value;
|
|
860
|
+
store.setVisible(value);
|
|
861
|
+
},
|
|
765
862
|
dispose
|
|
766
863
|
};
|
|
767
864
|
}
|
|
@@ -834,6 +931,7 @@ var MessageListView = defineComponent2({
|
|
|
834
931
|
messages: { type: Array, required: true },
|
|
835
932
|
currentUserId: { type: String, required: true },
|
|
836
933
|
readAtByUserId: { type: Object, default: () => /* @__PURE__ */ new Map() },
|
|
934
|
+
readPositionByUserId: { type: Object, default: () => /* @__PURE__ */ new Map() },
|
|
837
935
|
readersResolver: { type: Function, default: void 0 },
|
|
838
936
|
onLoadOlder: { type: Function, default: void 0 },
|
|
839
937
|
hasOlderMessages: { type: Boolean, default: false },
|
|
@@ -893,7 +991,7 @@ var MessageListView = defineComponent2({
|
|
|
893
991
|
const isCurrentUser = message.senderId === props.currentUserId;
|
|
894
992
|
const sender = participants.value.get(message.senderId);
|
|
895
993
|
const isPending = isConvoKitPendingMessage(message);
|
|
896
|
-
const readerIds = isPending ? /* @__PURE__ */ new Set() : props.readersResolver ? props.readersResolver(message) : readerIdsFor(message, props.readAtByUserId);
|
|
994
|
+
const readerIds = isPending ? /* @__PURE__ */ new Set() : props.readersResolver ? props.readersResolver(message) : readerIdsFor(message, props.readAtByUserId, props.readPositionByUserId);
|
|
897
995
|
const slotProps = { message, chronologicalIndex: index, isCurrentUser, sender, readerIds };
|
|
898
996
|
const custom = slots.message?.(slotProps);
|
|
899
997
|
if (custom) return h2("div", { key: message.id, role: "listitem" }, custom);
|
|
@@ -992,8 +1090,8 @@ var MessageListView = defineComponent2({
|
|
|
992
1090
|
};
|
|
993
1091
|
}
|
|
994
1092
|
});
|
|
995
|
-
function defaultReadersResolver(readAtByUserId) {
|
|
996
|
-
return (message) => readerIdsFor(message, readAtByUserId);
|
|
1093
|
+
function defaultReadersResolver(readAtByUserId, readPositionByUserId = /* @__PURE__ */ new Map()) {
|
|
1094
|
+
return (message) => readerIdsFor(message, readAtByUserId, readPositionByUserId);
|
|
997
1095
|
}
|
|
998
1096
|
|
|
999
1097
|
// src/components/conversation.ts
|
|
@@ -1011,6 +1109,7 @@ var viewProps = {
|
|
|
1011
1109
|
onSendMessage: { type: Function, required: true },
|
|
1012
1110
|
typingUserIds: { type: Object, default: () => /* @__PURE__ */ new Set() },
|
|
1013
1111
|
readAtByUserId: { type: Object, default: () => /* @__PURE__ */ new Map() },
|
|
1112
|
+
readPositionByUserId: { type: Object, default: () => /* @__PURE__ */ new Map() },
|
|
1014
1113
|
readersResolver: { type: Function, default: void 0 },
|
|
1015
1114
|
onBack: { type: Function, default: void 0 },
|
|
1016
1115
|
onRefresh: { type: Function, default: void 0 },
|
|
@@ -1239,6 +1338,7 @@ var ConversationView = defineComponent3({
|
|
|
1239
1338
|
messages: props.messages,
|
|
1240
1339
|
currentUserId: props.currentUserId,
|
|
1241
1340
|
readAtByUserId: props.readAtByUserId,
|
|
1341
|
+
readPositionByUserId: props.readPositionByUserId,
|
|
1242
1342
|
...props.readersResolver ? { readersResolver: props.readersResolver } : {},
|
|
1243
1343
|
...props.onLoadOlder ? { onLoadOlder: loadOlder } : {},
|
|
1244
1344
|
hasOlderMessages: props.hasOlderMessages,
|
|
@@ -1301,6 +1401,12 @@ var Conversation = defineComponent3({
|
|
|
1301
1401
|
watchEffect(() => {
|
|
1302
1402
|
emit("controller-change", controller);
|
|
1303
1403
|
});
|
|
1404
|
+
if (typeof document !== "undefined") {
|
|
1405
|
+
const syncVisibility = () => controller.setVisible(document.visibilityState !== "hidden");
|
|
1406
|
+
syncVisibility();
|
|
1407
|
+
document.addEventListener("visibilitychange", syncVisibility);
|
|
1408
|
+
onBeforeUnmount(() => document.removeEventListener("visibilitychange", syncVisibility));
|
|
1409
|
+
}
|
|
1304
1410
|
return () => {
|
|
1305
1411
|
const loadedConversation = controller.conversation.value;
|
|
1306
1412
|
if (!loadedConversation) {
|
|
@@ -1335,6 +1441,7 @@ var Conversation = defineComponent3({
|
|
|
1335
1441
|
onSendMessage: _onSendMessage,
|
|
1336
1442
|
typingUserIds: _typingUserIds,
|
|
1337
1443
|
readAtByUserId: _readAtByUserId,
|
|
1444
|
+
readPositionByUserId: _readPositionByUserId,
|
|
1338
1445
|
onRefresh: _onRefresh,
|
|
1339
1446
|
onLoadOlder: _onLoadOlder,
|
|
1340
1447
|
onTypingChange: _onTypingChange,
|
|
@@ -1357,6 +1464,7 @@ var Conversation = defineComponent3({
|
|
|
1357
1464
|
},
|
|
1358
1465
|
typingUserIds: controller.typingUserIds.value,
|
|
1359
1466
|
readAtByUserId: controller.readAtByUserId.value,
|
|
1467
|
+
readPositionByUserId: controller.readPositionByUserId.value,
|
|
1360
1468
|
onRefresh: controller.refresh,
|
|
1361
1469
|
onLoadOlder: controller.loadOlderMessages,
|
|
1362
1470
|
onTypingChange: controller.updateTyping,
|