Skip to content

Fix/offline cache widget notifications - #160

Merged
maugauwi-hash merged 5 commits into
ethos-protocol:mainfrom
Valreb001:fix/offline-cache-widget-notifications
Jul 28, 2026
Merged

Fix/offline cache widget notifications#160
maugauwi-hash merged 5 commits into
ethos-protocol:mainfrom
Valreb001:fix/offline-cache-widget-notifications

Conversation

@Valreb001

Copy link
Copy Markdown

Closes #77
Closes #78
Closes #79
Closes #80

Summary of what was implemented:

  1. Offline cache staleness — OfflineCache now wraps each cached entry in a CacheEnvelope(timestamp, data) JSON blob instead of raw bytes. ApiClient/ApiResult.Success propagate cachedAt, VaultViewModel exposes it, and OfflineBanner now reads "Offline — showing cached data (as of Xh ago)".
  2. Unbounded cache growth — Added an in-memory LRU (access-order tracked, not filesystem mtime, to avoid flaky ordering) with a 5MB byte cap; save() evicts least-recently-used entries once over cap. Added OfflineCache.clear(), wired into AuthViewModel.signOut().
  3. Widget vault selection — Extracted VaultWidgetUpdateWorker.selectUrgentVault(), filtering to active vaults and picking minByOrNull { ttlRemaining } — now matches iOS's TTLTimelineProvider exactly instead of firstOrNull().
  4. Notification ID collisions — Replaced vaultId.hashCode() with a persisted stable mapping (SharedPreferences) from vault ID to a reserved ID range (10,000+), guaranteeing zero collisions rather than relying on hash luck. Also added .setGroup(vaultId) so notifications visually cluster per vault as a second line of defense.

OfflineCache wrote raw response bytes keyed only by request-path hash,
so a cached vault list could be served as "current" no matter how old
it actually was — the same gap flagged in iOS ethos-protocol#25. Wrap cached entries
in a small JSON envelope carrying the write timestamp, thread that
through ApiResult/VaultViewModel as cachedAt, and have OfflineBanner
show "as of X ago" whenever displayed data came from the cache.
OfflineCache never deleted anything from context.cacheDir/ttl_offline,
so it grew without bound — the same gap as iOS ethos-protocol#26. Track access
recency in-memory and evict least-recently-used entries once a save
pushes the directory over a 5 MB cap, and wipe the whole cache on
sign-out (ethos-protocol#59) so a previous account's cached vault data doesn't
linger on a shared device.
VaultWidgetUpdateWorker.doWork() took result.data.firstOrNull(),
showing whichever vault the API happened to return first — arbitrary,
and inconsistent with iOS's TTLTimelineProvider, which filters to
active vaults and picks the minimum ttlRemaining. Extract that same
selection logic into VaultWidgetUpdateWorker.selectUrgentVault() so a
user with multiple vaults sees the one closest to expiring on both
platforms.
NotificationHelper.show() used vaultId.hashCode() as the notification
ID, and String.hashCode() collisions between two distinct vault IDs
are possible within the 32-bit hash space — one vault's check-in
reminder could silently replace another's instead of both appearing.
Persist a stable vaultId -> ID mapping in a reserved ID range instead,
so distinct vault IDs are guaranteed distinct notification IDs, and
group notifications by vault as a second line of defense against
relying on ID uniqueness alone for replace-vs-append behavior.
@drips-wave

drips-wave Bot commented Jul 27, 2026

Copy link
Copy Markdown

@Valreb001 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@maugauwi-hash
maugauwi-hash merged commit 1969e2e into ethos-protocol:main Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants