@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/internal.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { u as PalbeConfig, J as PalbeRuntime } from './analytics-facade-
|
|
2
|
-
export { K as buildRuntime } from './analytics-facade-
|
|
1
|
+
import { u as PalbeConfig, J as PalbeRuntime } from './analytics-facade-Ct3A1zop.cjs';
|
|
2
|
+
export { K as buildRuntime } from './analytics-facade-Ct3A1zop.cjs';
|
|
3
3
|
import { B as BackendError } from './errors-fDoNdTrJ.cjs';
|
|
4
|
-
export { P as PB, c as createBoundClient } from './pb-
|
|
4
|
+
export { P as PB, c as createBoundClient } from './pb-BlIfgBG-.cjs';
|
|
5
5
|
import 'livekit-client';
|
|
6
6
|
import './storage-BPaeSG8K.cjs';
|
|
7
7
|
import './pooled-flags-Bwq4usn0.js';
|
package/dist/internal.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { u as PalbeConfig, J as PalbeRuntime } from './analytics-facade-
|
|
2
|
-
export { K as buildRuntime } from './analytics-facade-
|
|
1
|
+
import { u as PalbeConfig, J as PalbeRuntime } from './analytics-facade-C93tr7dA.js';
|
|
2
|
+
export { K as buildRuntime } from './analytics-facade-C93tr7dA.js';
|
|
3
3
|
import { B as BackendError } from './errors-fDoNdTrJ.js';
|
|
4
|
-
export { P as PB, c as createBoundClient } from './pb-
|
|
4
|
+
export { P as PB, c as createBoundClient } from './pb-BtYdWClg.js';
|
|
5
5
|
import 'livekit-client';
|
|
6
6
|
import './storage-BPaeSG8K.js';
|
|
7
7
|
import './pooled-flags-Bwq4usn0.js';
|
package/dist/internal.js
CHANGED
package/dist/next/client.cjs
CHANGED
|
@@ -2552,6 +2552,18 @@ async function listDevices(rt, userId) {
|
|
|
2552
2552
|
}
|
|
2553
2553
|
|
|
2554
2554
|
// src/messaging/group-messaging.ts
|
|
2555
|
+
function encodeReaction(args) {
|
|
2556
|
+
return encodeUtf8(
|
|
2557
|
+
JSON.stringify({
|
|
2558
|
+
v: 1,
|
|
2559
|
+
type: "reaction",
|
|
2560
|
+
client_msg_id: args.clientMsgId,
|
|
2561
|
+
target_client_msg_id: args.targetClientMsgId,
|
|
2562
|
+
emoji: args.emoji,
|
|
2563
|
+
op: args.op
|
|
2564
|
+
})
|
|
2565
|
+
);
|
|
2566
|
+
}
|
|
2555
2567
|
function encodeEnvelope(args) {
|
|
2556
2568
|
const env = {
|
|
2557
2569
|
v: 1,
|
|
@@ -2566,18 +2578,32 @@ function decodeEnvelope(bytes) {
|
|
|
2566
2578
|
const s = decodeUtf8(bytes);
|
|
2567
2579
|
try {
|
|
2568
2580
|
const o = JSON.parse(s);
|
|
2581
|
+
if (typeof o === "object" && o !== null && o.type === "reaction") {
|
|
2582
|
+
return {
|
|
2583
|
+
type: "reaction",
|
|
2584
|
+
text: null,
|
|
2585
|
+
clientMsgId: o.client_msg_id ?? "",
|
|
2586
|
+
replyTo: null,
|
|
2587
|
+
reaction: {
|
|
2588
|
+
targetClientMsgId: o.target_client_msg_id ?? "",
|
|
2589
|
+
emoji: o.emoji ?? "",
|
|
2590
|
+
op: o.op ?? "add"
|
|
2591
|
+
}
|
|
2592
|
+
};
|
|
2593
|
+
}
|
|
2569
2594
|
if (typeof o === "object" && o !== null && o.type === "text" && typeof o.v === "number") {
|
|
2570
2595
|
return {
|
|
2596
|
+
type: "text",
|
|
2571
2597
|
text: o.text ?? null,
|
|
2572
2598
|
clientMsgId: o.client_msg_id ?? "",
|
|
2573
2599
|
replyTo: o.reply_to ?? null
|
|
2574
2600
|
};
|
|
2575
2601
|
}
|
|
2576
2602
|
if (typeof o === "object" && o !== null && o.type === "text") {
|
|
2577
|
-
return { text: o.text ?? null, clientMsgId: "", replyTo: null };
|
|
2603
|
+
return { type: "text", text: o.text ?? null, clientMsgId: "", replyTo: null };
|
|
2578
2604
|
}
|
|
2579
2605
|
if (typeof o === "object" && o !== null && o.type === "media")
|
|
2580
|
-
return { text: null, clientMsgId: "", replyTo: null };
|
|
2606
|
+
return { type: "media", text: null, clientMsgId: "", replyTo: null };
|
|
2581
2607
|
} catch {
|
|
2582
2608
|
}
|
|
2583
2609
|
return { text: s, clientMsgId: "", replyTo: null };
|
|
@@ -2829,6 +2855,56 @@ var GroupMessaging = class {
|
|
|
2829
2855
|
}
|
|
2830
2856
|
return { receipt: { serverSeq: wire.server_seq, epoch: wire.epoch }, clientMsgId };
|
|
2831
2857
|
}
|
|
2858
|
+
/** Send a reaction (add/remove of an emoji on a target message). Encrypts a
|
|
2859
|
+
* `type:'reaction'` envelope at the current epoch and sends through the SAME
|
|
2860
|
+
* MLS application path as `sendText` (the server stays blind — a reaction is
|
|
2861
|
+
* just another application message). Persists the outgoing reaction row so it
|
|
2862
|
+
* re-folds onto its target after a reload (the own-send half of the reload
|
|
2863
|
+
* parity). NEVER rebases (epoch-bound like any application message). */
|
|
2864
|
+
async sendReaction(group, args) {
|
|
2865
|
+
const plaintext = encodeReaction({
|
|
2866
|
+
clientMsgId: args.clientMsgId,
|
|
2867
|
+
targetClientMsgId: args.targetClientMsgId,
|
|
2868
|
+
emoji: args.emoji,
|
|
2869
|
+
op: args.op
|
|
2870
|
+
});
|
|
2871
|
+
const ct = await this.engine.encryptApplication(fromBase64(group.rfcGroupId), plaintext);
|
|
2872
|
+
const body = {
|
|
2873
|
+
ciphertext_b64: toBase64(ct),
|
|
2874
|
+
client_idem_key: randomId()
|
|
2875
|
+
};
|
|
2876
|
+
const wire = await palbeRequest(
|
|
2877
|
+
this.rt,
|
|
2878
|
+
"POST",
|
|
2879
|
+
MessagingPaths.groupMessages(group.displayId),
|
|
2880
|
+
{ body }
|
|
2881
|
+
);
|
|
2882
|
+
const stored = {
|
|
2883
|
+
id: `${group.rfcGroupId}#${wire.server_seq}`,
|
|
2884
|
+
direction: "outgoing",
|
|
2885
|
+
text: null,
|
|
2886
|
+
senderDeviceId: this.selfDeviceId,
|
|
2887
|
+
epoch: wire.epoch,
|
|
2888
|
+
serverSeq: wire.server_seq,
|
|
2889
|
+
at: Date.now(),
|
|
2890
|
+
clientMsgId: args.clientMsgId,
|
|
2891
|
+
replyTo: null,
|
|
2892
|
+
envelopeType: "reaction",
|
|
2893
|
+
reaction: {
|
|
2894
|
+
targetClientMsgId: args.targetClientMsgId,
|
|
2895
|
+
emoji: args.emoji,
|
|
2896
|
+
op: args.op
|
|
2897
|
+
}
|
|
2898
|
+
};
|
|
2899
|
+
try {
|
|
2900
|
+
await this.messageStore.append(group.rfcGroupId, stored);
|
|
2901
|
+
} catch {
|
|
2902
|
+
}
|
|
2903
|
+
return {
|
|
2904
|
+
receipt: { serverSeq: wire.server_seq, epoch: wire.epoch },
|
|
2905
|
+
clientMsgId: args.clientMsgId
|
|
2906
|
+
};
|
|
2907
|
+
}
|
|
2832
2908
|
// ── The rebase loop ──
|
|
2833
2909
|
async commitWithRebase(rfcGroupId, build) {
|
|
2834
2910
|
const gidBytes = fromBase64(rfcGroupId);
|
|
@@ -2890,6 +2966,61 @@ var GroupMessaging = class {
|
|
|
2890
2966
|
}
|
|
2891
2967
|
};
|
|
2892
2968
|
|
|
2969
|
+
// src/messaging/reaction-fold.ts
|
|
2970
|
+
function orderLte(aEpoch, aSeq, bEpoch, bSeq) {
|
|
2971
|
+
if (aEpoch !== bEpoch) return aEpoch < bEpoch;
|
|
2972
|
+
return aSeq <= bSeq;
|
|
2973
|
+
}
|
|
2974
|
+
var ReactionFold = class {
|
|
2975
|
+
// Map<targetClientMsgId, Map<actorUserId, UserState>>
|
|
2976
|
+
states = /* @__PURE__ */ new Map();
|
|
2977
|
+
// Dedup set: eventClientMsgId values already applied.
|
|
2978
|
+
seenEvents = /* @__PURE__ */ new Set();
|
|
2979
|
+
ingest(e) {
|
|
2980
|
+
if (this.seenEvents.has(e.eventClientMsgId)) return;
|
|
2981
|
+
this.seenEvents.add(e.eventClientMsgId);
|
|
2982
|
+
let byUser = this.states.get(e.targetClientMsgId);
|
|
2983
|
+
if (byUser === void 0) {
|
|
2984
|
+
byUser = /* @__PURE__ */ new Map();
|
|
2985
|
+
this.states.set(e.targetClientMsgId, byUser);
|
|
2986
|
+
}
|
|
2987
|
+
const prev = byUser.get(e.actorUserId);
|
|
2988
|
+
if (prev !== void 0) {
|
|
2989
|
+
if (orderLte(e.epoch, e.serverSeq, prev.orderEpoch, prev.orderSeq)) return;
|
|
2990
|
+
}
|
|
2991
|
+
byUser.set(e.actorUserId, {
|
|
2992
|
+
orderEpoch: e.epoch,
|
|
2993
|
+
orderSeq: e.serverSeq,
|
|
2994
|
+
emoji: e.op === "add" ? e.emoji : null
|
|
2995
|
+
});
|
|
2996
|
+
}
|
|
2997
|
+
/**
|
|
2998
|
+
* Returns a tally of `{ emoji → sorted userIds[] }` for the given target
|
|
2999
|
+
* message. Users whose last event was a `remove` (emoji === null) are
|
|
3000
|
+
* excluded. userId arrays are sorted for deterministic output (render
|
|
3001
|
+
* stability + golden tests).
|
|
3002
|
+
*/
|
|
3003
|
+
tally(targetClientMsgId) {
|
|
3004
|
+
const byUser = this.states.get(targetClientMsgId);
|
|
3005
|
+
if (byUser === void 0) return {};
|
|
3006
|
+
const out = /* @__PURE__ */ new Map();
|
|
3007
|
+
for (const [userId, st] of byUser) {
|
|
3008
|
+
if (st.emoji === null) continue;
|
|
3009
|
+
let users = out.get(st.emoji);
|
|
3010
|
+
if (users === void 0) {
|
|
3011
|
+
users = [];
|
|
3012
|
+
out.set(st.emoji, users);
|
|
3013
|
+
}
|
|
3014
|
+
users.push(userId);
|
|
3015
|
+
}
|
|
3016
|
+
const result = {};
|
|
3017
|
+
for (const [emoji, users] of out) {
|
|
3018
|
+
result[emoji] = users.sort();
|
|
3019
|
+
}
|
|
3020
|
+
return result;
|
|
3021
|
+
}
|
|
3022
|
+
};
|
|
3023
|
+
|
|
2893
3024
|
// src/messaging/chat.ts
|
|
2894
3025
|
var Chat = class {
|
|
2895
3026
|
/** Stable, URL/log-safe id (the grp_ display id once active; a reserved local id while draft). */
|
|
@@ -2908,6 +3039,8 @@ var Chat = class {
|
|
|
2908
3039
|
seenKeys = /* @__PURE__ */ new Set();
|
|
2909
3040
|
/** Index: clientMsgId → { text, senderUserId } for resolveReply lookups. */
|
|
2910
3041
|
byClientMsgId = /* @__PURE__ */ new Map();
|
|
3042
|
+
/** The single authoritative reaction fold for this chat (live + own-send + history). */
|
|
3043
|
+
reactionFold = new ReactionFold();
|
|
2911
3044
|
loadedEarliestSeq = null;
|
|
2912
3045
|
historyLoaded = false;
|
|
2913
3046
|
wired = false;
|
|
@@ -3014,7 +3147,7 @@ var Chat = class {
|
|
|
3014
3147
|
const key = this.internalKey(m.serverSeq);
|
|
3015
3148
|
if (this.seenKeys.has(key)) continue;
|
|
3016
3149
|
this.seenKeys.add(key);
|
|
3017
|
-
this.messageList.push(m);
|
|
3150
|
+
this.messageList.push(this.applyReactionTally(m));
|
|
3018
3151
|
changed = true;
|
|
3019
3152
|
this.loadedEarliestSeq = Math.min(this.loadedEarliestSeq ?? m.serverSeq, m.serverSeq);
|
|
3020
3153
|
}
|
|
@@ -3039,6 +3172,22 @@ var Chat = class {
|
|
|
3039
3172
|
senderUser = await this.backend.userIdForDevice(this._group, incoming.senderDeviceId);
|
|
3040
3173
|
}
|
|
3041
3174
|
const direction = senderUser !== null && senderUser === this.backend.selfUserId ? "outgoing" : "incoming";
|
|
3175
|
+
if (incoming.envelopeType === "reaction" && incoming.reaction) {
|
|
3176
|
+
const actorUserId = direction === "outgoing" ? this.backend.selfUserId : senderUser;
|
|
3177
|
+
if (actorUserId !== null) {
|
|
3178
|
+
this.reactionFold.ingest({
|
|
3179
|
+
targetClientMsgId: incoming.reaction.targetClientMsgId,
|
|
3180
|
+
actorUserId,
|
|
3181
|
+
emoji: incoming.reaction.emoji,
|
|
3182
|
+
op: incoming.reaction.op,
|
|
3183
|
+
epoch: incoming.epoch,
|
|
3184
|
+
serverSeq: incoming.serverSeq,
|
|
3185
|
+
eventClientMsgId: incoming.clientMsgId
|
|
3186
|
+
});
|
|
3187
|
+
this.recomputeReactions(incoming.reaction.targetClientMsgId);
|
|
3188
|
+
}
|
|
3189
|
+
return;
|
|
3190
|
+
}
|
|
3042
3191
|
const incomingClientMsgId = incoming.clientMsgId;
|
|
3043
3192
|
const incomingReplyRef = incoming.replyRef;
|
|
3044
3193
|
let resolvedReplyTo = null;
|
|
@@ -3054,7 +3203,10 @@ var Chat = class {
|
|
|
3054
3203
|
serverSeq: incoming.serverSeq,
|
|
3055
3204
|
sentAt: incoming.receivedAt,
|
|
3056
3205
|
clientMsgId: incomingClientMsgId,
|
|
3057
|
-
replyTo: resolvedReplyTo
|
|
3206
|
+
replyTo: resolvedReplyTo,
|
|
3207
|
+
// Attach any tally already folded for this message (a reaction that arrived
|
|
3208
|
+
// BEFORE its target — the dangling case — renders the moment the target lands).
|
|
3209
|
+
reactions: incomingClientMsgId ? this.reactionFold.tally(incomingClientMsgId) : {}
|
|
3058
3210
|
};
|
|
3059
3211
|
if (incomingClientMsgId && incoming.text !== null) {
|
|
3060
3212
|
this.byClientMsgId.set(incomingClientMsgId, {
|
|
@@ -3070,6 +3222,35 @@ var Chat = class {
|
|
|
3070
3222
|
);
|
|
3071
3223
|
this.emit();
|
|
3072
3224
|
}
|
|
3225
|
+
/**
|
|
3226
|
+
* Rebuild the target message's `reactions` from the authoritative fold and
|
|
3227
|
+
* re-emit. No-op when the target isn't present yet (its tally is attached the
|
|
3228
|
+
* moment it lands, via the append/merge paths) or when the tally is unchanged.
|
|
3229
|
+
*/
|
|
3230
|
+
recomputeReactions(targetClientMsgId) {
|
|
3231
|
+
if (!targetClientMsgId) return;
|
|
3232
|
+
const tally = this.reactionFold.tally(targetClientMsgId);
|
|
3233
|
+
let changed = false;
|
|
3234
|
+
this.messageList = this.messageList.map((m) => {
|
|
3235
|
+
if (m.clientMsgId !== targetClientMsgId) return m;
|
|
3236
|
+
if (sameReactions(m.reactions, tally)) return m;
|
|
3237
|
+
changed = true;
|
|
3238
|
+
return { ...m, reactions: tally };
|
|
3239
|
+
});
|
|
3240
|
+
if (changed) this.emit();
|
|
3241
|
+
}
|
|
3242
|
+
/**
|
|
3243
|
+
* Overlay the authoritative fold's tally onto a message as it is appended/merged.
|
|
3244
|
+
* The Chat fold WINS when it has a non-empty tally; otherwise the tally already
|
|
3245
|
+
* attached upstream (the coordinator's page-local history fold) is preserved.
|
|
3246
|
+
*/
|
|
3247
|
+
applyReactionTally(m) {
|
|
3248
|
+
if (!m.clientMsgId) return m;
|
|
3249
|
+
const tally = this.reactionFold.tally(m.clientMsgId);
|
|
3250
|
+
if (Object.keys(tally).length === 0) return m;
|
|
3251
|
+
if (sameReactions(m.reactions, tally)) return m;
|
|
3252
|
+
return { ...m, reactions: tally };
|
|
3253
|
+
}
|
|
3073
3254
|
/** @internal — called by the backend's conv subscription. */
|
|
3074
3255
|
applyConv(event, payload) {
|
|
3075
3256
|
const userId = typeof payload.user_id === "string" ? payload.user_id : null;
|
|
@@ -3212,7 +3393,10 @@ var Chat = class {
|
|
|
3212
3393
|
serverSeq: receipt.serverSeq,
|
|
3213
3394
|
sentAt: /* @__PURE__ */ new Date(),
|
|
3214
3395
|
clientMsgId,
|
|
3215
|
-
replyTo: resolvedReplyTo
|
|
3396
|
+
replyTo: resolvedReplyTo,
|
|
3397
|
+
// Attach any tally already folded for this own-sent message (rare, but keeps
|
|
3398
|
+
// the dangling-target invariant uniform across every append path).
|
|
3399
|
+
reactions: clientMsgId ? this.reactionFold.tally(clientMsgId) : {}
|
|
3216
3400
|
});
|
|
3217
3401
|
this.messageList.sort((a, b) => a.serverSeq - b.serverSeq);
|
|
3218
3402
|
this.emit();
|
|
@@ -3259,7 +3443,53 @@ var Chat = class {
|
|
|
3259
3443
|
this.readWatermark = Math.max(this.readWatermark, message.serverSeq);
|
|
3260
3444
|
this.emit();
|
|
3261
3445
|
}
|
|
3446
|
+
// ── Reactions ──
|
|
3447
|
+
/** Add an emoji reaction to a message. No-op if the message isn't reactable
|
|
3448
|
+
* (empty clientMsgId — a legacy/system row). The reaction folds locally with
|
|
3449
|
+
* the server receipt's `(epoch, serverSeq)` so the target's tally updates
|
|
3450
|
+
* instantly; the durable echo on the next pump is a fold no-op (dedup on the
|
|
3451
|
+
* SAME wire clientMsgId). Never appends a bubble. */
|
|
3452
|
+
async react(message, emoji) {
|
|
3453
|
+
await this.sendReaction(message, emoji, "add");
|
|
3454
|
+
}
|
|
3455
|
+
/** Remove this user's emoji reaction from a message (op:'remove'). */
|
|
3456
|
+
async unreact(message, emoji) {
|
|
3457
|
+
await this.sendReaction(message, emoji, "remove");
|
|
3458
|
+
}
|
|
3459
|
+
async sendReaction(message, emoji, op) {
|
|
3460
|
+
if (!message.clientMsgId) return;
|
|
3461
|
+
const group = await this.materializeIfNeeded();
|
|
3462
|
+
const clientMsgId = mintClientMsgId();
|
|
3463
|
+
const { receipt } = await this.backend.sendReaction(group, {
|
|
3464
|
+
clientMsgId,
|
|
3465
|
+
targetClientMsgId: message.clientMsgId,
|
|
3466
|
+
emoji,
|
|
3467
|
+
op
|
|
3468
|
+
});
|
|
3469
|
+
this.reactionFold.ingest({
|
|
3470
|
+
targetClientMsgId: message.clientMsgId,
|
|
3471
|
+
actorUserId: this.backend.selfUserId,
|
|
3472
|
+
emoji,
|
|
3473
|
+
op,
|
|
3474
|
+
epoch: receipt.epoch,
|
|
3475
|
+
serverSeq: receipt.serverSeq,
|
|
3476
|
+
eventClientMsgId: clientMsgId
|
|
3477
|
+
});
|
|
3478
|
+
this.recomputeReactions(message.clientMsgId);
|
|
3479
|
+
}
|
|
3262
3480
|
};
|
|
3481
|
+
function sameReactions(a, b) {
|
|
3482
|
+
const ak = Object.keys(a);
|
|
3483
|
+
const bk = Object.keys(b);
|
|
3484
|
+
if (ak.length !== bk.length) return false;
|
|
3485
|
+
for (const k of ak) {
|
|
3486
|
+
const av = a[k];
|
|
3487
|
+
const bv = b[k];
|
|
3488
|
+
if (!bv || av === void 0 || av.length !== bv.length) return false;
|
|
3489
|
+
for (let i = 0; i < av.length; i++) if (av[i] !== bv[i]) return false;
|
|
3490
|
+
}
|
|
3491
|
+
return true;
|
|
3492
|
+
}
|
|
3263
3493
|
|
|
3264
3494
|
// src/messaging/delivery-source.ts
|
|
3265
3495
|
var MessageHub = class {
|
|
@@ -3418,7 +3648,9 @@ var MessageDeliverySource = class {
|
|
|
3418
3648
|
try {
|
|
3419
3649
|
const received = await this.engine.processIncoming(fromBase64(group.rfcGroupId), blob);
|
|
3420
3650
|
if (received.type === "application") {
|
|
3421
|
-
const
|
|
3651
|
+
const decoded = decodeEnvelope(received.data);
|
|
3652
|
+
const { text, clientMsgId, replyTo } = decoded;
|
|
3653
|
+
const isReaction = decoded.type === "reaction" && decoded.reaction != null;
|
|
3422
3654
|
const stored = {
|
|
3423
3655
|
id: `${group.rfcGroupId}#${row.server_seq}`,
|
|
3424
3656
|
direction: "incoming",
|
|
@@ -3433,7 +3665,19 @@ var MessageDeliverySource = class {
|
|
|
3433
3665
|
previewBody: replyTo.preview?.body ?? null,
|
|
3434
3666
|
previewAuthorUserId: replyTo.preview?.author_user_id ?? null,
|
|
3435
3667
|
previewKind: replyTo.preview?.kind ?? "text"
|
|
3436
|
-
} : null
|
|
3668
|
+
} : null,
|
|
3669
|
+
// Thread the reaction discriminator + fields through the persisted row so
|
|
3670
|
+
// a reaction folded LIVE re-folds onto its target after a reload (the
|
|
3671
|
+
// reload-parity boundary — mirrors iOS T3). Omitted for non-reactions →
|
|
3672
|
+
// old rows hydrate as `'text'`/no-reaction (backward-compat).
|
|
3673
|
+
...isReaction && decoded.reaction ? {
|
|
3674
|
+
envelopeType: "reaction",
|
|
3675
|
+
reaction: {
|
|
3676
|
+
targetClientMsgId: decoded.reaction.targetClientMsgId,
|
|
3677
|
+
emoji: decoded.reaction.emoji,
|
|
3678
|
+
op: decoded.reaction.op
|
|
3679
|
+
}
|
|
3680
|
+
} : {}
|
|
3437
3681
|
};
|
|
3438
3682
|
try {
|
|
3439
3683
|
await this.messageStore.append(group.rfcGroupId, stored);
|
|
@@ -3449,7 +3693,9 @@ var MessageDeliverySource = class {
|
|
|
3449
3693
|
serverSeq: row.server_seq,
|
|
3450
3694
|
receivedAt: /* @__PURE__ */ new Date(),
|
|
3451
3695
|
clientMsgId,
|
|
3452
|
-
replyRef: replyTo
|
|
3696
|
+
replyRef: replyTo,
|
|
3697
|
+
envelopeType: decoded.type ?? "text",
|
|
3698
|
+
reaction: isReaction ? decoded.reaction : null
|
|
3453
3699
|
});
|
|
3454
3700
|
return true;
|
|
3455
3701
|
}
|
|
@@ -5439,7 +5685,6 @@ var MessagingCoordinator = class {
|
|
|
5439
5685
|
await r.groups.addMember(group, userId);
|
|
5440
5686
|
}
|
|
5441
5687
|
}
|
|
5442
|
-
r.registry.register(group);
|
|
5443
5688
|
void r.catalog.upsert({
|
|
5444
5689
|
displayId: group.displayId,
|
|
5445
5690
|
rfcGroupId: group.rfcGroupId,
|
|
@@ -5454,46 +5699,14 @@ var MessagingCoordinator = class {
|
|
|
5454
5699
|
const r = await this.resolve();
|
|
5455
5700
|
return r.groups.sendText(group, text, replyTo);
|
|
5456
5701
|
}
|
|
5702
|
+
async sendReaction(group, args) {
|
|
5703
|
+
const r = await this.resolve();
|
|
5704
|
+
return r.groups.sendReaction(group, args);
|
|
5705
|
+
}
|
|
5457
5706
|
async history(group, limit, before) {
|
|
5458
5707
|
const r = await this.resolve();
|
|
5459
5708
|
const rows = await r.messageStore.history(group.rfcGroupId, limit, before);
|
|
5460
|
-
|
|
5461
|
-
for (const s of rows) {
|
|
5462
|
-
const cid = s.clientMsgId ?? "";
|
|
5463
|
-
if (cid && s.text !== null) {
|
|
5464
|
-
const senderUserId = s.direction === "outgoing" ? this.selfUserId : "";
|
|
5465
|
-
lookup.set(cid, { text: s.text, senderUserId });
|
|
5466
|
-
}
|
|
5467
|
-
}
|
|
5468
|
-
return rows.map((s) => this.toChatMessage(group, s, (id) => lookup.get(id) ?? null));
|
|
5469
|
-
}
|
|
5470
|
-
toChatMessage(group, s, lookup) {
|
|
5471
|
-
const clientMsgId = s.clientMsgId ?? "";
|
|
5472
|
-
let replyTo = null;
|
|
5473
|
-
if (s.replyTo && lookup) {
|
|
5474
|
-
const ref = {
|
|
5475
|
-
v: 1,
|
|
5476
|
-
client_msg_id: s.replyTo.clientMsgId,
|
|
5477
|
-
preview: {
|
|
5478
|
-
kind: s.replyTo.previewKind,
|
|
5479
|
-
author_user_id: s.replyTo.previewAuthorUserId ?? "",
|
|
5480
|
-
body: s.replyTo.previewBody ?? void 0,
|
|
5481
|
-
body_truncated: false
|
|
5482
|
-
}
|
|
5483
|
-
};
|
|
5484
|
-
replyTo = resolveReply(ref, lookup);
|
|
5485
|
-
}
|
|
5486
|
-
return {
|
|
5487
|
-
id: `${group.displayId}#${s.serverSeq}`,
|
|
5488
|
-
kind: s.text != null ? "text" : "system",
|
|
5489
|
-
direction: s.direction,
|
|
5490
|
-
senderUserId: s.direction === "outgoing" ? this.selfUserId : null,
|
|
5491
|
-
text: s.text,
|
|
5492
|
-
serverSeq: s.serverSeq,
|
|
5493
|
-
sentAt: new Date(s.at),
|
|
5494
|
-
clientMsgId,
|
|
5495
|
-
replyTo
|
|
5496
|
-
};
|
|
5709
|
+
return projectHistory(group.displayId, rows, this.selfUserId);
|
|
5497
5710
|
}
|
|
5498
5711
|
async members(group) {
|
|
5499
5712
|
const r = await this.resolve();
|
|
@@ -5570,6 +5783,64 @@ var MessagingCoordinator = class {
|
|
|
5570
5783
|
return res.devices.map((d) => d.device_id);
|
|
5571
5784
|
}
|
|
5572
5785
|
};
|
|
5786
|
+
function projectHistory(displayId, rows, selfUserId, resolveActor) {
|
|
5787
|
+
const fold = new ReactionFold();
|
|
5788
|
+
for (const s of rows) {
|
|
5789
|
+
if (s.envelopeType !== "reaction" || !s.reaction) continue;
|
|
5790
|
+
const actor = s.direction === "outgoing" ? selfUserId : resolveActor?.(s.senderDeviceId ?? null) ?? null;
|
|
5791
|
+
if (actor === null) continue;
|
|
5792
|
+
fold.ingest({
|
|
5793
|
+
targetClientMsgId: s.reaction.targetClientMsgId,
|
|
5794
|
+
actorUserId: actor,
|
|
5795
|
+
emoji: s.reaction.emoji,
|
|
5796
|
+
op: s.reaction.op,
|
|
5797
|
+
epoch: s.epoch,
|
|
5798
|
+
serverSeq: s.serverSeq,
|
|
5799
|
+
eventClientMsgId: s.clientMsgId ?? `${s.id}`
|
|
5800
|
+
});
|
|
5801
|
+
}
|
|
5802
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
5803
|
+
for (const s of rows) {
|
|
5804
|
+
if (s.envelopeType === "reaction") continue;
|
|
5805
|
+
const cid = s.clientMsgId ?? "";
|
|
5806
|
+
if (cid && s.text !== null) {
|
|
5807
|
+
const senderUserId = s.direction === "outgoing" ? selfUserId : "";
|
|
5808
|
+
lookup.set(cid, { text: s.text, senderUserId });
|
|
5809
|
+
}
|
|
5810
|
+
}
|
|
5811
|
+
const out = [];
|
|
5812
|
+
for (const s of rows) {
|
|
5813
|
+
if (s.envelopeType === "reaction") continue;
|
|
5814
|
+
const clientMsgId = s.clientMsgId ?? "";
|
|
5815
|
+
let replyTo = null;
|
|
5816
|
+
if (s.replyTo) {
|
|
5817
|
+
const ref = {
|
|
5818
|
+
v: 1,
|
|
5819
|
+
client_msg_id: s.replyTo.clientMsgId,
|
|
5820
|
+
preview: {
|
|
5821
|
+
kind: s.replyTo.previewKind,
|
|
5822
|
+
author_user_id: s.replyTo.previewAuthorUserId ?? "",
|
|
5823
|
+
body: s.replyTo.previewBody ?? void 0,
|
|
5824
|
+
body_truncated: false
|
|
5825
|
+
}
|
|
5826
|
+
};
|
|
5827
|
+
replyTo = resolveReply(ref, (id) => lookup.get(id) ?? null);
|
|
5828
|
+
}
|
|
5829
|
+
out.push({
|
|
5830
|
+
id: `${displayId}#${s.serverSeq}`,
|
|
5831
|
+
kind: s.text != null ? "text" : "system",
|
|
5832
|
+
direction: s.direction,
|
|
5833
|
+
senderUserId: s.direction === "outgoing" ? selfUserId : null,
|
|
5834
|
+
text: s.text,
|
|
5835
|
+
serverSeq: s.serverSeq,
|
|
5836
|
+
sentAt: new Date(s.at),
|
|
5837
|
+
clientMsgId,
|
|
5838
|
+
replyTo,
|
|
5839
|
+
reactions: clientMsgId ? fold.tally(clientMsgId) : {}
|
|
5840
|
+
});
|
|
5841
|
+
}
|
|
5842
|
+
return out;
|
|
5843
|
+
}
|
|
5573
5844
|
|
|
5574
5845
|
// src/messaging/facade.ts
|
|
5575
5846
|
var PalbeMessaging = class {
|
|
@@ -6303,7 +6574,7 @@ function defaultSessionStorage(key) {
|
|
|
6303
6574
|
}
|
|
6304
6575
|
|
|
6305
6576
|
// src/version.ts
|
|
6306
|
-
var VERSION = "1.
|
|
6577
|
+
var VERSION = "1.2.0";
|
|
6307
6578
|
|
|
6308
6579
|
// src/runtime.ts
|
|
6309
6580
|
function buildRuntime(config) {
|