Skip to content
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

login: Support logging out of an account (take 2) #1010

Merged
merged 7 commits into from
Nov 14, 2024

Conversation

chrisbobbe
Copy link
Collaborator

@chrisbobbe chrisbobbe commented Oct 19, 2024

These are the three last commits from #948, following up on the just-merged #1007 to replace #948: #948 (review)

The screenshots in #948's description still match this code. As I said on #948, there might be some improvements to make to the UI, perhaps as a followup: #948 (comment)

Fixes: #463

@chrisbobbe chrisbobbe added the integration review Added by maintainers when PR may be ready for integration label Oct 19, 2024
@chrisbobbe chrisbobbe requested a review from gnprice October 19, 2024 00:41
return Card(
clipBehavior: Clip.hardEdge,
child: ListTile(
title: title,
subtitle: subtitle,
trailing: PopupMenuButton<AccountItemOverflowMenuItem>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the PopupMenuButton doc recommends using the newer MenuAnchor instead. What if we used that — how would it differ?

Copy link
Collaborator Author

@chrisbobbe chrisbobbe Oct 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PopupMenuButton MenuAnchor
image image

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not much different, but I'm happy to use the more modern thing. 🤷‍♂️

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, SGTM

chrisbobbe added a commit to chrisbobbe/zulip-flutter that referenced this pull request Oct 25, 2024
Greg spotted this opportunity to be a bit more modern:
  zulip#1010 (comment)
@chrisbobbe
Copy link
Collaborator Author

chrisbobbe commented Oct 25, 2024

Thanks for the review! Revision pushed.

I've added a commit to update the existing place we use PopupMenuButton so it uses MenuAnchor instead:

app: Update three-dots button to use new M3 MenuAnchor widget
Before After
image image

Copy link
Member

@gnprice gnprice left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Generally this looks good. Comments below from a full review.

@alya see question below about wording for a confirmation modal.

Then one other thing this should have before we merge it is some tests, specifically for the last commit. This is kind of tricky code, and it's the sort of feature that we infrequently exercise but that is important to people when they do use it — so that makes tests extra useful.

return IconButton(
tooltip: materialLocalizations.showMenuTooltip, // "Show menu"
onPressed: () {
if (controller.isOpen) {
controller.close();
} else {
controller.open();
}
},
icon: Icon(Icons.adaptive.more, color: designVariables.icon));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems annoying that with the new API we have to recite all this standard stuff. But so be it. If we find this coming up more, we can abstract it out.

Comment on lines 236 to 259
MenuItemButton(
onPressed: () => unawaited(_logOutAccount(context, accountId)),
child: Text(zulipLocalizations.chooseAccountPageLogOutButton)),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before actually logging the user out, let's have a confirmation modal. This is something that would be pretty annoying to do by accident.

Some draft text for the modal:

Log out?

This will sign you out on this device from this Zulip account.

(buttons:) Log out / Cancel

with red "destructive action" styling on the "Log out" button.

Or I guess here's what we have in zulip-mobile:

      showConfirmationDialog({
        destructive: true,
        title: 'Remove account',
        message: {
          text: 'This will make the mobile app on this device forget {email} on {realmUrl}.',
          values: { realmUrl: realm.toString(), email },

so we could always just copy that.

@alya, do you have feedback on the possible confirmation text?

Copy link
Collaborator Author

@chrisbobbe chrisbobbe Oct 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

red "destructive action" styling

Sure. I've just filed #1032 for this; how about I leave this part as a TODO(#1032) in my next revision?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, SGTM

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, maybe we should try to explain the impact on the user a bit more? Something like this, assuming it's accurate?

Log out?
To use this account in the future, you will have to re-enter the URL for your organization and your account information.
(buttons:) Log out / Cancel

Comment on lines 259 to 271
Future<void> _logOutAccount(BuildContext context, int accountId) async {
final globalStore = GlobalStoreWidget.of(context);

final account = globalStore.getAccount(accountId);
if (account == null) return; // TODO(log)

// Unawaited, to not block removing the account on this request.
unawaited(_unregisterToken(globalStore, account));

await globalStore.removeAccount(accountId);
}

Future<void> _unregisterToken(GlobalStore globalStore, Account account) async {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From a code-organization perspective, this class and this file don't feel like the right home for these two methods — they're not really part of this UI, but rather the logic for an action the user might take, where this page just happens to be the initial home of the only place to reach that action in the UI. (It might not always be — for example in a future settings page, we might have a button to log out.)

Probably widgets/actions.dart would be good.

I also considered widgets/login.dart , since it's logically related to the things that happen there. But it doesn't really fit with any of the particular UI widgets there.

Ultimately they likely belong as methods on GlobalStore. But I wouldn't want to put them in store.dart, which is too crowded as it is — they'd be defined elsewhere and mixed in, akin to ChannelStore and its various accessor methods. So I think actions.dart is a fine home for them at least until then.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I think actions.dart is a fine home for them at least until then.

Cool, yeah, that does look like a good place for it.

Comment on lines 281 to 284
await NotificationService.unregisterToken(connection, token: token);
} catch (e) {
// TODO retry? handle failures?
} finally {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely post-launch to do anything about that — zulip-mobile doesn't do anything about failures here either.

chrisbobbe added a commit to chrisbobbe/zulip-flutter that referenced this pull request Nov 9, 2024
Greg spotted this opportunity to be a bit more modern:
  zulip#1010 (comment)
@chrisbobbe
Copy link
Collaborator Author

Thanks for the review! Revision pushed, this time with a bunch of tests. 🙂

Copy link
Member

@gnprice gnprice left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the revision! Generally this all looks good; just small comments below.

Would you also post a screenshot of the new confirmation dialog?


final future = logOutAccount(context, eg.selfAccount.id);
// Unregister-token request and account removal dispatched together
assertSingleUnregisterRequest(newConnection);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this function wraps a check call, so use the verb "check" to name it:

Suggested change
assertSingleUnregisterRequest(newConnection);
checkSingleUnregisterRequest(newConnection);

Comment on lines 125 to 127
// No unexpected requests made on either connection
check(connection.takeRequests()).isEmpty();
check(newConnection.takeRequests()).isEmpty();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these are redundant with the check in send — requests that are unexpected won't have a prepared response, so they'll throw, and the test framework will catch the unhandled exception and report.

await future;
assertSingleUnregisterRequest(newConnection, expectedToken: '123');
check(newConnection.isOpen).isFalse();
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
});
}, variant: const TargetPlatformVariant({TargetPlatform.android, TargetPlatform.iOS}));

That way we exercise both the Android and iOS cases in unregisterToken.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I think adding this to just this test is enough.)

@@ -30,19 +41,247 @@ void main() {

Future<void> prepare(WidgetTester tester, {
UnreadMessagesSnapshot? unreadMsgs,
String? ackedPushToken = '123',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change can be squashed into the commit after it, which starts using this feature.

(It doesn't touch the same code as the later commit; and it doesn't really need its own commit message, in that it's self-explanatory given the context of that other commit.)

chrisbobbe added a commit to chrisbobbe/zulip-flutter that referenced this pull request Nov 13, 2024
Greg spotted this opportunity to be a bit more modern:
  zulip#1010 (comment)
@chrisbobbe
Copy link
Collaborator Author

Thanks! Revision pushed, and here's that screenshot of the new confirmation dialog:

image

Greg spotted this opportunity to be a bit more modern:
  zulip#1010 (comment)
So far this function just has two callers in uncommon paths
involving app permissions. For those, and for the new caller we're
about to add -- logging out an account -- it's unhelpful for the
dialog to linger unchanged after the action button is tapped. Better
to make it go away, preventing confusion about whether the action
was actually done.
Like we added `checkErrorDialog` in 02fd562.
Without this line, if a test `globalStore.add`s an account with a
non-null `ackedPushToken`, the field will be null in app code or
test code that retrieves the account from `globalStore`. Thankfully
no tests have been doing that, but it'll be convenient to do that
soon, for testing the unregister-token part of logging out.
@gnprice gnprice force-pushed the pr-logout-account-2 branch from 9f6a033 to 0344686 Compare November 14, 2024 00:18
@gnprice
Copy link
Member

gnprice commented Nov 14, 2024

Thanks! Looks good; merging.

@gnprice gnprice merged commit 0344686 into zulip:main Nov 14, 2024
@chrisbobbe chrisbobbe deleted the pr-logout-account-2 branch November 20, 2024 19:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
integration review Added by maintainers when PR may be ready for integration
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support logging out / forgetting about an account
3 participants