- Delete UploadProgress component (was debug UI, no longer needed)
- All 3 pages (home, memories, profile) now use simple inline error state
instead of the step-by-step toast
- /api/img proxy: fetch R2 objects server-side to bypass Cloudflare Bot
Management 503s on pub-xxx.r2.dev cross-origin img requests
- All API responses (memories, children, profile) now return /api/img proxy
URLs via toProxyUrl() helper in src/lib/r2-proxy.ts
- Fix memory pipeline: vision failure now marks status='ready' instead of
'failed'; thumbnail failure no longer blocks vision via .catch() separation
- Reset stuck 'processing' memories via debug-migration endpoint
- memories page: replace full-screen overlay with small ⏳ badge on tile
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each upload now shows a persistent card with 3 labelled steps and their
live status (pending → active → done / error). Errors include the exact
HTTP status code + raw response body (handles non-JSON from Traefik,
nginx, etc. that return HTML error pages). The card stays visible after
failure so the user can read the diagnostic before dismissing.
Changes per surface:
- src/components/UploadProgress.tsx — new shared step-tracker component
- profile/page.tsx — step card rendered below avatar; safeResponseText()
reads raw body so a Traefik 413 shows "HTTP 413: <html>..." not just
"Upload failed"
- memories/page.tsx — fixed toast expands to show all 3 steps; dismissible
after done/error; same safeResponseText pattern
- home/page.tsx (baby photo) — same fixed toast as memories; 3 steps with
HTTP codes and raw body on error
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Memories "count 2 but no images" root cause:
- DB rows exist with processing_status='failed'/'uploading' from aborted uploads
whose R2 objects never actually landed. The img onError fires and hides the
tile, but the count still includes these orphaned rows.
- Fix: GET /api/memories now excludes failed rows and uploading rows older than
30 min from both the SELECT and the count. Also fires a background DELETE to
clean up orphaned rows so they stop accumulating.
Profile / memories upload silent failures:
- Some Android cameras return file.type="" which caused the avatar API to reject
the upload with a 400 error. Error was caught but shown in a small text node
buried below the form — invisible when looking at the avatar area.
- Fix: added resolveContentType() helper (used in profile, memories, home) that
falls back to extension-based detection when file.type is empty/octet-stream.
- Fix: profile page now uses a separate uploadMsg state rendered immediately
below the avatar so errors/success are always visible on mobile without scroll.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two-pronged fix for Android PWA shell launching to the wrong page:
1. middleware.ts: if a logged-in user (valid tia_session cookie) visits /,
immediately redirect them to /home — catches all existing installs whose
cached start_url still points to /?source=pwa
2. manifest.ts: change start_url from /?source=pwa to /home?source=pwa
so any fresh install or reinstall opens directly to the app home
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Bug 2: raise all FABs (activity, memories, circle) to z-50 and bottom-24
so they sit above the bottom nav (z-40, ~56px tall)
- Bug 1: add onError handlers to baby photo (home) and parent avatar (profile)
so a broken R2 URL shows the 👶 / initials placeholder instead of a broken
browser icon; reset error flag after a successful upload
- Bug 3: replace ⏳ emoji spinner with a proper CSS spinner on home page baby
photo; add a fixed upload-progress toast to the memories page that appears
at the top of the screen for the full duration of the upload
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>