@convokitapp/vue-ui 0.2.1 → 0.2.2
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 +6 -0
- package/PARITY.md +1 -0
- package/README.md +4 -0
- package/dist/index.cjs +16 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +15 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.2
|
|
4
|
+
|
|
5
|
+
- Show `Sending…` for optimistic messages instead of a device-clock timestamp that can jump after acknowledgement.
|
|
6
|
+
- Keep pending messages after canonical history and exclude them from read receipts.
|
|
7
|
+
- Continue displaying acknowledged server timestamps in the viewer's local timezone.
|
|
8
|
+
|
|
3
9
|
## 0.2.1
|
|
4
10
|
|
|
5
11
|
- Show outgoing messages immediately while the request completes, then reconcile the server response and realtime echo without duplicates.
|
package/PARITY.md
CHANGED
|
@@ -16,6 +16,7 @@ while using Vue-native composition patterns.
|
|
|
16
16
|
| Offset pagination and de-duplication | Yes | Yes | Yes |
|
|
17
17
|
| Search, archived, participant, predicate, sort filters | Yes | Yes | Yes |
|
|
18
18
|
| Realtime messages, typing, reads | Yes | Yes | Yes |
|
|
19
|
+
| Pending state without provisional timestamps or receipts | Yes | Yes | Yes |
|
|
19
20
|
| Read markers and reader resolution | Yes | Yes | Yes |
|
|
20
21
|
| Mark read on load/receive | Yes | Yes | Yes |
|
|
21
22
|
| Typing idle timeout | Yes | Yes | Yes |
|
package/README.md
CHANGED
|
@@ -87,6 +87,10 @@ Use `useConversationList` and `useConversation` when you want ConvoKit's
|
|
|
87
87
|
pagination, de-duplication, realtime, typing, and read state without the
|
|
88
88
|
default UI. Both return readonly Vue refs plus actions and a `dispose()` method.
|
|
89
89
|
|
|
90
|
+
Optimistic rows display `Sending…` until acknowledgement. The final server
|
|
91
|
+
timestamp is formatted in the viewer's local timezone. Custom message slots can
|
|
92
|
+
use `isConvoKitPendingMessage(message)` to present the same state.
|
|
93
|
+
|
|
90
94
|
## Appearance
|
|
91
95
|
|
|
92
96
|
Wrap any subtree with `ConvoKitThemeProvider`, set `density="compact"`, or use
|
package/dist/index.cjs
CHANGED
|
@@ -32,6 +32,7 @@ __export(index_exports, {
|
|
|
32
32
|
defaultConvoKitTheme: () => defaultConvoKitTheme,
|
|
33
33
|
defaultReadersResolver: () => defaultReadersResolver,
|
|
34
34
|
formatFileSize: () => formatFileSize,
|
|
35
|
+
isConvoKitPendingMessage: () => isConvoKitPendingMessage,
|
|
35
36
|
matchesConversation: () => matchesConversation,
|
|
36
37
|
mergeConversations: () => mergeConversations,
|
|
37
38
|
mergeMessages: () => mergeMessages,
|
|
@@ -75,6 +76,10 @@ var import_vue = require("vue");
|
|
|
75
76
|
|
|
76
77
|
// src/utils.ts
|
|
77
78
|
var import_clsx = require("clsx");
|
|
79
|
+
var pendingMessageIdPrefix = "convokit-pending-";
|
|
80
|
+
function isConvoKitPendingMessage(message) {
|
|
81
|
+
return message.id.startsWith(pendingMessageIdPrefix);
|
|
82
|
+
}
|
|
78
83
|
function cx(...values) {
|
|
79
84
|
return (0, import_clsx.clsx)(values);
|
|
80
85
|
}
|
|
@@ -117,11 +122,15 @@ function mergeMessages(current, incoming) {
|
|
|
117
122
|
const byId = new Map(current.map((message) => [message.id, message]));
|
|
118
123
|
for (const message of incoming) byId.set(message.id, message);
|
|
119
124
|
return [...byId.values()].sort((left, right) => {
|
|
125
|
+
const leftPending = isConvoKitPendingMessage(left);
|
|
126
|
+
const rightPending = isConvoKitPendingMessage(right);
|
|
127
|
+
if (leftPending !== rightPending) return leftPending ? 1 : -1;
|
|
120
128
|
const byTime = left.createdAt.getTime() - right.createdAt.getTime();
|
|
121
129
|
return byTime === 0 ? left.id.localeCompare(right.id) : byTime;
|
|
122
130
|
});
|
|
123
131
|
}
|
|
124
132
|
function readerIdsFor(message, readAtByUserId) {
|
|
133
|
+
if (isConvoKitPendingMessage(message)) return /* @__PURE__ */ new Set();
|
|
125
134
|
return new Set([...readAtByUserId.entries()].filter(([userId, readAt]) => userId !== message.senderId && readAt.getTime() >= message.createdAt.getTime()).map(([userId]) => userId));
|
|
126
135
|
}
|
|
127
136
|
function partClass(part, appearance, defaultClass) {
|
|
@@ -520,7 +529,8 @@ var MessageListView = (0, import_vue4.defineComponent)({
|
|
|
520
529
|
const renderMessage = (message, index) => {
|
|
521
530
|
const isCurrentUser = message.senderId === props.currentUserId;
|
|
522
531
|
const sender = participants.value.get(message.senderId);
|
|
523
|
-
const
|
|
532
|
+
const isPending = isConvoKitPendingMessage(message);
|
|
533
|
+
const readerIds = isPending ? /* @__PURE__ */ new Set() : props.readersResolver ? props.readersResolver(message) : readerIdsFor(message, props.readAtByUserId);
|
|
524
534
|
const slotProps = { message, chronologicalIndex: index, isCurrentUser, sender, readerIds };
|
|
525
535
|
const custom = slots.message?.(slotProps);
|
|
526
536
|
if (custom) return (0, import_vue4.h)("div", { key: message.id, role: "listitem" }, custom);
|
|
@@ -553,12 +563,12 @@ var MessageListView = (0, import_vue4.defineComponent)({
|
|
|
553
563
|
!isCurrentUser ? (0, import_vue4.h)("strong", { class: "ckui-message-sender" }, sender?.name || message.senderId) : null,
|
|
554
564
|
message.text ? (0, import_vue4.h)("div", { class: "ckui-message-text" }, message.text) : null,
|
|
555
565
|
...mediaNodes,
|
|
556
|
-
(0, import_vue4.h)("
|
|
557
|
-
props.formatTime(message.createdAt),
|
|
558
|
-
isCurrentUser ? readerIds.size > 0 ? (0, import_vue4.h)(import_vue3.CheckCheck, { size: 14, "aria-label": "Read" }) : (0, import_vue4.h)(import_vue3.Check, { size: 14, "aria-label": "Delivered" }) : null
|
|
566
|
+
(0, import_vue4.h)("span", { class: "ckui-message-time" }, [
|
|
567
|
+
isPending ? "Sending\u2026" : props.formatTime(message.createdAt),
|
|
568
|
+
isCurrentUser && !isPending ? readerIds.size > 0 ? (0, import_vue4.h)(import_vue3.CheckCheck, { size: 14, "aria-label": "Read" }) : (0, import_vue4.h)(import_vue3.Check, { size: 14, "aria-label": "Delivered" }) : null
|
|
559
569
|
])
|
|
560
570
|
]),
|
|
561
|
-
isCurrentUser ? slots["read-receipt"]?.(receiptSlotProps) ?? (0, import_vue4.h)("div", {
|
|
571
|
+
isCurrentUser && !isPending ? slots["read-receipt"]?.(receiptSlotProps) ?? (0, import_vue4.h)("div", {
|
|
562
572
|
class: partClass("receipt", currentAppearance, "ckui-read-receipt"),
|
|
563
573
|
style: partStyle("receipt", currentAppearance)
|
|
564
574
|
}, readerIds.size > 0 ? `Read by ${readerIds.size}` : "Delivered") : null
|
|
@@ -1427,6 +1437,7 @@ function useConvoKitTheme() {
|
|
|
1427
1437
|
defaultConvoKitTheme,
|
|
1428
1438
|
defaultReadersResolver,
|
|
1429
1439
|
formatFileSize,
|
|
1440
|
+
isConvoKitPendingMessage,
|
|
1430
1441
|
matchesConversation,
|
|
1431
1442
|
mergeConversations,
|
|
1432
1443
|
mergeMessages,
|