-
Notifications
You must be signed in to change notification settings - Fork 321
On launch, go to the last visited account #1784
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
db70e92
to
52a8954
Compare
fa65075
to
e68d2fb
Compare
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.
Thanks! Comments below.
@@ -73,6 +73,16 @@ class BoolGlobalSettings extends Table { | |||
Set<Column<Object>>? get primaryKey => {name}; | |||
} | |||
|
|||
@DataClassName('IntGlobalSettingRow') | |||
class IntGlobalSettings extends Table { |
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 commit 5cfd4ae, which added BoolGlobalSettings
, included a lot of useful material that I think should be easy to propagate to this new thing:
(EDIT: and I see some of these have actually been addressed in the latest revision; I'll strike out those parts. 🙂)
- The commit message points to why the new thing is helpful, in fact as soon as the summary line
- An implementation comment in
GlobalSettings
saying "consider whether [etc.] can do the job instead", which helps us get the most usefulness from the thing. Let's change that comment so that it also mentions the int global settings Dartdocs on(Let's also add bidirectional "See also:" notes in that class's dartdoc and inBoolGlobalSettings
and its fields.IntGlobalSettings
's.)- Ditto the
BoolGlobalSetting
enum, and it also got this helpful-looking implementation comment:(No need to add a// Former settings which might exist in the database, // whose names should therefore not be reused: // (this list is empty so far)
placeholderIgnore
member, though; the pseudo-setting we're adding here isn't an "experimental feature" setting and we don't plan to remove it.) Tests
lib/widgets/app.dart
Outdated
@override | ||
void didPush(Route<void> route, Route<void>? previousRoute) { | ||
_changeLastVisitedAccountIfNecessary(route); | ||
} | ||
|
||
@override | ||
void didPop(Route<void> route, Route<void>? previousRoute) { | ||
_changeLastVisitedAccountIfNecessary(previousRoute); | ||
} | ||
|
||
@override | ||
void didRemove(Route<void> route, Route<void>? previousRoute) { | ||
_changeLastVisitedAccountIfNecessary(previousRoute); | ||
} | ||
|
||
@override | ||
void didReplace({Route<void>? newRoute, Route<void>? oldRoute}) { | ||
_changeLastVisitedAccountIfNecessary(newRoute); | ||
} |
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.
Can we just use didChangeTop
instead of all these, and inline _changeLastVisitedAccountIfNecessary
?
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.
Thanks, that's interesting. I think it can replace them!
unawaited(logOutAccount(GlobalStoreWidget.of(context), accountId)); | ||
unawaited(Future(() async { | ||
if (!context.mounted) return; | ||
await logOutAccount(GlobalStoreWidget.of(context), accountId); | ||
if (!context.mounted) return; | ||
await removeLastVisitedAccountIfNecessary( | ||
GlobalStoreWidget.of(context), accountId); | ||
})); |
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 if (!context.mounted) return;
—really the fact that we clear lastVisitedAccountId
in a separate database transaction from removing the Account in logOutAccount
—means it's possible to enter a state where lastVisitedAccountId
is an ID of an account that doesn't actually exist. That seems problematic when, later, we pass lastVisitedAccountId
to HomePage.buildRoute
, which is UI code to show a page for the account. From reading code, I think the behavior might not be worse than showing the page for a frame then immediately hiding it…but even that seems glitchy and worth avoiding.
I see two options:
- Clear
lastVisitedAccountId
in the same DB transaction as the one indoRemoveAccount
- Don't bother clearing
lastVisitedAccountId
on logout, but make its interface clear (in dartdoc) that it might point to an account that doesn't exist because it was logged out. Then, before passing the value toHomePage.buildRoute
, check that it refers to an account that actually exists. If not, just do the same as we do when no account has ever been visited, i.e., open the choose-account page.
Curious for @gnprice's thoughts on this too :)
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.
Went with the second option for the new revision until Greg shares his thoughts!
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 looks like the new revision implements something that's not really either of those options. :) To do the second option completely, let's remove the removeLastVisitedAccountIfNecessary
method and the "very-rare-edge-case" branding, and just say that lastVisitedAccountId
will commonly refer to a nonexistent account, and handling that possibility is the responsibility of any code that reads it.
test/widgets/app_test.dart
Outdated
final (actionButton, _) = await prepare(tester, | ||
accounts: [eg.selfAccount], logoutAccount: eg.selfAccount); |
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.
How about using [eg.selfAccount]
and eg.selfAccount
by default?
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.
Made them the defaults in the new revision. But wouldn't it be good to have them specified explicitly in each test case? That way, the reader can easily confirm the lines in these test cases where it looks for the accounts list (without looking inside prepare
and seeing what the default is):
testWidgets('user confirms logging out', (tester) async {
final (actionButton, _) = await prepare(tester);
// ...
check(testBinding.globalStore).accounts.isEmpty();
}
testWidgets('user cancels logging out', (tester) async {
final (_, cancelButton) = await prepare(tester);
// ...
check(testBinding.globalStore).accounts.deepEquals([eg.selfAccount]);
}
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 see; sure, that's reasonable, SGTM.
This will result in better organization when more tests are added for another type of settings (IntGlobalSettings) in the following commits.
Also make it explicitly specify which account to log out in the list of accounts.
e68d2fb
to
e29b7f0
Compare
Thanks @chrisbobbe for the review. New changes pushed, PTAL. |
e29b7f0
to
1b742f3
Compare
Pushed a new revision, making a small change to the newly-merged share feature, with the content being shared to the last visited account if present, or otherwise to the first one. |
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.
Thanks! Comments below.
unawaited(logOutAccount(GlobalStoreWidget.of(context), accountId)); | ||
unawaited(Future(() async { | ||
if (!context.mounted) return; | ||
await logOutAccount(GlobalStoreWidget.of(context), accountId); | ||
if (!context.mounted) return; | ||
await removeLastVisitedAccountIfNecessary( | ||
GlobalStoreWidget.of(context), accountId); | ||
})); |
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 looks like the new revision implements something that's not really either of those options. :) To do the second option completely, let's remove the removeLastVisitedAccountIfNecessary
method and the "very-rare-edge-case" branding, and just say that lastVisitedAccountId
will commonly refer to a nonexistent account, and handling that possibility is the responsibility of any code that reads it.
test/widgets/app_test.dart
Outdated
final (actionButton, _) = await prepare(tester, | ||
accounts: [eg.selfAccount], logoutAccount: eg.selfAccount); |
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 see; sure, that's reasonable, SGTM.
await testBinding.globalStore.settings | ||
.setInt(IntGlobalSetting.lastVisitedAccountId, eg.selfAccount.id); |
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.
What's the purpose of this setup; what goes wrong if we don't include it? IIUC these tests are about what happens when you tap on a notification, and that shouldn't be affected by the last-visited account, right? (It should just open whichever account the notification is for.)
]); | ||
}); | ||
|
||
testWidgets('with last account visited, go to home page for last account', (tester) async { |
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.
nit: I would order the most common, "happy-path" test before the others
Fixes: #524