Skip to content

fix(widgets): deep-link, socket refresh, lock-screen families, dark-mode snapshots (#248–#251) - #360

Open
Blak-Codes wants to merge 1 commit into
ethos-protocol:mainfrom
Blak-Codes:feat/widget-improvements-248-249-250-251
Open

fix(widgets): deep-link, socket refresh, lock-screen families, dark-mode snapshots (#248–#251)#360
Blak-Codes wants to merge 1 commit into
ethos-protocol:mainfrom
Blak-Codes:feat/widget-improvements-248-249-250-251

Conversation

@Blak-Codes

@Blak-Codes Blak-Codes commented Aug 31, 2026

Copy link
Copy Markdown

Summary

Resolves #248, #249, #250, #251 — all four widget improvements in a single branch.


closes #248 — Widget tap deep-links to the correct vault detail

Problem: The widget's tap target opened the app without carrying the vault's ID, landing users on the vault list instead of the specific vault displayed by the widget.

Fix:

  • Extracted a vaultDeepLink(for:) helper in TTLWidget.swift that returns nil when vaultID is empty (vault deleted / no active vault), so WidgetKit falls back to the app's default route (vault list) rather than opening a dead URL.
  • Each view family (.systemSmall, .accessoryRectangular, .accessoryCircular) calls .widgetURL(vaultDeepLink(for:)) with the displayed vault's ID.
  • Android was already correct (VaultStatusWidget.deepLinkUri + FLAG_ACTIVITY_NEW_TASK); tests were already present.

Tests added: TTLWidgetDeepLinkTests — correct URL scheme/path, per-vault uniqueness, nil-on-empty-ID fallback edge case.


closes #249 — Immediate widget refresh on WebSocket vault_updated

Problem: Widgets waited for their next scheduled poll (up to 15 min) even when the foregrounded app received a vault_updated event via its WebSocket connection.

Fix:

  • iOS: VaultStore.subscribeToEvents now calls widgetReloader("TTLWidget") on every .vaultUpdated event. widgetReloader defaults to WidgetCenter.shared.reloadTimelines(ofKind:) but is injectable for unit tests (no real WidgetCenter in SPM tests).
  • Android: VaultViewModel.subscribeToEvents now calls VaultStatusWidget.saveVaultData with the updated vault's values, then VaultWidgetUpdateWorker.schedule(context, intervalMinutes = 0) to trigger an immediate worker run. The existing REPLACE work policy means rapid events coalesce to a single run.

Tests added:

  • iOS: TTLWidgetReloadOnVaultUpdatedTests — reload fires on vault_updated, fires for any subscribed vault, non-update events are ignored.
  • Android: VaultWidgetRefreshOnSocketEventTestschedule(0) is called, data is saved before reschedule, null-vault events are ignored, three rapid events each trigger their own reschedule.

closes #250 — Lock Screen / StandBy widget families for iOS

Problem: TTLWidget declared .accessoryRectangular/.accessoryCircular in supportedFamilies but rendered the full home-screen view for those families — no compact layout, no balance-data guard.

Fix:

  • Added TTLAccessoryRectangularView: vault name + TTL countdown, no balance data, .containerBackground(.clear).
  • Added TTLAccessoryCircularView: lock icon + short duration (2d / 4h), no vault name, no balance.
  • Added TTLWidgetEntryView dispatcher that routes on @Environment(\.widgetFamily); home-screen families continue to use the existing TTLWidgetView.

Tests added: TTLWidgetAccessoryViewTests — both compact views build without crashing for normal, expiring-soon, and nil-TTL states.


closes #251 — Dark-mode widget snapshot tests (both platforms)

Problem: Dark-mode rendering for widgets was not snapshot-tested, unlike in-app screens (covered by ScreenshotDarkTest).

Fix:

  • iOS: TTLWidgetSnapshotTests — covers all three widget families in both light and dark appearances, plus nil-TTL and expiring-soon states. Uses the same _ = view.body pattern as the rest of the SPM test suite (no SnapshotTesting dependency yet); see comments in the file for how to upgrade to PNG baselines.
  • Android: WidgetScreenshotDarkTest (Paparazzi, NightMode.NIGHT) — snapshots normal, expiring-soon, and unavailable widget states. WidgetScreenshotLightTest provides the light-mode baseline for dark/light comparison in CI. Both follow the exact same Paparazzi @Rule pattern as ScreenshotDarkTest/ScreenshotLightTest.

Files changed

File Change
ios/…/Sources/Widget/TTLWidget.swift Compact accessory views, deep-link helper, family dispatcher
ios/…/Sources/ViewModels/Stores.swift widgetReloader seam + call on vault_updated
android/…/ui/ViewModels.kt saveVaultData + schedule(0) on vault_updated
ios/…/Tests/TTLWidgetTests.swift ✨ New — #248 deep-link, #249 reload, #250 compact views, #251 snapshots
android/…/VaultWidgetRefreshOnSocketEventTest.kt ✨ New — #249 socket→widget refresh tests
android/…/WidgetScreenshotTest.kt ✨ New — #251 dark + light widget Paparazzi snapshots

Testing

  • iOS: swift test — all existing tests pass; new TTLWidgetTests suite added.
  • Android: ./gradlew test — new VaultWidgetRefreshOnSocketEventTest and WidgetScreenshotTest suites added. Run ./gradlew recordPaparazziDebug once to commit the snapshot baselines.

…ode snapshots (ethos-protocol#248ethos-protocol#251)

ethos-protocol#248 – Widget tap deep-links to the correct vault detail
- TTLWidget.swift: extract vaultDeepLink(for:) helper that returns nil for an
  empty vaultID (vault deleted / no active vault), letting WidgetKit fall back
  to the app's default route (vault list) instead of navigating to a dead URL.
- Each accessory view (.systemSmall, .accessoryRectangular, .accessoryCircular)
  calls widgetURL(vaultDeepLink(for:)) with the displayed vault's ID.
- Tests: TTLWidgetDeepLinkTests covers correct URL, per-vault uniqueness,
  nil-on-empty-ID fallback, and scheme/path component assertions.

ethos-protocol#249 – Immediate widget refresh on WebSocket vault_updated
- iOS: VaultStore.subscribeToEvents now calls widgetReloader("TTLWidget") on
  every .vaultUpdated event. widgetReloader defaults to
  WidgetCenter.shared.reloadTimelines(ofKind:) but is injectable for unit tests.
- Android: VaultViewModel.subscribeToEvents now calls
  VaultStatusWidget.saveVaultData + VaultWidgetUpdateWorker.schedule(context, 0)
  on each vault_updated event, triggering an immediate worker run.
- Tests: TTLWidgetReloadOnVaultUpdatedTests (iOS),
  VaultWidgetRefreshOnSocketEventTest (Android) assert reload fires, data is
  saved before reschedule, non-update events are ignored, and multiple events
  each trigger an independent reschedule.

ethos-protocol#250 – Lock Screen / StandBy widget families for iOS
- TTLWidget.swift: add TTLAccessoryRectangularView (vault name + TTL countdown,
  no balance data) and TTLAccessoryCircularView (icon + short duration, no name).
- TTLWidgetEntryView dispatcher routes .accessoryRectangular/.accessoryCircular
  to the compact views; all other families continue to use TTLWidgetView.
- Tests: TTLWidgetAccessoryViewTests asserts both compact views build without
  crashing across normal / expiring-soon / nil-TTL states.

ethos-protocol#251 – Dark-mode widget snapshot tests
- iOS: TTLWidgetSnapshotTests covers all three widget families in both light and
  dark appearances, plus nil-TTL and expiring-soon states.
- Android: WidgetScreenshotDarkTest (Paparazzi, NightMode.NIGHT) snapshots
  normal, expiring-soon, and unavailable widget states; WidgetScreenshotLightTest
  adds the light-mode baseline for dark/light comparison in CI.
@drips-wave

drips-wave Bot commented Aug 31, 2026

Copy link
Copy Markdown

@Blak-Codes 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant