diff --git a/ui/src/names.test.ts b/ui/src/names.test.ts index a63e15f..5a72310 100644 --- a/ui/src/names.test.ts +++ b/ui/src/names.test.ts @@ -114,6 +114,14 @@ describe('unnamed', () => { const sonarr = { ...radarr, name: 'sonarr' }; expect(unnamed([radarr, db, self, sonarr], ready).map((t) => t.key)).toEqual(['sonarr']); }); + // tailscale, adopted and stopped: the provider cannot reach it, so Add name + // failed and the prompt came back for it every time. + it('leaves out a stopped app until it runs', () => { + const stopped = { ...radarr, name: 'tailscale', status: { state: 'stopped' } }; + const partial = { ...radarr, name: 'sonarr', status: { state: 'partial' } }; + expect(unnamed([stopped, partial], ready).map((t) => t.key)).toEqual(['sonarr']); + expect(unnamed([{ ...stopped, status: { state: 'running' } }], ready).map((t) => t.key)).toEqual(['tailscale']); + }); }); describe('trustPage', () => { diff --git a/ui/src/names.ts b/ui/src/names.ts index 00577ad..3c3324e 100644 --- a/ui/src/names.ts +++ b/ui/src/names.ts @@ -129,7 +129,11 @@ export async function checkTrust(trustName: string): Promise { return t; } -type NamedStack = AppUrlStack & { name: string; services?: { name: string; address?: string }[] }; +type NamedStack = AppUrlStack & { + name: string; + services?: { name: string; address?: string }[]; + status?: AppUrlStack['status'] & { state?: string }; +}; /** One app a name can point at. A stack of several apps (crafting-apps) has * one per app, keyed "/" and named after the service; any @@ -259,9 +263,13 @@ export function nameLabel(s: string): string { .replace(/^-+|-+$/g, ''); } -/** Apps that could have a name and have none yet (not the provider itself). */ +/** Apps that could have a name and have none yet (not the provider itself). + * A stopped stack is left out: the provider probes an app before naming it, + * so naming one that is not running always failed, and the prompt offered + * it again on every visit. It is offered once it runs. */ export function unnamed(stacks: NamedStack[], p: NamesState): NameTarget[] { - return stacks.filter((s) => s.name !== p.provider).flatMap((s) => nameTargets(s).filter((t) => !p.names?.[t.key])); + const stopped = (s: NamedStack) => !!s.status?.state && s.status.state !== 'running' && s.status.state !== 'partial'; + return stacks.filter((s) => s.name !== p.provider && !stopped(s)).flatMap((s) => nameTargets(s).filter((t) => !p.names?.[t.key])); } export type NameResult = { stack: string; host?: AppName; error?: string };