Skip to content

Commit 96c248d

Browse files
authored
fix(app): say a project still has channels before offering to remove it (#647)
* fix(app): say a project still has channels before offering to remove it * chore: confirm lane notes
1 parent 21d782c commit 96c248d

4 files changed

Lines changed: 69 additions & 35 deletions

File tree

‎.lane/memory/apps/app/src/app.tsx/01M4YT6XB7NQ89E6F4P375REE7-all-projects-and-project-vie.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ anchor: function App
44
created: 2026-10-15T03:38:04Z
55
norm: '1'
66
sig: 063d0f00fea02511
7-
body_hash: 543e14e26111c21c
8-
raw_hash: 36221fa4f08e0523
9-
vouched: 2026-10-22T20:42:40Z
7+
body_hash: c729811995ffcaa0
8+
raw_hash: 9fd83c4d176cf4c6
9+
vouched: 2026-10-22T20:47:29Z
1010
lines: 235-824
1111
---
1212

‎.lane/memory/apps/app/src/app.tsx/01M5JMVG9G87F31FGVV571Y6BJ-the-project-list-is-built-on.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ anchor: function App
44
created: 2026-10-22T20:29:16Z
55
norm: '1'
66
sig: 063d0f00fea02511
7-
body_hash: 543e14e26111c21c
8-
raw_hash: 36221fa4f08e0523
9-
vouched: 2026-10-22T20:42:40Z
7+
body_hash: c729811995ffcaa0
8+
raw_hash: 9fd83c4d176cf4c6
9+
vouched: 2026-10-22T20:47:29Z
1010
lines: 330-1237
1111
---
1212

‎apps/app/src/app.tsx‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -618,8 +618,17 @@ export function App() {
618618
return connected && !!opened?.some((checkout) => isLocal(checkout, value));
619619
}
620620

621+
/** This host's channels in the project, archived ones too, which the host refuses to orphan. */
622+
function held(value: AppProject) {
623+
const paths = new Set(
624+
opened?.flatMap((checkout) => isLocal(checkout, value) ? [checkout.path] : []),
625+
);
626+
return channels.filter((channel) => channel.host === hello.host && paths.has(channel.project))
627+
.length;
628+
}
629+
621630
function remove(value: AppProject) {
622-
if (askRemove) setRemoving(value);
631+
if (askRemove || held(value)) setRemoving(value);
623632
else void forget(value);
624633
}
625634

@@ -1165,6 +1174,7 @@ export function App() {
11651174
<RemoveProject
11661175
name={label(removing, hello.host)}
11671176
path={removing.path}
1177+
channels={held(removing)}
11681178
onRemove={async (ask) => {
11691179
setAskRemove(ask);
11701180
await forget(removing);

‎apps/app/src/remove-project.tsx‎

Lines changed: 52 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,15 @@ import {
1010
DialogTitle,
1111
} from "@ace/ui";
1212

13-
/** Removing only forgets the folder, so a person may turn the question off. */
14-
export function RemoveProject({ name, path, onRemove, onCancel }: {
13+
/**
14+
* Removing only forgets the folder, so a person may turn the question off. The host refuses while
15+
* the project has channels, so then this says how to delete them instead of offering to remove.
16+
*/
17+
export function RemoveProject({ name, path, channels, onRemove, onCancel }: {
1518
name: string;
1619
path: string;
20+
/** This host's channels in it, counting archived ones. */
21+
channels: number;
1722
onRemove: (ask: boolean) => Promise<void>;
1823
onCancel: () => void;
1924
}) {
@@ -34,35 +39,54 @@ export function RemoveProject({ name, path, onRemove, onCancel }: {
3439
<Dialog open onOpenChange={next => (next || busy ? undefined : onCancel())}>
3540
<DialogContent className="gap-3 sm:max-w-80" showCloseButton={false}>
3641
<DialogHeader>
37-
<DialogTitle className="break-all">Remove {name}?</DialogTitle>
42+
<DialogTitle className="break-all">
43+
{channels
44+
? `${name} still has ${channels === 1 ? "a channel" : "channels"}`
45+
: `Remove ${name}?`}
46+
</DialogTitle>
3847
<DialogDescription className="break-words">
39-
Ace forgets the project. Its files and lanes stay in{" "}
40-
{path}, and opening the folder adds it back.
48+
{channels
49+
? `It has ${
50+
channels === 1 ? "a channel" : `${channels} channels`
51+
}, counting archived ones. Archive ${
52+
channels === 1 ? "it" : "each"
53+
} from its ⋯ menu, delete it from the Archived group, then remove the project.`
54+
: `Ace forgets the project. Its files and lanes stay in ${path}, and opening the folder adds it back.`}
4155
</DialogDescription>
4256
</DialogHeader>
43-
<label htmlFor={id} className="flex items-center gap-2 text-xs text-muted-foreground">
44-
<input
45-
id={id}
46-
type="checkbox"
47-
checked={skip}
48-
disabled={busy}
49-
onChange={(event) => setSkip(event.target.checked)}
50-
/>
51-
Don't ask again
52-
</label>
53-
<DialogFooter>
54-
<Button type="button" variant="outline" disabled={busy} onClick={onCancel}>
55-
Cancel
56-
</Button>
57-
<Button
58-
type="button"
59-
variant="destructive"
60-
disabled={busy}
61-
onClick={() => void confirm()}
62-
>
63-
{busy ? "Removing…" : "Remove project"}
64-
</Button>
65-
</DialogFooter>
57+
{channels > 0
58+
? (
59+
<DialogFooter>
60+
<Button type="button" variant="outline" onClick={onCancel}>OK</Button>
61+
</DialogFooter>
62+
)
63+
: (
64+
<>
65+
<label htmlFor={id} className="flex items-center gap-2 text-xs text-muted-foreground">
66+
<input
67+
id={id}
68+
type="checkbox"
69+
checked={skip}
70+
disabled={busy}
71+
onChange={(event) => setSkip(event.target.checked)}
72+
/>
73+
Don't ask again
74+
</label>
75+
<DialogFooter>
76+
<Button type="button" variant="outline" disabled={busy} onClick={onCancel}>
77+
Cancel
78+
</Button>
79+
<Button
80+
type="button"
81+
variant="destructive"
82+
disabled={busy}
83+
onClick={() => void confirm()}
84+
>
85+
{busy ? "Removing…" : "Remove project"}
86+
</Button>
87+
</DialogFooter>
88+
</>
89+
)}
6690
</DialogContent>
6791
</Dialog>
6892
);

0 commit comments

Comments
 (0)