fix(pages): resync congrats screen after filtered deck rebuild - #21409
fix(pages): resync congrats screen after filtered deck rebuild#21409Ayush-Patel-56 wants to merge 1 commit into
Conversation
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
|
A fix for the main branch lint failure was merged. Rebase on top of main to clear CI failures(if your PR doesn't introduce any). |
33440d6 to
ae2247f
Compare
ae2247f to
27612d1
Compare
| .onEach { state -> | ||
| when (state) { | ||
| CongratsRefreshState.ReloadPage -> webViewLayout.post { webViewLayout.reload() } | ||
| CongratsRefreshState.DeckHasCardsToStudy -> openStudyOptionsAndFinish() | ||
| } | ||
| }.launchIn(lifecycleScope) |
There was a problem hiding this comment.
I don't see how this would work. We wouldn't be subscribed when the event is emitted as the screen wouldn't be active/visible.
There was a problem hiding this comment.
emit() suspends until something's collecting, it doesn't drop the value. flowWithLifecycle just cancels/restarts the collector on stop/start, so a rebuild while we're stopped just parks until we resume. can add a test for that if you want it nailed down.
There was a problem hiding this comment.
emit() suspends until something's collecting
I don't believe this is the case, could you confirm empirically. In your test, you have a subscriber, driven by turbine.
We are calling MutableSharedFlow.emit: https://github.com/Kotlin/kotlinx.coroutines/blob/b157ff07152dc2adcfdb2e7e45ff2a1fd4dca0bd/kotlinx-coroutines-core/common/src/flow/SharedFlow.kt#L418-L421
We built the MutableStateFlow as: MutableSharedFlow<CongratsRefreshState>() which defines replay = 0
See the implementation of tryEmitNoCollectorsLocked: https://github.com/Kotlin/kotlinx.coroutines/blob/b157ff07152dc2adcfdb2e7e45ff2a1fd4dca0bd/kotlinx-coroutines-core/common/src/flow/SharedFlow.kt#L447-L455
* If there are no subscribers, the buffer is not used.
* Instead, the most recently emitted value is simply stored into
* the replay cache if one was configured, displacing the older elements there,
* or dropped if no replay cache was configured.There was a problem hiding this comment.
you're right, with replay=0 and no active subscriber the emit is dropped, confirmed via the docs you linked. real fix: added an onResume() recheck in CongratsPage so it re-evaluates deck state every time the screen becomes visible again, not just when it catches the live event. wrote a test that rebuilds the deck while the fragment is stopped, then resumes it, failed without the fix, passes now.
27612d1 to
406a43d
Compare
|
Ok, I've looked into this more deeply: This works, but only because undoableOp<OpChangesWithId> {
val safeAddUpdateResult = safeAddOrUpdateFilteredDeck(newFilterForUpdate)
if (safeAddUpdateResult.isSuccess) {
state.update { DeckBuilt } // this calls through to `finish()`Instead, could we explicitly perform a refresh on |
|
Snapshot diff report vs
All 2 changed screenshotsDeckPickerScreenshotTest
PreferencesScreenshotTest
|
406a43d to
6e04ec8
Compare
plain |
| lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { | ||
| launch { viewModel.onStudyQueuesChanged() } | ||
| viewModel.congratsRefreshState.collect { state -> | ||
| when (state) { |
There was a problem hiding this comment.
Could you add a Timber.d here, I suspect this is going to be called twice. In the case of reload(), this will cause noticeable latency (and we'll want to resolve this).
There was a problem hiding this comment.
good catch, was scoped to the fragment's own lifecycle - onViewCreated can run more than once (webview gets torn down/recreated), so old collectors stuck around. switched to viewLifecycleOwner, also fixes the lint failure. added the Timber.d.
There was a problem hiding this comment.
onViewCreated can run more than once (webview gets torn down/recreated)
I don't believe this is the case, and the fix adds complexity to the code (with a verbose comment).
Please revert this (unless there's a case I haven't thought about): SafeWebView is the only 'refresh' we have, and this works on the WebView component.
Note: My 'double reload' was referring to the case where the opChanges occurred from a rebuild after the screen moved back to STARTED - I don't think I was correct here, but the line is useful as reload is expensive.
6e04ec8 to
6e03c06
Compare
david-allison
left a comment
There was a problem hiding this comment.
Thanks!! One more round I feel
| lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { | ||
| launch { viewModel.onStudyQueuesChanged() } | ||
| viewModel.congratsRefreshState.collect { state -> | ||
| when (state) { |
There was a problem hiding this comment.
onViewCreated can run more than once (webview gets torn down/recreated)
I don't believe this is the case, and the fix adds complexity to the code (with a verbose comment).
Please revert this (unless there's a case I haven't thought about): SafeWebView is the only 'refresh' we have, and this works on the WebView component.
Note: My 'double reload' was referring to the case where the opChanges occurred from a rebuild after the screen moved back to STARTED - I don't think I was correct here, but the line is useful as reload is expensive.
6e03c06 to
91501ba
Compare
Purpose / Description
The congrats screen for a filtered deck stays stuck on "Congratulations" after rebuilding, even when the rebuild pulls cards back in.
Fixes
Approach
CongratsPagereloaded its own congrats WebView route on everystudyQueueschange without checking if the deck was actually still empty.CongratsViewModelnow checkssched.counts()first and navigates toStudyOptionsActivityinstead of reloading when the deck has cards due. Desktop'sOverviewalready does this same check on every refresh, AnkiDroid's split-screen version just never ported it over.How Has This Been Tested?
Added
CongratsViewModelTest, reproducing the repro steps from the issue (fill deck, empty it, rebuild). Ran the full unit test suite locally. Manually reproduced and verified the fix on a Pixel 7 emulator, API 33.Learning (optional, can help others)
Compared against desktop's
qt/aqt/overview.pyto see how it avoids this class of bug: it re-checkssched._is_finished()on every refresh instead of assuming the previous state still holds.Checklist
Please, go through these checks before submitting the PR.