@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
|
@@ -2730,6 +2730,18 @@ async function listDevices(rt, userId) {
|
|
|
2730
2730
|
}
|
|
2731
2731
|
|
|
2732
2732
|
// src/messaging/group-messaging.ts
|
|
2733
|
+
function encodeReaction(args) {
|
|
2734
|
+
return encodeUtf8(
|
|
2735
|
+
JSON.stringify({
|
|
2736
|
+
v: 1,
|
|
2737
|
+
type: "reaction",
|
|
2738
|
+
client_msg_id: args.clientMsgId,
|
|
2739
|
+
target_client_msg_id: args.targetClientMsgId,
|
|
2740
|
+
emoji: args.emoji,
|
|
2741
|
+
op: args.op
|
|
2742
|
+
})
|
|
2743
|
+
);
|
|
2744
|
+
}
|
|
2733
2745
|
function encodeEnvelope(args) {
|
|
2734
2746
|
const env = {
|
|
2735
2747
|
v: 1,
|
|
@@ -2744,18 +2756,32 @@ function decodeEnvelope(bytes) {
|
|
|
2744
2756
|
const s = decodeUtf8(bytes);
|
|
2745
2757
|
try {
|
|
2746
2758
|
const o = JSON.parse(s);
|
|
2759
|
+
if (typeof o === "object" && o !== null && o.type === "reaction") {
|
|
2760
|
+
return {
|
|
2761
|
+
type: "reaction",
|
|
2762
|
+
text: null,
|
|
2763
|
+
clientMsgId: o.client_msg_id ?? "",
|
|
2764
|
+
replyTo: null,
|
|
2765
|
+
reaction: {
|
|
2766
|
+
targetClientMsgId: o.target_client_msg_id ?? "",
|
|
2767
|
+
emoji: o.emoji ?? "",
|
|
2768
|
+
op: o.op ?? "add"
|
|
2769
|
+
}
|
|
2770
|
+
};
|
|
2771
|
+
}
|
|
2747
2772
|
if (typeof o === "object" && o !== null && o.type === "text" && typeof o.v === "number") {
|
|
2748
2773
|
return {
|
|
2774
|
+
type: "text",
|
|
2749
2775
|
text: o.text ?? null,
|
|
2750
2776
|
clientMsgId: o.client_msg_id ?? "",
|
|
2751
2777
|
replyTo: o.reply_to ?? null
|
|
2752
2778
|
};
|
|
2753
2779
|
}
|
|
2754
2780
|
if (typeof o === "object" && o !== null && o.type === "text") {
|
|
2755
|
-
return { text: o.text ?? null, clientMsgId: "", replyTo: null };
|
|
2781
|
+
return { type: "text", text: o.text ?? null, clientMsgId: "", replyTo: null };
|
|
2756
2782
|
}
|
|
2757
2783
|
if (typeof o === "object" && o !== null && o.type === "media")
|
|
2758
|
-
return { text: null, clientMsgId: "", replyTo: null };
|
|
2784
|
+
return { type: "media", text: null, clientMsgId: "", replyTo: null };
|
|
2759
2785
|
} catch {
|
|
2760
2786
|
}
|
|
2761
2787
|
return { text: s, clientMsgId: "", replyTo: null };
|
|
@@ -3007,6 +3033,56 @@ var GroupMessaging = class {
|
|
|
3007
3033
|
}
|
|
3008
3034
|
return { receipt: { serverSeq: wire.server_seq, epoch: wire.epoch }, clientMsgId };
|
|
3009
3035
|
}
|
|
3036
|
+
/** Send a reaction (add/remove of an emoji on a target message). Encrypts a
|
|
3037
|
+
* `type:'reaction'` envelope at the current epoch and sends through the SAME
|
|
3038
|
+
* MLS application path as `sendText` (the server stays blind — a reaction is
|
|
3039
|
+
* just another application message). Persists the outgoing reaction row so it
|
|
3040
|
+
* re-folds onto its target after a reload (the own-send half of the reload
|
|
3041
|
+
* parity). NEVER rebases (epoch-bound like any application message). */
|
|
3042
|
+
async sendReaction(group, args) {
|
|
3043
|
+
const plaintext = encodeReaction({
|
|
3044
|
+
clientMsgId: args.clientMsgId,
|
|
3045
|
+
targetClientMsgId: args.targetClientMsgId,
|
|
3046
|
+
emoji: args.emoji,
|
|
3047
|
+
op: args.op
|
|
3048
|
+
});
|
|
3049
|
+
const ct = await this.engine.encryptApplication(fromBase64(group.rfcGroupId), plaintext);
|
|
3050
|
+
const body = {
|
|
3051
|
+
ciphertext_b64: toBase64(ct),
|
|
3052
|
+
client_idem_key: randomId()
|
|
3053
|
+
};
|
|
3054
|
+
const wire = await palbeRequest(
|
|
3055
|
+
this.rt,
|
|
3056
|
+
"POST",
|
|
3057
|
+
MessagingPaths.groupMessages(group.displayId),
|
|
3058
|
+
{ body }
|
|
3059
|
+
);
|
|
3060
|
+
const stored = {
|
|
3061
|
+
id: `${group.rfcGroupId}#${wire.server_seq}`,
|
|
3062
|
+
direction: "outgoing",
|
|
3063
|
+
text: null,
|
|
3064
|
+
senderDeviceId: this.selfDeviceId,
|
|
3065
|
+
epoch: wire.epoch,
|
|
3066
|
+
serverSeq: wire.server_seq,
|
|
3067
|
+
at: Date.now(),
|
|
3068
|
+
clientMsgId: args.clientMsgId,
|
|
3069
|
+
replyTo: null,
|
|
3070
|
+
envelopeType: "reaction",
|
|
3071
|
+
reaction: {
|
|
3072
|
+
targetClientMsgId: args.targetClientMsgId,
|
|
3073
|
+
emoji: args.emoji,
|
|
3074
|
+
op: args.op
|
|
3075
|
+
}
|
|
3076
|
+
};
|
|
3077
|
+
try {
|
|
3078
|
+
await this.messageStore.append(group.rfcGroupId, stored);
|
|
3079
|
+
} catch {
|
|
3080
|
+
}
|
|
3081
|
+
return {
|
|
3082
|
+
receipt: { serverSeq: wire.server_seq, epoch: wire.epoch },
|
|
3083
|
+
clientMsgId: args.clientMsgId
|
|
3084
|
+
};
|
|
3085
|
+
}
|
|
3010
3086
|
// ── The rebase loop ──
|
|
3011
3087
|
async commitWithRebase(rfcGroupId, build) {
|
|
3012
3088
|
const gidBytes = fromBase64(rfcGroupId);
|
|
@@ -3068,6 +3144,61 @@ var GroupMessaging = class {
|
|
|
3068
3144
|
}
|
|
3069
3145
|
};
|
|
3070
3146
|
|
|
3147
|
+
// src/messaging/reaction-fold.ts
|
|
3148
|
+
function orderLte(aEpoch, aSeq, bEpoch, bSeq) {
|
|
3149
|
+
if (aEpoch !== bEpoch) return aEpoch < bEpoch;
|
|
3150
|
+
return aSeq <= bSeq;
|
|
3151
|
+
}
|
|
3152
|
+
var ReactionFold = class {
|
|
3153
|
+
// Map<targetClientMsgId, Map<actorUserId, UserState>>
|
|
3154
|
+
states = /* @__PURE__ */ new Map();
|
|
3155
|
+
// Dedup set: eventClientMsgId values already applied.
|
|
3156
|
+
seenEvents = /* @__PURE__ */ new Set();
|
|
3157
|
+
ingest(e) {
|
|
3158
|
+
if (this.seenEvents.has(e.eventClientMsgId)) return;
|
|
3159
|
+
this.seenEvents.add(e.eventClientMsgId);
|
|
3160
|
+
let byUser = this.states.get(e.targetClientMsgId);
|
|
3161
|
+
if (byUser === void 0) {
|
|
3162
|
+
byUser = /* @__PURE__ */ new Map();
|
|
3163
|
+
this.states.set(e.targetClientMsgId, byUser);
|
|
3164
|
+
}
|
|
3165
|
+
const prev = byUser.get(e.actorUserId);
|
|
3166
|
+
if (prev !== void 0) {
|
|
3167
|
+
if (orderLte(e.epoch, e.serverSeq, prev.orderEpoch, prev.orderSeq)) return;
|
|
3168
|
+
}
|
|
3169
|
+
byUser.set(e.actorUserId, {
|
|
3170
|
+
orderEpoch: e.epoch,
|
|
3171
|
+
orderSeq: e.serverSeq,
|
|
3172
|
+
emoji: e.op === "add" ? e.emoji : null
|
|
3173
|
+
});
|
|
3174
|
+
}
|
|
3175
|
+
/**
|
|
3176
|
+
* Returns a tally of `{ emoji → sorted userIds[] }` for the given target
|
|
3177
|
+
* message. Users whose last event was a `remove` (emoji === null) are
|
|
3178
|
+
* excluded. userId arrays are sorted for deterministic output (render
|
|
3179
|
+
* stability + golden tests).
|
|
3180
|
+
*/
|
|
3181
|
+
tally(targetClientMsgId) {
|
|
3182
|
+
const byUser = this.states.get(targetClientMsgId);
|
|
3183
|
+
if (byUser === void 0) return {};
|
|
3184
|
+
const out = /* @__PURE__ */ new Map();
|
|
3185
|
+
for (const [userId, st] of byUser) {
|
|
3186
|
+
if (st.emoji === null) continue;
|
|
3187
|
+
let users = out.get(st.emoji);
|
|
3188
|
+
if (users === void 0) {
|
|
3189
|
+
users = [];
|
|
3190
|
+
out.set(st.emoji, users);
|
|
3191
|
+
}
|
|
3192
|
+
users.push(userId);
|
|
3193
|
+
}
|
|
3194
|
+
const result = {};
|
|
3195
|
+
for (const [emoji, users] of out) {
|
|
3196
|
+
result[emoji] = users.sort();
|
|
3197
|
+
}
|
|
3198
|
+
return result;
|
|
3199
|
+
}
|
|
3200
|
+
};
|
|
3201
|
+
|
|
3071
3202
|
// src/messaging/chat.ts
|
|
3072
3203
|
var Chat = class {
|
|
3073
3204
|
/** Stable, URL/log-safe id (the grp_ display id once active; a reserved local id while draft). */
|
|
@@ -3086,6 +3217,8 @@ var Chat = class {
|
|
|
3086
3217
|
seenKeys = /* @__PURE__ */ new Set();
|
|
3087
3218
|
/** Index: clientMsgId → { text, senderUserId } for resolveReply lookups. */
|
|
3088
3219
|
byClientMsgId = /* @__PURE__ */ new Map();
|
|
3220
|
+
/** The single authoritative reaction fold for this chat (live + own-send + history). */
|
|
3221
|
+
reactionFold = new ReactionFold();
|
|
3089
3222
|
loadedEarliestSeq = null;
|
|
3090
3223
|
historyLoaded = false;
|
|
3091
3224
|
wired = false;
|
|
@@ -3192,7 +3325,7 @@ var Chat = class {
|
|
|
3192
3325
|
const key = this.internalKey(m.serverSeq);
|
|
3193
3326
|
if (this.seenKeys.has(key)) continue;
|
|
3194
3327
|
this.seenKeys.add(key);
|
|
3195
|
-
this.messageList.push(m);
|
|
3328
|
+
this.messageList.push(this.applyReactionTally(m));
|
|
3196
3329
|
changed = true;
|
|
3197
3330
|
this.loadedEarliestSeq = Math.min(this.loadedEarliestSeq ?? m.serverSeq, m.serverSeq);
|
|
3198
3331
|
}
|
|
@@ -3217,6 +3350,22 @@ var Chat = class {
|
|
|
3217
3350
|
senderUser = await this.backend.userIdForDevice(this._group, incoming.senderDeviceId);
|
|
3218
3351
|
}
|
|
3219
3352
|
const direction = senderUser !== null && senderUser === this.backend.selfUserId ? "outgoing" : "incoming";
|
|
3353
|
+
if (incoming.envelopeType === "reaction" && incoming.reaction) {
|
|
3354
|
+
const actorUserId = direction === "outgoing" ? this.backend.selfUserId : senderUser;
|
|
3355
|
+
if (actorUserId !== null) {
|
|
3356
|
+
this.reactionFold.ingest({
|
|
3357
|
+
targetClientMsgId: incoming.reaction.targetClientMsgId,
|
|
3358
|
+
actorUserId,
|
|
3359
|
+
emoji: incoming.reaction.emoji,
|
|
3360
|
+
op: incoming.reaction.op,
|
|
3361
|
+
epoch: incoming.epoch,
|
|
3362
|
+
serverSeq: incoming.serverSeq,
|
|
3363
|
+
eventClientMsgId: incoming.clientMsgId
|
|
3364
|
+
});
|
|
3365
|
+
this.recomputeReactions(incoming.reaction.targetClientMsgId);
|
|
3366
|
+
}
|
|
3367
|
+
return;
|
|
3368
|
+
}
|
|
3220
3369
|
const incomingClientMsgId = incoming.clientMsgId;
|
|
3221
3370
|
const incomingReplyRef = incoming.replyRef;
|
|
3222
3371
|
let resolvedReplyTo = null;
|
|
@@ -3232,7 +3381,10 @@ var Chat = class {
|
|
|
3232
3381
|
serverSeq: incoming.serverSeq,
|
|
3233
3382
|
sentAt: incoming.receivedAt,
|
|
3234
3383
|
clientMsgId: incomingClientMsgId,
|
|
3235
|
-
replyTo: resolvedReplyTo
|
|
3384
|
+
replyTo: resolvedReplyTo,
|
|
3385
|
+
// Attach any tally already folded for this message (a reaction that arrived
|
|
3386
|
+
// BEFORE its target — the dangling case — renders the moment the target lands).
|
|
3387
|
+
reactions: incomingClientMsgId ? this.reactionFold.tally(incomingClientMsgId) : {}
|
|
3236
3388
|
};
|
|
3237
3389
|
if (incomingClientMsgId && incoming.text !== null) {
|
|
3238
3390
|
this.byClientMsgId.set(incomingClientMsgId, {
|
|
@@ -3248,6 +3400,35 @@ var Chat = class {
|
|
|
3248
3400
|
);
|
|
3249
3401
|
this.emit();
|
|
3250
3402
|
}
|
|
3403
|
+
/**
|
|
3404
|
+
* Rebuild the target message's `reactions` from the authoritative fold and
|
|
3405
|
+
* re-emit. No-op when the target isn't present yet (its tally is attached the
|
|
3406
|
+
* moment it lands, via the append/merge paths) or when the tally is unchanged.
|
|
3407
|
+
*/
|
|
3408
|
+
recomputeReactions(targetClientMsgId) {
|
|
3409
|
+
if (!targetClientMsgId) return;
|
|
3410
|
+
const tally = this.reactionFold.tally(targetClientMsgId);
|
|
3411
|
+
let changed = false;
|
|
3412
|
+
this.messageList = this.messageList.map((m) => {
|
|
3413
|
+
if (m.clientMsgId !== targetClientMsgId) return m;
|
|
3414
|
+
if (sameReactions(m.reactions, tally)) return m;
|
|
3415
|
+
changed = true;
|
|
3416
|
+
return { ...m, reactions: tally };
|
|
3417
|
+
});
|
|
3418
|
+
if (changed) this.emit();
|
|
3419
|
+
}
|
|
3420
|
+
/**
|
|
3421
|
+
* Overlay the authoritative fold's tally onto a message as it is appended/merged.
|
|
3422
|
+
* The Chat fold WINS when it has a non-empty tally; otherwise the tally already
|
|
3423
|
+
* attached upstream (the coordinator's page-local history fold) is preserved.
|
|
3424
|
+
*/
|
|
3425
|
+
applyReactionTally(m) {
|
|
3426
|
+
if (!m.clientMsgId) return m;
|
|
3427
|
+
const tally = this.reactionFold.tally(m.clientMsgId);
|
|
3428
|
+
if (Object.keys(tally).length === 0) return m;
|
|
3429
|
+
if (sameReactions(m.reactions, tally)) return m;
|
|
3430
|
+
return { ...m, reactions: tally };
|
|
3431
|
+
}
|
|
3251
3432
|
/** @internal — called by the backend's conv subscription. */
|
|
3252
3433
|
applyConv(event, payload) {
|
|
3253
3434
|
const userId = typeof payload.user_id === "string" ? payload.user_id : null;
|
|
@@ -3390,7 +3571,10 @@ var Chat = class {
|
|
|
3390
3571
|
serverSeq: receipt.serverSeq,
|
|
3391
3572
|
sentAt: /* @__PURE__ */ new Date(),
|
|
3392
3573
|
clientMsgId,
|
|
3393
|
-
replyTo: resolvedReplyTo
|
|
3574
|
+
replyTo: resolvedReplyTo,
|
|
3575
|
+
// Attach any tally already folded for this own-sent message (rare, but keeps
|
|
3576
|
+
// the dangling-target invariant uniform across every append path).
|
|
3577
|
+
reactions: clientMsgId ? this.reactionFold.tally(clientMsgId) : {}
|
|
3394
3578
|
});
|
|
3395
3579
|
this.messageList.sort((a, b) => a.serverSeq - b.serverSeq);
|
|
3396
3580
|
this.emit();
|
|
@@ -3437,7 +3621,53 @@ var Chat = class {
|
|
|
3437
3621
|
this.readWatermark = Math.max(this.readWatermark, message.serverSeq);
|
|
3438
3622
|
this.emit();
|
|
3439
3623
|
}
|
|
3624
|
+
// ── Reactions ──
|
|
3625
|
+
/** Add an emoji reaction to a message. No-op if the message isn't reactable
|
|
3626
|
+
* (empty clientMsgId — a legacy/system row). The reaction folds locally with
|
|
3627
|
+
* the server receipt's `(epoch, serverSeq)` so the target's tally updates
|
|
3628
|
+
* instantly; the durable echo on the next pump is a fold no-op (dedup on the
|
|
3629
|
+
* SAME wire clientMsgId). Never appends a bubble. */
|
|
3630
|
+
async react(message, emoji) {
|
|
3631
|
+
await this.sendReaction(message, emoji, "add");
|
|
3632
|
+
}
|
|
3633
|
+
/** Remove this user's emoji reaction from a message (op:'remove'). */
|
|
3634
|
+
async unreact(message, emoji) {
|
|
3635
|
+
await this.sendReaction(message, emoji, "remove");
|
|
3636
|
+
}
|
|
3637
|
+
async sendReaction(message, emoji, op) {
|
|
3638
|
+
if (!message.clientMsgId) return;
|
|
3639
|
+
const group = await this.materializeIfNeeded();
|
|
3640
|
+
const clientMsgId = mintClientMsgId();
|
|
3641
|
+
const { receipt } = await this.backend.sendReaction(group, {
|
|
3642
|
+
clientMsgId,
|
|
3643
|
+
targetClientMsgId: message.clientMsgId,
|
|
3644
|
+
emoji,
|
|
3645
|
+
op
|
|
3646
|
+
});
|
|
3647
|
+
this.reactionFold.ingest({
|
|
3648
|
+
targetClientMsgId: message.clientMsgId,
|
|
3649
|
+
actorUserId: this.backend.selfUserId,
|
|
3650
|
+
emoji,
|
|
3651
|
+
op,
|
|
3652
|
+
epoch: receipt.epoch,
|
|
3653
|
+
serverSeq: receipt.serverSeq,
|
|
3654
|
+
eventClientMsgId: clientMsgId
|
|
3655
|
+
});
|
|
3656
|
+
this.recomputeReactions(message.clientMsgId);
|
|
3657
|
+
}
|
|
3440
3658
|
};
|
|
3659
|
+
function sameReactions(a, b) {
|
|
3660
|
+
const ak = Object.keys(a);
|
|
3661
|
+
const bk = Object.keys(b);
|
|
3662
|
+
if (ak.length !== bk.length) return false;
|
|
3663
|
+
for (const k of ak) {
|
|
3664
|
+
const av = a[k];
|
|
3665
|
+
const bv = b[k];
|
|
3666
|
+
if (!bv || av === void 0 || av.length !== bv.length) return false;
|
|
3667
|
+
for (let i = 0; i < av.length; i++) if (av[i] !== bv[i]) return false;
|
|
3668
|
+
}
|
|
3669
|
+
return true;
|
|
3670
|
+
}
|
|
3441
3671
|
|
|
3442
3672
|
// src/messaging/delivery-source.ts
|
|
3443
3673
|
var MessageHub = class {
|
|
@@ -3467,6 +3697,11 @@ var MessageHub = class {
|
|
|
3467
3697
|
};
|
|
3468
3698
|
}
|
|
3469
3699
|
};
|
|
3700
|
+
function decodeSenderDeviceId(sender) {
|
|
3701
|
+
if (sender.length === 0) return null;
|
|
3702
|
+
const id = decodeUtf8(sender);
|
|
3703
|
+
return id.length > 0 ? id : null;
|
|
3704
|
+
}
|
|
3470
3705
|
var MessageDeliverySource = class {
|
|
3471
3706
|
constructor(rt, engine, hub, registry, messageStore, deviceId, selfUserId) {
|
|
3472
3707
|
this.rt = rt;
|
|
@@ -3596,12 +3831,15 @@ var MessageDeliverySource = class {
|
|
|
3596
3831
|
try {
|
|
3597
3832
|
const received = await this.engine.processIncoming(fromBase64(group.rfcGroupId), blob);
|
|
3598
3833
|
if (received.type === "application") {
|
|
3599
|
-
const
|
|
3834
|
+
const decoded = decodeEnvelope(received.data);
|
|
3835
|
+
const { text, clientMsgId, replyTo } = decoded;
|
|
3836
|
+
const isReaction = decoded.type === "reaction" && decoded.reaction != null;
|
|
3837
|
+
const senderDeviceId = row.sender_device_id ?? decodeSenderDeviceId(received.sender);
|
|
3600
3838
|
const stored = {
|
|
3601
3839
|
id: `${group.rfcGroupId}#${row.server_seq}`,
|
|
3602
3840
|
direction: "incoming",
|
|
3603
3841
|
text,
|
|
3604
|
-
senderDeviceId
|
|
3842
|
+
senderDeviceId,
|
|
3605
3843
|
epoch: row.epoch,
|
|
3606
3844
|
serverSeq: row.server_seq,
|
|
3607
3845
|
at: Date.now(),
|
|
@@ -3611,7 +3849,19 @@ var MessageDeliverySource = class {
|
|
|
3611
3849
|
previewBody: replyTo.preview?.body ?? null,
|
|
3612
3850
|
previewAuthorUserId: replyTo.preview?.author_user_id ?? null,
|
|
3613
3851
|
previewKind: replyTo.preview?.kind ?? "text"
|
|
3614
|
-
} : null
|
|
3852
|
+
} : null,
|
|
3853
|
+
// Thread the reaction discriminator + fields through the persisted row so
|
|
3854
|
+
// a reaction folded LIVE re-folds onto its target after a reload (the
|
|
3855
|
+
// reload-parity boundary — mirrors iOS T3). Omitted for non-reactions →
|
|
3856
|
+
// old rows hydrate as `'text'`/no-reaction (backward-compat).
|
|
3857
|
+
...isReaction && decoded.reaction ? {
|
|
3858
|
+
envelopeType: "reaction",
|
|
3859
|
+
reaction: {
|
|
3860
|
+
targetClientMsgId: decoded.reaction.targetClientMsgId,
|
|
3861
|
+
emoji: decoded.reaction.emoji,
|
|
3862
|
+
op: decoded.reaction.op
|
|
3863
|
+
}
|
|
3864
|
+
} : {}
|
|
3615
3865
|
};
|
|
3616
3866
|
try {
|
|
3617
3867
|
await this.messageStore.append(group.rfcGroupId, stored);
|
|
@@ -3622,12 +3872,14 @@ var MessageDeliverySource = class {
|
|
|
3622
3872
|
kind: "application",
|
|
3623
3873
|
group,
|
|
3624
3874
|
text,
|
|
3625
|
-
senderDeviceId
|
|
3875
|
+
senderDeviceId,
|
|
3626
3876
|
epoch: row.epoch,
|
|
3627
3877
|
serverSeq: row.server_seq,
|
|
3628
3878
|
receivedAt: /* @__PURE__ */ new Date(),
|
|
3629
3879
|
clientMsgId,
|
|
3630
|
-
replyRef: replyTo
|
|
3880
|
+
replyRef: replyTo,
|
|
3881
|
+
envelopeType: decoded.type ?? "text",
|
|
3882
|
+
reaction: isReaction ? decoded.reaction : null
|
|
3631
3883
|
});
|
|
3632
3884
|
return true;
|
|
3633
3885
|
}
|
|
@@ -5630,46 +5882,14 @@ var MessagingCoordinator = class {
|
|
|
5630
5882
|
const r = await this.resolve();
|
|
5631
5883
|
return r.groups.sendText(group, text, replyTo);
|
|
5632
5884
|
}
|
|
5885
|
+
async sendReaction(group, args) {
|
|
5886
|
+
const r = await this.resolve();
|
|
5887
|
+
return r.groups.sendReaction(group, args);
|
|
5888
|
+
}
|
|
5633
5889
|
async history(group, limit, before) {
|
|
5634
5890
|
const r = await this.resolve();
|
|
5635
5891
|
const rows = await r.messageStore.history(group.rfcGroupId, limit, before);
|
|
5636
|
-
|
|
5637
|
-
for (const s of rows) {
|
|
5638
|
-
const cid = s.clientMsgId ?? "";
|
|
5639
|
-
if (cid && s.text !== null) {
|
|
5640
|
-
const senderUserId = s.direction === "outgoing" ? this.selfUserId : "";
|
|
5641
|
-
lookup.set(cid, { text: s.text, senderUserId });
|
|
5642
|
-
}
|
|
5643
|
-
}
|
|
5644
|
-
return rows.map((s) => this.toChatMessage(group, s, (id) => lookup.get(id) ?? null));
|
|
5645
|
-
}
|
|
5646
|
-
toChatMessage(group, s, lookup) {
|
|
5647
|
-
const clientMsgId = s.clientMsgId ?? "";
|
|
5648
|
-
let replyTo = null;
|
|
5649
|
-
if (s.replyTo && lookup) {
|
|
5650
|
-
const ref = {
|
|
5651
|
-
v: 1,
|
|
5652
|
-
client_msg_id: s.replyTo.clientMsgId,
|
|
5653
|
-
preview: {
|
|
5654
|
-
kind: s.replyTo.previewKind,
|
|
5655
|
-
author_user_id: s.replyTo.previewAuthorUserId ?? "",
|
|
5656
|
-
body: s.replyTo.previewBody ?? void 0,
|
|
5657
|
-
body_truncated: false
|
|
5658
|
-
}
|
|
5659
|
-
};
|
|
5660
|
-
replyTo = resolveReply(ref, lookup);
|
|
5661
|
-
}
|
|
5662
|
-
return {
|
|
5663
|
-
id: `${group.displayId}#${s.serverSeq}`,
|
|
5664
|
-
kind: s.text != null ? "text" : "system",
|
|
5665
|
-
direction: s.direction,
|
|
5666
|
-
senderUserId: s.direction === "outgoing" ? this.selfUserId : null,
|
|
5667
|
-
text: s.text,
|
|
5668
|
-
serverSeq: s.serverSeq,
|
|
5669
|
-
sentAt: new Date(s.at),
|
|
5670
|
-
clientMsgId,
|
|
5671
|
-
replyTo
|
|
5672
|
-
};
|
|
5892
|
+
return projectHistory(group.displayId, rows, this.selfUserId);
|
|
5673
5893
|
}
|
|
5674
5894
|
async members(group) {
|
|
5675
5895
|
const r = await this.resolve();
|
|
@@ -5746,6 +5966,64 @@ var MessagingCoordinator = class {
|
|
|
5746
5966
|
return res.devices.map((d) => d.device_id);
|
|
5747
5967
|
}
|
|
5748
5968
|
};
|
|
5969
|
+
function projectHistory(displayId, rows, selfUserId, resolveActor) {
|
|
5970
|
+
const fold = new ReactionFold();
|
|
5971
|
+
for (const s of rows) {
|
|
5972
|
+
if (s.envelopeType !== "reaction" || !s.reaction) continue;
|
|
5973
|
+
const actor = s.direction === "outgoing" ? selfUserId : resolveActor?.(s.senderDeviceId ?? null) ?? null;
|
|
5974
|
+
if (actor === null) continue;
|
|
5975
|
+
fold.ingest({
|
|
5976
|
+
targetClientMsgId: s.reaction.targetClientMsgId,
|
|
5977
|
+
actorUserId: actor,
|
|
5978
|
+
emoji: s.reaction.emoji,
|
|
5979
|
+
op: s.reaction.op,
|
|
5980
|
+
epoch: s.epoch,
|
|
5981
|
+
serverSeq: s.serverSeq,
|
|
5982
|
+
eventClientMsgId: s.clientMsgId ?? `${s.id}`
|
|
5983
|
+
});
|
|
5984
|
+
}
|
|
5985
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
5986
|
+
for (const s of rows) {
|
|
5987
|
+
if (s.envelopeType === "reaction") continue;
|
|
5988
|
+
const cid = s.clientMsgId ?? "";
|
|
5989
|
+
if (cid && s.text !== null) {
|
|
5990
|
+
const senderUserId = s.direction === "outgoing" ? selfUserId : "";
|
|
5991
|
+
lookup.set(cid, { text: s.text, senderUserId });
|
|
5992
|
+
}
|
|
5993
|
+
}
|
|
5994
|
+
const out = [];
|
|
5995
|
+
for (const s of rows) {
|
|
5996
|
+
if (s.envelopeType === "reaction") continue;
|
|
5997
|
+
const clientMsgId = s.clientMsgId ?? "";
|
|
5998
|
+
let replyTo = null;
|
|
5999
|
+
if (s.replyTo) {
|
|
6000
|
+
const ref = {
|
|
6001
|
+
v: 1,
|
|
6002
|
+
client_msg_id: s.replyTo.clientMsgId,
|
|
6003
|
+
preview: {
|
|
6004
|
+
kind: s.replyTo.previewKind,
|
|
6005
|
+
author_user_id: s.replyTo.previewAuthorUserId ?? "",
|
|
6006
|
+
body: s.replyTo.previewBody ?? void 0,
|
|
6007
|
+
body_truncated: false
|
|
6008
|
+
}
|
|
6009
|
+
};
|
|
6010
|
+
replyTo = resolveReply(ref, (id) => lookup.get(id) ?? null);
|
|
6011
|
+
}
|
|
6012
|
+
out.push({
|
|
6013
|
+
id: `${displayId}#${s.serverSeq}`,
|
|
6014
|
+
kind: s.text != null ? "text" : "system",
|
|
6015
|
+
direction: s.direction,
|
|
6016
|
+
senderUserId: s.direction === "outgoing" ? selfUserId : null,
|
|
6017
|
+
text: s.text,
|
|
6018
|
+
serverSeq: s.serverSeq,
|
|
6019
|
+
sentAt: new Date(s.at),
|
|
6020
|
+
clientMsgId,
|
|
6021
|
+
replyTo,
|
|
6022
|
+
reactions: clientMsgId ? fold.tally(clientMsgId) : {}
|
|
6023
|
+
});
|
|
6024
|
+
}
|
|
6025
|
+
return out;
|
|
6026
|
+
}
|
|
5749
6027
|
|
|
5750
6028
|
// src/messaging/facade.ts
|
|
5751
6029
|
var PalbeMessaging = class {
|
|
@@ -6479,7 +6757,7 @@ function defaultSessionStorage(key) {
|
|
|
6479
6757
|
}
|
|
6480
6758
|
|
|
6481
6759
|
// src/version.ts
|
|
6482
|
-
var VERSION = "1.
|
|
6760
|
+
var VERSION = "1.2.1";
|
|
6483
6761
|
|
|
6484
6762
|
// src/runtime.ts
|
|
6485
6763
|
function buildRuntime(config) {
|
|
@@ -6839,4 +7117,4 @@ export {
|
|
|
6839
7117
|
pb,
|
|
6840
7118
|
createBoundClient
|
|
6841
7119
|
};
|
|
6842
|
-
//# sourceMappingURL=chunk-
|
|
7120
|
+
//# sourceMappingURL=chunk-KJXRY4S3.js.map
|