feat(udgaver): fladen følger udgaven, og søgningen holder opgaverne inde
Trin 2. Serveren kunne allerede lade være med at montere opgaveruterne; fladen tilbød dem stadig, så en noter-instans havde links til sider, der ikke fandtes. --- hvordan fladen får det at vide --- `/auth/setup` bærer nu `edition`. Dér og ikke på `/auth/me`: udgaven er en egenskab ved instansen, ikke ved den, der er logget ind, og fladen skal kende den, før nogen er. Det er i forvejen kaldet, der spørger "hvad er det her for en instans", så den følger med uden en ekstra tur. Butikken henter formen ved siden af kontoen og tier, hvis den fejler: en 401 fra `me` må ikke tage udgaven med sig — login-skærmen er også en flade i en udgave. Afventet før `loading` slippes, så fladen aldrig tegnes, før den ved hvilket program den er; tegnet først og rettet bagefter ville vise opgaver i et sekund på en instans, der ikke har nogen. `full` som udgangspunkt, fordi det er det, programmet altid har været. --- hvad der forsvinder --- Menuens opgaveindgange er udeladt, ikke gråtonet. Forsiden er noterne. En gemt adresse til /upcoming, /kalender, /uddelegeret, /faerdige, /opgave, /filter eller /etiket fører til noterne frem for en blindgyde — der er ikke sket noget forkert, adressen hører bare til et andet program end det, der kører her. Listen står ét sted i layoutet frem for som en vagt pr. rute: listen ER beslutningen, og en vagt pr. fil er syv steder at glemme den ottende. --- et hul, prøven fandt --- /search er monteret i begge udgaver, fordi noter og notesbøger skal kunne findes — og den returnerede opgaver. Ikke teoretisk: en instans lavet om fra en fuld har opgaverne liggende i databasen endnu, så ⌘K ville være den ene dør, der stod åben ind til den halvdel, resten af programmet lige havde lukket. Lukket på serveren, hvor det hører hjemme, og ⌘K spørger også selv — en tom liste af den rigtige grund og en tom liste ved et tilfælde ser ens ud. Prøven har en kontrol: den fulde udgave SKAL finde opgaven, ellers beviser nullet i noter-udgaven ingenting. --- prøverne --- Et nyt Playwright-projekt med sin egen server, sin egen port og sin egen database. Ingen lånt session: den instans har aldrig set den konto, den fulde udgaves opsætning lavede. En skakt, der kun er prøvet ved at kalde en funktion, er prøvet på det, nogen mente — det her måler programmet, som en bruger møder det. Seks prøver, heriblandt forskellen på skjult og fraværende målt udefra: /api/v1/tasks, /labels, /upcoming og /filters skal svare 404, og /api/v1/notes skal svare 200 — ellers ville de fire bestå på en server, der bare var væk. `testIgnore` på chromium-projektet, fordi filen ellers også kørte dér, mod den fulde udgave, hvor den fejler med beskeder der læser som om skakten ikke virker. Den rigtige fejl stod i projektnavnet, ikke i påstanden. Go-suiten grøn. chromium + notes-edition: 110 bestået, 0 fejlet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
9b067f7057
commit
3cee9bfb6e
@@ -85,7 +85,11 @@
|
||||
...settings.map((s) => ({ kind: 'setting', id: s.href, label: `${s.tab} · ${s.title}` })),
|
||||
...projects.map((p) => ({ kind: 'project', id: p.id, label: p.name })),
|
||||
...notes.map((n) => ({ kind: 'note', id: n.id, label: n.title || n.body.slice(0, 60) })),
|
||||
...tasks.map((t) => ({
|
||||
// Serveren sender ingen opgaver i noter-udgaven, så listen er alligevel tom.
|
||||
// Spurgt her også, fordi en tom liste af den rigtige grund og en tom liste
|
||||
// ved et tilfælde ser ens ud — og den dag nogen mounter søgningen anderledes,
|
||||
// skal ⌘K ikke være stedet, det viser sig.
|
||||
...(app.notesOnly ? [] : tasks).map((t) => ({
|
||||
kind: 'task',
|
||||
id: t.id,
|
||||
label: t.content,
|
||||
|
||||
@@ -155,9 +155,12 @@
|
||||
// entry is a question the sidebar keeps asking and answering "no".
|
||||
let navItems = $derived(
|
||||
[
|
||||
{ key: 'today', href: '/', label: 'nav.today' },
|
||||
{ key: 'upcoming', href: '/upcoming', label: 'nav.upcoming' },
|
||||
app.projects.some((p) => p.shared)
|
||||
// Opgavernes egne indgange findes ikke i noter-udgaven. Ikke gråtonet og
|
||||
// ikke skjult med CSS — udeladt, fordi ruterne bag dem ikke er monteret
|
||||
// på serveren. Et link til noget, der ikke findes, er ikke en skønhedsfejl.
|
||||
app.notesOnly ? null : { key: 'today', href: '/', label: 'nav.today' },
|
||||
app.notesOnly ? null : { key: 'upcoming', href: '/upcoming', label: 'nav.upcoming' },
|
||||
!app.notesOnly && app.projects.some((p) => p.shared)
|
||||
? { key: 'delegated', href: '/uddelegeret', label: 'nav.delegated' }
|
||||
: null,
|
||||
// No href: the inbox is a project and is drawn by projectRow, which knows
|
||||
@@ -185,7 +188,7 @@
|
||||
// Sidst i rækken, fordi `navOrder` føjer en ukendt nøgle til bagest: står
|
||||
// den et andet sted her, ser en ny konto én rækkefølge og alle
|
||||
// eksisterende en anden.
|
||||
{ key: 'calendar', href: '/kalender', label: 'nav.calendar' }
|
||||
app.notesOnly ? null : { key: 'calendar', href: '/kalender', label: 'nav.calendar' }
|
||||
].filter(Boolean)
|
||||
);
|
||||
|
||||
|
||||
@@ -43,6 +43,27 @@ class AppState {
|
||||
*/
|
||||
unreachable = $state(false);
|
||||
|
||||
/**
|
||||
* Hvilken udgave af programmet den her instans er.
|
||||
*
|
||||
* `full` er opgaver, projekter og noter sammen. `notes` er notehalvdelen, hvor
|
||||
* opgaveruterne slet ikke er monteret på serveren — så fladen må ikke tilbyde
|
||||
* en vej til dem. Et link til en rute, der ikke findes, er ikke en skønhedsfejl:
|
||||
* det er en knap, der fører et sted hen, hvor der ikke er noget.
|
||||
*
|
||||
* Instansens form, ikke personens rettigheder. Derfor hentes den ved siden af
|
||||
* kontoen frem for efter den: den skal også være kendt, når ingen er logget ind.
|
||||
*
|
||||
* `full` som udgangspunkt, fordi det er det, programmet altid har været — en
|
||||
* instans, der ikke når at svare, skal ikke pludselig se ud som et andet
|
||||
* program.
|
||||
*/
|
||||
edition = $state('full');
|
||||
|
||||
get notesOnly() {
|
||||
return this.edition === 'notes';
|
||||
}
|
||||
|
||||
/** Transient messages: a failed save, a rolled-back change. */
|
||||
toasts = $state([]);
|
||||
connected = $state(false);
|
||||
@@ -108,6 +129,17 @@ class AppState {
|
||||
|
||||
async load() {
|
||||
this.loading = true;
|
||||
|
||||
// Formen hentes for sig, og en fejl på den tier. En 401 fra `me` må ikke
|
||||
// tage udgaven med sig — login-skærmen er også en flade i en udgave — og
|
||||
// en instans, der slet ikke svarer, melder `me` ordentligt nedenfor.
|
||||
const shape = api
|
||||
.setupState()
|
||||
.then((state) => {
|
||||
if (state?.edition) this.edition = state.edition;
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
try {
|
||||
this.user = await api.me();
|
||||
this.unreachable = false;
|
||||
@@ -131,6 +163,10 @@ class AppState {
|
||||
// arriving. A 401 is the server answering, and that is a real sign-out.
|
||||
this.unreachable = e?.code === 'offline';
|
||||
} finally {
|
||||
// Afventet her, så fladen aldrig tegnes, før den ved hvilket program den
|
||||
// er. Tegnet først og rettet bagefter ville vise opgaver i et sekund på
|
||||
// en instans, der ikke har nogen.
|
||||
await shape;
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user