diff --git a/backend/app/main.py b/backend/app/main.py
index 6ee03bf..158891a 100644
--- a/backend/app/main.py
+++ b/backend/app/main.py
@@ -11,7 +11,7 @@ import os
from app.config import settings
from app.database import init_db
-from app.routers import photos, folders, heaps, tags, trash, library
+from app.routers import photos, folders, heaps, tags, discard, library
from app.services.scanner import start_initial_scan
# Configure logging
@@ -64,7 +64,7 @@ app.include_router(photos.router, prefix="/api/v1/photos", tags=["photos"])
app.include_router(folders.router, prefix="/api/v1/folders", tags=["folders"])
app.include_router(heaps.router, prefix="/api/v1/heaps", tags=["heaps"])
app.include_router(tags.router, prefix="/api/v1/tags", tags=["tags"])
-app.include_router(trash.router, prefix="/api/v1/trash", tags=["trash"])
+app.include_router(discard.router, prefix="/api/v1/discard", tags=["discard"])
app.include_router(library.router, prefix="/api/v1/library", tags=["library"])
@app.get("/")
diff --git a/backend/app/models/photos.py b/backend/app/models/photos.py
index 4f28da4..e7c3791 100644
--- a/backend/app/models/photos.py
+++ b/backend/app/models/photos.py
@@ -33,9 +33,10 @@ class Photo(Base):
added_at = Column(DateTime, server_default=func.now())
updated_at = Column(DateTime, onupdate=func.now())
- # Trash status
- is_trashed = Column(Boolean, default=False)
- trashed_at = Column(DateTime)
+ # Discard status. The DB column names stay is_trashed/trashed_at to avoid
+ # a migration; only the Python attribute name reflects the rename.
+ is_discarded = Column('is_trashed', Boolean, default=False)
+ discarded_at = Column('trashed_at', DateTime)
# Thumbnail paths
thumb_small = Column(String) # path to 240px thumb
@@ -55,7 +56,7 @@ class Photo(Base):
rating = Column(Integer, default=0) # 0-5 stars
color_label = Column(String) # 'red'|'orange'|'yellow'|'green'|'blue'|'purple'|NULL
is_picked = Column(Boolean, default=False)
- # Note: is_rejected was merged into is_trashed (a single soft "trashed"
+ # Note: is_rejected was merged into is_discarded (a single soft "discarded"
# concept). The DB column may still exist on legacy installs but is no
# longer read or written.
diff --git a/backend/app/routers/trash.py b/backend/app/routers/discard.py
similarity index 62%
rename from backend/app/routers/trash.py
rename to backend/app/routers/discard.py
index 7f6a54e..8604ecd 100644
--- a/backend/app/routers/trash.py
+++ b/backend/app/routers/discard.py
@@ -1,5 +1,5 @@
"""
-Trash API router
+Discard API router
"""
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy import select, and_
@@ -12,39 +12,39 @@ from app.models import Photo
router = APIRouter()
@router.get("")
-async def list_trashed(db: AsyncSession = Depends(get_db)):
- """List trashed photos"""
+async def list_discarded(db: AsyncSession = Depends(get_db)):
+ """List discarded photos"""
result = await db.execute(
- select(Photo).where(Photo.is_trashed == True)
+ select(Photo).where(Photo.is_discarded == True)
)
photos = result.scalars().all()
return photos
@router.post("/restore")
async def restore_photos(photo_ids: list[str], db: AsyncSession = Depends(get_db)):
- """Restore photos from trash"""
+ """Restore photos from the discard pile"""
result = await db.execute(
- select(Photo).where(and_(Photo.id.in_(photo_ids), Photo.is_trashed == True))
+ select(Photo).where(and_(Photo.id.in_(photo_ids), Photo.is_discarded == True))
)
photos = result.scalars().all()
-
+
for photo in photos:
- photo.is_trashed = False
- photo.trashed_at = None
-
+ photo.is_discarded = False
+ photo.discarded_at = None
+
await db.commit()
return {"status": "success", "restored": len(photos)}
@router.delete("/empty")
-async def empty_trash(db: AsyncSession = Depends(get_db)):
- """Permanently delete all trashed photos"""
+async def empty_discard(db: AsyncSession = Depends(get_db)):
+ """Permanently delete all discarded photos"""
result = await db.execute(
- select(Photo).where(Photo.is_trashed == True)
+ select(Photo).where(Photo.is_discarded == True)
)
photos = result.scalars().all()
-
+
for photo in photos:
await db.delete(photo)
-
+
await db.commit()
- return {"status": "success", "deleted": len(photos)}
\ No newline at end of file
+ return {"status": "success", "deleted": len(photos)}
diff --git a/backend/app/routers/photos.py b/backend/app/routers/photos.py
index 4b052fb..0b963b7 100644
--- a/backend/app/routers/photos.py
+++ b/backend/app/routers/photos.py
@@ -33,7 +33,7 @@ async def list_photos(
rating_max: Optional[int] = Query(None, ge=0, le=5),
color_label: Optional[str] = None,
is_picked: Optional[bool] = None,
- is_trashed: Optional[bool] = False,
+ is_discarded: Optional[bool] = False,
heap_id: Optional[str] = None,
sort: str = "taken_at",
order: str = "desc",
@@ -93,8 +93,8 @@ async def list_photos(
if is_picked is not None:
filters.append(Photo.is_picked == is_picked)
- # Trash filter — defaults to hiding trashed photos
- filters.append(Photo.is_trashed == is_trashed)
+ # Discard filter — defaults to hiding discarded photos
+ filters.append(Photo.is_discarded == is_discarded)
# Apply all filters
if filters:
@@ -407,13 +407,13 @@ async def update_photo(
return PhotoResponse.from_orm(photo)
@router.delete("/{photo_id}")
-async def trash_photo(
+async def discard_photo(
photo_id: str,
db: AsyncSession = Depends(get_db)
):
- """Soft-trash a photo: sets is_trashed=true. The file stays on disk so
+ """Soft-discard a photo: sets is_discarded=true. The file stays on disk so
restore is just a flag flip. Permanent deletion happens via DELETE
- /trash/{id} or DELETE /trash/empty.
+ /discard/{id} or DELETE /discard/empty.
"""
result = await db.execute(
select(Photo).where(Photo.id == photo_id)
@@ -423,11 +423,11 @@ async def trash_photo(
if not photo:
raise HTTPException(status_code=404, detail="Photo not found")
- photo.is_trashed = True
- photo.trashed_at = datetime.utcnow()
+ photo.is_discarded = True
+ photo.discarded_at = datetime.utcnow()
await db.commit()
- return {"status": "success", "message": "Photo moved to trash"}
+ return {"status": "success", "message": "Photo discarded"}
@router.post("/bulk")
async def bulk_action(
@@ -445,14 +445,14 @@ async def bulk_action(
raise HTTPException(status_code=404, detail="No photos found")
# Perform action based on type
- if action.action == 'trash':
+ if action.action == 'discard':
for photo in photos:
- photo.is_trashed = True
- photo.trashed_at = datetime.utcnow()
+ photo.is_discarded = True
+ photo.discarded_at = datetime.utcnow()
elif action.action == 'restore':
for photo in photos:
- photo.is_trashed = False
- photo.trashed_at = None
+ photo.is_discarded = False
+ photo.discarded_at = None
elif action.action == 'set_rating':
for photo in photos:
photo.rating = action.value
@@ -462,7 +462,7 @@ async def bulk_action(
elif action.action == 'pick':
for photo in photos:
photo.is_picked = True
- photo.is_trashed = False
+ photo.is_discarded = False
else:
raise HTTPException(status_code=400, detail="Invalid action")
diff --git a/backend/app/schemas/photos.py b/backend/app/schemas/photos.py
index baaf710..70a703b 100644
--- a/backend/app/schemas/photos.py
+++ b/backend/app/schemas/photos.py
@@ -29,8 +29,8 @@ class PhotoResponse(PhotoBase):
file_hash: Optional[str] = None
added_at: datetime
updated_at: Optional[datetime] = None
- is_trashed: bool = False
- trashed_at: Optional[datetime] = None
+ is_discarded: bool = False
+ discarded_at: Optional[datetime] = None
thumb_small: Optional[str] = None
thumb_medium: Optional[str] = None
thumb_large: Optional[str] = None
@@ -52,7 +52,7 @@ class PhotoUpdate(BaseModel):
rating: Optional[int] = Field(None, ge=0, le=5)
color_label: Optional[str] = None
is_picked: Optional[bool] = None
- is_trashed: Optional[bool] = None
+ is_discarded: Optional[bool] = None
taken_at: Optional[datetime] = None
class PhotoListResponse(BaseModel):
@@ -66,5 +66,5 @@ class PhotoListResponse(BaseModel):
class BulkAction(BaseModel):
"""Bulk action on photos"""
ids: List[str]
- action: str # 'trash', 'restore', 'delete_permanent', 'move', 'copy', 'add_tag', 'remove_tag', 'set_rating', 'set_color', 'pick', 'reject'
+ action: str # 'discard', 'restore', 'delete_permanent', 'move', 'copy', 'add_tag', 'remove_tag', 'set_rating', 'set_color', 'pick'
value: Optional[Any] = None # For actions that need a value (rating, color, tag_id, folder_id)
\ No newline at end of file
diff --git a/backend/app/tasks/scan.py b/backend/app/tasks/scan.py
index 0321348..c21f19f 100644
--- a/backend/app/tasks/scan.py
+++ b/backend/app/tasks/scan.py
@@ -292,7 +292,7 @@ async def handle_file_deletion(filepath: str):
if photo:
# Mark as missing or delete from database
- photo.is_trashed = True
- photo.trashed_at = datetime.utcnow()
+ photo.is_discarded = True
+ photo.discarded_at = datetime.utcnow()
await session.commit()
- logger.info(f"Marked photo as trashed: {filepath}")
\ No newline at end of file
+ logger.info(f"Marked photo as discarded: {filepath}")
\ No newline at end of file
diff --git a/frontend/src/components/KeyboardHints.tsx b/frontend/src/components/KeyboardHints.tsx
index 6fa2a10..5b19cd3 100644
--- a/frontend/src/components/KeyboardHints.tsx
+++ b/frontend/src/components/KeyboardHints.tsx
@@ -12,7 +12,7 @@ export function KeyboardHints() {
? [
{ key: '1-5', action: 'Rate' },
{ key: 'P', action: 'Pick' },
- { key: 'X', action: 'Trash' },
+ { key: 'X', action: 'Discard' },
{ key: 'E / Space', action: 'Preview' },
{ key: 'Esc', action: 'Deselect' },
]
diff --git a/frontend/src/components/filter/FilterBar.tsx b/frontend/src/components/filter/FilterBar.tsx
index 47b8ed3..1741e70 100644
--- a/frontend/src/components/filter/FilterBar.tsx
+++ b/frontend/src/components/filter/FilterBar.tsx
@@ -26,7 +26,7 @@ const COLOR_LABELS: { value: ColorLabel; className: string }[] = [
const FLAG_OPTIONS: { value: FlagFilter; label: string }[] = [
{ value: 'any', label: 'Any' },
{ value: 'picked', label: 'Picked' },
- { value: 'trashed', label: 'Trashed' },
+ { value: 'discarded', label: 'Discarded' },
{ value: 'unflagged', label: 'Unflagged' },
]
diff --git a/frontend/src/components/layout/LeftSidebar.tsx b/frontend/src/components/layout/LeftSidebar.tsx
index 48339cb..453add1 100644
--- a/frontend/src/components/layout/LeftSidebar.tsx
+++ b/frontend/src/components/layout/LeftSidebar.tsx
@@ -111,7 +111,7 @@ export function LeftSidebar() {
{ id: 'by-date', label: 'By Date', icon: