refactor: rename trash to discard end-to-end

User-facing labels and code now use "discard" (verb) and "Discarded"
(state/view label) instead of "trash" / "Trashed". The DB column names
stay (is_trashed / trashed_at) so no migration is required — only the
SQLAlchemy attribute names are renamed via Column('old_name', ...).

Backend
- Photo model: is_discarded / discarded_at attributes (DB columns
  unchanged).
- PhotoBase / PhotoResponse / PhotoUpdate schemas use the new field
  names.
- Photos list endpoint: is_discarded query param, filter logic.
- DELETE /photos/{id} now sets is_discarded; success message updated.
- Bulk action 'trash' renamed to 'discard'.
- backend/app/routers/trash.py renamed to discard.py with renamed
  functions and route prefix /api/v1/discard.
- main.py imports and mounts the discard router.
- tasks/scan.py marks missing files as is_discarded.

Frontend
- Photo TS type: is_discarded.
- PhotoThumbnail: shows the trash-can icon when is_discarded.
- RightSidebar: button label "Discard"; mutation field name; local
  variable rename.
- TopBar: discardPhotosMutation and "Discard" button; toast text
  "Discarded".
- LeftSidebar: virtual node id 'discarded' / label "Discarded".
- FilterBar / filterStore / useFilterUrlSync: FlagFilter enum value
  'trashed' → 'discarded'; backend param key is_discarded.
- KeyboardHints: X label "Discard".
- useKeyboardShortcuts: PhotoUpdate field rename, X handler.
- api.ts: /trash routes → /discard, trash export → discard,
  bulkUpdate trash field → discard.

Out of scope (intentional): the docker-compose trash_data volume,
backend/Dockerfile mkdir /data/trash, config.py TrashSettings, and
the spec doc — all unused since soft-discard, and renaming them is
churn for no benefit.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-07 22:54:31 +02:00
parent 2679214cb9
commit 997e11db78
17 changed files with 82 additions and 81 deletions

View File

@@ -2,7 +2,7 @@ import { create } from 'zustand'
export type MediaType = 'photo' | 'video' | 'raw' | 'heic'
export type ColorLabel = 'red' | 'orange' | 'yellow' | 'green' | 'blue' | 'purple'
export type FlagFilter = 'any' | 'picked' | 'trashed' | 'unflagged'
export type FlagFilter = 'any' | 'picked' | 'discarded' | 'unflagged'
export interface FilterState {
q: string
@@ -77,7 +77,7 @@ export function filtersToParams(f: FilterState): Record<string, string | number>
if (f.ratingMin > 0) params.rating_min = f.ratingMin
if (f.colorLabel) params.color_label = f.colorLabel
if (f.flag === 'picked') params.is_picked = 'true'
else if (f.flag === 'trashed') params.is_trashed = 'true'
else if (f.flag === 'discarded') params.is_discarded = 'true'
else if (f.flag === 'unflagged') params.is_picked = 'false'
return params
}