Fix live agent reactions in Inbox and channels (#1921)

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Pinky <44b8e82baa6e0e254e0208d68f335c283c94e7b78dd1fa10d5a49d3f13dd0435@sprout-oss.stage.blox.sqprod.co>
This commit is contained in:
Wes
2026-07-15 15:28:12 -07:00
committed by GitHub
co-authored by Pinky
parent 7fbdacb03f
commit 8212a87231
4 changed files with 66 additions and 9 deletions
@@ -16,7 +16,10 @@ import {
resolveChannelReadMarker,
resolveObservedUnreadRootId,
} from "./useUnreadChannels.ts";
import { isChannelUnreadTriggerKind } from "./useLiveChannelUpdates.ts";
import {
isChannelUnreadTriggerKind,
withChannelTagFallback,
} from "./useLiveChannelUpdates.ts";
import {
KIND_HUDDLE_ENDED,
KIND_HUDDLE_STARTED,
@@ -58,6 +61,35 @@ test("receiveThenReopen_frontierAtLatestArrival_clobbersDivider", () => {
assert.equal(marker.unreadCount, 0);
});
test("live reaction without h tag inherits its subscription channel", () => {
const reaction = {
id: "reaction",
kind: 7,
pubkey: "agent",
created_at: 1,
content: "👀",
tags: [["e", "message"]],
};
assert.deepEqual(withChannelTagFallback(reaction, "channel-a").tags, [
["e", "message"],
["h", "channel-a"],
]);
});
test("live event with h tag is preserved", () => {
const message = {
id: "message",
kind: KIND_STREAM_MESSAGE,
pubkey: "author",
created_at: 1,
content: "hello",
tags: [["h", "channel-from-event"]],
};
assert.equal(withChannelTagFallback(message, "other-channel"), message);
});
test("dmHuddleStart_isDmOnlyUnreadTrigger", () => {
assert.equal(
isChannelUnreadTriggerKind(KIND_HUDDLE_STARTED, true),
@@ -88,6 +88,15 @@ export function isChannelUnreadTriggerKind(kind: number, isDmChannel: boolean) {
: UNREAD_TRIGGER_KINDS.has(kind);
}
export function withChannelTagFallback(
event: RelayEvent,
channelId: string,
): RelayEvent {
return getChannelIdFromTags(event.tags)
? event
: { ...event, tags: [...event.tags, ["h", channelId]] };
}
function isExternalMentionEvent(event: RelayEvent, currentPubkey: string) {
return (
currentPubkey.length > 0 && event.pubkey.toLowerCase() !== currentPubkey
@@ -359,7 +368,8 @@ export function useLiveChannelUpdates(
limit: 1000,
since: Math.floor(Date.now() / 1_000),
},
handleIncomingMessage,
(event) =>
handleIncomingMessage(withChannelTagFallback(event, channelId)),
);
if (isCancelled) {
void dispose().catch(() => {});
+4 -7
View File
@@ -7707,13 +7707,10 @@ async function handleAddReaction(
}
const emoji = args.emoji.trim();
// `h` routes the live event to the channel store (getChannelIdFromTags);
// `e` names the reaction target. For a custom emoji, the NIP-30
// `["emoji", shortcode, url]` tag carries the image URL.
const tags: string[][] = [
["h", channelId],
["e", args.eventId],
];
// Real add_reaction events carry only the target `e` tag. Channel live
// subscriptions already know which channel matched and restore that context
// before merging the event into the timeline cache.
const tags: string[][] = [["e", args.eventId]];
if (args.emojiUrl) {
const shortcode = emoji.replace(/^:+/, "").replace(/:+$/, "").toLowerCase();
tags.push(["emoji", shortcode, args.emojiUrl]);
+18
View File
@@ -121,6 +121,24 @@ test("inbox reaction on a thread-reply mention persists after refetch", async ({
await expect(detail).toContainText("please react to this");
const selectedMessage = page.getByTestId("home-inbox-selected-message");
// Deliver a live reaction with the real add_reaction wire shape: `e` target,
// no `h` channel tag. The Inbox must render it without waiting for another
// message or a context refetch.
await page.evaluate(async (eventId) => {
const invoke = (
window as Window & {
__BUZZ_E2E_INVOKE_MOCK_COMMAND__?: (
command: string,
payload?: Record<string, unknown>,
) => Promise<unknown>;
}
).__BUZZ_E2E_INVOKE_MOCK_COMMAND__;
if (!invoke) throw new Error("Mock Tauri invoke bridge is unavailable.");
await invoke("add_reaction", { eventId, emoji: "❤️" });
}, replyEvent.id);
await expect(selectedMessage.getByLabel("Toggle ❤️ reaction")).toBeVisible();
await selectedMessage.hover();
await selectedMessage
.getByRole("button", { name: "React with :+1:" })