ReviewOS

stacks/wildloop

Support photos/media on activities

#956
Closed glennmichael123 opened this 9 days ago ·
9 days ago

Problem

No photo/media support on activities (no column, no upload, no display). The activity detail page has a map but no gallery.

Acceptance

  • Storage + upload endpoint for activity photos.
  • Persist references on the activity; render a gallery on detail + a thumbnail in the feed.
9 days ago

Done, in 641436e6, 2edf25b5, 27f17b9f and 5adb4e31.

  • Storage and an upload endpoint. POST /api/activities/{id}/photos (multipart photo), capped at 20, into the private bucket the trail photos already use. GET /api/activity-photos/{activityId}/{file} serves the bytes through the app, answering only while a photo is visible. DELETE /api/activity-photos/{uuid} for its owner or an admin.
  • References persisted, gallery on the detail page, thumbnail in the feed. activity_photos keyed by UUID rather than row id, so a URL says nothing about how many exist. The activity page shows a grid that opens full screen with arrows and a count, plus an Add photos control and per-photo delete for the athlete whose run it is. The feed card shows the first photo and a badge with the total.

The work was mostly widening what existed rather than building a second copy: the private storage, the re-encode that strips a photo of its GPS position, and the serving route were all built for trail photos and none of it was trail-specific.

The one real difference is who may add a photo. Anyone signed in may contribute to a trail, which is a shared public page. An activity is one person's account of their own afternoon, so the action checks ownership rather than existence — and answers a stranger with the same 404 as for an activity that does not exist, so it cannot be used to discover which ids are real. The GPS stripping matters more here too: a trail already has published coordinates, while the photos on a run say where one person was on a particular afternoon.

Verified in two layers. Eight server-side cases against a real server and a throwaway database (tests/browser/activity-photos.test.ts): an upload survives the round trip and comes back as this app's JPEG rather than the posted file, a stranger gets 404 adding and 403 deleting, a key this app did not write is never served, and a deleted photo stops being served immediately. Then in a browser at desktop and phone width in both themes: three photos render, the lightbox steps through them, the feed shows one with a count badge, and nothing scrolls sideways at 375px.

Not built, deliberately: the report/moderation count trail photos carry. There is no reporting flow for activity photos and one was not in scope here, so the column is absent rather than present and unwritten. status is there, so a photo can still be hidden.

Sign in to comment on this issue.