@palbase/web 1.1.0 → 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-XVLR3HGD.js → chunk-OZLVL7G2.js} +319 -48
- package/dist/chunk-OZLVL7G2.js.map +1 -0
- package/dist/index.cjs +318 -47
- 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 -47
- 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 -47
- package/dist/next/client.cjs.map +1 -1
- package/dist/next/client.js +1 -1
- package/dist/next/index.cjs +318 -47
- 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-XVLR3HGD.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 {
|
|
@@ -3633,7 +3863,9 @@ var MessageDeliverySource = class {
|
|
|
3633
3863
|
try {
|
|
3634
3864
|
const received = await this.engine.processIncoming(fromBase64(group.rfcGroupId), blob);
|
|
3635
3865
|
if (received.type === "application") {
|
|
3636
|
-
const
|
|
3866
|
+
const decoded = decodeEnvelope(received.data);
|
|
3867
|
+
const { text, clientMsgId, replyTo } = decoded;
|
|
3868
|
+
const isReaction = decoded.type === "reaction" && decoded.reaction != null;
|
|
3637
3869
|
const stored = {
|
|
3638
3870
|
id: `${group.rfcGroupId}#${row.server_seq}`,
|
|
3639
3871
|
direction: "incoming",
|
|
@@ -3648,7 +3880,19 @@ var MessageDeliverySource = class {
|
|
|
3648
3880
|
previewBody: replyTo.preview?.body ?? null,
|
|
3649
3881
|
previewAuthorUserId: replyTo.preview?.author_user_id ?? null,
|
|
3650
3882
|
previewKind: replyTo.preview?.kind ?? "text"
|
|
3651
|
-
} : null
|
|
3883
|
+
} : null,
|
|
3884
|
+
// Thread the reaction discriminator + fields through the persisted row so
|
|
3885
|
+
// a reaction folded LIVE re-folds onto its target after a reload (the
|
|
3886
|
+
// reload-parity boundary — mirrors iOS T3). Omitted for non-reactions →
|
|
3887
|
+
// old rows hydrate as `'text'`/no-reaction (backward-compat).
|
|
3888
|
+
...isReaction && decoded.reaction ? {
|
|
3889
|
+
envelopeType: "reaction",
|
|
3890
|
+
reaction: {
|
|
3891
|
+
targetClientMsgId: decoded.reaction.targetClientMsgId,
|
|
3892
|
+
emoji: decoded.reaction.emoji,
|
|
3893
|
+
op: decoded.reaction.op
|
|
3894
|
+
}
|
|
3895
|
+
} : {}
|
|
3652
3896
|
};
|
|
3653
3897
|
try {
|
|
3654
3898
|
await this.messageStore.append(group.rfcGroupId, stored);
|
|
@@ -3664,7 +3908,9 @@ var MessageDeliverySource = class {
|
|
|
3664
3908
|
serverSeq: row.server_seq,
|
|
3665
3909
|
receivedAt: /* @__PURE__ */ new Date(),
|
|
3666
3910
|
clientMsgId,
|
|
3667
|
-
replyRef: replyTo
|
|
3911
|
+
replyRef: replyTo,
|
|
3912
|
+
envelopeType: decoded.type ?? "text",
|
|
3913
|
+
reaction: isReaction ? decoded.reaction : null
|
|
3668
3914
|
});
|
|
3669
3915
|
return true;
|
|
3670
3916
|
}
|
|
@@ -5654,7 +5900,6 @@ var MessagingCoordinator = class {
|
|
|
5654
5900
|
await r.groups.addMember(group, userId);
|
|
5655
5901
|
}
|
|
5656
5902
|
}
|
|
5657
|
-
r.registry.register(group);
|
|
5658
5903
|
void r.catalog.upsert({
|
|
5659
5904
|
displayId: group.displayId,
|
|
5660
5905
|
rfcGroupId: group.rfcGroupId,
|
|
@@ -5669,46 +5914,14 @@ var MessagingCoordinator = class {
|
|
|
5669
5914
|
const r = await this.resolve();
|
|
5670
5915
|
return r.groups.sendText(group, text, replyTo);
|
|
5671
5916
|
}
|
|
5917
|
+
async sendReaction(group, args) {
|
|
5918
|
+
const r = await this.resolve();
|
|
5919
|
+
return r.groups.sendReaction(group, args);
|
|
5920
|
+
}
|
|
5672
5921
|
async history(group, limit, before) {
|
|
5673
5922
|
const r = await this.resolve();
|
|
5674
5923
|
const rows = await r.messageStore.history(group.rfcGroupId, limit, before);
|
|
5675
|
-
|
|
5676
|
-
for (const s of rows) {
|
|
5677
|
-
const cid = s.clientMsgId ?? "";
|
|
5678
|
-
if (cid && s.text !== null) {
|
|
5679
|
-
const senderUserId = s.direction === "outgoing" ? this.selfUserId : "";
|
|
5680
|
-
lookup.set(cid, { text: s.text, senderUserId });
|
|
5681
|
-
}
|
|
5682
|
-
}
|
|
5683
|
-
return rows.map((s) => this.toChatMessage(group, s, (id) => lookup.get(id) ?? null));
|
|
5684
|
-
}
|
|
5685
|
-
toChatMessage(group, s, lookup) {
|
|
5686
|
-
const clientMsgId = s.clientMsgId ?? "";
|
|
5687
|
-
let replyTo = null;
|
|
5688
|
-
if (s.replyTo && lookup) {
|
|
5689
|
-
const ref = {
|
|
5690
|
-
v: 1,
|
|
5691
|
-
client_msg_id: s.replyTo.clientMsgId,
|
|
5692
|
-
preview: {
|
|
5693
|
-
kind: s.replyTo.previewKind,
|
|
5694
|
-
author_user_id: s.replyTo.previewAuthorUserId ?? "",
|
|
5695
|
-
body: s.replyTo.previewBody ?? void 0,
|
|
5696
|
-
body_truncated: false
|
|
5697
|
-
}
|
|
5698
|
-
};
|
|
5699
|
-
replyTo = resolveReply(ref, lookup);
|
|
5700
|
-
}
|
|
5701
|
-
return {
|
|
5702
|
-
id: `${group.displayId}#${s.serverSeq}`,
|
|
5703
|
-
kind: s.text != null ? "text" : "system",
|
|
5704
|
-
direction: s.direction,
|
|
5705
|
-
senderUserId: s.direction === "outgoing" ? this.selfUserId : null,
|
|
5706
|
-
text: s.text,
|
|
5707
|
-
serverSeq: s.serverSeq,
|
|
5708
|
-
sentAt: new Date(s.at),
|
|
5709
|
-
clientMsgId,
|
|
5710
|
-
replyTo
|
|
5711
|
-
};
|
|
5924
|
+
return projectHistory(group.displayId, rows, this.selfUserId);
|
|
5712
5925
|
}
|
|
5713
5926
|
async members(group) {
|
|
5714
5927
|
const r = await this.resolve();
|
|
@@ -5785,6 +5998,64 @@ var MessagingCoordinator = class {
|
|
|
5785
5998
|
return res.devices.map((d) => d.device_id);
|
|
5786
5999
|
}
|
|
5787
6000
|
};
|
|
6001
|
+
function projectHistory(displayId, rows, selfUserId, resolveActor) {
|
|
6002
|
+
const fold = new ReactionFold();
|
|
6003
|
+
for (const s of rows) {
|
|
6004
|
+
if (s.envelopeType !== "reaction" || !s.reaction) continue;
|
|
6005
|
+
const actor = s.direction === "outgoing" ? selfUserId : resolveActor?.(s.senderDeviceId ?? null) ?? null;
|
|
6006
|
+
if (actor === null) continue;
|
|
6007
|
+
fold.ingest({
|
|
6008
|
+
targetClientMsgId: s.reaction.targetClientMsgId,
|
|
6009
|
+
actorUserId: actor,
|
|
6010
|
+
emoji: s.reaction.emoji,
|
|
6011
|
+
op: s.reaction.op,
|
|
6012
|
+
epoch: s.epoch,
|
|
6013
|
+
serverSeq: s.serverSeq,
|
|
6014
|
+
eventClientMsgId: s.clientMsgId ?? `${s.id}`
|
|
6015
|
+
});
|
|
6016
|
+
}
|
|
6017
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
6018
|
+
for (const s of rows) {
|
|
6019
|
+
if (s.envelopeType === "reaction") continue;
|
|
6020
|
+
const cid = s.clientMsgId ?? "";
|
|
6021
|
+
if (cid && s.text !== null) {
|
|
6022
|
+
const senderUserId = s.direction === "outgoing" ? selfUserId : "";
|
|
6023
|
+
lookup.set(cid, { text: s.text, senderUserId });
|
|
6024
|
+
}
|
|
6025
|
+
}
|
|
6026
|
+
const out = [];
|
|
6027
|
+
for (const s of rows) {
|
|
6028
|
+
if (s.envelopeType === "reaction") continue;
|
|
6029
|
+
const clientMsgId = s.clientMsgId ?? "";
|
|
6030
|
+
let replyTo = null;
|
|
6031
|
+
if (s.replyTo) {
|
|
6032
|
+
const ref = {
|
|
6033
|
+
v: 1,
|
|
6034
|
+
client_msg_id: s.replyTo.clientMsgId,
|
|
6035
|
+
preview: {
|
|
6036
|
+
kind: s.replyTo.previewKind,
|
|
6037
|
+
author_user_id: s.replyTo.previewAuthorUserId ?? "",
|
|
6038
|
+
body: s.replyTo.previewBody ?? void 0,
|
|
6039
|
+
body_truncated: false
|
|
6040
|
+
}
|
|
6041
|
+
};
|
|
6042
|
+
replyTo = resolveReply(ref, (id) => lookup.get(id) ?? null);
|
|
6043
|
+
}
|
|
6044
|
+
out.push({
|
|
6045
|
+
id: `${displayId}#${s.serverSeq}`,
|
|
6046
|
+
kind: s.text != null ? "text" : "system",
|
|
6047
|
+
direction: s.direction,
|
|
6048
|
+
senderUserId: s.direction === "outgoing" ? selfUserId : null,
|
|
6049
|
+
text: s.text,
|
|
6050
|
+
serverSeq: s.serverSeq,
|
|
6051
|
+
sentAt: new Date(s.at),
|
|
6052
|
+
clientMsgId,
|
|
6053
|
+
replyTo,
|
|
6054
|
+
reactions: clientMsgId ? fold.tally(clientMsgId) : {}
|
|
6055
|
+
});
|
|
6056
|
+
}
|
|
6057
|
+
return out;
|
|
6058
|
+
}
|
|
5788
6059
|
|
|
5789
6060
|
// src/messaging/facade.ts
|
|
5790
6061
|
var PalbeMessaging = class {
|
|
@@ -6518,7 +6789,7 @@ function defaultSessionStorage(key) {
|
|
|
6518
6789
|
}
|
|
6519
6790
|
|
|
6520
6791
|
// src/version.ts
|
|
6521
|
-
var VERSION = "1.
|
|
6792
|
+
var VERSION = "1.2.0";
|
|
6522
6793
|
|
|
6523
6794
|
// src/runtime.ts
|
|
6524
6795
|
function buildRuntime(config) {
|