Public: anyone can read this; the crew can change it.
Archive, part 2a-ii: the terminal home leaves archived repositories out of Yours and On you #2500
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/2494-archive-part-2a-ii-the-terminal-home-lea → main 2 commits fork 1 thread · 1 unresolved
3 files
changed +42
−2
src/core/repos.ts +4
| @@ -1053,6 +1053,10 @@ export interface RepoSettings { | ||
| 1053 | 1053 | plansNeedNod?: boolean; |
| 1054 | 1054 | } |
| 1055 | 1055 | |
| 1056 | /** The repositories still awake: what Yours lists before anyone types a filter. */ | |
| 1057 | export const withoutArchived = (repos: readonly Repo[]): Repo[] => | |
| 1058 | repos.filter((repo) => repo.archivedAt === null); | |
| 1059 | ||
| 1056 | 1060 | /** The one line said wherever the crew declines to start a run here. */ |
| 1057 | 1061 | export const ARCHIVED_WHY = "This repository is archived; wake it to start a run."; |
| 1058 | 1062 |
src/tui/screens/dashboard.ts +6 −2
| @@ -48,6 +48,7 @@ import { | ||
| 48 | 48 | getRepo, |
| 49 | 49 | getReposByIds, |
| 50 | 50 | listReposVisibleTo, |
| 51 | withoutArchived, | |
| 51 | 52 | type Repo, |
| 52 | 53 | getRepoById, |
| 53 | 54 | } from "../../core/repos.ts"; |
| @@ -201,12 +202,14 @@ export function dashboardScreen(f: Session, ctx: Ctx): Screen { | ||
| 201 | 202 | const orgs = f.user |
| 202 | 203 | ? listOrgsForUser(f.db, f.user.id).map((o) => o.username) |
| 203 | 204 | : []; |
| 205 | // Archived ones leave Yours as they leave the web home; the filter still finds them. | |
| 206 | const live = withoutArchived(all); | |
| 204 | 207 | repos = f.user |
| 205 | ? all.filter( | |
| 208 | ? live.filter( | |
| 206 | 209 | (repo) => |
| 207 | 210 | repo.ownerId === f.user?.id || orgs.includes(repo.ownerName), |
| 208 | 211 | ) |
| 209 | : all; | |
| 212 | : live; | |
| 210 | 213 | ctx.invalidate(); |
| 211 | 214 | }; |
| 212 | 215 | |
| @@ -240,6 +243,7 @@ export function dashboardScreen(f: Session, ctx: Ctx): Screen { | ||
| 240 | 243 | const repo = byId.get(issue.repoId); |
| 241 | 244 | if ( |
| 242 | 245 | repo === undefined || |
| 246 | repo.archivedAt !== null || | |
| 243 | 247 | !canRead(f.db, repo, f.user) || |
| 244 | 248 | !mayView(f.db, repo, issue, f.user) |
| 245 | 249 | ) |
test/tui-archive.test.ts +32
| @@ -6,6 +6,8 @@ import { tmpdir } from "node:os"; | ||
| 6 | 6 | import { join } from "node:path"; |
| 7 | 7 | import { updateRepoSettings } from "../src/actions/repos.ts"; |
| 8 | 8 | import { createRepo } from "../src/actions/repos.ts"; |
| 9 | import { openIssue } from "../src/actions/issues.ts"; | |
| 10 | import { setAssignee } from "../src/core/issues.ts"; | |
| 9 | 11 | import { signUp } from "../src/actions/users.ts"; |
| 10 | 12 | import { createApp, type App as Forge } from "../src/app.ts"; |
| 11 | 13 | import { getRepo, type Repo } from "../src/core/repos.ts"; |
| @@ -111,4 +113,34 @@ describe("archive in the terminal", () => { | ||
| 111 | 113 | h.app.stop(); |
| 112 | 114 | } |
| 113 | 115 | }); |
| 116 | ||
| 117 | test("the home leaves an archived repository out of Yours and On you", async () => { | |
| 118 | const live = await createRepo(forge, alice, { name: "livelyrepo", visibility: "public" }); | |
| 119 | const old = await createRepo(forge, alice, { name: "oldrepo", visibility: "public" }); | |
| 120 | if (!live.ok || !old.ok) throw new Error("repo failed"); | |
| 121 | for (const [r, title] of [ | |
| 122 | [live.value, "livelytask"], | |
| 123 | [old.value, "oldtask"], | |
| 124 | ] as const) { | |
| 125 | const issue = await openIssue(forge, alice, r, { title, body: "x" }); | |
| 126 | if (!issue.ok) throw new Error("issue failed"); | |
| 127 | setAssignee(forge.db, issue.value.id, alice.id); | |
| 128 | } | |
| 129 | await updateRepoSettings(forge, alice, old.value, { archived: true }); | |
| 130 | const h = boot({ | |
| 131 | sshUsername: "alice", | |
| 132 | remoteAddress: "127.0.0.1", | |
| 133 | fingerprint: "SHA256:test", | |
| 134 | user: alice, | |
| 135 | }); | |
| 136 | try { | |
| 137 | await settleUntil(h.app, "livelyrepo"); | |
| 138 | const text = h.text(); | |
| 139 | expect(text).toContain("livelytask"); | |
| 140 | expect(text).not.toContain("alice/oldrepo"); | |
| 141 | expect(text).not.toContain("oldtask"); | |
| 142 | } finally { | |
| 143 | h.app.stop(); | |
| 144 | } | |
| 145 | }); | |
| 114 | 146 | }); |
new above
offline
This doc comment described ARCHIVED_WHY, but withoutArchived now sits between them, leaving two stacked doc comments. Move the helper above this comment.