@palbase/web 1.1.1 → 1.2.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/dist/{analytics-facade-DLH-KivI.d.ts → analytics-facade-C93tr7dA.d.ts} +55 -0
- package/dist/{analytics-facade-CAKBIH_U.d.cts → analytics-facade-Ct3A1zop.d.cts} +55 -0
- package/dist/{chunk-EMQGOKW6.js → chunk-OZLVL7G2.js} +319 -47
- package/dist/chunk-OZLVL7G2.js.map +1 -0
- package/dist/index.cjs +318 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/internal.cjs +318 -46
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +3 -3
- package/dist/internal.d.ts +3 -3
- package/dist/internal.js +1 -1
- package/dist/next/client.cjs +318 -46
- package/dist/next/client.cjs.map +1 -1
- package/dist/next/client.js +1 -1
- package/dist/next/index.cjs +318 -46
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +2 -2
- package/dist/next/index.d.ts +2 -2
- package/dist/next/index.js +1 -1
- package/dist/{pb-DioxNuEV.d.cts → pb-BlIfgBG-.d.cts} +1 -1
- package/dist/{pb-HegMSSk-.d.ts → pb-BtYdWClg.d.ts} +1 -1
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-EMQGOKW6.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1746,6 +1746,18 @@ async function listDevices(rt, userId) {
|
|
|
1746
1746
|
}
|
|
1747
1747
|
|
|
1748
1748
|
// src/messaging/group-messaging.ts
|
|
1749
|
+
function encodeReaction(args) {
|
|
1750
|
+
return encodeUtf8(
|
|
1751
|
+
JSON.stringify({
|
|
1752
|
+
v: 1,
|
|
1753
|
+
type: "reaction",
|
|
1754
|
+
client_msg_id: args.clientMsgId,
|
|
1755
|
+
target_client_msg_id: args.targetClientMsgId,
|
|
1756
|
+
emoji: args.emoji,
|
|
1757
|
+
op: args.op
|
|
1758
|
+
})
|
|
1759
|
+
);
|
|
1760
|
+
}
|
|
1749
1761
|
function encodeEnvelope(args) {
|
|
1750
1762
|
const env = {
|
|
1751
1763
|
v: 1,
|
|
@@ -1760,18 +1772,32 @@ function decodeEnvelope(bytes) {
|
|
|
1760
1772
|
const s = decodeUtf8(bytes);
|
|
1761
1773
|
try {
|
|
1762
1774
|
const o = JSON.parse(s);
|
|
1775
|
+
if (typeof o === "object" && o !== null && o.type === "reaction") {
|
|
1776
|
+
return {
|
|
1777
|
+
type: "reaction",
|
|
1778
|
+
text: null,
|
|
1779
|
+
clientMsgId: o.client_msg_id ?? "",
|
|
1780
|
+
replyTo: null,
|
|
1781
|
+
reaction: {
|
|
1782
|
+
targetClientMsgId: o.target_client_msg_id ?? "",
|
|
1783
|
+
emoji: o.emoji ?? "",
|
|
1784
|
+
op: o.op ?? "add"
|
|
1785
|
+
}
|
|
1786
|
+
};
|
|
1787
|
+
}
|
|
1763
1788
|
if (typeof o === "object" && o !== null && o.type === "text" && typeof o.v === "number") {
|
|
1764
1789
|
return {
|
|
1790
|
+
type: "text",
|
|
1765
1791
|
text: o.text ?? null,
|
|
1766
1792
|
clientMsgId: o.client_msg_id ?? "",
|
|
1767
1793
|
replyTo: o.reply_to ?? null
|
|
1768
1794
|
};
|
|
1769
1795
|
}
|
|
1770
1796
|
if (typeof o === "object" && o !== null && o.type === "text") {
|
|
1771
|
-
return { text: o.text ?? null, clientMsgId: "", replyTo: null };
|
|
1797
|
+
return { type: "text", text: o.text ?? null, clientMsgId: "", replyTo: null };
|
|
1772
1798
|
}
|
|
1773
1799
|
if (typeof o === "object" && o !== null && o.type === "media")
|
|
1774
|
-
return { text: null, clientMsgId: "", replyTo: null };
|
|
1800
|
+
return { type: "media", text: null, clientMsgId: "", replyTo: null };
|
|
1775
1801
|
} catch {
|
|
1776
1802
|
}
|
|
1777
1803
|
return { text: s, clientMsgId: "", replyTo: null };
|
|
@@ -2023,6 +2049,56 @@ var GroupMessaging = class {
|
|
|
2023
2049
|
}
|
|
2024
2050
|
return { receipt: { serverSeq: wire.server_seq, epoch: wire.epoch }, clientMsgId };
|
|
2025
2051
|
}
|
|
2052
|
+
/** Send a reaction (add/remove of an emoji on a target message). Encrypts a
|
|
2053
|
+
* `type:'reaction'` envelope at the current epoch and sends through the SAME
|
|
2054
|
+
* MLS application path as `sendText` (the server stays blind — a reaction is
|
|
2055
|
+
* just another application message). Persists the outgoing reaction row so it
|
|
2056
|
+
* re-folds onto its target after a reload (the own-send half of the reload
|
|
2057
|
+
* parity). NEVER rebases (epoch-bound like any application message). */
|
|
2058
|
+
async sendReaction(group, args) {
|
|
2059
|
+
const plaintext = encodeReaction({
|
|
2060
|
+
clientMsgId: args.clientMsgId,
|
|
2061
|
+
targetClientMsgId: args.targetClientMsgId,
|
|
2062
|
+
emoji: args.emoji,
|
|
2063
|
+
op: args.op
|
|
2064
|
+
});
|
|
2065
|
+
const ct = await this.engine.encryptApplication(fromBase64(group.rfcGroupId), plaintext);
|
|
2066
|
+
const body = {
|
|
2067
|
+
ciphertext_b64: toBase64(ct),
|
|
2068
|
+
client_idem_key: randomId()
|
|
2069
|
+
};
|
|
2070
|
+
const wire = await palbeRequest(
|
|
2071
|
+
this.rt,
|
|
2072
|
+
"POST",
|
|
2073
|
+
MessagingPaths.groupMessages(group.displayId),
|
|
2074
|
+
{ body }
|
|
2075
|
+
);
|
|
2076
|
+
const stored = {
|
|
2077
|
+
id: `${group.rfcGroupId}#${wire.server_seq}`,
|
|
2078
|
+
direction: "outgoing",
|
|
2079
|
+
text: null,
|
|
2080
|
+
senderDeviceId: this.selfDeviceId,
|
|
2081
|
+
epoch: wire.epoch,
|
|
2082
|
+
serverSeq: wire.server_seq,
|
|
2083
|
+
at: Date.now(),
|
|
2084
|
+
clientMsgId: args.clientMsgId,
|
|
2085
|
+
replyTo: null,
|
|
2086
|
+
envelopeType: "reaction",
|
|
2087
|
+
reaction: {
|
|
2088
|
+
targetClientMsgId: args.targetClientMsgId,
|
|
2089
|
+
emoji: args.emoji,
|
|
2090
|
+
op: args.op
|
|
2091
|
+
}
|
|
2092
|
+
};
|
|
2093
|
+
try {
|
|
2094
|
+
await this.messageStore.append(group.rfcGroupId, stored);
|
|
2095
|
+
} catch {
|
|
2096
|
+
}
|
|
2097
|
+
return {
|
|
2098
|
+
receipt: { serverSeq: wire.server_seq, epoch: wire.epoch },
|
|
2099
|
+
clientMsgId: args.clientMsgId
|
|
2100
|
+
};
|
|
2101
|
+
}
|
|
2026
2102
|
// ── The rebase loop ──
|
|
2027
2103
|
async commitWithRebase(rfcGroupId, build) {
|
|
2028
2104
|
const gidBytes = fromBase64(rfcGroupId);
|
|
@@ -2084,6 +2160,61 @@ var GroupMessaging = class {
|
|
|
2084
2160
|
}
|
|
2085
2161
|
};
|
|
2086
2162
|
|
|
2163
|
+
// src/messaging/reaction-fold.ts
|
|
2164
|
+
function orderLte(aEpoch, aSeq, bEpoch, bSeq) {
|
|
2165
|
+
if (aEpoch !== bEpoch) return aEpoch < bEpoch;
|
|
2166
|
+
return aSeq <= bSeq;
|
|
2167
|
+
}
|
|
2168
|
+
var ReactionFold = class {
|
|
2169
|
+
// Map<targetClientMsgId, Map<actorUserId, UserState>>
|
|
2170
|
+
states = /* @__PURE__ */ new Map();
|
|
2171
|
+
// Dedup set: eventClientMsgId values already applied.
|
|
2172
|
+
seenEvents = /* @__PURE__ */ new Set();
|
|
2173
|
+
ingest(e) {
|
|
2174
|
+
if (this.seenEvents.has(e.eventClientMsgId)) return;
|
|
2175
|
+
this.seenEvents.add(e.eventClientMsgId);
|
|
2176
|
+
let byUser = this.states.get(e.targetClientMsgId);
|
|
2177
|
+
if (byUser === void 0) {
|
|
2178
|
+
byUser = /* @__PURE__ */ new Map();
|
|
2179
|
+
this.states.set(e.targetClientMsgId, byUser);
|
|
2180
|
+
}
|
|
2181
|
+
const prev = byUser.get(e.actorUserId);
|
|
2182
|
+
if (prev !== void 0) {
|
|
2183
|
+
if (orderLte(e.epoch, e.serverSeq, prev.orderEpoch, prev.orderSeq)) return;
|
|
2184
|
+
}
|
|
2185
|
+
byUser.set(e.actorUserId, {
|
|
2186
|
+
orderEpoch: e.epoch,
|
|
2187
|
+
orderSeq: e.serverSeq,
|
|
2188
|
+
emoji: e.op === "add" ? e.emoji : null
|
|
2189
|
+
});
|
|
2190
|
+
}
|
|
2191
|
+
/**
|
|
2192
|
+
* Returns a tally of `{ emoji → sorted userIds[] }` for the given target
|
|
2193
|
+
* message. Users whose last event was a `remove` (emoji === null) are
|
|
2194
|
+
* excluded. userId arrays are sorted for deterministic output (render
|
|
2195
|
+
* stability + golden tests).
|
|
2196
|
+
*/
|
|
2197
|
+
tally(targetClientMsgId) {
|
|
2198
|
+
const byUser = this.states.get(targetClientMsgId);
|
|
2199
|
+
if (byUser === void 0) return {};
|
|
2200
|
+
const out = /* @__PURE__ */ new Map();
|
|
2201
|
+
for (const [userId, st] of byUser) {
|
|
2202
|
+
if (st.emoji === null) continue;
|
|
2203
|
+
let users = out.get(st.emoji);
|
|
2204
|
+
if (users === void 0) {
|
|
2205
|
+
users = [];
|
|
2206
|
+
out.set(st.emoji, users);
|
|
2207
|
+
}
|
|
2208
|
+
users.push(userId);
|
|
2209
|
+
}
|
|
2210
|
+
const result = {};
|
|
2211
|
+
for (const [emoji, users] of out) {
|
|
2212
|
+
result[emoji] = users.sort();
|
|
2213
|
+
}
|
|
2214
|
+
return result;
|
|
2215
|
+
}
|
|
2216
|
+
};
|
|
2217
|
+
|
|
2087
2218
|
// src/messaging/chat.ts
|
|
2088
2219
|
var Chat = class {
|
|
2089
2220
|
/** Stable, URL/log-safe id (the grp_ display id once active; a reserved local id while draft). */
|
|
@@ -2102,6 +2233,8 @@ var Chat = class {
|
|
|
2102
2233
|
seenKeys = /* @__PURE__ */ new Set();
|
|
2103
2234
|
/** Index: clientMsgId → { text, senderUserId } for resolveReply lookups. */
|
|
2104
2235
|
byClientMsgId = /* @__PURE__ */ new Map();
|
|
2236
|
+
/** The single authoritative reaction fold for this chat (live + own-send + history). */
|
|
2237
|
+
reactionFold = new ReactionFold();
|
|
2105
2238
|
loadedEarliestSeq = null;
|
|
2106
2239
|
historyLoaded = false;
|
|
2107
2240
|
wired = false;
|
|
@@ -2208,7 +2341,7 @@ var Chat = class {
|
|
|
2208
2341
|
const key = this.internalKey(m.serverSeq);
|
|
2209
2342
|
if (this.seenKeys.has(key)) continue;
|
|
2210
2343
|
this.seenKeys.add(key);
|
|
2211
|
-
this.messageList.push(m);
|
|
2344
|
+
this.messageList.push(this.applyReactionTally(m));
|
|
2212
2345
|
changed = true;
|
|
2213
2346
|
this.loadedEarliestSeq = Math.min(this.loadedEarliestSeq ?? m.serverSeq, m.serverSeq);
|
|
2214
2347
|
}
|
|
@@ -2233,6 +2366,22 @@ var Chat = class {
|
|
|
2233
2366
|
senderUser = await this.backend.userIdForDevice(this._group, incoming.senderDeviceId);
|
|
2234
2367
|
}
|
|
2235
2368
|
const direction = senderUser !== null && senderUser === this.backend.selfUserId ? "outgoing" : "incoming";
|
|
2369
|
+
if (incoming.envelopeType === "reaction" && incoming.reaction) {
|
|
2370
|
+
const actorUserId = direction === "outgoing" ? this.backend.selfUserId : senderUser;
|
|
2371
|
+
if (actorUserId !== null) {
|
|
2372
|
+
this.reactionFold.ingest({
|
|
2373
|
+
targetClientMsgId: incoming.reaction.targetClientMsgId,
|
|
2374
|
+
actorUserId,
|
|
2375
|
+
emoji: incoming.reaction.emoji,
|
|
2376
|
+
op: incoming.reaction.op,
|
|
2377
|
+
epoch: incoming.epoch,
|
|
2378
|
+
serverSeq: incoming.serverSeq,
|
|
2379
|
+
eventClientMsgId: incoming.clientMsgId
|
|
2380
|
+
});
|
|
2381
|
+
this.recomputeReactions(incoming.reaction.targetClientMsgId);
|
|
2382
|
+
}
|
|
2383
|
+
return;
|
|
2384
|
+
}
|
|
2236
2385
|
const incomingClientMsgId = incoming.clientMsgId;
|
|
2237
2386
|
const incomingReplyRef = incoming.replyRef;
|
|
2238
2387
|
let resolvedReplyTo = null;
|
|
@@ -2248,7 +2397,10 @@ var Chat = class {
|
|
|
2248
2397
|
serverSeq: incoming.serverSeq,
|
|
2249
2398
|
sentAt: incoming.receivedAt,
|
|
2250
2399
|
clientMsgId: incomingClientMsgId,
|
|
2251
|
-
replyTo: resolvedReplyTo
|
|
2400
|
+
replyTo: resolvedReplyTo,
|
|
2401
|
+
// Attach any tally already folded for this message (a reaction that arrived
|
|
2402
|
+
// BEFORE its target — the dangling case — renders the moment the target lands).
|
|
2403
|
+
reactions: incomingClientMsgId ? this.reactionFold.tally(incomingClientMsgId) : {}
|
|
2252
2404
|
};
|
|
2253
2405
|
if (incomingClientMsgId && incoming.text !== null) {
|
|
2254
2406
|
this.byClientMsgId.set(incomingClientMsgId, {
|
|
@@ -2264,6 +2416,35 @@ var Chat = class {
|
|
|
2264
2416
|
);
|
|
2265
2417
|
this.emit();
|
|
2266
2418
|
}
|
|
2419
|
+
/**
|
|
2420
|
+
* Rebuild the target message's `reactions` from the authoritative fold and
|
|
2421
|
+
* re-emit. No-op when the target isn't present yet (its tally is attached the
|
|
2422
|
+
* moment it lands, via the append/merge paths) or when the tally is unchanged.
|
|
2423
|
+
*/
|
|
2424
|
+
recomputeReactions(targetClientMsgId) {
|
|
2425
|
+
if (!targetClientMsgId) return;
|
|
2426
|
+
const tally = this.reactionFold.tally(targetClientMsgId);
|
|
2427
|
+
let changed = false;
|
|
2428
|
+
this.messageList = this.messageList.map((m) => {
|
|
2429
|
+
if (m.clientMsgId !== targetClientMsgId) return m;
|
|
2430
|
+
if (sameReactions(m.reactions, tally)) return m;
|
|
2431
|
+
changed = true;
|
|
2432
|
+
return { ...m, reactions: tally };
|
|
2433
|
+
});
|
|
2434
|
+
if (changed) this.emit();
|
|
2435
|
+
}
|
|
2436
|
+
/**
|
|
2437
|
+
* Overlay the authoritative fold's tally onto a message as it is appended/merged.
|
|
2438
|
+
* The Chat fold WINS when it has a non-empty tally; otherwise the tally already
|
|
2439
|
+
* attached upstream (the coordinator's page-local history fold) is preserved.
|
|
2440
|
+
*/
|
|
2441
|
+
applyReactionTally(m) {
|
|
2442
|
+
if (!m.clientMsgId) return m;
|
|
2443
|
+
const tally = this.reactionFold.tally(m.clientMsgId);
|
|
2444
|
+
if (Object.keys(tally).length === 0) return m;
|
|
2445
|
+
if (sameReactions(m.reactions, tally)) return m;
|
|
2446
|
+
return { ...m, reactions: tally };
|
|
2447
|
+
}
|
|
2267
2448
|
/** @internal — called by the backend's conv subscription. */
|
|
2268
2449
|
applyConv(event, payload) {
|
|
2269
2450
|
const userId = typeof payload.user_id === "string" ? payload.user_id : null;
|
|
@@ -2406,7 +2587,10 @@ var Chat = class {
|
|
|
2406
2587
|
serverSeq: receipt.serverSeq,
|
|
2407
2588
|
sentAt: /* @__PURE__ */ new Date(),
|
|
2408
2589
|
clientMsgId,
|
|
2409
|
-
replyTo: resolvedReplyTo
|
|
2590
|
+
replyTo: resolvedReplyTo,
|
|
2591
|
+
// Attach any tally already folded for this own-sent message (rare, but keeps
|
|
2592
|
+
// the dangling-target invariant uniform across every append path).
|
|
2593
|
+
reactions: clientMsgId ? this.reactionFold.tally(clientMsgId) : {}
|
|
2410
2594
|
});
|
|
2411
2595
|
this.messageList.sort((a, b) => a.serverSeq - b.serverSeq);
|
|
2412
2596
|
this.emit();
|
|
@@ -2453,7 +2637,53 @@ var Chat = class {
|
|
|
2453
2637
|
this.readWatermark = Math.max(this.readWatermark, message.serverSeq);
|
|
2454
2638
|
this.emit();
|
|
2455
2639
|
}
|
|
2640
|
+
// ── Reactions ──
|
|
2641
|
+
/** Add an emoji reaction to a message. No-op if the message isn't reactable
|
|
2642
|
+
* (empty clientMsgId — a legacy/system row). The reaction folds locally with
|
|
2643
|
+
* the server receipt's `(epoch, serverSeq)` so the target's tally updates
|
|
2644
|
+
* instantly; the durable echo on the next pump is a fold no-op (dedup on the
|
|
2645
|
+
* SAME wire clientMsgId). Never appends a bubble. */
|
|
2646
|
+
async react(message, emoji) {
|
|
2647
|
+
await this.sendReaction(message, emoji, "add");
|
|
2648
|
+
}
|
|
2649
|
+
/** Remove this user's emoji reaction from a message (op:'remove'). */
|
|
2650
|
+
async unreact(message, emoji) {
|
|
2651
|
+
await this.sendReaction(message, emoji, "remove");
|
|
2652
|
+
}
|
|
2653
|
+
async sendReaction(message, emoji, op) {
|
|
2654
|
+
if (!message.clientMsgId) return;
|
|
2655
|
+
const group = await this.materializeIfNeeded();
|
|
2656
|
+
const clientMsgId = mintClientMsgId();
|
|
2657
|
+
const { receipt } = await this.backend.sendReaction(group, {
|
|
2658
|
+
clientMsgId,
|
|
2659
|
+
targetClientMsgId: message.clientMsgId,
|
|
2660
|
+
emoji,
|
|
2661
|
+
op
|
|
2662
|
+
});
|
|
2663
|
+
this.reactionFold.ingest({
|
|
2664
|
+
targetClientMsgId: message.clientMsgId,
|
|
2665
|
+
actorUserId: this.backend.selfUserId,
|
|
2666
|
+
emoji,
|
|
2667
|
+
op,
|
|
2668
|
+
epoch: receipt.epoch,
|
|
2669
|
+
serverSeq: receipt.serverSeq,
|
|
2670
|
+
eventClientMsgId: clientMsgId
|
|
2671
|
+
});
|
|
2672
|
+
this.recomputeReactions(message.clientMsgId);
|
|
2673
|
+
}
|
|
2456
2674
|
};
|
|
2675
|
+
function sameReactions(a, b) {
|
|
2676
|
+
const ak = Object.keys(a);
|
|
2677
|
+
const bk = Object.keys(b);
|
|
2678
|
+
if (ak.length !== bk.length) return false;
|
|
2679
|
+
for (const k of ak) {
|
|
2680
|
+
const av = a[k];
|
|
2681
|
+
const bv = b[k];
|
|
2682
|
+
if (!bv || av === void 0 || av.length !== bv.length) return false;
|
|
2683
|
+
for (let i = 0; i < av.length; i++) if (av[i] !== bv[i]) return false;
|
|
2684
|
+
}
|
|
2685
|
+
return true;
|
|
2686
|
+
}
|
|
2457
2687
|
|
|
2458
2688
|
// src/messaging/delivery-source.ts
|
|
2459
2689
|
var MessageHub = class {
|
|
@@ -2612,7 +2842,9 @@ var MessageDeliverySource = class {
|
|
|
2612
2842
|
try {
|
|
2613
2843
|
const received = await this.engine.processIncoming(fromBase64(group.rfcGroupId), blob);
|
|
2614
2844
|
if (received.type === "application") {
|
|
2615
|
-
const
|
|
2845
|
+
const decoded = decodeEnvelope(received.data);
|
|
2846
|
+
const { text, clientMsgId, replyTo } = decoded;
|
|
2847
|
+
const isReaction = decoded.type === "reaction" && decoded.reaction != null;
|
|
2616
2848
|
const stored = {
|
|
2617
2849
|
id: `${group.rfcGroupId}#${row.server_seq}`,
|
|
2618
2850
|
direction: "incoming",
|
|
@@ -2627,7 +2859,19 @@ var MessageDeliverySource = class {
|
|
|
2627
2859
|
previewBody: replyTo.preview?.body ?? null,
|
|
2628
2860
|
previewAuthorUserId: replyTo.preview?.author_user_id ?? null,
|
|
2629
2861
|
previewKind: replyTo.preview?.kind ?? "text"
|
|
2630
|
-
} : null
|
|
2862
|
+
} : null,
|
|
2863
|
+
// Thread the reaction discriminator + fields through the persisted row so
|
|
2864
|
+
// a reaction folded LIVE re-folds onto its target after a reload (the
|
|
2865
|
+
// reload-parity boundary — mirrors iOS T3). Omitted for non-reactions →
|
|
2866
|
+
// old rows hydrate as `'text'`/no-reaction (backward-compat).
|
|
2867
|
+
...isReaction && decoded.reaction ? {
|
|
2868
|
+
envelopeType: "reaction",
|
|
2869
|
+
reaction: {
|
|
2870
|
+
targetClientMsgId: decoded.reaction.targetClientMsgId,
|
|
2871
|
+
emoji: decoded.reaction.emoji,
|
|
2872
|
+
op: decoded.reaction.op
|
|
2873
|
+
}
|
|
2874
|
+
} : {}
|
|
2631
2875
|
};
|
|
2632
2876
|
try {
|
|
2633
2877
|
await this.messageStore.append(group.rfcGroupId, stored);
|
|
@@ -2643,7 +2887,9 @@ var MessageDeliverySource = class {
|
|
|
2643
2887
|
serverSeq: row.server_seq,
|
|
2644
2888
|
receivedAt: /* @__PURE__ */ new Date(),
|
|
2645
2889
|
clientMsgId,
|
|
2646
|
-
replyRef: replyTo
|
|
2890
|
+
replyRef: replyTo,
|
|
2891
|
+
envelopeType: decoded.type ?? "text",
|
|
2892
|
+
reaction: isReaction ? decoded.reaction : null
|
|
2647
2893
|
});
|
|
2648
2894
|
return true;
|
|
2649
2895
|
}
|
|
@@ -4647,46 +4893,14 @@ var MessagingCoordinator = class {
|
|
|
4647
4893
|
const r = await this.resolve();
|
|
4648
4894
|
return r.groups.sendText(group, text, replyTo);
|
|
4649
4895
|
}
|
|
4896
|
+
async sendReaction(group, args) {
|
|
4897
|
+
const r = await this.resolve();
|
|
4898
|
+
return r.groups.sendReaction(group, args);
|
|
4899
|
+
}
|
|
4650
4900
|
async history(group, limit, before) {
|
|
4651
4901
|
const r = await this.resolve();
|
|
4652
4902
|
const rows = await r.messageStore.history(group.rfcGroupId, limit, before);
|
|
4653
|
-
|
|
4654
|
-
for (const s of rows) {
|
|
4655
|
-
const cid = s.clientMsgId ?? "";
|
|
4656
|
-
if (cid && s.text !== null) {
|
|
4657
|
-
const senderUserId = s.direction === "outgoing" ? this.selfUserId : "";
|
|
4658
|
-
lookup.set(cid, { text: s.text, senderUserId });
|
|
4659
|
-
}
|
|
4660
|
-
}
|
|
4661
|
-
return rows.map((s) => this.toChatMessage(group, s, (id) => lookup.get(id) ?? null));
|
|
4662
|
-
}
|
|
4663
|
-
toChatMessage(group, s, lookup) {
|
|
4664
|
-
const clientMsgId = s.clientMsgId ?? "";
|
|
4665
|
-
let replyTo = null;
|
|
4666
|
-
if (s.replyTo && lookup) {
|
|
4667
|
-
const ref = {
|
|
4668
|
-
v: 1,
|
|
4669
|
-
client_msg_id: s.replyTo.clientMsgId,
|
|
4670
|
-
preview: {
|
|
4671
|
-
kind: s.replyTo.previewKind,
|
|
4672
|
-
author_user_id: s.replyTo.previewAuthorUserId ?? "",
|
|
4673
|
-
body: s.replyTo.previewBody ?? void 0,
|
|
4674
|
-
body_truncated: false
|
|
4675
|
-
}
|
|
4676
|
-
};
|
|
4677
|
-
replyTo = resolveReply(ref, lookup);
|
|
4678
|
-
}
|
|
4679
|
-
return {
|
|
4680
|
-
id: `${group.displayId}#${s.serverSeq}`,
|
|
4681
|
-
kind: s.text != null ? "text" : "system",
|
|
4682
|
-
direction: s.direction,
|
|
4683
|
-
senderUserId: s.direction === "outgoing" ? this.selfUserId : null,
|
|
4684
|
-
text: s.text,
|
|
4685
|
-
serverSeq: s.serverSeq,
|
|
4686
|
-
sentAt: new Date(s.at),
|
|
4687
|
-
clientMsgId,
|
|
4688
|
-
replyTo
|
|
4689
|
-
};
|
|
4903
|
+
return projectHistory(group.displayId, rows, this.selfUserId);
|
|
4690
4904
|
}
|
|
4691
4905
|
async members(group) {
|
|
4692
4906
|
const r = await this.resolve();
|
|
@@ -4763,6 +4977,64 @@ var MessagingCoordinator = class {
|
|
|
4763
4977
|
return res.devices.map((d) => d.device_id);
|
|
4764
4978
|
}
|
|
4765
4979
|
};
|
|
4980
|
+
function projectHistory(displayId, rows, selfUserId, resolveActor) {
|
|
4981
|
+
const fold = new ReactionFold();
|
|
4982
|
+
for (const s of rows) {
|
|
4983
|
+
if (s.envelopeType !== "reaction" || !s.reaction) continue;
|
|
4984
|
+
const actor = s.direction === "outgoing" ? selfUserId : resolveActor?.(s.senderDeviceId ?? null) ?? null;
|
|
4985
|
+
if (actor === null) continue;
|
|
4986
|
+
fold.ingest({
|
|
4987
|
+
targetClientMsgId: s.reaction.targetClientMsgId,
|
|
4988
|
+
actorUserId: actor,
|
|
4989
|
+
emoji: s.reaction.emoji,
|
|
4990
|
+
op: s.reaction.op,
|
|
4991
|
+
epoch: s.epoch,
|
|
4992
|
+
serverSeq: s.serverSeq,
|
|
4993
|
+
eventClientMsgId: s.clientMsgId ?? `${s.id}`
|
|
4994
|
+
});
|
|
4995
|
+
}
|
|
4996
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
4997
|
+
for (const s of rows) {
|
|
4998
|
+
if (s.envelopeType === "reaction") continue;
|
|
4999
|
+
const cid = s.clientMsgId ?? "";
|
|
5000
|
+
if (cid && s.text !== null) {
|
|
5001
|
+
const senderUserId = s.direction === "outgoing" ? selfUserId : "";
|
|
5002
|
+
lookup.set(cid, { text: s.text, senderUserId });
|
|
5003
|
+
}
|
|
5004
|
+
}
|
|
5005
|
+
const out = [];
|
|
5006
|
+
for (const s of rows) {
|
|
5007
|
+
if (s.envelopeType === "reaction") continue;
|
|
5008
|
+
const clientMsgId = s.clientMsgId ?? "";
|
|
5009
|
+
let replyTo = null;
|
|
5010
|
+
if (s.replyTo) {
|
|
5011
|
+
const ref = {
|
|
5012
|
+
v: 1,
|
|
5013
|
+
client_msg_id: s.replyTo.clientMsgId,
|
|
5014
|
+
preview: {
|
|
5015
|
+
kind: s.replyTo.previewKind,
|
|
5016
|
+
author_user_id: s.replyTo.previewAuthorUserId ?? "",
|
|
5017
|
+
body: s.replyTo.previewBody ?? void 0,
|
|
5018
|
+
body_truncated: false
|
|
5019
|
+
}
|
|
5020
|
+
};
|
|
5021
|
+
replyTo = resolveReply(ref, (id) => lookup.get(id) ?? null);
|
|
5022
|
+
}
|
|
5023
|
+
out.push({
|
|
5024
|
+
id: `${displayId}#${s.serverSeq}`,
|
|
5025
|
+
kind: s.text != null ? "text" : "system",
|
|
5026
|
+
direction: s.direction,
|
|
5027
|
+
senderUserId: s.direction === "outgoing" ? selfUserId : null,
|
|
5028
|
+
text: s.text,
|
|
5029
|
+
serverSeq: s.serverSeq,
|
|
5030
|
+
sentAt: new Date(s.at),
|
|
5031
|
+
clientMsgId,
|
|
5032
|
+
replyTo,
|
|
5033
|
+
reactions: clientMsgId ? fold.tally(clientMsgId) : {}
|
|
5034
|
+
});
|
|
5035
|
+
}
|
|
5036
|
+
return out;
|
|
5037
|
+
}
|
|
4766
5038
|
|
|
4767
5039
|
// src/messaging/facade.ts
|
|
4768
5040
|
var PalbeMessaging = class {
|
|
@@ -5615,7 +5887,7 @@ function localStorageSessionStorage(key = DEFAULT_KEY) {
|
|
|
5615
5887
|
}
|
|
5616
5888
|
|
|
5617
5889
|
// src/version.ts
|
|
5618
|
-
var VERSION = "1.
|
|
5890
|
+
var VERSION = "1.2.0";
|
|
5619
5891
|
|
|
5620
5892
|
// src/internal.ts
|
|
5621
5893
|
function getRuntime() {
|