Skip to content

Per-folder memory + per-share recursive (v1.3.1)#66

Merged
merlin1de merged 7 commits intomasterfrom
folder-recursive-memory
Apr 28, 2026
Merged

Per-folder memory + per-share recursive (v1.3.1)#66
merlin1de merged 7 commits intomasterfrom
folder-recursive-memory

Conversation

@merlin1de
Copy link
Copy Markdown
Owner

v1.3.1

Two new features building on the recursive folder view from v1.3.0, plus a ShareList refactor that makes share configuration cleaner.

What's new for users

Per-folder memory for recursive view — when you flip recursive on (or change the depth) for a folder, the next time you visit that same folder it remembers your choice. Works without URL params, without changing personal settings — just stays per folder. Cap at 50 folders, oldest rotates out.

Per-share recursive view — when creating or editing a share, you can now choose whether the guest sees a recursive view (all images from subfolders in one grid) and the group depth. The share carries its own configuration; the photographer's personal setting only seeds the defaults when the share is created. Guests have no way to override — they see exactly what the photographer chose.

Edit existing shares — instead of multiple small toggle buttons in the share list, each share now has a pencil icon that opens the same modal you used to create it, prefilled with the current values. Change permissions, password, recursive view, or even the shared folder path — all in one place. The status of each share (password set, pick allowed, export allowed, etc.) is still visible at a glance via small read-only icons in the share row.

Faster guest revisits — guest thumbnails and previews are now cached in the browser for 7 days. When the same recipient comes back for a second rating round, the gallery loads instantly from local cache. ETag revalidation kicks in if the underlying files have changed.

Smarter prefill when changing share path — if you edit a share and change the folder path, the recursive settings reset to your default for the new folder (matching what you'd see opening that folder yourself). One less manual step for "I want to repurpose this share for a different shoot".

Compact share list layout — folder path and recipient name share one line now (separated with ), saving vertical space in the share list.

Notes for testers

  • The master switch in personal settings (Recursive view enabled) still gates everything — defaults to off for existing users.
  • Old shares (created before v1.3.1) are treated as non-recursive automatically — no migration needed.
  • Browser cache lasts 7 days; if you want to test changes immediately, hard-reload (Ctrl+F5) bypasses the cache.

Notes on architecture

The guest-side recursive listing is a deliberate code copy of the owner-side listing (with explicit security comment), kept that way to make any divergence loud in code review — a drift between the two would be a privilege-escalation risk.

Closes nothing standalone but extends what was delivered for #35.

V1.3.1 erstes Feature. Speichert pro Folder-Pfad das zuletzt gewählte
recursive/depth-Setting in localStorage. Beim Wiederbesuch desselben Folders
landet der User wieder im gleichen View — ohne URL-Manipulation, ohne in
die Personal Settings zu gehen.

Hierarchie der Werte-Quellen (höchste Priorität zuerst):
1. URL-Query (?recursive=… / ?depth=…) — Sharing/Bookmarking
2. Per-Folder-Memory (localStorage) — Convenience NEU
3. Personal Settings Default — globaler Fallback

Geschrieben wird NUR bei expliziter UI-Interaktion (FilterBar-Toggle/
Stepper); reine URL-Aufrufe (z.B. von Share-Links) hinterlassen keine
Spuren im localStorage. Damit shar't sich der Persistenz-State nicht
versehentlich über externe Links.

Mechanik:
- Util src/utils/folderRecursiveState.js mit read/write/clear-Funktionen
- Single localStorage-Key 'starrate_folder_recursive_v1', JSON-Map
  { path: { recursive, depth } }
- Cap 50 Einträge — älteste Insertion-Order-Eintrag wird beim Überlauf
  gedroppt
- Defensiv gegen kaputte/manipulierte Werte: depth wird auf 0-4 geclamped,
  recursive zu Boolean coerced, JSON-Parse-Fehler liefern null
- 14 Util-Tests + 4 neue Gallery-Tests für die End-to-End-Verkabelung

Verkabelung in Gallery.vue: recursive/depth Computeds checken nach URL-
Query gegen folderRecursiveState; setRecursive/setDepth schreiben nach
URL-Update auch in den localStorage.
Macht aus 'Neuer Share' ein universelles Anlegen+Bearbeiten-Modal, ersetzt die kleinen Toggles in ShareList, gibt jedem Share eine recursive/depth-Konfig.

Backend:
- ShareService.createShare/updateShare akzeptieren recursive, depth, nc_path
- Bestandsshares ohne Felder via ?? false / ?? 0 als flach
- getImagesForShare ruft bei share.recursive den neuen Helper
  listImagesRecursiveForShare auf — bewusste Kopie von
  GalleryController::listImagesRecursiveFromDb mit Security-Comment
  (Drift = Privilege-Escalation, Doppelung erzwingt synchrone Pflege)
- Bei recursive ignoriert die Guest-API ?path= (kein Folder-Drill)
- ShareController validiert depth 0-4
- 3 neue PHPUnit-Tests

Frontend:
- ShareModal: editShare-Prop schaltet auf Edit-Mode (Title, Submit,
  PUT vs POST, Pfad-editierbar)
- Recursive-Felder nur wenn settings.recursion_enabled
- Path-@blur löst die folderRecursiveState→Settings-Auflösung aus
  (Sarah-Use-Case)
- Passwort: leer=behalten, gefüllt=ändern, neue Remove-Checkbox
- ShareList: Stift-Button ersetzt alle Toggles, Read-only Badges
  zeigen Status (Pick, Export, Comment, Recursive, Password)
- Active-Toggle bleibt inline
- Gallery: editingShare verdrahtet ShareList → ShareModal
1) Guest-Thumbs neu generiert beim ersten Besuch:
   Owner-Frontend nutzt /core/preview?mode=cover, NC speichert die
   gecroppten Vorschauen unter MODE_COVER. Unsere ShareService rief
   getPreview() ohne Mode-Argument auf — NC-Default ist MODE_FILL,
   also andere Cache-Datei. Folge: jeder Gast triggerte eine komplette
   Neugenerierung der Thumbs, die schon im NC-Cache lagen.

   Fix: bei Thumbs (crop=true) explizit MODE_COVER setzen, bei
   Previews (crop=false) MODE_FILL. Damit teilen Owner und Gast
   denselben NC-Preview-Cache, Gast-Erstbesuch ist instant.

2) ShareList kompakter:
   Pfad und Empfängername jetzt in einer Zeile (statt zwei),
   getrennt mit Em-Dash. Empfängername in helleres Grau (#a1a1aa
   statt #d4d4d8) — der Pfad ist die primäre Identität des Shares,
   der Name sekundär. Spart eine Zeile pro Share, gerade bei vielen
   Freigaben relevant.
Quick-Win für den 'zweite Bewertungsrunde'-Use-Case. Vorher musste der
Browser des Gasts nach 1h alle Thumbs neu vom Server holen — auch wenn
sich nichts geändert hat. NC liefert dann zwar aus dem Server-Cache,
aber der HTTP-Roundtrip pro Tile summiert sich auf merkliche Wartezeit.

7 Tage Browser-Cache deckt typische Mehrfachbesuche durch Guest und
Bewertungs-Iterationen ab. FileDisplayResponse setzt ETag automatisch:
nach Ablauf revalidiert der Browser via If-None-Match, Server antwortet
304 (kein Body) wenn die Datei sich nicht geändert hat. Bei mtime-
Änderung wird komplett neu geladen.

private/nicht-immutable als Default — kein CDN/Proxy-Caching (Share-
spezifische URLs), aber Revalidation möglich.
Vorher rief PUT /api/share/{token} updateShare() ohne Body-Validation auf.
Update-Endpunkt verließ sich auf inline-Clamping (depth/min_rating) und
throw-on-bad-permissions. Folge: Bad-Body führte zu HTTP 500 statt 422
mit klarer Fehlermeldung — schlechte UX und inkonsistent zu create.

Fix: validateShareBody um $isCreate-Flag erweitert. nc_path ist nur bei
create Pflicht (weil ein neuer Share einen Pfad braucht), bei update
optional (Teilfelder-Updates ändern den Pfad nicht zwingend). Update
nutzt validateShareBody(false) und gibt 422 mit ordentlichen Fehlern
zurück.

Inline-Clamping in updateShare bleibt als Defense-in-Depth — falls die
Validation jemals umgangen würde, sind die Werte trotzdem sane.
16 neue Vitests für die V1.3.1-ShareModal-Erweiterung. Decken die im
Code-Review als Lücke identifizierten Pfade ab:

Recursive-Felder:
- Hidden wenn recursionEnabled=false (Default)
- Sichtbar bei recursionEnabled=true
- Tiefe-Dropdown nur wenn recursive UND recursionEnabled
- recursiveDefault wird beim Anlegen vorbefüllt

Edit-Mode:
- Title 'Freigabe bearbeiten'
- Submit-Label 'Speichern'
- Pfad-Input editierbar (kein readonly)
- Felder aus Share vorbefüllt (Permissions, Pick, Recursive, GuestName)
- Passwort-Feld leer + Remove-Checkbox sichtbar bei has_password=true
- Remove-Checkbox versteckt bei has_password=false
- Submit ruft PUT, nicht POST
- recursive + depth landen im PUT-Body
- Leeres Passwort + Remove=false → kein password-Key (behalten)
- Remove=true → password=null (entfernen)
- 'updated'+'close' Events nach erfolgreichem Submit

Path-Blur Re-Resolve (Sarah-Use-Case):
- Mit recursionEnabled: Pfad-Wechsel löst folderRecursiveState/Settings-
  Hierarchie neu aus, Felder werden aktualisiert
- Ohne recursionEnabled: Pfad-Wechsel ändert recursive-Wert nicht
@merlin1de merlin1de merged commit c4e6b8e into master Apr 28, 2026
7 checks passed
@merlin1de merlin1de deleted the folder-recursive-memory branch April 28, 2026 21:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant