Skip to content

Conversation

@rajveermalviya
Copy link
Member

@rajveermalviya rajveermalviya commented Sep 30, 2025

Stacked on top of ea5cd48 and b8e0143 from #1937.


Separated from #1816 to preserve some drafts commits there from @chrisbobbe.

Screenshots coming..

Tests are TODO and will happen as part of #1787 in a follow-up PR.

Figma: https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=12853-76543&p=f&t=oBRXWxFjbkz1yeI7-0
Fixes: #1779

@rajveermalviya
Copy link
Member Author

rajveermalviya commented Oct 3, 2025

Screenshots:

Channels DMs Choose account
image image image
image image image

Copy link
Collaborator

@chrisbobbe chrisbobbe left a comment

Choose a reason for hiding this comment

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

Thanks! Comments below.

SubscriptionListPageBody(
showTopicListButtonInActionSheet: false,
hideChannelsIfUserCantSendMessage: true,
allowGoToAllChannels: false,
Copy link
Collaborator

Choose a reason for hiding this comment

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

This removal looks unintentional.

Comment on lines 190 to 192
// We should already have the `store.realmIcon` after the
// PerAccountStore has completed loading, hence the `!` here.
final realmIconUrl = store.realmUrl.resolveUri(store.realmIcon!);
Copy link
Collaborator

Choose a reason for hiding this comment

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

If a PerAccountStore is guaranteed to have non-null .realmIcon, then why is it typed as optional? If we can make it required, we should do so, right? (Ditto .realmName.) Then if there's some subtlety in explaining that, we can do it just once where the field is declared, instead of all the places that try to consume it.

Comment on lines 212 to 216
SizedBox.square(
dimension: 42,
child: Padding(
padding: const EdgeInsets.all(7),
child: RealmContentNetworkImage(realmIconUrl))),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Following the link to the "company-logo" component in Figma—

https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=421-14693&m=dev

—it looks like this is supposed to have a 4px border radius:

image

Would the AvatarShape widget be appropriate here? Its dartdoc says "A rounded square shape, to wrap an [AvatarImage] or similar.".

Copy link
Collaborator

Choose a reason for hiding this comment

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

I also see an additional 4px horizontal padding in the Figma that's not implemented in this revision:

https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=12853-76635&m=dev

image

labelStyle: labelStyle,
labelColor: designVariables.iconSelected,
unselectedLabelColor: designVariables.icon,
indicatorWeight: 0,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmm, two things stand out to me in the dartdoc of indicatorWeight:

  • /// The value of this parameter must be greater than zero.
  • /// If [indicator] is specified […] this property is ignored.

What happens if we don't pass indicatorWeight?

Copy link
Member Author

Choose a reason for hiding this comment

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

Added a bit of context in a comment on why the indicatorWeight: 0 is needed here.

final labelStyle = TextStyle(
fontSize: 18,
height: 24 / 18,
letterSpacing: 0,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Isn't letterSpacing: 0 the default, from zulipTypography?

Comment on lines 234 to 235
child: RealmContentNetworkImage(realmIconUrl))),
),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
child: RealmContentNetworkImage(realmIconUrl))),
),
child: RealmContentNetworkImage(realmIconUrl)))),

Comment on lines 103 to 107
ShareDialog.show(
pageContext: context,
initialAccountId: accountId,
sharedFiles: sharedFiles,
sharedText: intentSendEvent.extraText);
Copy link
Collaborator

Choose a reason for hiding this comment

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

share: Re-design from page to a dialog

This commit breaks the "New DM" button in the "Direct messages" tab, because a PageRoot ancestor is no longer found when the button is tapped:

======== Exception caught by gesture ===============================================================
The following assertion was thrown while handling a gesture:
No PageRoot ancestor
'package:zulip/widgets/page.dart':
Failed assertion: line 26 pos 12: 'element != null'

When the exception was thrown, this was the stack: 
#2      PageRoot.contextOf (package:zulip/widgets/page.dart:26:12)
#3      showNewDmSheet (package:zulip/widgets/new_dm_sheet.dart:17:32)
#4      _NewDmButtonState.build.<anonymous closure> (package:zulip/widgets/recent_dm_conversations.dart:270:20)

[etc.]

Comment on lines 344 to 380
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);
}
}());
Copy link
Collaborator

Choose a reason for hiding this comment

The 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:

  • The choose-account page/dialog doesn't yet show the name and icon, but it will, and it'll be equally useful there to refresh them as here.
  • We've written careful "refuse-to-connect-to-ancient-servers" logic that should eliminate ancient-server behavior as a possible source of bugs, which should help simplify debugging. This revision puts a small hole in that logic, which we could fix by checking ZulipVersionData.isUnsupported before interpreting realmName and realmIcon. Like we do around the other getServerSettings call.
    • I don't think any ancient servers format realmName and realmIcon in weird, interfering ways…but the point is being confident that it wouldn't matter even if they did.
  • The realm name and icon are also provided via the event system, and we should defer to that: if a PerAccountStore finishes loading before we've received and recorded the data from get-server-settings, we should abort instead of clobbering the store with this data that arrived out-of-band.

Copy link
Collaborator

Choose a reason for hiding this comment

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

more centralized

I'm not sure yet where's the most helpful place to put this. Maybe in a stateful widget that gets passed to PerAccountStoreWidget.placeholder? I'd be happy to think about that when I revisit my draft commits in #1816, and in the meantime perhaps you can just address my second and third bullet points above, which are correctness issues.

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 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.

return const SizedBox.shrink();
}

return ListTile(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Perhaps add a TODO(#1038) for aligning this dialog with other choose-account dialogs (link: #1038)

Comment on lines 221 to 228
onTap: hasMultipleAccounts
? () {
ChooseAccountForShareDialog.show(
pageContext: context,
selectedAccountId: store.accountId,
sharedFiles: sharedFiles,
sharedText: sharedText);
}
Copy link
Collaborator

@chrisbobbe chrisbobbe Oct 8, 2025

Choose a reason for hiding this comment

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

I wonder if there's some way we can make it clearer that the org icon can be tapped to switch accounts. Like the chevron-down icon on the emoji selector on the set-status page:

image

Copy link
Collaborator

Choose a reason for hiding this comment

The 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.

@chrisbobbe
Copy link
Collaborator

@alya curious for your thoughts on #1883 (comment) too.

@chrisbobbe
Copy link
Collaborator

@rajveermalviya gentle bump on this; there's material from my draft commits in #1816 that I'd like to refresh and send as a PR after this PR settles.

@rajveermalviya rajveermalviya force-pushed the pr-switch-account-share branch from 01bc09d to db3de4b Compare December 10, 2025 18:28
@chrisbobbe
Copy link
Collaborator

I see you've pushed changes but I'm not sure if you're intending them to be ready for review 🙂 please let me know when ready.

@rajveermalviya rajveermalviya force-pushed the pr-switch-account-share branch 2 times, most recently from f02fe4a to c02e265 Compare December 11, 2025 16:36
@rajveermalviya
Copy link
Member Author

Thanks for the review @chrisbobbe! Sorry for the delay. I've pushed a new revision, PTAL.

@rajveermalviya rajveermalviya force-pushed the pr-switch-account-share branch 2 times, most recently from 79ceed6 to 955d505 Compare December 12, 2025 06:49
Copy link
Collaborator

@chrisbobbe chrisbobbe left a comment

Choose a reason for hiding this comment

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

Thanks! Small comments below.

/// to update the realm metadata (name and icon).
///
/// The updation for some accounts may be skipped if:
/// - The per account store for the account is already loaded or is loading.
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: Helpful to use the identifier [PerAccountStore] here

/// - The version of corresponding realm server is unsupported.
///
/// Returns immediately; the fetches and updates are done asynchronously.
void ensureFreshRealmMetadata() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This name makes me think that…well, that it'll ensure the realm metadata is fresh 🙂, and that it'll accomplish that before it returns. (I guess I'm thinking of calls like TestZulipBinding.ensureInitialized();.)

Really, it just kicks off an attempt at refreshing the metadata, under certain conditions (like that the server isn't ancient); an attempt which may or may not succeed, sometime after the function returns.

How about tryRefreshRealmMetadata, or just refreshRealmMetadata? I think the latter is fine (it's shorter); I think the important improvement is just to remove the really strong "ensure" wording.

Comment on lines 150 to 157
child: PageRoot(
child: ShareSheet(
sharedFiles: sharedFiles,
sharedText: sharedText),
));
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: parens at end

text,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: TextStyle(
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: extra space

splashColor: Colors.transparent,
leading: AspectRatio(
aspectRatio: 1,
child: account.realmIcon == null
Copy link
Collaborator

Choose a reason for hiding this comment

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

Isn't the account.realmIcon == null case impossible here, because of the account.realmIcon! above?

Copy link
Member Author

Choose a reason for hiding this comment

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

Ahh, the account.realmIcon! above should have a conditional, because account.{realmIcon,realmName} == null is possible for accounts that never had the realm metadata stored. i.e PerAccountStore wasn't loaded recently (specifically after a8b236d) or GlobalStore.refreshRealmMetadata hasn't updated the data yet.

sharedText: widget.sharedText);
},
splashColor: Colors.transparent,
leading: AspectRatio(
Copy link
Collaborator

Choose a reason for hiding this comment

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

How about using AvatarShape for the realm icon, as elsewhere

headers: userAgentHeader(),
filterQuality: FilterQuality.medium,
fit: BoxFit.cover)),
title: Text(account.realmName ?? account.realmUrl.toString()),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can account.realmName ever be null here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, see: #1883 (comment)

Comment on lines 402 to 404
header: Padding(
padding: const EdgeInsets.only(top: 8),
child: BottomSheetHeader(title: zulipLocalizations.shareChooseAccountLabel)),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead of the Padding widget, pass outerVerticalPadding: true to BottomSheetHeader. (I think this was an API change in BottomSheetHeader that landed after your first revision of this.)

@rajveermalviya rajveermalviya force-pushed the pr-switch-account-share branch from 955d505 to 49b5c97 Compare December 16, 2025 10:54
A helper that attempts to update the realm metadata (name and icon)
for each account.
@rajveermalviya rajveermalviya force-pushed the pr-switch-account-share branch from 49b5c97 to 3c9cc57 Compare December 16, 2025 13:36
@rajveermalviya rajveermalviya force-pushed the pr-switch-account-share branch from 3c9cc57 to 5d559ae Compare December 16, 2025 13:39
@rajveermalviya
Copy link
Member Author

Thanks for the review @chrisbobbe! Pushed an update, PTAL.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintainer review PR ready for review by Zulip maintainers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Android] Allow selecting an account when sharing to Zulip from other apps

4 participants