mirror of
https://github.com/block/buzz.git
synced 2026-11-03 16:01:35 +00:00
Address review feedback round 5
Keep cold-cache channel management actions visible but disabled until permissions resolve, and cover the loading state plus negative menu assertions. Co-authored-by: npub1ux8n2yfs8qfvgd75s7kyhar2mztac355v6vmrz4juc9l3msw4pgstums9e <e18f3511303812c437d487ac4bf46ad897dc46946699b18ab2e60bf8ee0ea851@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1ux8n2yfs8qfvgd75s7kyhar2mztac355v6vmrz4juc9l3msw4pgstums9e <e18f3511303812c437d487ac4bf46ad897dc46946699b18ab2e60bf8ee0ea851@sprout-oss.stage.blox.sqprod.co>
This commit is contained in:
parent
120bb7d2a2
commit
e1dcc064c7
@@ -1,5 +1,3 @@
|
||||
import * as React from "react";
|
||||
|
||||
import {
|
||||
Archive,
|
||||
Bell,
|
||||
@@ -231,15 +229,18 @@ export function ChannelContextMenuItems({
|
||||
selfMember?.role === "owner" ||
|
||||
selfMember?.role === "admin" ||
|
||||
canManageOwnedAgentChannel;
|
||||
// Context-menu content mounts once per open session, so this capability is a
|
||||
// stable snapshot. Late query/refetch results cannot insert an immediate
|
||||
// Archive action or move another item beneath the user's pointer.
|
||||
const [showManagementActions] = React.useState(
|
||||
() =>
|
||||
channel.channelType !== "dm" &&
|
||||
channel.archivedAt === null &&
|
||||
canManageChannel,
|
||||
);
|
||||
const hasResolvedMembers = membersQuery.data !== undefined;
|
||||
const hasResolvedOwnerProfiles =
|
||||
ownerPubkeys.length === 0 || ownerProfilesQuery.data !== undefined;
|
||||
const isManagementCandidate =
|
||||
channel.channelType !== "dm" && channel.archivedAt === null;
|
||||
// Reserve disabled rows while capability queries resolve. This keeps the
|
||||
// menu stable under the pointer without making owners close and reopen a
|
||||
// cold-cache menu before the actions appear.
|
||||
const showManagementActions =
|
||||
isManagementCandidate &&
|
||||
(!hasResolvedMembers || !hasResolvedOwnerProfiles || canManageChannel);
|
||||
const disableManagementActions = !canManageChannel;
|
||||
const showStar = Boolean(onStarChannel && onUnstarChannel);
|
||||
const showReadToggle = hasUnread
|
||||
? Boolean(onMarkChannelRead)
|
||||
@@ -270,6 +271,7 @@ export function ChannelContextMenuItems({
|
||||
<>
|
||||
<ContextMenuSeparator />
|
||||
<ContextMenuItem
|
||||
disabled={disableManagementActions}
|
||||
onSelect={() =>
|
||||
deferMenuAction(() =>
|
||||
openChannelManagement(channel.id, { edit: true }),
|
||||
@@ -283,6 +285,7 @@ export function ChannelContextMenuItems({
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem
|
||||
className="text-destructive focus:text-destructive"
|
||||
disabled={disableManagementActions}
|
||||
onSelect={() =>
|
||||
deferMenuAction(() => {
|
||||
void archiveChannelMutation.mutateAsync().catch((error) => {
|
||||
|
||||
@@ -143,6 +143,7 @@ type E2eConfig = {
|
||||
* tests can observe the in-flight prepend window. 0/undefined = instant. */
|
||||
channelWindowDelayMs?: number;
|
||||
profileReadDelayMs?: number;
|
||||
channelMembersDelayMs?: number;
|
||||
profileReadError?: string;
|
||||
profileUpdateError?: string;
|
||||
profileUpdateErrors?: string[];
|
||||
@@ -5503,6 +5504,11 @@ async function handleGetChannelMembers(
|
||||
args: { channelId: string },
|
||||
config: E2eConfig | undefined,
|
||||
): Promise<RawChannelMembersResponse> {
|
||||
const delayMs = config?.mock?.channelMembersDelayMs ?? 0;
|
||||
if (delayMs > 0) {
|
||||
await new Promise((resolve) => window.setTimeout(resolve, delayMs));
|
||||
}
|
||||
|
||||
const identity = getIdentity(config);
|
||||
if (!identity) {
|
||||
const channel = getMockChannel(args.channelId);
|
||||
|
||||
@@ -2335,8 +2335,17 @@ test("open channel management supports join and leave", async ({ page }) => {
|
||||
});
|
||||
|
||||
test("channel context menu edits a stream", async ({ page }) => {
|
||||
await installMockBridge(page, { channelMembersDelayMs: 500 });
|
||||
await page.goto("/");
|
||||
|
||||
await page.getByTestId("channel-agents").click({ button: "right" });
|
||||
const pendingEditItem = page.getByRole("menuitem", { name: "Edit channel" });
|
||||
await expect(pendingEditItem).toBeVisible();
|
||||
await expect(pendingEditItem).toBeDisabled();
|
||||
await expect(pendingEditItem).toBeEnabled();
|
||||
await page.keyboard.press("Escape");
|
||||
await expect(pendingEditItem).toHaveCount(0);
|
||||
|
||||
await page.getByTestId("channel-general").click({ button: "right" });
|
||||
const editItem = page.getByRole("menuitem", { name: "Edit channel" });
|
||||
await expect(editItem).toBeVisible();
|
||||
@@ -2384,6 +2393,7 @@ test("channel context menu hides management actions from members and DMs", async
|
||||
await page.getByTestId("auxiliary-panel-close").click();
|
||||
|
||||
await page.getByTestId("channel-random").click({ button: "right" });
|
||||
await expect(page.getByRole("menuitem", { name: "Copy" })).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("menuitem", { name: "Edit channel" }),
|
||||
).toHaveCount(0);
|
||||
@@ -2397,6 +2407,7 @@ test("channel context menu hides management actions from members and DMs", async
|
||||
.locator('[data-testid^="channel-"]')
|
||||
.first();
|
||||
await firstDm.click({ button: "right" });
|
||||
await expect(page.getByRole("menuitem", { name: "Copy" })).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("menuitem", { name: "Edit channel" }),
|
||||
).toHaveCount(0);
|
||||
|
||||
@@ -168,6 +168,7 @@ type MockBridgeOptions = {
|
||||
/** Delay (ms) for older-history fetches; see e2eBridge mock config. */
|
||||
channelWindowDelayMs?: number;
|
||||
profileReadDelayMs?: number;
|
||||
channelMembersDelayMs?: number;
|
||||
profileReadError?: string;
|
||||
profileUpdateError?: string;
|
||||
profileUpdateErrors?: string[];
|
||||
|
||||
Reference in New Issue
Block a user