feat: settings screen with re-pair, lyrics prefs (#84, #114)#121
Open
joelkanyi wants to merge 1 commit into
Open
feat: settings screen with re-pair, lyrics prefs (#84, #114)#121joelkanyi wants to merge 1 commit into
joelkanyi wants to merge 1 commit into
Conversation
Adds a Settings screen reachable via a gear icon on the Folders header.
Follows the MVI screen skill (UiState + UiEvent + UiEffect + ViewModel +
stateless ScreenContent + ObserverAsEvent for effects).
What the screen shows:
- Account section with the connected server URL and a Re-pair device
button. Tapping it signs out and routes to QR scan.
- Lyrics section with three toggles bound to AppSettingsRepository: the
online lyrics search master switch, auto-search when a track has no
lyrics, and the option to upgrade unsynced lyrics to a synced version
found online.
- About section with the app version.
Auth changes to support sign out:
- AuthRepository.signOut() clears tokens, base URL, the stored user
record, and the in-memory holders.
- BaseUrlDao.clearBaseUrl and UserDao.clearLoggedInUser are now suspend
so they don't error from a coroutine context.
- AuthTokensDataStore.clear wipes the auth datastore.
Fixes for null base URL issues that surfaced while testing the sign out
flow:
- storeBaseUrl normalizes the URL (trimEnd('/') + "/") so it ends with a
single slash regardless of input. Previously each call appended another
slash, producing legacy DB rows like https://host/// over time.
getBaseUrl also normalizes on read so existing bad rows self heal on
next launch.
- storeBaseUrl now updates BaseUrlHolder in memory the same way
storeAuthTokens does, so the first request after pairing has the URL.
- getFreshTokensFromServer early returns if base URL or refresh token is
null. The TokenRefreshWorker fires on schedule and was hitting
http://default/null/auth/refresh in the window between sign out and
sign in.
- DataFolderRepository.getPagingContent returns an empty flow if base URL
is null instead of constructing a PagingSource with "nullfolder" baked
into its URL.
Closes #84 and #114. The user profile fetch (showing name/email instead
of just server URL) is a separate concern, AuthViewModel has a TODO for
it and the LogInResult doesn't carry user info yet.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #84. Closes #114.
Adds a Settings screen, gives users a way to re-pair their device without clearing app data, surfaces the three lyrics preferences that shipped silently in #113, and fixes a handful of base URL bugs that became visible while testing the sign-out flow.
What the user sees
The Folders header now has a gear icon in the top right. Tapping it opens the Settings screen.
The screen has three sections:
Account. Shows the connected server URL. Below it, a Re-pair device button. Tapping it signs out (clears tokens, server URL, user record, in-memory holders) and routes to the QR scan screen. Pair again and you land back on Folders, signed in.
Lyrics. Three toggles wired to the existing preferences in
AppSettingsRepository:/plugins/lyrics/searchto fetch lyrics from external sources.About. App version.
Pattern
The screen follows the MVI screen skill we baselined in #119:
Initial data loads in the VM
init { }. One-shot effects (navigate back, navigate to QR scan, snackbar) flow through aChanneland are collected withObserverAsEvent. TheScreenContentis stateless and has a@Preview.Auth changes to support sign out
AuthRepository.signOut()clears tokens, base URL, the stored user record, and the in-memory holders.BaseUrlDao.clearBaseUrlandUserDao.clearLoggedInUserare nowsuspendso they don't error from a coroutine context.AuthTokensDataStore.clear()wipes the auth datastore.Base URL bugs uncovered while testing
These were not introduced by this PR but became reproducible once sign-out wiped the in-memory holder and forced the app to round-trip through storage.
storeBaseUrlwas unconditionally appending/, so each call against an already-normalized URL added another slash. Over enough pair cycles the DB ended up withhttps://host///. Now usestrimEnd('/') + "/".getBaseUrlalso normalizes on read so any existing bad row self-heals on the next launch.storeBaseUrldid not updateBaseUrlHolderin memory the waystoreAuthTokensupdatesAuthTokenHolder. The very first request after pairing fell back to a DB read, but timing-sensitive callers (folder paging, token refresh worker) sometimes hit a null holder. Now updates the holder on write.getFreshTokensFromServermade a request unconditionally. TheTokenRefreshWorkerfires on a schedule, so during the window between sign-out and successful sign-in, the worker hithttp://default/null/auth/refresh. Early-returns now if base URL or refresh token is missing.DataFolderRepository.getPagingContentbuilt aPagingSourcewith"${baseUrl}folder"even whenbaseUrlwas null, baking"nullfolder"into a long-lived URL string that kept retrying. ReturnsemptyFlow()when not signed in.What's deliberately out of scope
Userrecord.AuthRepository.storeLoggedInUser()exists but is never called, andLogInResultdoesn't carry user info.AuthViewModel.getAuthenticatedUser()is a// TODO. Showing name/email needs a profile-fetch endpoint and storage path. Separate concern.AppSettingsRepositorywith no UI either. Worth a follow-up but doesn't block this issue.Tested on Pixel 7 Pro
Across multiple sign-out and re-pair cycles:
//or///).null...URLs in the network inspector for new requests.