fix(desktop): keep project branches after reload (#2214)

This commit is contained in:
thomaspblock
2026-07-21 13:28:37 +02:00
committed by GitHub
parent 166f27be4b
commit 001184395e
4 changed files with 62 additions and 3 deletions

View File

@@ -2,6 +2,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import * as React from "react";
import { relayClient } from "@/shared/api/relayClient";
import { getRelaySelf } from "@/features/moderation/lib/relaySelf";
import { getCachedRelayOrigin } from "@/shared/lib/mediaUrl";
import { signRelayEvent } from "@/shared/api/tauri";
import { getIdentity } from "@/shared/api/tauriIdentity";
@@ -345,9 +346,17 @@ function eventToRepoState(event: RelayEvent): RepoState {
}
async function fetchRepoState(project: Project): Promise<RepoState | null> {
const relaySelf = await getRelaySelf();
const trustedAuthors = [
...new Set(
[project.owner, relaySelf].filter((value): value is string =>
Boolean(value),
),
),
];
const events = await relayClient.fetchEvents({
kinds: [KIND_REPO_STATE],
authors: [project.owner],
authors: trustedAuthors,
"#d": [project.dtag],
limit: 1,
});

View File

@@ -4748,6 +4748,36 @@ function mulberry32(seed: number) {
}
let mockProjectEventStore: RelayEvent[] | null = null;
const MOCK_PROJECT_BRANCHES_KEY = "buzz-e2e-project-branches";
function readMockProjectBranches(): Record<string, Record<string, string>> {
try {
return JSON.parse(
window.sessionStorage.getItem(MOCK_PROJECT_BRANCHES_KEY) ?? "{}",
);
} catch {
return {};
}
}
function writeMockProjectBranch(
dtag: string,
branch: string,
commit: string | null,
) {
const projects = readMockProjectBranches();
const branches = { ...(projects[dtag] ?? {}) };
if (commit) {
branches[branch] = commit;
} else {
delete branches[branch];
}
projects[dtag] = branches;
window.sessionStorage.setItem(
MOCK_PROJECT_BRANCHES_KEY,
JSON.stringify(projects),
);
}
function buildMockProjectEvents(): RelayEvent[] {
const events: RelayEvent[] = [];
@@ -4795,8 +4825,11 @@ function buildMockProjectEvents(): RelayEvent[] {
}`,
],
["refs/heads/main", "0123456789abcdef0123456789abcdef01234567"],
...Object.entries(readMockProjectBranches()[seed.dtag] ?? {}).map(
([branch, commit]) => [`refs/heads/${branch}`, commit],
),
],
owner,
getConfig()?.mock?.relaySelf ?? owner,
now - projectIndex,
`mock-repo-state-${seed.dtag}`.replace(/[^a-zA-Z0-9]/g, ""),
),
@@ -9427,6 +9460,9 @@ export function maybeInstallE2eTauriMocks() {
]);
repoState.created_at = Math.floor(Date.now() / 1000);
}
if (dtag) {
writeMockProjectBranch(dtag, input.newBranch, input.expectedCommit);
}
return {
branch: input.newBranch,
commit: input.expectedCommit,
@@ -9455,6 +9491,9 @@ export function maybeInstallE2eTauriMocks() {
);
repoState.created_at = Math.floor(Date.now() / 1000);
}
if (dtag) {
writeMockProjectBranch(dtag, input.branch, null);
}
return {
branch: input.branch,
commit: input.expectedCommit,

View File

@@ -712,7 +712,10 @@ test("project branches can be created from the selected remote branch", async ({
page,
}) => {
await enableProjectsFeature(page);
await installMockBridge(page, { projectHeadBranch: "master" });
await installMockBridge(page, {
projectHeadBranch: "master",
relaySelf: TEST_IDENTITIES.bob.pubkey,
});
await openBuzzProject(page);
await page.getByRole("button", { name: /main/ }).click();
@@ -745,6 +748,12 @@ test("project branches can be created from the selected remote branch", async ({
() => window.__BUZZ_E2E_COMMANDS__ ?? [],
);
expect(commands).toContain("create_project_remote_branch");
await openBuzzProject(page);
await page.getByRole("button", { name: /main/ }).click();
await expect(
page.getByRole("menuitemradio", { name: "feature/branch-management" }),
).toBeVisible();
});
test("project branches can be deleted but the default branch cannot", async ({

View File

@@ -129,6 +129,8 @@ export type MockAgentMemoryListing = {
type MockBridgeOptions = {
/** Advertised HEAD for the first mock project without adding that branch. */
projectHeadBranch?: string;
/** Relay NIP-11 identity used to sign authoritative repository state. */
relaySelf?: string | null;
/** Builderlab account returned by hosted-community onboarding. Null/omitted = signed out. */
builderlabAuth?: { email?: string; name?: string; expiresAt: string } | null;
/** Bound Builderlab Nostr identity. Null/omitted = not linked yet. */