Commit Graph

3 Commits

Author SHA1 Message Date
07b9660e92 feat: undo for destructive photo actions
Add a global last-action stack with toast-based "Undo" buttons and a
Cmd/Ctrl+Z hotkey for the destructive photo operations.

Reversible:
- X (discard) → bulkRestore
- U (restore) → bulkDiscard
- Drag-onto-Discarded → bulkRestore
- Drag-onto-folder (move) → move back to per-photo source folders. The
  source folder ids are snapshotted from the photos cache before the
  move runs, then grouped so multi-source moves restore correctly.
- Restore button in the discard action bar → bulkDiscard

Toast gains an optional action button (label + onClick); toasts with an
action stay visible longer so the user has time to click. The undo
store caps at 20 entries; failed undo re-pushes the entry so the user
can try again.

Not reversible (call out, document later): rating, color label, copy,
permanent delete from trash, tag changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 14:08:58 +02:00
b870084be0 feat: per-photo permanent delete + discarded thumbnail treatment
Discarded photos now look discarded in the grid (50% opacity + grayscale)
with a red trash badge in the corner instead of a bare icon. The discard
action bar gains a "Delete N" button that permanently deletes only the
current selection, complementing the existing "Empty discard pile".

Backend: new DELETE /discard endpoint accepting {photo_ids: [...]} that
permanently removes only listed photos. Skips ids that aren't in the
discard pile so it can never bypass the soft-delete safety net.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:12:11 +02:00
322969c938 feat: discard view with restore and empty actions
Adds the destructive-action loop the discard concept needed:

- Click "Discarded" in the left sidebar → activates the discarded
  filter; the timeline reloads showing discarded photos.
- DiscardActionBar appears at the top of the timeline only when the
  discarded filter is active. Shows the count, a Restore button (when
  photos are selected), and an Empty discard pile button.
- Empty action goes through a ConfirmDialog (new tiny reusable modal,
  same overlay pattern as AddSourceFolderDialog).
- Restore goes through POST /api/v1/discard/restore.
- DELETE /api/v1/discard/empty now actually os.unlink()s the files
  from disk in addition to removing the DB rows. Per-file failures
  are logged and reported in the response so a single permission
  error doesn't abort the batch.

Other library nodes wired in passing:
- "All Photos"   → clearAll()
- "Rated"        → setRatingMin(1)
- "Flagged"      → setFlag('picked')
- "Discarded"    → setFlag('discarded')
- "By Date"      left unwired (needs a date-grouping UI)

Single-photo restore via the U keyboard shortcut already worked from
an earlier round, no change needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 22:59:34 +02:00