@palbase/web 1.1.1 → 1.2.1
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-KJXRY4S3.js} +327 -49
- package/dist/chunk-KJXRY4S3.js.map +1 -0
- package/dist/index.cjs +326 -48
- 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 +326 -48
- 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 +326 -48
- package/dist/next/client.cjs.map +1 -1
- package/dist/next/client.js +1 -1
- package/dist/next/index.cjs +326 -48
- 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 +1 -1
- package/dist/chunk-EMQGOKW6.js.map +0 -1
package/dist/next/client.js
CHANGED
package/dist/next/index.cjs
CHANGED
|
@@ -2767,6 +2767,18 @@ async function listDevices(rt, userId) {
|
|
|
2767
2767
|
}
|
|
2768
2768
|
|
|
2769
2769
|
// src/messaging/group-messaging.ts
|
|
2770
|
+
function encodeReaction(args) {
|
|
2771
|
+
return encodeUtf8(
|
|
2772
|
+
JSON.stringify({
|
|
2773
|
+
v: 1,
|
|
2774
|
+
type: "reaction",
|
|
2775
|
+
client_msg_id: args.clientMsgId,
|
|
2776
|
+
target_client_msg_id: args.targetClientMsgId,
|
|
2777
|
+
emoji: args.emoji,
|
|
2778
|
+
op: args.op
|
|
2779
|
+
})
|
|
2780
|
+
);
|
|
2781
|
+
}
|
|
2770
2782
|
function encodeEnvelope(args) {
|
|
2771
2783
|
const env = {
|
|
2772
2784
|
v: 1,
|
|
@@ -2781,18 +2793,32 @@ function decodeEnvelope(bytes) {
|
|
|
2781
2793
|
const s = decodeUtf8(bytes);
|
|
2782
2794
|
try {
|
|
2783
2795
|
const o = JSON.parse(s);
|
|
2796
|
+
if (typeof o === "object" && o !== null && o.type === "reaction") {
|
|
2797
|
+
return {
|
|
2798
|
+
type: "reaction",
|
|
2799
|
+
text: null,
|
|
2800
|
+
clientMsgId: o.client_msg_id ?? "",
|
|
2801
|
+
replyTo: null,
|
|
2802
|
+
reaction: {
|
|
2803
|
+
targetClientMsgId: o.target_client_msg_id ?? "",
|
|
2804
|
+
emoji: o.emoji ?? "",
|
|
2805
|
+
op: o.op ?? "add"
|
|
2806
|
+
}
|
|
2807
|
+
};
|
|
2808
|
+
}
|
|
2784
2809
|
if (typeof o === "object" && o !== null && o.type === "text" && typeof o.v === "number") {
|
|
2785
2810
|
return {
|
|
2811
|
+
type: "text",
|
|
2786
2812
|
text: o.text ?? null,
|
|
2787
2813
|
clientMsgId: o.client_msg_id ?? "",
|
|
2788
2814
|
replyTo: o.reply_to ?? null
|
|
2789
2815
|
};
|
|
2790
2816
|
}
|
|
2791
2817
|
if (typeof o === "object" && o !== null && o.type === "text") {
|
|
2792
|
-
return { text: o.text ?? null, clientMsgId: "", replyTo: null };
|
|
2818
|
+
return { type: "text", text: o.text ?? null, clientMsgId: "", replyTo: null };
|
|
2793
2819
|
}
|
|
2794
2820
|
if (typeof o === "object" && o !== null && o.type === "media")
|
|
2795
|
-
return { text: null, clientMsgId: "", replyTo: null };
|
|
2821
|
+
return { type: "media", text: null, clientMsgId: "", replyTo: null };
|
|
2796
2822
|
} catch {
|
|
2797
2823
|
}
|
|
2798
2824
|
return { text: s, clientMsgId: "", replyTo: null };
|
|
@@ -3044,6 +3070,56 @@ var GroupMessaging = class {
|
|
|
3044
3070
|
}
|
|
3045
3071
|
return { receipt: { serverSeq: wire.server_seq, epoch: wire.epoch }, clientMsgId };
|
|
3046
3072
|
}
|
|
3073
|
+
/** Send a reaction (add/remove of an emoji on a target message). Encrypts a
|
|
3074
|
+
* `type:'reaction'` envelope at the current epoch and sends through the SAME
|
|
3075
|
+
* MLS application path as `sendText` (the server stays blind — a reaction is
|
|
3076
|
+
* just another application message). Persists the outgoing reaction row so it
|
|
3077
|
+
* re-folds onto its target after a reload (the own-send half of the reload
|
|
3078
|
+
* parity). NEVER rebases (epoch-bound like any application message). */
|
|
3079
|
+
async sendReaction(group, args) {
|
|
3080
|
+
const plaintext = encodeReaction({
|
|
3081
|
+
clientMsgId: args.clientMsgId,
|
|
3082
|
+
targetClientMsgId: args.targetClientMsgId,
|
|
3083
|
+
emoji: args.emoji,
|
|
3084
|
+
op: args.op
|
|
3085
|
+
});
|
|
3086
|
+
const ct = await this.engine.encryptApplication(fromBase64(group.rfcGroupId), plaintext);
|
|
3087
|
+
const body = {
|
|
3088
|
+
ciphertext_b64: toBase64(ct),
|
|
3089
|
+
client_idem_key: randomId()
|
|
3090
|
+
};
|
|
3091
|
+
const wire = await palbeRequest(
|
|
3092
|
+
this.rt,
|
|
3093
|
+
"POST",
|
|
3094
|
+
MessagingPaths.groupMessages(group.displayId),
|
|
3095
|
+
{ body }
|
|
3096
|
+
);
|
|
3097
|
+
const stored = {
|
|
3098
|
+
id: `${group.rfcGroupId}#${wire.server_seq}`,
|
|
3099
|
+
direction: "outgoing",
|
|
3100
|
+
text: null,
|
|
3101
|
+
senderDeviceId: this.selfDeviceId,
|
|
3102
|
+
epoch: wire.epoch,
|
|
3103
|
+
serverSeq: wire.server_seq,
|
|
3104
|
+
at: Date.now(),
|
|
3105
|
+
clientMsgId: args.clientMsgId,
|
|
3106
|
+
replyTo: null,
|
|
3107
|
+
envelopeType: "reaction",
|
|
3108
|
+
reaction: {
|
|
3109
|
+
targetClientMsgId: args.targetClientMsgId,
|
|
3110
|
+
emoji: args.emoji,
|
|
3111
|
+
op: args.op
|
|
3112
|
+
}
|
|
3113
|
+
};
|
|
3114
|
+
try {
|
|
3115
|
+
await this.messageStore.append(group.rfcGroupId, stored);
|
|
3116
|
+
} catch {
|
|
3117
|
+
}
|
|
3118
|
+
return {
|
|
3119
|
+
receipt: { serverSeq: wire.server_seq, epoch: wire.epoch },
|
|
3120
|
+
clientMsgId: args.clientMsgId
|
|
3121
|
+
};
|
|
3122
|
+
}
|
|
3047
3123
|
// ── The rebase loop ──
|
|
3048
3124
|
async commitWithRebase(rfcGroupId, build) {
|
|
3049
3125
|
const gidBytes = fromBase64(rfcGroupId);
|
|
@@ -3105,6 +3181,61 @@ var GroupMessaging = class {
|
|
|
3105
3181
|
}
|
|
3106
3182
|
};
|
|
3107
3183
|
|
|
3184
|
+
// src/messaging/reaction-fold.ts
|
|
3185
|
+
function orderLte(aEpoch, aSeq, bEpoch, bSeq) {
|
|
3186
|
+
if (aEpoch !== bEpoch) return aEpoch < bEpoch;
|
|
3187
|
+
return aSeq <= bSeq;
|
|
3188
|
+
}
|
|
3189
|
+
var ReactionFold = class {
|
|
3190
|
+
// Map<targetClientMsgId, Map<actorUserId, UserState>>
|
|
3191
|
+
states = /* @__PURE__ */ new Map();
|
|
3192
|
+
// Dedup set: eventClientMsgId values already applied.
|
|
3193
|
+
seenEvents = /* @__PURE__ */ new Set();
|
|
3194
|
+
ingest(e) {
|
|
3195
|
+
if (this.seenEvents.has(e.eventClientMsgId)) return;
|
|
3196
|
+
this.seenEvents.add(e.eventClientMsgId);
|
|
3197
|
+
let byUser = this.states.get(e.targetClientMsgId);
|
|
3198
|
+
if (byUser === void 0) {
|
|
3199
|
+
byUser = /* @__PURE__ */ new Map();
|
|
3200
|
+
this.states.set(e.targetClientMsgId, byUser);
|
|
3201
|
+
}
|
|
3202
|
+
const prev = byUser.get(e.actorUserId);
|
|
3203
|
+
if (prev !== void 0) {
|
|
3204
|
+
if (orderLte(e.epoch, e.serverSeq, prev.orderEpoch, prev.orderSeq)) return;
|
|
3205
|
+
}
|
|
3206
|
+
byUser.set(e.actorUserId, {
|
|
3207
|
+
orderEpoch: e.epoch,
|
|
3208
|
+
orderSeq: e.serverSeq,
|
|
3209
|
+
emoji: e.op === "add" ? e.emoji : null
|
|
3210
|
+
});
|
|
3211
|
+
}
|
|
3212
|
+
/**
|
|
3213
|
+
* Returns a tally of `{ emoji → sorted userIds[] }` for the given target
|
|
3214
|
+
* message. Users whose last event was a `remove` (emoji === null) are
|
|
3215
|
+
* excluded. userId arrays are sorted for deterministic output (render
|
|
3216
|
+
* stability + golden tests).
|
|
3217
|
+
*/
|
|
3218
|
+
tally(targetClientMsgId) {
|
|
3219
|
+
const byUser = this.states.get(targetClientMsgId);
|
|
3220
|
+
if (byUser === void 0) return {};
|
|
3221
|
+
const out = /* @__PURE__ */ new Map();
|
|
3222
|
+
for (const [userId, st] of byUser) {
|
|
3223
|
+
if (st.emoji === null) continue;
|
|
3224
|
+
let users = out.get(st.emoji);
|
|
3225
|
+
if (users === void 0) {
|
|
3226
|
+
users = [];
|
|
3227
|
+
out.set(st.emoji, users);
|
|
3228
|
+
}
|
|
3229
|
+
users.push(userId);
|
|
3230
|
+
}
|
|
3231
|
+
const result = {};
|
|
3232
|
+
for (const [emoji, users] of out) {
|
|
3233
|
+
result[emoji] = users.sort();
|
|
3234
|
+
}
|
|
3235
|
+
return result;
|
|
3236
|
+
}
|
|
3237
|
+
};
|
|
3238
|
+
|
|
3108
3239
|
// src/messaging/chat.ts
|
|
3109
3240
|
var Chat = class {
|
|
3110
3241
|
/** Stable, URL/log-safe id (the grp_ display id once active; a reserved local id while draft). */
|
|
@@ -3123,6 +3254,8 @@ var Chat = class {
|
|
|
3123
3254
|
seenKeys = /* @__PURE__ */ new Set();
|
|
3124
3255
|
/** Index: clientMsgId → { text, senderUserId } for resolveReply lookups. */
|
|
3125
3256
|
byClientMsgId = /* @__PURE__ */ new Map();
|
|
3257
|
+
/** The single authoritative reaction fold for this chat (live + own-send + history). */
|
|
3258
|
+
reactionFold = new ReactionFold();
|
|
3126
3259
|
loadedEarliestSeq = null;
|
|
3127
3260
|
historyLoaded = false;
|
|
3128
3261
|
wired = false;
|
|
@@ -3229,7 +3362,7 @@ var Chat = class {
|
|
|
3229
3362
|
const key = this.internalKey(m.serverSeq);
|
|
3230
3363
|
if (this.seenKeys.has(key)) continue;
|
|
3231
3364
|
this.seenKeys.add(key);
|
|
3232
|
-
this.messageList.push(m);
|
|
3365
|
+
this.messageList.push(this.applyReactionTally(m));
|
|
3233
3366
|
changed = true;
|
|
3234
3367
|
this.loadedEarliestSeq = Math.min(this.loadedEarliestSeq ?? m.serverSeq, m.serverSeq);
|
|
3235
3368
|
}
|
|
@@ -3254,6 +3387,22 @@ var Chat = class {
|
|
|
3254
3387
|
senderUser = await this.backend.userIdForDevice(this._group, incoming.senderDeviceId);
|
|
3255
3388
|
}
|
|
3256
3389
|
const direction = senderUser !== null && senderUser === this.backend.selfUserId ? "outgoing" : "incoming";
|
|
3390
|
+
if (incoming.envelopeType === "reaction" && incoming.reaction) {
|
|
3391
|
+
const actorUserId = direction === "outgoing" ? this.backend.selfUserId : senderUser;
|
|
3392
|
+
if (actorUserId !== null) {
|
|
3393
|
+
this.reactionFold.ingest({
|
|
3394
|
+
targetClientMsgId: incoming.reaction.targetClientMsgId,
|
|
3395
|
+
actorUserId,
|
|
3396
|
+
emoji: incoming.reaction.emoji,
|
|
3397
|
+
op: incoming.reaction.op,
|
|
3398
|
+
epoch: incoming.epoch,
|
|
3399
|
+
serverSeq: incoming.serverSeq,
|
|
3400
|
+
eventClientMsgId: incoming.clientMsgId
|
|
3401
|
+
});
|
|
3402
|
+
this.recomputeReactions(incoming.reaction.targetClientMsgId);
|
|
3403
|
+
}
|
|
3404
|
+
return;
|
|
3405
|
+
}
|
|
3257
3406
|
const incomingClientMsgId = incoming.clientMsgId;
|
|
3258
3407
|
const incomingReplyRef = incoming.replyRef;
|
|
3259
3408
|
let resolvedReplyTo = null;
|
|
@@ -3269,7 +3418,10 @@ var Chat = class {
|
|
|
3269
3418
|
serverSeq: incoming.serverSeq,
|
|
3270
3419
|
sentAt: incoming.receivedAt,
|
|
3271
3420
|
clientMsgId: incomingClientMsgId,
|
|
3272
|
-
replyTo: resolvedReplyTo
|
|
3421
|
+
replyTo: resolvedReplyTo,
|
|
3422
|
+
// Attach any tally already folded for this message (a reaction that arrived
|
|
3423
|
+
// BEFORE its target — the dangling case — renders the moment the target lands).
|
|
3424
|
+
reactions: incomingClientMsgId ? this.reactionFold.tally(incomingClientMsgId) : {}
|
|
3273
3425
|
};
|
|
3274
3426
|
if (incomingClientMsgId && incoming.text !== null) {
|
|
3275
3427
|
this.byClientMsgId.set(incomingClientMsgId, {
|
|
@@ -3285,6 +3437,35 @@ var Chat = class {
|
|
|
3285
3437
|
);
|
|
3286
3438
|
this.emit();
|
|
3287
3439
|
}
|
|
3440
|
+
/**
|
|
3441
|
+
* Rebuild the target message's `reactions` from the authoritative fold and
|
|
3442
|
+
* re-emit. No-op when the target isn't present yet (its tally is attached the
|
|
3443
|
+
* moment it lands, via the append/merge paths) or when the tally is unchanged.
|
|
3444
|
+
*/
|
|
3445
|
+
recomputeReactions(targetClientMsgId) {
|
|
3446
|
+
if (!targetClientMsgId) return;
|
|
3447
|
+
const tally = this.reactionFold.tally(targetClientMsgId);
|
|
3448
|
+
let changed = false;
|
|
3449
|
+
this.messageList = this.messageList.map((m) => {
|
|
3450
|
+
if (m.clientMsgId !== targetClientMsgId) return m;
|
|
3451
|
+
if (sameReactions(m.reactions, tally)) return m;
|
|
3452
|
+
changed = true;
|
|
3453
|
+
return { ...m, reactions: tally };
|
|
3454
|
+
});
|
|
3455
|
+
if (changed) this.emit();
|
|
3456
|
+
}
|
|
3457
|
+
/**
|
|
3458
|
+
* Overlay the authoritative fold's tally onto a message as it is appended/merged.
|
|
3459
|
+
* The Chat fold WINS when it has a non-empty tally; otherwise the tally already
|
|
3460
|
+
* attached upstream (the coordinator's page-local history fold) is preserved.
|
|
3461
|
+
*/
|
|
3462
|
+
applyReactionTally(m) {
|
|
3463
|
+
if (!m.clientMsgId) return m;
|
|
3464
|
+
const tally = this.reactionFold.tally(m.clientMsgId);
|
|
3465
|
+
if (Object.keys(tally).length === 0) return m;
|
|
3466
|
+
if (sameReactions(m.reactions, tally)) return m;
|
|
3467
|
+
return { ...m, reactions: tally };
|
|
3468
|
+
}
|
|
3288
3469
|
/** @internal — called by the backend's conv subscription. */
|
|
3289
3470
|
applyConv(event, payload) {
|
|
3290
3471
|
const userId = typeof payload.user_id === "string" ? payload.user_id : null;
|
|
@@ -3427,7 +3608,10 @@ var Chat = class {
|
|
|
3427
3608
|
serverSeq: receipt.serverSeq,
|
|
3428
3609
|
sentAt: /* @__PURE__ */ new Date(),
|
|
3429
3610
|
clientMsgId,
|
|
3430
|
-
replyTo: resolvedReplyTo
|
|
3611
|
+
replyTo: resolvedReplyTo,
|
|
3612
|
+
// Attach any tally already folded for this own-sent message (rare, but keeps
|
|
3613
|
+
// the dangling-target invariant uniform across every append path).
|
|
3614
|
+
reactions: clientMsgId ? this.reactionFold.tally(clientMsgId) : {}
|
|
3431
3615
|
});
|
|
3432
3616
|
this.messageList.sort((a, b) => a.serverSeq - b.serverSeq);
|
|
3433
3617
|
this.emit();
|
|
@@ -3474,7 +3658,53 @@ var Chat = class {
|
|
|
3474
3658
|
this.readWatermark = Math.max(this.readWatermark, message.serverSeq);
|
|
3475
3659
|
this.emit();
|
|
3476
3660
|
}
|
|
3661
|
+
// ── Reactions ──
|
|
3662
|
+
/** Add an emoji reaction to a message. No-op if the message isn't reactable
|
|
3663
|
+
* (empty clientMsgId — a legacy/system row). The reaction folds locally with
|
|
3664
|
+
* the server receipt's `(epoch, serverSeq)` so the target's tally updates
|
|
3665
|
+
* instantly; the durable echo on the next pump is a fold no-op (dedup on the
|
|
3666
|
+
* SAME wire clientMsgId). Never appends a bubble. */
|
|
3667
|
+
async react(message, emoji) {
|
|
3668
|
+
await this.sendReaction(message, emoji, "add");
|
|
3669
|
+
}
|
|
3670
|
+
/** Remove this user's emoji reaction from a message (op:'remove'). */
|
|
3671
|
+
async unreact(message, emoji) {
|
|
3672
|
+
await this.sendReaction(message, emoji, "remove");
|
|
3673
|
+
}
|
|
3674
|
+
async sendReaction(message, emoji, op) {
|
|
3675
|
+
if (!message.clientMsgId) return;
|
|
3676
|
+
const group = await this.materializeIfNeeded();
|
|
3677
|
+
const clientMsgId = mintClientMsgId();
|
|
3678
|
+
const { receipt } = await this.backend.sendReaction(group, {
|
|
3679
|
+
clientMsgId,
|
|
3680
|
+
targetClientMsgId: message.clientMsgId,
|
|
3681
|
+
emoji,
|
|
3682
|
+
op
|
|
3683
|
+
});
|
|
3684
|
+
this.reactionFold.ingest({
|
|
3685
|
+
targetClientMsgId: message.clientMsgId,
|
|
3686
|
+
actorUserId: this.backend.selfUserId,
|
|
3687
|
+
emoji,
|
|
3688
|
+
op,
|
|
3689
|
+
epoch: receipt.epoch,
|
|
3690
|
+
serverSeq: receipt.serverSeq,
|
|
3691
|
+
eventClientMsgId: clientMsgId
|
|
3692
|
+
});
|
|
3693
|
+
this.recomputeReactions(message.clientMsgId);
|
|
3694
|
+
}
|
|
3477
3695
|
};
|
|
3696
|
+
function sameReactions(a, b) {
|
|
3697
|
+
const ak = Object.keys(a);
|
|
3698
|
+
const bk = Object.keys(b);
|
|
3699
|
+
if (ak.length !== bk.length) return false;
|
|
3700
|
+
for (const k of ak) {
|
|
3701
|
+
const av = a[k];
|
|
3702
|
+
const bv = b[k];
|
|
3703
|
+
if (!bv || av === void 0 || av.length !== bv.length) return false;
|
|
3704
|
+
for (let i = 0; i < av.length; i++) if (av[i] !== bv[i]) return false;
|
|
3705
|
+
}
|
|
3706
|
+
return true;
|
|
3707
|
+
}
|
|
3478
3708
|
|
|
3479
3709
|
// src/messaging/delivery-source.ts
|
|
3480
3710
|
var MessageHub = class {
|
|
@@ -3504,6 +3734,11 @@ var MessageHub = class {
|
|
|
3504
3734
|
};
|
|
3505
3735
|
}
|
|
3506
3736
|
};
|
|
3737
|
+
function decodeSenderDeviceId(sender) {
|
|
3738
|
+
if (sender.length === 0) return null;
|
|
3739
|
+
const id = decodeUtf8(sender);
|
|
3740
|
+
return id.length > 0 ? id : null;
|
|
3741
|
+
}
|
|
3507
3742
|
var MessageDeliverySource = class {
|
|
3508
3743
|
constructor(rt, engine, hub, registry, messageStore, deviceId, selfUserId) {
|
|
3509
3744
|
this.rt = rt;
|
|
@@ -3633,12 +3868,15 @@ var MessageDeliverySource = class {
|
|
|
3633
3868
|
try {
|
|
3634
3869
|
const received = await this.engine.processIncoming(fromBase64(group.rfcGroupId), blob);
|
|
3635
3870
|
if (received.type === "application") {
|
|
3636
|
-
const
|
|
3871
|
+
const decoded = decodeEnvelope(received.data);
|
|
3872
|
+
const { text, clientMsgId, replyTo } = decoded;
|
|
3873
|
+
const isReaction = decoded.type === "reaction" && decoded.reaction != null;
|
|
3874
|
+
const senderDeviceId = row.sender_device_id ?? decodeSenderDeviceId(received.sender);
|
|
3637
3875
|
const stored = {
|
|
3638
3876
|
id: `${group.rfcGroupId}#${row.server_seq}`,
|
|
3639
3877
|
direction: "incoming",
|
|
3640
3878
|
text,
|
|
3641
|
-
senderDeviceId
|
|
3879
|
+
senderDeviceId,
|
|
3642
3880
|
epoch: row.epoch,
|
|
3643
3881
|
serverSeq: row.server_seq,
|
|
3644
3882
|
at: Date.now(),
|
|
@@ -3648,7 +3886,19 @@ var MessageDeliverySource = class {
|
|
|
3648
3886
|
previewBody: replyTo.preview?.body ?? null,
|
|
3649
3887
|
previewAuthorUserId: replyTo.preview?.author_user_id ?? null,
|
|
3650
3888
|
previewKind: replyTo.preview?.kind ?? "text"
|
|
3651
|
-
} : null
|
|
3889
|
+
} : null,
|
|
3890
|
+
// Thread the reaction discriminator + fields through the persisted row so
|
|
3891
|
+
// a reaction folded LIVE re-folds onto its target after a reload (the
|
|
3892
|
+
// reload-parity boundary — mirrors iOS T3). Omitted for non-reactions →
|
|
3893
|
+
// old rows hydrate as `'text'`/no-reaction (backward-compat).
|
|
3894
|
+
...isReaction && decoded.reaction ? {
|
|
3895
|
+
envelopeType: "reaction",
|
|
3896
|
+
reaction: {
|
|
3897
|
+
targetClientMsgId: decoded.reaction.targetClientMsgId,
|
|
3898
|
+
emoji: decoded.reaction.emoji,
|
|
3899
|
+
op: decoded.reaction.op
|
|
3900
|
+
}
|
|
3901
|
+
} : {}
|
|
3652
3902
|
};
|
|
3653
3903
|
try {
|
|
3654
3904
|
await this.messageStore.append(group.rfcGroupId, stored);
|
|
@@ -3659,12 +3909,14 @@ var MessageDeliverySource = class {
|
|
|
3659
3909
|
kind: "application",
|
|
3660
3910
|
group,
|
|
3661
3911
|
text,
|
|
3662
|
-
senderDeviceId
|
|
3912
|
+
senderDeviceId,
|
|
3663
3913
|
epoch: row.epoch,
|
|
3664
3914
|
serverSeq: row.server_seq,
|
|
3665
3915
|
receivedAt: /* @__PURE__ */ new Date(),
|
|
3666
3916
|
clientMsgId,
|
|
3667
|
-
replyRef: replyTo
|
|
3917
|
+
replyRef: replyTo,
|
|
3918
|
+
envelopeType: decoded.type ?? "text",
|
|
3919
|
+
reaction: isReaction ? decoded.reaction : null
|
|
3668
3920
|
});
|
|
3669
3921
|
return true;
|
|
3670
3922
|
}
|
|
@@ -5668,46 +5920,14 @@ var MessagingCoordinator = class {
|
|
|
5668
5920
|
const r = await this.resolve();
|
|
5669
5921
|
return r.groups.sendText(group, text, replyTo);
|
|
5670
5922
|
}
|
|
5923
|
+
async sendReaction(group, args) {
|
|
5924
|
+
const r = await this.resolve();
|
|
5925
|
+
return r.groups.sendReaction(group, args);
|
|
5926
|
+
}
|
|
5671
5927
|
async history(group, limit, before) {
|
|
5672
5928
|
const r = await this.resolve();
|
|
5673
5929
|
const rows = await r.messageStore.history(group.rfcGroupId, limit, before);
|
|
5674
|
-
|
|
5675
|
-
for (const s of rows) {
|
|
5676
|
-
const cid = s.clientMsgId ?? "";
|
|
5677
|
-
if (cid && s.text !== null) {
|
|
5678
|
-
const senderUserId = s.direction === "outgoing" ? this.selfUserId : "";
|
|
5679
|
-
lookup.set(cid, { text: s.text, senderUserId });
|
|
5680
|
-
}
|
|
5681
|
-
}
|
|
5682
|
-
return rows.map((s) => this.toChatMessage(group, s, (id) => lookup.get(id) ?? null));
|
|
5683
|
-
}
|
|
5684
|
-
toChatMessage(group, s, lookup) {
|
|
5685
|
-
const clientMsgId = s.clientMsgId ?? "";
|
|
5686
|
-
let replyTo = null;
|
|
5687
|
-
if (s.replyTo && lookup) {
|
|
5688
|
-
const ref = {
|
|
5689
|
-
v: 1,
|
|
5690
|
-
client_msg_id: s.replyTo.clientMsgId,
|
|
5691
|
-
preview: {
|
|
5692
|
-
kind: s.replyTo.previewKind,
|
|
5693
|
-
author_user_id: s.replyTo.previewAuthorUserId ?? "",
|
|
5694
|
-
body: s.replyTo.previewBody ?? void 0,
|
|
5695
|
-
body_truncated: false
|
|
5696
|
-
}
|
|
5697
|
-
};
|
|
5698
|
-
replyTo = resolveReply(ref, lookup);
|
|
5699
|
-
}
|
|
5700
|
-
return {
|
|
5701
|
-
id: `${group.displayId}#${s.serverSeq}`,
|
|
5702
|
-
kind: s.text != null ? "text" : "system",
|
|
5703
|
-
direction: s.direction,
|
|
5704
|
-
senderUserId: s.direction === "outgoing" ? this.selfUserId : null,
|
|
5705
|
-
text: s.text,
|
|
5706
|
-
serverSeq: s.serverSeq,
|
|
5707
|
-
sentAt: new Date(s.at),
|
|
5708
|
-
clientMsgId,
|
|
5709
|
-
replyTo
|
|
5710
|
-
};
|
|
5930
|
+
return projectHistory(group.displayId, rows, this.selfUserId);
|
|
5711
5931
|
}
|
|
5712
5932
|
async members(group) {
|
|
5713
5933
|
const r = await this.resolve();
|
|
@@ -5784,6 +6004,64 @@ var MessagingCoordinator = class {
|
|
|
5784
6004
|
return res.devices.map((d) => d.device_id);
|
|
5785
6005
|
}
|
|
5786
6006
|
};
|
|
6007
|
+
function projectHistory(displayId, rows, selfUserId, resolveActor) {
|
|
6008
|
+
const fold = new ReactionFold();
|
|
6009
|
+
for (const s of rows) {
|
|
6010
|
+
if (s.envelopeType !== "reaction" || !s.reaction) continue;
|
|
6011
|
+
const actor = s.direction === "outgoing" ? selfUserId : resolveActor?.(s.senderDeviceId ?? null) ?? null;
|
|
6012
|
+
if (actor === null) continue;
|
|
6013
|
+
fold.ingest({
|
|
6014
|
+
targetClientMsgId: s.reaction.targetClientMsgId,
|
|
6015
|
+
actorUserId: actor,
|
|
6016
|
+
emoji: s.reaction.emoji,
|
|
6017
|
+
op: s.reaction.op,
|
|
6018
|
+
epoch: s.epoch,
|
|
6019
|
+
serverSeq: s.serverSeq,
|
|
6020
|
+
eventClientMsgId: s.clientMsgId ?? `${s.id}`
|
|
6021
|
+
});
|
|
6022
|
+
}
|
|
6023
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
6024
|
+
for (const s of rows) {
|
|
6025
|
+
if (s.envelopeType === "reaction") continue;
|
|
6026
|
+
const cid = s.clientMsgId ?? "";
|
|
6027
|
+
if (cid && s.text !== null) {
|
|
6028
|
+
const senderUserId = s.direction === "outgoing" ? selfUserId : "";
|
|
6029
|
+
lookup.set(cid, { text: s.text, senderUserId });
|
|
6030
|
+
}
|
|
6031
|
+
}
|
|
6032
|
+
const out = [];
|
|
6033
|
+
for (const s of rows) {
|
|
6034
|
+
if (s.envelopeType === "reaction") continue;
|
|
6035
|
+
const clientMsgId = s.clientMsgId ?? "";
|
|
6036
|
+
let replyTo = null;
|
|
6037
|
+
if (s.replyTo) {
|
|
6038
|
+
const ref = {
|
|
6039
|
+
v: 1,
|
|
6040
|
+
client_msg_id: s.replyTo.clientMsgId,
|
|
6041
|
+
preview: {
|
|
6042
|
+
kind: s.replyTo.previewKind,
|
|
6043
|
+
author_user_id: s.replyTo.previewAuthorUserId ?? "",
|
|
6044
|
+
body: s.replyTo.previewBody ?? void 0,
|
|
6045
|
+
body_truncated: false
|
|
6046
|
+
}
|
|
6047
|
+
};
|
|
6048
|
+
replyTo = resolveReply(ref, (id) => lookup.get(id) ?? null);
|
|
6049
|
+
}
|
|
6050
|
+
out.push({
|
|
6051
|
+
id: `${displayId}#${s.serverSeq}`,
|
|
6052
|
+
kind: s.text != null ? "text" : "system",
|
|
6053
|
+
direction: s.direction,
|
|
6054
|
+
senderUserId: s.direction === "outgoing" ? selfUserId : null,
|
|
6055
|
+
text: s.text,
|
|
6056
|
+
serverSeq: s.serverSeq,
|
|
6057
|
+
sentAt: new Date(s.at),
|
|
6058
|
+
clientMsgId,
|
|
6059
|
+
replyTo,
|
|
6060
|
+
reactions: clientMsgId ? fold.tally(clientMsgId) : {}
|
|
6061
|
+
});
|
|
6062
|
+
}
|
|
6063
|
+
return out;
|
|
6064
|
+
}
|
|
5787
6065
|
|
|
5788
6066
|
// src/messaging/facade.ts
|
|
5789
6067
|
var PalbeMessaging = class {
|
|
@@ -6517,7 +6795,7 @@ function defaultSessionStorage(key) {
|
|
|
6517
6795
|
}
|
|
6518
6796
|
|
|
6519
6797
|
// src/version.ts
|
|
6520
|
-
var VERSION = "1.
|
|
6798
|
+
var VERSION = "1.2.1";
|
|
6521
6799
|
|
|
6522
6800
|
// src/runtime.ts
|
|
6523
6801
|
function buildRuntime(config) {
|