-
Notifications
You must be signed in to change notification settings - Fork 350
share: Support switching accounts #1883
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?
Changes from 1 commit
5dbcb34
52b3bbb
ab52325
96e87ff
415b1e9
f3e6429
765d19e
6b9694c
01bc09d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -5,15 +5,19 @@ import 'package:flutter/material.dart'; | |||||||||||||||||||||||||||||||||||||
| import 'package:flutter/scheduler.dart'; | ||||||||||||||||||||||||||||||||||||||
| import 'package:mime/mime.dart'; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| import '../api/core.dart'; | ||||||||||||||||||||||||||||||||||||||
| import '../api/route/realm.dart'; | ||||||||||||||||||||||||||||||||||||||
| import '../generated/l10n/zulip_localizations.dart'; | ||||||||||||||||||||||||||||||||||||||
| import '../host/android_intents.dart'; | ||||||||||||||||||||||||||||||||||||||
| import '../log.dart'; | ||||||||||||||||||||||||||||||||||||||
| import '../model/binding.dart'; | ||||||||||||||||||||||||||||||||||||||
| import '../model/narrow.dart'; | ||||||||||||||||||||||||||||||||||||||
| import 'action_sheet.dart'; | ||||||||||||||||||||||||||||||||||||||
| import 'app.dart'; | ||||||||||||||||||||||||||||||||||||||
| import 'compose_box.dart'; | ||||||||||||||||||||||||||||||||||||||
| import 'content.dart'; | ||||||||||||||||||||||||||||||||||||||
| import 'dialog.dart'; | ||||||||||||||||||||||||||||||||||||||
| import 'home.dart'; | ||||||||||||||||||||||||||||||||||||||
| import 'icons.dart'; | ||||||||||||||||||||||||||||||||||||||
| import 'message_list.dart'; | ||||||||||||||||||||||||||||||||||||||
| import 'recent_dm_conversations.dart'; | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -183,6 +187,7 @@ class ShareDialog extends StatelessWidget { | |||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||
| Widget build(BuildContext context) { | ||||||||||||||||||||||||||||||||||||||
| final globalStore = GlobalStoreWidget.of(context); | ||||||||||||||||||||||||||||||||||||||
| final store = PerAccountStoreWidget.of(context); | ||||||||||||||||||||||||||||||||||||||
| final designVariables = DesignVariables.of(context); | ||||||||||||||||||||||||||||||||||||||
| final zulipLocalizations = ZulipLocalizations.of(context); | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -205,15 +210,29 @@ class ShareDialog extends StatelessWidget { | |||||||||||||||||||||||||||||||||||||
| maxLines: 1); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| final hasMultipleAccounts = | ||||||||||||||||||||||||||||||||||||||
| List<int>.unmodifiable(globalStore.accountIds).length > 1; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| return DefaultTabController( | ||||||||||||||||||||||||||||||||||||||
| length: 2, | ||||||||||||||||||||||||||||||||||||||
| child: Column(children: [ | ||||||||||||||||||||||||||||||||||||||
| Row(children: [ | ||||||||||||||||||||||||||||||||||||||
| SizedBox.square( | ||||||||||||||||||||||||||||||||||||||
| dimension: 42, | ||||||||||||||||||||||||||||||||||||||
| child: Padding( | ||||||||||||||||||||||||||||||||||||||
| padding: const EdgeInsets.all(7), | ||||||||||||||||||||||||||||||||||||||
| child: RealmContentNetworkImage(realmIconUrl))), | ||||||||||||||||||||||||||||||||||||||
| GestureDetector( | ||||||||||||||||||||||||||||||||||||||
| onTap: hasMultipleAccounts | ||||||||||||||||||||||||||||||||||||||
| ? () { | ||||||||||||||||||||||||||||||||||||||
| ChooseAccountForShareDialog.show( | ||||||||||||||||||||||||||||||||||||||
| pageContext: context, | ||||||||||||||||||||||||||||||||||||||
| selectedAccountId: store.accountId, | ||||||||||||||||||||||||||||||||||||||
| sharedFiles: sharedFiles, | ||||||||||||||||||||||||||||||||||||||
| sharedText: sharedText); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+221
to
+228
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I dunno, at least as an Android user, I feel like one is pretty well trained that clicking on your avatar opens the account switcher. |
||||||||||||||||||||||||||||||||||||||
| : null, | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+221
to
+229
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||||
| child: SizedBox.square( | ||||||||||||||||||||||||||||||||||||||
| dimension: 42, | ||||||||||||||||||||||||||||||||||||||
| child: Padding( | ||||||||||||||||||||||||||||||||||||||
| padding: const EdgeInsets.all(7), | ||||||||||||||||||||||||||||||||||||||
| child: RealmContentNetworkImage(realmIconUrl))), | ||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+234
to
+235
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||||
| Expanded(child: TabBar( | ||||||||||||||||||||||||||||||||||||||
| labelStyle: labelStyle, | ||||||||||||||||||||||||||||||||||||||
| labelColor: designVariables.iconSelected, | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -266,3 +285,127 @@ class ShareDialog extends StatelessWidget { | |||||||||||||||||||||||||||||||||||||
| ])); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| class ChooseAccountForShareDialog extends StatefulWidget { | ||||||||||||||||||||||||||||||||||||||
| const ChooseAccountForShareDialog({ | ||||||||||||||||||||||||||||||||||||||
| super.key, | ||||||||||||||||||||||||||||||||||||||
| required this.sharedFiles, | ||||||||||||||||||||||||||||||||||||||
| required this.sharedText, | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| final Iterable<FileToUpload>? sharedFiles; | ||||||||||||||||||||||||||||||||||||||
| final String? sharedText; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| static void show({ | ||||||||||||||||||||||||||||||||||||||
| required BuildContext pageContext, | ||||||||||||||||||||||||||||||||||||||
| required int selectedAccountId, | ||||||||||||||||||||||||||||||||||||||
| required Iterable<FileToUpload>? sharedFiles, | ||||||||||||||||||||||||||||||||||||||
| required String? sharedText, | ||||||||||||||||||||||||||||||||||||||
| }) async { | ||||||||||||||||||||||||||||||||||||||
| unawaited(showModalBottomSheet<void>( | ||||||||||||||||||||||||||||||||||||||
| context: pageContext, | ||||||||||||||||||||||||||||||||||||||
| // Clip.hardEdge looks bad; Clip.antiAliasWithSaveLayer looks pixel-perfect | ||||||||||||||||||||||||||||||||||||||
| // on my iPhone 13 Pro but is marked as "much slower": | ||||||||||||||||||||||||||||||||||||||
| // https://api.flutter.dev/flutter/dart-ui/Clip.html | ||||||||||||||||||||||||||||||||||||||
| clipBehavior: Clip.antiAlias, | ||||||||||||||||||||||||||||||||||||||
| useSafeArea: true, | ||||||||||||||||||||||||||||||||||||||
| isScrollControlled: true, | ||||||||||||||||||||||||||||||||||||||
| builder: (_) { | ||||||||||||||||||||||||||||||||||||||
| return SafeArea( | ||||||||||||||||||||||||||||||||||||||
| minimum: const EdgeInsets.only(bottom: 16), | ||||||||||||||||||||||||||||||||||||||
| child: ChooseAccountForShareDialog( | ||||||||||||||||||||||||||||||||||||||
| sharedFiles: sharedFiles, | ||||||||||||||||||||||||||||||||||||||
| sharedText: sharedText)); | ||||||||||||||||||||||||||||||||||||||
| })); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||
| State<ChooseAccountForShareDialog> createState() => _ChooseAccountForShareDialogState(); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| class _ChooseAccountForShareDialogState extends State<ChooseAccountForShareDialog> { | ||||||||||||||||||||||||||||||||||||||
| late List<int> accountIds; | ||||||||||||||||||||||||||||||||||||||
| bool _hasUpdatedAccountsOnce = false; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||
| void didChangeDependencies() { | ||||||||||||||||||||||||||||||||||||||
| super.didChangeDependencies(); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| final globalStore = GlobalStoreWidget.of(context); | ||||||||||||||||||||||||||||||||||||||
| accountIds = List.unmodifiable(globalStore.accountIds); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if (_hasUpdatedAccountsOnce) return; | ||||||||||||||||||||||||||||||||||||||
| _hasUpdatedAccountsOnce = true; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| for (final accountId in accountIds) { | ||||||||||||||||||||||||||||||||||||||
| final account = globalStore.getAccount(accountId); | ||||||||||||||||||||||||||||||||||||||
| if (account == null) continue; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| unawaited(() async { | ||||||||||||||||||||||||||||||||||||||
| final GetServerSettingsResult serverSettings; | ||||||||||||||||||||||||||||||||||||||
| final connection = globalStore.apiConnection( | ||||||||||||||||||||||||||||||||||||||
| realmUrl: account.realmUrl, | ||||||||||||||||||||||||||||||||||||||
| zulipFeatureLevel: null); | ||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||
| serverSettings = await getServerSettings(connection); | ||||||||||||||||||||||||||||||||||||||
| } catch (_) { | ||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||
| } finally { | ||||||||||||||||||||||||||||||||||||||
| connection.close(); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if (globalStore.getAccount(accountId) != null) { | ||||||||||||||||||||||||||||||||||||||
| await globalStore.updateRealmData( | ||||||||||||||||||||||||||||||||||||||
| accountId, | ||||||||||||||||||||||||||||||||||||||
| realmName: serverSettings.realmName, | ||||||||||||||||||||||||||||||||||||||
| realmIcon: serverSettings.realmIcon); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| }()); | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+344
to
+363
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The goal is to make sure the realm name and icon are present and up-to-date, right? I think my ideal way to do this would be more centralized and a bit more rigorous:
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I'm not sure yet where's the most helpful place to put this. Maybe in a stateful widget that gets passed to
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think a good home for this would be as a method on GlobalStore, perhaps named like ensureFreshAccountData. (Or ensureFreshRealmMetadata? The point is it's the data (a) that we store in the Account record but (b) about the realm as a whole.) Then that method will have access to _perAccountStoresLoading as well as _perAccountStores. It can look at those in order to ensure that it doesn't do anything if there's already a PerAccountStore or one being loaded. |
||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||
| Widget build(BuildContext context) { | ||||||||||||||||||||||||||||||||||||||
| final globalStore = GlobalStoreWidget.of(context); | ||||||||||||||||||||||||||||||||||||||
| final zulipLocalizations = ZulipLocalizations.of(context); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| final content = SliverList.builder( | ||||||||||||||||||||||||||||||||||||||
| itemCount: accountIds.length, | ||||||||||||||||||||||||||||||||||||||
| itemBuilder: (context, index) { | ||||||||||||||||||||||||||||||||||||||
| final accountId = accountIds[index]; | ||||||||||||||||||||||||||||||||||||||
| final account = globalStore.getAccount(accountId); | ||||||||||||||||||||||||||||||||||||||
| if (account == null) { | ||||||||||||||||||||||||||||||||||||||
| return const SizedBox.shrink(); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| return ListTile( | ||||||||||||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps add a |
||||||||||||||||||||||||||||||||||||||
| onTap: () { | ||||||||||||||||||||||||||||||||||||||
| // First change home page account to the selected account. | ||||||||||||||||||||||||||||||||||||||
| HomePage.navigate(context, accountId: accountId); | ||||||||||||||||||||||||||||||||||||||
| // Then push a new share dialog for the selected account. | ||||||||||||||||||||||||||||||||||||||
| ShareDialog.show( | ||||||||||||||||||||||||||||||||||||||
| pageContext: context, | ||||||||||||||||||||||||||||||||||||||
| initialAccountId: accountId, | ||||||||||||||||||||||||||||||||||||||
| sharedFiles: widget.sharedFiles, | ||||||||||||||||||||||||||||||||||||||
| sharedText: widget.sharedText); | ||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||
| leading: AspectRatio( | ||||||||||||||||||||||||||||||||||||||
| aspectRatio: 1, | ||||||||||||||||||||||||||||||||||||||
| child: account.realmIcon != null | ||||||||||||||||||||||||||||||||||||||
| ? Image.network( | ||||||||||||||||||||||||||||||||||||||
| account.realmUrl.resolveUri(account.realmIcon!).toString(), | ||||||||||||||||||||||||||||||||||||||
| headers: userAgentHeader()) | ||||||||||||||||||||||||||||||||||||||
| : null), | ||||||||||||||||||||||||||||||||||||||
| title: Text(account.realmName ?? account.realmUrl.toString()), | ||||||||||||||||||||||||||||||||||||||
| subtitle: Text(account.email)); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| return DraggableScrollableModalBottomSheet( | ||||||||||||||||||||||||||||||||||||||
| header: Padding( | ||||||||||||||||||||||||||||||||||||||
| padding: const EdgeInsets.only(top: 8), | ||||||||||||||||||||||||||||||||||||||
| child: BottomSheetHeader(title: zulipLocalizations.shareChooseAccountLabel)), | ||||||||||||||||||||||||||||||||||||||
| contentSliver: SliverPadding( | ||||||||||||||||||||||||||||||||||||||
| padding: const EdgeInsets.symmetric(horizontal: 8), | ||||||||||||||||||||||||||||||||||||||
| sliver: content)); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||

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.
This can just be
right?