mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
Fix custom emoji status in profile popover (#874)
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:
@@ -25,6 +25,7 @@ export default defineConfig({
|
||||
"**/channel-browser.spec.ts",
|
||||
"**/messaging.spec.ts",
|
||||
"**/custom-emoji.spec.ts",
|
||||
"**/profile-custom-emoji-status.spec.ts",
|
||||
"**/custom-emoji-screenshots.spec.ts",
|
||||
"**/channel-mute-screenshots.spec.ts",
|
||||
"**/channel-star-screenshots.spec.ts",
|
||||
|
||||
@@ -6,6 +6,7 @@ import { ProfileAvatar } from "@/features/profile/ui/ProfileAvatar";
|
||||
import { PresenceDot } from "@/features/presence/ui/PresenceBadge";
|
||||
import { getPresenceLabel } from "@/features/presence/lib/presence";
|
||||
import { SetStatusDialog } from "@/features/user-status/ui/SetStatusDialog";
|
||||
import { StatusEmoji } from "@/features/user-status/ui/StatusEmoji";
|
||||
import type { PresenceStatus } from "@/shared/api/types";
|
||||
import { isMacPlatform } from "@/shared/lib/platform";
|
||||
|
||||
@@ -179,7 +180,10 @@ export function ProfilePopover({
|
||||
{hasUserStatus ? (
|
||||
<span className="flex min-w-0 flex-1 items-center gap-1 truncate text-popover-foreground">
|
||||
{userStatusEmoji ? (
|
||||
<span className="shrink-0">{userStatusEmoji}</span>
|
||||
<StatusEmoji
|
||||
className="h-3.5 w-3.5 shrink-0"
|
||||
value={userStatusEmoji}
|
||||
/>
|
||||
) : null}
|
||||
<span className="truncate">{userStatusText}</span>
|
||||
</span>
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
import {
|
||||
KIND_STREAM_MESSAGE_EDIT,
|
||||
KIND_SYSTEM_MESSAGE,
|
||||
KIND_USER_STATUS,
|
||||
} from "@/shared/constants/kinds";
|
||||
import type {
|
||||
RawAcpProviderCatalogEntry,
|
||||
@@ -393,6 +394,13 @@ type MockSubscription = {
|
||||
kinds: number[] | null;
|
||||
};
|
||||
|
||||
type MockFilter = {
|
||||
"#d"?: string[];
|
||||
"#h"?: string[];
|
||||
authors?: string[];
|
||||
kinds?: number[];
|
||||
};
|
||||
|
||||
type MockSocket = {
|
||||
handler: WsHandler;
|
||||
subscriptions: Map<string, MockSubscription>;
|
||||
@@ -1230,6 +1238,7 @@ const mockChannels: MockChannel[] = [
|
||||
];
|
||||
|
||||
const mockMessages = new Map<string, RelayEvent[]>();
|
||||
const mockUserStatuses: RelayEvent[] = [];
|
||||
let mockRelayMembers: RawRelayMember[] = [];
|
||||
const mockSockets = new Map<number, MockSocket>();
|
||||
let mockWebsocketSendMutexWedged = false;
|
||||
@@ -1923,6 +1932,17 @@ function emitMockLiveEvent(channelId: string, event: RelayEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
function emitMockGlobalEvent(event: RelayEvent) {
|
||||
for (const socket of mockSockets.values()) {
|
||||
for (const [subId, subscription] of socket.subscriptions) {
|
||||
if (subscription.kinds && !subscription.kinds.includes(event.kind)) {
|
||||
continue;
|
||||
}
|
||||
sendWsText(socket.handler, ["EVENT", subId, event]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function hasMockLiveSubscription(channelId: string, kind?: number) {
|
||||
for (const socket of mockSockets.values()) {
|
||||
for (const subscription of socket.subscriptions.values()) {
|
||||
@@ -1954,6 +1974,46 @@ function recordMockMessage(channelId: string, event: RelayEvent) {
|
||||
touchMockChannel(channel);
|
||||
}
|
||||
|
||||
function resetMockUserStatuses() {
|
||||
mockUserStatuses.length = 0;
|
||||
}
|
||||
|
||||
function recordMockUserStatus(event: RelayEvent) {
|
||||
const dTag = event.tags.find((tag) => tag[0] === "d")?.[1];
|
||||
if (dTag) {
|
||||
const index = mockUserStatuses.findIndex(
|
||||
(stored) =>
|
||||
stored.pubkey.toLowerCase() === event.pubkey.toLowerCase() &&
|
||||
stored.tags.some((tag) => tag[0] === "d" && tag[1] === dTag),
|
||||
);
|
||||
if (index >= 0) {
|
||||
mockUserStatuses.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
mockUserStatuses.push(event);
|
||||
}
|
||||
|
||||
function filterMockUserStatuses(filter: MockFilter) {
|
||||
const authors = filter.authors?.map((author) => author.toLowerCase());
|
||||
const dTags = filter["#d"];
|
||||
|
||||
return mockUserStatuses
|
||||
.filter((event) => {
|
||||
if (authors && !authors.includes(event.pubkey.toLowerCase())) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
dTags &&
|
||||
!event.tags.some((tag) => tag[0] === "d" && dTags.includes(tag[1]))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.sort((a, b) => b.created_at - a.created_at);
|
||||
}
|
||||
|
||||
function emitMockChannelMessage(
|
||||
channelId: string,
|
||||
content: string,
|
||||
@@ -5022,11 +5082,7 @@ function sendToMockSocket(args: {
|
||||
return;
|
||||
}
|
||||
|
||||
const filter = rest[1] as {
|
||||
"#h"?: string[];
|
||||
kinds?: number[];
|
||||
authors?: string[];
|
||||
};
|
||||
const filter = rest[1] as MockFilter;
|
||||
if (filter.kinds?.includes(13534)) {
|
||||
sendWsText(socket.handler, [
|
||||
"EVENT",
|
||||
@@ -5052,6 +5108,14 @@ function sendToMockSocket(args: {
|
||||
return;
|
||||
}
|
||||
|
||||
if (filter.kinds?.includes(KIND_USER_STATUS)) {
|
||||
for (const statusEvent of filterMockUserStatuses(filter)) {
|
||||
sendWsText(socket.handler, ["EVENT", subId, statusEvent]);
|
||||
}
|
||||
sendWsText(socket.handler, ["EOSE", subId]);
|
||||
return;
|
||||
}
|
||||
|
||||
const channelId = filter["#h"]?.[0];
|
||||
if (!channelId) {
|
||||
sendWsText(socket.handler, ["EOSE", subId]);
|
||||
@@ -5109,6 +5173,26 @@ function sendToMockSocket(args: {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.kind === KIND_USER_STATUS) {
|
||||
const hasGeneralDTag = event.tags.some(
|
||||
(tag) => tag[0] === "d" && tag[1] === "general",
|
||||
);
|
||||
if (!hasGeneralDTag) {
|
||||
sendWsText(socket.handler, [
|
||||
"OK",
|
||||
event.id,
|
||||
false,
|
||||
"invalid: user status missing d tag.",
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
recordMockUserStatus(event);
|
||||
emitMockGlobalEvent(event);
|
||||
sendWsText(socket.handler, ["OK", event.id, true, ""]);
|
||||
return;
|
||||
}
|
||||
|
||||
const channelId = getChannelIdFromTags(event.tags);
|
||||
if (!channelId) {
|
||||
sendWsText(socket.handler, [
|
||||
@@ -5152,6 +5236,7 @@ export function maybeInstallE2eTauriMocks() {
|
||||
resetMockTeams();
|
||||
resetMockWorkflows();
|
||||
resetMockMesh();
|
||||
resetMockUserStatuses();
|
||||
mockWebsocketSendMutexWedged = false;
|
||||
mockWindows("main");
|
||||
window.__SPROUT_E2E_COMMANDS__ = [];
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
|
||||
import { installMockBridge } from "../helpers/bridge";
|
||||
|
||||
const SHORTCODE = "sprout";
|
||||
const STATUS_TEXT = "testing custom status";
|
||||
|
||||
async function openProfilePopover(page: import("@playwright/test").Page) {
|
||||
await page.getByTestId("open-settings").click();
|
||||
await expect(page.getByTestId("profile-popover")).toBeVisible();
|
||||
}
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await installMockBridge(page);
|
||||
const PNG = Buffer.from(
|
||||
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGUlEQVR4nGMwuBPxnxLMMGrAqAGjBgwXAwBwOGMf1PPhVwAAAABJRU5ErkJggg==",
|
||||
"base64",
|
||||
);
|
||||
await page.route("https://example.com/e2e/**", (route) =>
|
||||
route.fulfill({ contentType: "image/png", body: PNG }),
|
||||
);
|
||||
});
|
||||
|
||||
test("profile popover renders a custom emoji status as an image", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto("/");
|
||||
await openProfilePopover(page);
|
||||
|
||||
await page.getByTestId("profile-popover-set-status").click();
|
||||
await expect(page.getByTestId("set-status-dialog")).toBeVisible();
|
||||
await page.getByLabel("Choose status emoji").click();
|
||||
|
||||
const picker = page.locator("em-emoji-picker");
|
||||
await picker.locator("input[type='search']").fill(SHORTCODE);
|
||||
await picker.locator(`button[title='${SHORTCODE}']`).first().click();
|
||||
await page.getByTestId("set-status-input").fill(STATUS_TEXT);
|
||||
await page.getByTestId("set-status-save").click();
|
||||
|
||||
await openProfilePopover(page);
|
||||
|
||||
const statusButton = page.getByTestId("profile-popover-set-status");
|
||||
await expect(statusButton).toContainText(STATUS_TEXT);
|
||||
await expect(statusButton.locator(`img[alt=":${SHORTCODE}:"]`)).toBeVisible();
|
||||
await expect(statusButton).not.toContainText(`:${SHORTCODE}:`);
|
||||
});
|
||||
Reference in New Issue
Block a user