mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
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:
@@ -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(() => {});
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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:" })
|
||||
|
||||
Reference in New Issue
Block a user