Skip to content

fix: update credential gates without an app restart - #49

Merged
jvsena42 merged 1 commit into
mainfrom
fix/reactive-credential-gates
Aug 3, 2026
Merged

fix: update credential gates without an app restart#49
jvsena42 merged 1 commit into
mainfrom
fix/reactive-credential-gates

Conversation

@jvsena42

@jvsena42 jvsena42 commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Summary

Adding YouCam credentials (or a Claude key) on the Settings screen did not unlock the screens gated on them until the app was restarted.

TryItState.hasApiKey / hasYouCamKey were read once in the ViewModel's init, and both platforms keep every tab's screen alive — Android in a single HorizontalPager composition with an Activity-scoped koinViewModel(), iOS in a paged TabView holding a @StateObject. So init never ran again and the flag stayed stale. WardrobeViewModel and GapsViewModel sampled isAiAvailable() the same way.

The platform secret stores (Keystore, Keychain) have no change notification, so SettingsRepositoryImpl now owns the signal: a private revision StateFlow bumped after every successful credential write, which the new observable gates re-read through. Because the repository is a Koin single, a write from Settings reaches every other screen's collector. All the reactivity lives in commonMain — the expect/actual SecretStore stays a plain get/set, and no UI changed on either platform since both already render from state.

Changes

  • SettingsRepository: added hasApiKeyFlow(), hasYouCamCredentialsFlow() and isAiAvailableFlow() alongside the existing one-shot suspend checks
  • SettingsRepositoryImpl: private credentialRevision MutableStateFlow, bumped inside withContext(dispatcher) after each successful save/clear so a failed write does not emit; isAiAvailableFlow() combines it with the DataStore isOnDeviceAiEnabled() flow and re-queries on-device availability per emission
  • TryItViewModel: observeCredentials() combines both credential flows into state; loadCredentialState() kept for the share-sheet path, which still needs a synchronous read so it does not race the collector's first emission
  • WardrobeViewModel: refreshAiAvailability()observeAiAvailability()
  • GapsViewModel: collects availability and re-runs loadGaps() on change, so gaining a key switches it from heuristic to AI recommendations immediately
  • FakeSettingsRepository: mirrors the revision tick via property setters, so existing tests that assign apiKey / youCamClientId drive the flows too
  • Added four regression tests covering credentials saved after construction, cleared after construction, and the Claude-key path

Test plan

  • ./gradlew :shared:allTests — passes; includes the new regression tests, and covers the iOS Simulator target compile
  • ./gradlew :composeApp:assembleDebug — passes
  • Manual: open Settings, connect YouCam credentials, swipe back to Try-It — the virtual try-on section is available without restarting the app
  • Manual: same flow with a Claude key, checking that Wardrobe and Gaps unlock their AI features (Gaps should switch from heuristic to AI recommendations)
  • Manual: clear the credentials and confirm the screens re-lock

Checklist

  • ./gradlew detekt passes
  • Tested on Android
  • Tested on iOS
  • Updated documentation (if applicable)

🤖 Generated with Claude Code

Screens read hasApiKey/hasYouCamKey once in their ViewModel's init. Both
platforms keep every tab's screen alive — Android in a HorizontalPager,
iOS in a paged TabView — so connecting YouCam or Claude on Settings left
Try-It, Wardrobe and Gaps showing their locked state until the process
restarted.

The platform secret stores have no change notification, so the shared
repository owns the signal: a revision StateFlow bumped after every
successful credential write, which the new flows re-read through. The
ViewModels collect those instead of sampling once.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jvsena42
jvsena42 enabled auto-merge August 3, 2026 00:25
@jvsena42
jvsena42 merged commit 202155b into main Aug 3, 2026
2 checks passed
@jvsena42
jvsena42 deleted the fix/reactive-credential-gates branch August 3, 2026 00:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant