mirror of
https://github.com/block/buzz.git
synced 2026-10-30 22:11:36 +00:00
fix(profile): show archive control while snapshot loads
Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
This commit is contained in:
parent
15a73aa27f
commit
02ff18b469
@@ -101,7 +101,7 @@ export function useUnarchiveIdentityMutation() {
|
||||
export type IdentityArchiveActions = {
|
||||
/** Render guard only — the relay re-verifies authority on submit. */
|
||||
canArchive: boolean;
|
||||
/** `undefined` while the snapshot loads — defer flair + Manage until known. */
|
||||
/** `undefined` while the snapshot loads — defer flair + mutation affordances until known. */
|
||||
isArchived: boolean | undefined;
|
||||
isPending: boolean;
|
||||
archive: () => void;
|
||||
|
||||
@@ -63,9 +63,8 @@ export function UserProfileAgentSettingsMenu({
|
||||
onToggleAutoStart !== undefined;
|
||||
const autoStartSwitchId = `user-profile-agent-auto-start-${actionKey}`;
|
||||
const hasPrimaryActions = Boolean(onDuplicatePersona || onExportPersona);
|
||||
const hasArchiveAction =
|
||||
archiveActions?.canArchive === true &&
|
||||
archiveActions.isArchived !== undefined;
|
||||
const hasArchiveAction = archiveActions?.canArchive === true;
|
||||
const archiveStateKnown = archiveActions?.isArchived !== undefined;
|
||||
const shouldConfirmAgentDelete =
|
||||
managedAgent !== undefined && onDelete !== undefined;
|
||||
const hasManageActions = hasArchiveAction || Boolean(onDelete);
|
||||
@@ -146,7 +145,7 @@ export function UserProfileAgentSettingsMenu({
|
||||
<DropdownMenuSeparator />
|
||||
) : null}
|
||||
{hasArchiveAction && archiveActions ? (
|
||||
archiveActions.isArchived ? (
|
||||
archiveActions.isArchived === true ? (
|
||||
<DropdownMenuItem
|
||||
data-testid="user-profile-unarchive-identity"
|
||||
disabled={isPending}
|
||||
@@ -158,11 +157,21 @@ export function UserProfileAgentSettingsMenu({
|
||||
) : (
|
||||
<DropdownMenuItem
|
||||
data-testid="user-profile-archive-identity"
|
||||
disabled={isPending}
|
||||
onSelect={() => setArchiveConfirmOpen(true)}
|
||||
disabled={isPending || !archiveStateKnown}
|
||||
onSelect={(event) => {
|
||||
if (!archiveStateKnown) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
setArchiveConfirmOpen(true);
|
||||
}}
|
||||
>
|
||||
<Archive className="h-4 w-4" />
|
||||
{archiveActions.isPending ? "Archiving…" : archiveLabel}
|
||||
{archiveActions.isPending
|
||||
? "Archiving…"
|
||||
: archiveStateKnown
|
||||
? archiveLabel
|
||||
: "Checking archive status…"}
|
||||
</DropdownMenuItem>
|
||||
)
|
||||
) : null}
|
||||
@@ -186,11 +195,12 @@ export function UserProfileAgentSettingsMenu({
|
||||
) : null}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
{hasArchiveAction && archiveActions ? (
|
||||
{hasArchiveAction && archiveActions && archiveStateKnown ? (
|
||||
<ArchiveConfirmDialog
|
||||
isBot={isBot}
|
||||
isPending={archiveActions.isPending}
|
||||
onConfirm={() => {
|
||||
if (archiveActions.isArchived !== false) return;
|
||||
archiveActions.archive();
|
||||
setArchiveConfirmOpen(false);
|
||||
}}
|
||||
@@ -245,8 +255,7 @@ export function UserProfileAgentSettingsMenuSlot({
|
||||
personaActionKey?: string;
|
||||
viewerIsOwner: boolean;
|
||||
}) {
|
||||
const canShowArchiveAction =
|
||||
archiveActions.canArchive && archiveActions.isArchived !== undefined;
|
||||
const canShowArchiveAction = archiveActions.canArchive;
|
||||
const settingsActionPending =
|
||||
isAgentActionPending || archiveActions.isPending;
|
||||
const sharedProps = {
|
||||
|
||||
@@ -106,12 +106,13 @@ type E2eConfig = {
|
||||
stallWebsocketSends?: boolean;
|
||||
userSearchDelayMs?: number;
|
||||
// NIP-IA gate inputs — see tests/helpers/bridge.ts:MockBridgeOptions for
|
||||
// semantics. These three drive the archive-button gate matrix in
|
||||
// semantics. These options drive the archive-button gate matrix in
|
||||
// tests/e2e/identity-archive.spec.ts; they're plumbed into:
|
||||
// - `list_archived_identities` (archivedIdentities)
|
||||
// - `list_archived_identities` (archivedIdentities / archivedIdentitiesDelayMs)
|
||||
// - `resolve_oa_owner` (oaOwnerIsMe)
|
||||
// - `resetMockRelayMembers` (relayRole)
|
||||
archivedIdentities?: string[];
|
||||
archivedIdentitiesDelayMs?: number;
|
||||
oaOwnerIsMe?: boolean;
|
||||
relayRole?: "owner" | "admin" | "member" | null;
|
||||
// Descriptors returned by the mocked `pick_and_upload_media` /
|
||||
@@ -7649,6 +7650,10 @@ export function maybeInstallE2eTauriMocks() {
|
||||
return { owner, is_me: isMe };
|
||||
}
|
||||
case "list_archived_identities": {
|
||||
const delayMs = activeConfig?.mock?.archivedIdentitiesDelayMs ?? 0;
|
||||
if (delayMs > 0) {
|
||||
await new Promise((resolve) => window.setTimeout(resolve, delayMs));
|
||||
}
|
||||
const archived = activeConfig?.mock?.archivedIdentities ?? [];
|
||||
return { archived };
|
||||
}
|
||||
|
||||
@@ -97,6 +97,26 @@ test.describe("NIP-IA archive button gate", () => {
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test("case 3b — verified OA owner viewing Alice while snapshot is pending: Archive visible but disabled", async ({
|
||||
page,
|
||||
}) => {
|
||||
await installMockBridge(page, {
|
||||
relayRole: null,
|
||||
oaOwnerIsMe: true,
|
||||
archivedIdentities: [],
|
||||
archivedIdentitiesDelayMs: 60_000,
|
||||
});
|
||||
await openAliceProfile(page);
|
||||
await openProfileSettingsMenu(page);
|
||||
|
||||
const archiveButton = page.getByTestId("user-profile-archive-identity");
|
||||
await expect(archiveButton).toBeVisible();
|
||||
await expect(archiveButton).toBeDisabled();
|
||||
await expect(archiveButton).toContainText("Checking archive status…");
|
||||
await archiveButton.click({ force: true });
|
||||
await expect(page.getByTestId("archive-confirm-dialog")).toHaveCount(0);
|
||||
});
|
||||
|
||||
test("case 4 — no authority viewing Alice: Archive hidden", async ({
|
||||
page,
|
||||
}) => {
|
||||
|
||||
@@ -134,6 +134,8 @@ type MockBridgeOptions = {
|
||||
* "Archived on this relay" flair + Unarchive button.
|
||||
*/
|
||||
archivedIdentities?: string[];
|
||||
/** Delay (ms) for `list_archived_identities`; lets specs assert pending snapshot UI. */
|
||||
archivedIdentitiesDelayMs?: number;
|
||||
/**
|
||||
* Drives the `is_me` field of `resolve_oa_owner`. When true, the harness
|
||||
* reports the active identity as the verified NIP-OA owner of the viewee
|
||||
|
||||
Reference in New Issue
Block a user