Skip to content

✨ Mobile | Harden offline mode – close the loop on #1306 #1567

Description

@jernejk

Follow-up to #1306 — that epic delivered crash-proofing and read caching for the main tabs. A code audit (Jul 2026) found the remaining gaps below. This PBI hardens offline mode and closes the loop so #1306 can be closed.

cc: @jernejk

Pain

Offline support today is opt-in per ViewModel (FileCacheService + AdvancedObservableCollection<T>), so every new page ships offline-broken until someone remembers to wire it up — Activity Feed proves it. There is also no reconnect story: users get no signal they are viewing stale data, and nothing refreshes when connectivity returns.

Gaps found:

  1. Activity Feed — no offline support at all. ActivityPageViewModel fetches into a plain ObservableRangeCollection; offline shows an "Oops…" alert and an empty page. PR ✨ Mobile | Add offline support to Activity Feed #1537 would have fixed this but was closed unmerged.
  2. Posts (list + detail) — no caching. Won't crash, but show nothing offline.
  3. No connectivity-change handling anywhere. Zero ConnectivityChanged subscriptions, no offline banner in any XAML, HasLoadedFromCache is never surfaced to the UI, no auto-refresh on reconnect. The ✨ Implement Full Offline Support [JK] #1306 AC "seamless transition between online and offline modes" isn't met yet.
  4. Scanner fails generically offline. A QR scan offline lands in ScanResult.Error with no offline-specific message — the user isn't told connectivity was the cause.
  5. Inconsistent offline messaging. ExceptionHelper only special-cases 401; each page shows its own wording ("Device Offline" / "Oops…" / nothing).
  6. No HTTP resilience. No retry/timeout pipeline on the API clients; one transient blip = error path (long-standing TODO, 🔒 Use Polly for retry policies for authentication failures #276).

Suggested Solution

  • Move caching down to the service layer so it is default-on (evaluate Akavache GetAndFetchLatest() — it is exactly the cache-then-network pattern we hand-rolled, with expiry + key management built in; we already reference System.Reactive).
  • One injectable IConnectivityService + ConnectivityChanged subscription → global offline banner (CommunityToolkit.Maui Snackbar/Toast) + auto-refresh on reconnect; centralize the "online-only action" guard.
  • Add Microsoft.Extensions.Http.Resilience (Polly v8) to the API clients — closes the 🔒 Use Polly for retry policies for authentication failures #276 TODO as a side effect.
  • Cache envelope with {schemaVersion, timestamp, etag} + TTL; investigate ETag support against the backend (profile pictures are the likely first win).
  • Offline-aware scanner message; keep writes online-only (no outbox) as an explicit scope decision.

Acceptance Criteria

  1. Activity Feed and Posts show cached content when offline (no empty page, no crash).
  2. A global offline indicator is shown when connectivity is lost, and pages auto-refresh when connectivity returns.
  3. Users viewing cached/stale data can tell (e.g. subtle "offline — showing saved data" banner).
  4. Scanning a QR code offline shows a clear "you're offline" message instead of a generic error.
  5. Offline error messaging is consistent across all pages.
  6. Transient network failures are retried via a resilience pipeline instead of surfacing an error.
  7. New pages get offline caching by default (service-layer caching, not per-ViewModel opt-in).
  8. Meets Definition of Done - https://github.com/SSWConsulting/SSW.Rewards.Mobile/blob/main/_docs/Definition-of-Done.md

Tasks

Thanks!

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions