You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
vescape-app/vescape-server#33 defines Map Point photo attachment, public reads, and Asset ownership.
What to build
Replace the parked device-local Map Point media prototype with server-backed Hosted Asset photos. Riders can pick or capture a photo, keep seeing its normalized local preview while upload or attachment is pending, retry recoverable failures, and later see resized public photos on Map Points loaded from the server.
Map Points remain server-owned with no offline Map Point store. Shared Asset infrastructure owns normalization, local files, multipart transfer, Device Token auth, background retry, and remote Asset deletion. This slice owns Map Point attachment, UI state, and cleanup after replacement, removal, or Map Point deletion.
Server contract
A Map Point carries at most four photos.
Photo order is the server-assigned attachment order. This slice has no reorder route.
Attach through POST /map-points/:id/photos with { "assetId": "..." }.
Detach through DELETE /map-points/:id/photos/:assetId.
Nearby, create, patch, and attach responses expose photos as ordered { assetId, url } references.
Signed-out nearby reads receive the same public photo references without authentication.
Attachment requires the same Account to own the Map Point and the Asset, and the Asset must belong to the map-points group.
Missing, wrong-group, and wrong-owner Assets return 404 without exposing ownership. Duplicate attachment and the four-photo cap return documented 409 errors.
Attachment references never delete Asset bytes. Deleting bytes while a live Map Point still references them returns 409 asset-in-use.
Deleting a Map Point removes its photo references but does not delete Hosted Assets.
Local and UI contract
A picked or captured photo immediately gets a normalized local preview and durable Asset job owned by the Map Point identity.
UI renders stable normalizing, pending or uploading, attached, retryable failure, permanent failure, and deleting states.
Closing the sheet or killing JS does not discard work.
An uploaded but unattached Asset retains its ID and retries only attachment. It never intentionally uploads the bytes again.
Only owner-facing UI may add, replace, remove, or retry photos. Public readers never receive mutation controls.
Public UI requests only supported bounded variants and never constructs bucket keys or presigned URLs.
After attachment is durable and the remote URL is usable, release the normalized local file. Map Points have no offline media store.
Replacement and cleanup
Replacement orders upload new, attach new, confirm the returned Map Point references it, detach old, then delete the old Hosted Asset.
Removal orders detach, confirm success, then enqueue remote Asset deletion.
Map Point deletion first persists cleanup jobs containing every attached Asset ID. It then deletes the Map Point and marks those Asset deletes eligible only after the server confirms deletion. This prevents both leaked Assets and deletion of bytes still referenced by a live Map Point.
Pending upload and attachment state survives sheet dismissal, JS death, process restart, and offline periods.
Uploaded but unattached work retries attachment with the existing Asset ID.
Native clients strictly parse the ordered { assetId, url } photo contract and enforce the four-photo bound.
Signed-out nearby reads render bounded public photos.
Owner and public controls follow existing ownedByMe semantics.
Replacement never deletes the old Asset until the new attachment is durable.
Removal never deletes Asset bytes before detachment is durable.
Map Point deletion durably records every Asset ID before server deletion and deletes those Assets only after the Map Point is gone.
Successful attachment releases the normalized local file without breaking the remote preview.
Missing, wrong-group, wrong-owner, duplicate, over-capacity, and deleted-Map-Point responses leave consistent local state and a useful rider-facing result.
The parked raw photo and video prototype needs no migration because it never shipped enabled. Remove its obsolete storage code.
Tests cover owner and public reads, local pending preview, process restart, offline retry, upload-success and attach-failure, replacement ordering, removal ordering, Map Point deletion cleanup, Account mismatch, four-photo capacity, resized URL selection, and strict response parsing.
Reusable media transfer states have component showcase previews.
TS, Swift, Kotlin, and server contracts stay linked by @parity.
Parent
vescape-app/vescape-server#33defines Map Point photo attachment, public reads, and Asset ownership.What to build
Replace the parked device-local Map Point media prototype with server-backed Hosted Asset photos. Riders can pick or capture a photo, keep seeing its normalized local preview while upload or attachment is pending, retry recoverable failures, and later see resized public photos on Map Points loaded from the server.
Map Points remain server-owned with no offline Map Point store. Shared Asset infrastructure owns normalization, local files, multipart transfer, Device Token auth, background retry, and remote Asset deletion. This slice owns Map Point attachment, UI state, and cleanup after replacement, removal, or Map Point deletion.
Server contract
POST /map-points/:id/photoswith{ "assetId": "..." }.DELETE /map-points/:id/photos/:assetId.photosas ordered{ assetId, url }references.map-pointsgroup.404without exposing ownership. Duplicate attachment and the four-photo cap return documented409errors.409 asset-in-use.Local and UI contract
Replacement and cleanup
Likely files
src/modules/map-points/hooks/useMapPointMedia.ts- replace the in-memory picker and camera controller with durable native Asset state.src/modules/map-points/store/mapPointPhotoFiles.ts- remove the raw local-copy prototype after shared normalization owns pending files.src/modules/map-points/components/MapPointMediaPreview.tsx- render local and remote previews plus transfer states.src/modules/map-points/components/MapTargetSheetEditBody.tsx- owner add, remove, replace, and retry intents.src/modules/map-points/constants/mapPoints.ts- removeMAP_POINT_MEDIA_ENABLEDonce the server-backed path is complete.modules/vescape-core/ios/mappoints/MapPointApi.swift- iOS attachment and read decoding through the existing API boundary.modules/vescape-core/android/src/main/java/expo/modules/vescapecore/mappoints/MapPointApi.kt- Android parity peer.modules/vescape-core/src/index.ts- Map Point photo and mutation contracts mirrored from the server.Acceptance criteria
POST /assets/map-pointswith Device Token auth.{ assetId, url }photo contract and enforce the four-photo bound.ownedByMesemantics.@parity.Blocked by
vescape-app/vescape-server#33Explicit exclusions
vescape-webMap Point consumption.