-
Notifications
You must be signed in to change notification settings - Fork 1.3k
for #24665: Show recent synced placeholders early #24894
for #24665: Show recent synced placeholders early #24894
Conversation
app/src/main/java/org/mozilla/fenix/home/recentsyncedtabs/RecentSyncedTabFeature.kt
Show resolved
Hide resolved
|
||
override fun onError(error: SyncedTabsView.ErrorType) { | ||
store.dispatch(AppAction.RecentSyncedTabStateChange(RecentSyncedTabState.None)) | ||
if ((!isSyncing && error == SyncedTabsView.ErrorType.NO_TABS_AVAILABLE) || error.isFatal()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of having an isSyncing
variable, could we check store.state.recentSyncedTabState
to see if it's loading? Not saying this is necessarily the correct thing to do, but in prior reviews I had an inkling that maybe recentSyncedTabState
should be tri-state with stopLoading
implemented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll update it to inspect the store's state, but would you mind clarifying about the tri-state? Maybe it's time to revisit that since we've already talked about it at least once.
Is the idea to introduce an error state? From the consumer's perspective, there isn't (currently) a desire to display error messages, which is why I've been defaulting to NONE
on error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be something more like START
( or NONE
), LOADING
, DONE
. I don't know if this will necessarily be correct, but instead of only having NONE
, we need to have a state that differentiates between whether or not loading was attempted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There currently are three states that I think map pretty well to those: NONE
, LOADING
, and SUCCESS
. Switching SUCCESS
to a DONE
state that had either successes or failures could potentially be an improvement, but I'm not sure what it benefits over the current implementation.
Honestly I think the underlying Controller implementation's usage of stopLoading
is generally a bit confusing. The only case it's ever really useful IMO is the one where an account isn't authenticated, but refreshSyncedTabs
shouldn't be called unless an account is authenticated anyway.
e51cf70
to
f1d8e5b
Compare
…eholders accordingly
f1d8e5b
to
4ed9267
Compare
@@ -1012,8 +1012,8 @@ class Settings(private val appContext: Context) : PreferencesHolder { | |||
default = true | |||
) | |||
|
|||
var fxaHasSyncedItems by booleanPreference( | |||
appContext.getPreferenceKey(R.string.pref_key_fxa_has_synced_items), | |||
var hasFxaAuthenticated by booleanPreference( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using Settings
/Preferences
to monitor the authentication state seems a little sus. Were there other approaches you tried that didn't pan out?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The underlying issue is that auth is (relatively) slow, to the point where there will be noticeable pop-in of the recent synced tab if we wait for it to finish. To me that demonstrates that we need some kind of cache mechanism so that we can show the placeholders immediately on app startup. If you have any other suggestions, I'm definitely all ears. Is it specifically using Settings
? Do we have another easy file abstraction that I've so far missed?
One alternative I can think of off the top of my head is to cache it in the FxaAccountManager
itself, but that would require adding a storage layer there as well and would ultimately be the same type of implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(for the purposes of this PR, this can still land)
I was just curious if you looked into having this live in AppState
instead. That way it could be observed in other places via appState.observeAsComposableState
in case other areas of the app can easily consume this in Compose, if necessary in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I filed #24911 to move it into app state, but the more I think about it the more I wonder whether it's actually the right move. Here's some of my thoughts:
Store
s are generally meant to be an observable entity. Any component would need to observe changes tohasFxaAuthenticated
can also observe changes to auth status directly from anFxaAccountManager
.hasFxaAuthenticated
is really meant as a one-time read value on startup- Even if we move it into the store, it will still need to be provided from settings or some other kind of cache mechanism, so I think we'd just be creating a level of indirection
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think those concerns/points are fair
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I closed it for now, then. Let me know if want to do something different in the future, happy to revisit this again.
I would have preferred we didn't land this. Using settings isn't a great idea because we're duplicating and persisting the same state to multiple places. We also have other account state holders, so we've added yet another mechanism to do the same/similar thing. We should add the account status into the AppStore with a better abstraction that holds the Sync state (e.g. an account data class with an enum and maybe some other attributes) - this was one of the original applications for adding the AppStore. It removes the need to know how the account manager works for features that just want to know what is current state at this point in time, or observe it which idioms that we've already created with the Store model.
This is true, but we can also "peek" at the current state in the store if it's a safe use-case for it.
We're currently fetching the account state remotely, so another caching layer would be an in-memory solution which the AppStore would suffice? If you mean a secondary persistent caching, it has risks and benefits there which we can discuss separately. |
@jonalmeida Sorry about that, I'll try to be more patient in the future when waiting for reviews. I'm happy to back out that particular part of the change in favor of something else.
Could you point me to where the auth state of an account specifically is persisted? That's the key requirement. The goal of this issue was to avoid pop-in on the home screen on app startup. Since there is a significant delay in account authentication, the only way to do that is with a disk cache. |
This is the same problem that we encounter on the home screen today with our other sources of data, so we could still use the same solution: the first cold start will be slow, we store it in the AppStore, when we return to the home screen again, we will already have it in memory. Why is the Synced tab a higher priority than the others? We can also go the persistent cache route, so let's walk backwards to figure out how account manager persists it's state today and see what our options are. Writing out my learnings here as I walk through the code:
Why go this route? If we start storing loosely maintained state in Fenix, we could start putting clients at risk of incorrectly saying they are (not) authenticated, which over time builds up a painful kind of tech debt. :) |
Thanks for the detailed walkthrough! I'm still working on growing the skill of navigating our stack, so I appreciate the breakdown and reminder to think more about what belongs where. I posted some more thoughts below. Next week I'll keep taking a look into it, and figure out the best way to back out the change. This work is currently hidden (even from nightly) behind a flag, so it shouldn't have any real negative impact.
I think the biggest difference is just source of the data. AFAIK, most of that other app store data is coming from local sources. In any case, the previous implementation of the synced tab was causing the loading placeholder to appear a full few seconds after the rest of the screen had populated.
Just to add some detail about expected UX as a result of that: we decided that it would be better for the placeholder to disappear in the case that it was shown before an auth failure. The thinking being that pop-in was more visually disruptive than the pop-out. I'm initially wondering if exposing whether I'm not sure I'm following yet why it would be required to add additional state at the A-S layer. I would've thought that checking the existence of any state there would get us what we were looking for. |
I'm wondering if we should possible take this a step further. I see additional benefits to introducing the sync state and data to |
How often does the auth failure happen that we should prioritize the visual disruption? With the solution in this PR, you would still run into this case regardless so we are still not covered there; we need to wait for the Sync servers to get back and tell us that the auth token we're trying to use now is no longer valid. Our best effort might just be to live with the edge case of an auth failure from a user that hasn't used Fenix in a while so they see a brief visual error. See below how we can still solve your problem with the help of the A-S team's guidance.
This is a good question for the A-S team. They would be able to tell us exactly what is the right attribute to observe in order to consider the account authenticated at some point.
The
Oh yes, 100%. Moving the state to the AC level was an original ask. It doesn't necessarily mean that we need to replace the current API immediately. Rather, provide a nicer API for consumers to migrate towards in addition to the current API, and then we have more freedom to alter the internals of the manager (and re-design the observer pattern it uses or even remove it). I don't have strong opinions towards whether it should be a separate store or within the |
I am leaning towards a separate store as well. This isn't state needed for core browsing functionality, and from the perspective of Focus, why would this empty state be in the browser state? I think it also aligns better with our module separation and composable design e.g., to only bring in state as needed by the application. |
I filed A-C 12065 to track the work of adding a sync store. It's work I'm very interested in taking on, but may need to wait until more progress has been made on various items planned for H1. At the latest, I'll plan to push for it to be included in H2. Additionally, it looks like my understanding of the issue this PR was intended to solve was just completely wrong. I've continued tinkering with it on the A-C and Fenix side, and it appears that auth state will be available as soon as other data from the homepage is. I'll have PRs up today to revert the change introduced here and still achieve desired behavior. Thanks for everyone's patience in engaging in the conversation, as it was very educational. That said, I don't think there will be any need to engage A-S for improvements and any required changes can be handled on our end. |
…vious auth state
for #24665
Separate commits since the first is technically unrelated, in case we want to file it for a different issue. I could also squash these if a single commit makes sense. During investigation I thought to maybe reuse
fxaHasSyncedItems
, but then I noticed that it was actually unused.To download an APK when reviewing a PR: