Skip to content

Handle async image processing: refetch/poll for updated thumbnail URLs after upload #69

Description

@nimmolo

Context

MushroomObserver's server-side image pipeline is moving from synchronous to background (async) processing - resize/reorient/transfer now happen in a job after upload, rather than blocking the API response (see mushroom-observer#4735). To avoid regressing this app, the server currently makes API-originated uploads (this app's postImage) wait for processing to finish before responding - so today, nothing changes for us. But that's a stopgap on the server side, not a real fix, and it means this app doesn't get the latency win the server-side change was meant to deliver. This issue is the plan for actually handling async processing here, so the API-side synchronous workaround can eventually be dropped.

The gap today

Once uploads go async, the files array returned right after upload may point at fallback URLs (the full-size original, serving in place of a not-yet-generated thumbnail) rather than correctly-sized derivatives, until the server's background job finishes. This app has no mechanism to ever pick up the corrected URLs:

  • postImage's RTK Query mutation has no invalidatesTags.
  • The uploaded image is written directly into the images Redux slice via addImage(newImage) from the POST response, and that slice persists to disk (redux-persist), surviving app restarts.
  • useGetImagesQuery is imported once (Card.tsx) but never actually called anywhere.
  • The updateImage/updateImages reducer exists but is never dispatched anywhere in the app.

Net effect without a fix: a photo's thumbnail would show (and download) the full-size original permanently, in every screen that renders it, until the app ships this fix.

Also needed: a status field from the API

The current files array (and the rest of the image JSON) has no field indicating whether processing is done - no transferred, no equivalent. Client-side polling/refetch logic needs something to check against, or it has no way to know when to stop refetching (or would have to guess via fragile URL comparison). Recommend requesting a small, additive field (e.g. transferred: true/false) from the MO API - non-breaking, since it'd just be a new key in the existing JSON object, not a change to the files array's shape.

Options considered

1. Refetch on screen focus (useFocusEffect from @react-navigation/native, already a dependency - react-navigation v6 is in package.json). Whenever a screen that renders photo.files becomes focused (ObservationListItem, ViewObservation, ViewPhoto, Photo), check the image's local transferred flag; if false, refetch and dispatch updateImage. No new dependency, well-established RN pattern, naturally covers "user comes back to look at their photo" without running anything in the background when the app isn't in use.

2. Short bounded polling right after upload, while the create/upload screen is still open (e.g. every 3s, capped at ~6-8 attempts / 20-30s). Covers the case where the user stays on-screen watching the upload complete. No existing polling infrastructure in the app today (setInterval/useInterval isn't used anywhere) - would be new, but simple (interval + cleanup + cap).

3. Push notifications when processing completes. Ruled out for now: this app has no push notification library at all (no Firebase, Notifee, expo-notifications, or APN integration in package.json) - would mean standing up an entirely new capability (native config on both platforms, permission prompts, a server-side sender), disproportionate to the problem.

Recommendation

Combine 1 + 2: useFocusEffect-based refetch as the baseline safety net (catches every case eventually, whenever the user looks at the photo again), plus a short bounded poll right after upload for the common case (user stays on the upload/create screen and expects to see the real thumbnail appear without navigating away and back). Both gated on the new transferred field so a photo that's already fully processed never gets refetched again.

Rough scope

  • MO-side (separate, small PR on the main repo): add transferred to _image.json.jbuilder.
  • This app:
    • Wire the already-defined updateImage/updateImages reducer to actually get dispatched (currently dead code).
    • Add useFocusEffect to the four screens/components above.
    • Add a short bounded poll in the upload flow (CreateDraft/index.tsx), gated on the new transferred field.
    • Once this ships and is confirmed working, the MO-side synchronous-API workaround can be removed (follow-up on the MO repo, not blocking this).

Effort: low-to-medium. No new dependencies for the recommended approach.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions