-
Notifications
You must be signed in to change notification settings - Fork 368
Show current organization's name and logo atop main menu #1937
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
Conversation
|
Hm, looking at this now, I don't like the "Organizations" button there. It's not immediately apparent what organizations it's referring to/why. Where does it go? The account switcher? |
Correct in the current revision it goes to the account switcher button, also the Figma design doesn't have a separate "Switch account" button in the main menu. |
|
Can you just click on the org name itself to switch accounts? |
|
(If we're moving the account switching action to this new top line, we'll want to remove the "switch account" option in the same PR.) |
chrisbobbe
left a comment
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, and see also Alya's feedback (mentioning it so it doesn't get lost in the thread).
lib/model/store.dart
Outdated
| String? get realmName => account.realmName; | ||
|
|
||
| Uri? get realmIcon => account.realmIcon; | ||
| // The `account` is populated with the `realmName` before | ||
| // PerAccountStore is created, so this should never be null. | ||
| // See `UpdateMachine.load`. | ||
| String get realmName => account.realmName!; | ||
|
|
||
| // The `account` is populated with the `realmIcon` before | ||
| // PerAccountStore is created, so this should never be null. | ||
| // See `UpdateMachine.load`. | ||
| Uri get realmIcon => account.realmIcon!; |
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.
store: Make realmName and realmIcon getters non-null
Commit-message nit: "non-nullable", right? Also the commit message repeats reasoning that's already clear from added code comments, so we can remove it from the commit message.
| testWidgets('_AboutZulipButton', (tester) async { | ||
| tester.view.physicalSize = Size(1080, 1920); | ||
| prepareBoringImageHttpClient(); | ||
| await prepare(tester); | ||
| await tapOpenMenuAndAwait(tester); | ||
| await tapButtonAndAwaitTransition(tester, find.byIcon(ZulipIcons.info)); | ||
| check(find.byType(AboutZulipPage)).findsOne(); | ||
| debugNetworkImageHttpClientProvider = null; | ||
| }); |
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.
Rather than thinking through what's accomplished with the numbers 1080 and 1920 and whether they're correct, I'd rather just scroll until the button is in view:
testWidgets('_AboutZulipButton', (tester) async {
prepareBoringImageHttpClient();
await prepare(tester);
await tapOpenMenuAndAwait(tester);
await tester.ensureVisible(find.byIcon(ZulipIcons.info));
await tapButtonAndAwaitTransition(tester, find.byIcon(ZulipIcons.info));
check(find.byType(AboutZulipPage)).findsOne();
debugNetworkImageHttpClientProvider = null;
});That's simpler and more transparent, and also adds some coverage of the scrolling logic; it seems like a win-win :)
lib/widgets/home.dart
Outdated
| final designVariables = DesignVariables.of(context); | ||
| final store = PerAccountStoreWidget.of(context); | ||
|
|
||
| final realmIconUrl = store.tryResolveUrl(store.realmIcon.toString()); |
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.
Most of this line seems like code we'd rather not need to recite whenever we need the realm icon—should be possible to encapsulate it in PerAccountStore (maybe even Account?) where we have access to the realm URL.
lib/widgets/home.dart
Outdated
| padding: const EdgeInsets.only(top: 6), | ||
| child: Row(children: [ | ||
| Expanded(child: Padding( | ||
| padding: const EdgeInsets.fromLTRB(12, 6, 4, 6), |
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.
Use EdgeInsetsDirectional.fromSTEB here, for RTL support.
lib/widgets/home.dart
Outdated
| MaterialWidgetRoute(page: const ChooseAccountPage())); | ||
| }, | ||
| style: TextButton.styleFrom( | ||
| padding: const EdgeInsets.fromLTRB(8, 7, 14, 7), |
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.
(Same comment about EdgeInsetsDirectional.fromSTEB.)
lib/widgets/home.dart
Outdated
| ? RealmContentNetworkImage(realmIconUrl) | ||
| : const SizedBox.shrink()), |
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.
When the icon URL doesn't parse for whatever reason, and also when it does but the network request fails, how about we show an empty rounded square colored by DesignVariables.avatarPlaceholderBg? Maybe we could have a reusable RealmIconImage widget that takes care of that, much like AvatarImage takes care of choosing a placeholder (which it'll do in more cases with #1558).
lib/widgets/home.dart
Outdated
| /// The main-menu sheet. | ||
| /// | ||
| /// Figma link: | ||
| /// https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=11367-20647&t=lSnHudU6l7NWx0Fa-0 |
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 URL is taking me to the "Read receipts" sheet; do you reproduce? (Could be a Figma issue)
lib/widgets/home.dart
Outdated
| backgroundColor: Colors.transparent, | ||
| overlayColor: Colors.transparent, | ||
| splashFactory: NoSplash.splashFactory, | ||
| shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), |
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 border radius shouldn't make a difference, with a transparent background/overlay/etc., right? I see it in the Figma, but it looks accidental there, too; I think we can leave it out without comment.
4e5d5f9 to
0d41609
Compare
|
Thanks for the reviews @chrisbobbe, @alya. |
1dc1fb7 to
c9c67d6
Compare
|
Seems reasonable to me, thanks! Open to feedback from @terpimost if he has another suggestion. |
chrisbobbe
left a comment
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.
lib/model/store.dart
Outdated
| /// | ||
| /// This returns null if [reference] fails to parse as a URL. | ||
| Uri? tryResolveUrl(String reference) => _tryResolveUrl(realmUrl, reference); | ||
| Uri? tryResolveUrl(String reference) => _tryResolveUrlStr(realmUrl, reference); |
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.
store [nfc]: Rename _tryResolveUrl to _tryResolveUrlStr
flutter analyze is complaining to me at this commit:
Analyzing zulip-flutter...
info • Unnecessary 'this.' qualifier • lib/model/emoji.dart:229:25 • unnecessary_this
info • Unnecessary 'this.' qualifier • lib/model/emoji.dart:234:26 • unnecessary_this
2 issues found. (ran in 3.5s)
lib/model/store.dart
Outdated
| /// Like [Uri.resolve], but on failure return null instead of throwing. | ||
| Uri? tryResolveUrl(Uri baseUrl, String reference) { | ||
| Uri? tryResolveUrlStr(Uri baseUrl, String reference) { |
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 move this helper to a different file, like maybe lib/basic.dart? It's a convenience function encapsulating some control flow (a try/catch) and doesn't depend on details of a store.
Ditto the new, parallel helper that you're adding, which takes a Uri instead of String for reference.
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.
Or, alternatively, I think we could just make both of these helpers private.
test/widgets/content_test.dart
Outdated
| check(() => Uri.parse(expectedImages.single.srcUrl)).throws<void>(); | ||
| check(tryResolveUrl(eg.realmUrl, expectedImages.single.srcUrl)).isNull(); | ||
| check(tryResolveUrlStr(eg.realmUrl, expectedImages.single.srcUrl)).isNull(); |
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 rename brings attention to this existing check 🙂 which looks redundant with the one above it (.throws<void>()). I think we're free to remove it.
lib/widgets/home.dart
Outdated
|
|
||
| return Padding( | ||
| padding: const EdgeInsets.only(top: 6), | ||
| child: AnimatedScaleOnTap( |
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 this would look better without the AnimatedScaleOnTap effect. It looks like two things that are moving, rather than one thing that's resizing.
I think we should reserve this effect for buttons that are a rounded-rectangle surface:
Thoughts @alya?
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 touch feedback here, how about opacity? Per Vlad's suggestion at #mobile-design > all channels view design @ 💬:
we could use opacity by default to indicate touch if its not provided
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.
Agreed that the effect in that screen capture isn't working. Trying Vlad's suggestion sounds good to me.
lib/widgets/home.dart
Outdated
| child: Icon(ZulipIcons.arrow_left_right, | ||
| size: 19, | ||
| color: designVariables.icon)), |
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 wonder if this would be a good place to use ZulipIconButton.
lib/widgets/home.dart
Outdated
| ).merge(weightVariableTextStyle(context, wght: 600)))), | ||
| ]))), | ||
| Padding( | ||
| padding: EdgeInsetsDirectional.fromSTEB(8, 7, 14, 7), |
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 wonder if the end padding should be 12px, to match the padding before the org icon?
This 14px value comes from the Figma, but the Figma uses end-aligned text, and I think the 14px value might be tailored to that case. Also maybe the 7px vertical padding? Seems like it would be simpler, and logical, if we just wrapped the whole row with 12px horizontal padding and 6px vertical padding, and said spacing: 12 (or some other appropriate value) on the Row.
c9c67d6 to
f22180b
Compare
|
Thanks for the review @chrisbobbe! Pushed an update, PTAL. |
|
cc @alya Ah hmm, with the
Can we remove the 6px top and bottom padding on just the Also, seeing the doubled touch feedback (everything faded, and also a gray highlight appearing on —what if we forget about making the org name/icon tappable, and instead just use |
|
Hmm, yeah, that double feedback is odd. @terpimost might have opinions here, but I don't feel strongly about the whole row being a switcher. |
f22180b to
13981ab
Compare
|
Thanks for the review @chrisbobbe! Pushed an update, PTAL. |
chrisbobbe
left a comment
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! Just small comments below.
| } | ||
| } | ||
|
|
||
| class _MainMenuHeader extends StatefulWidget { |
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 be a StatelessWidget now, right?
lib/widgets/home.dart
Outdated
| return child; | ||
| }, | ||
| errorBuilder: (_, e, st) { | ||
| debugLog('$e\n$st'); // TODO(log) |
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.
Wrap this in an assert (see debugLog's dartdoc)
lib/widgets/home.dart
Outdated
| Tooltip( | ||
| message: zulipLocalizations.switchAccountButtonTooltip, | ||
| child: ZulipIconButton( | ||
| icon: ZulipIcons.arrow_left_right, | ||
| onPressed: () => _onPressed(context))), |
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.
Could we factor the tooltip text as a param on ZulipIconButton that just gets passed along to the IconButton in the implementation?
13981ab to
ca099a4
Compare
|
Thanks for the review @chrisbobbe! Pushed an update, PTAL. |
chrisbobbe
left a comment
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, LGTM! Marking for Greg's review.
lib/widgets/home.dart
Outdated
| icon: ZulipIcons.arrow_left_right, | ||
| onPressed: () => _onPressed(context), | ||
| tooltip: zulipLocalizations.switchAccountButtonTooltip), |
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 put icon and tooltip next to each other, because they're both communicating the button's meaning
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.
Ah makes sense, I was trying to match the parameter ordering. Pushed a revision with this addressed.
ca099a4 to
2617eba
Compare
gnprice
left a comment
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 @rajveermalviya for building this, and thanks @chrisbobbe for the previous reviews!
Comments below. I'm not quite done reading the last commit (and haven't yet looked at the UI); but about to go AFK for now, so here's what I have so far.
lib/model/store.dart
Outdated
| // The `account` is populated with the `realmName` before | ||
| // PerAccountStore is created, so this should never be null. | ||
| // See `UpdateMachine.load`. | ||
| String get realmName => account.realmName!; |
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 reasoning makes sense.
It's kind of non-local, though: the reader has to go look at that faraway piece of code (700 lines later in this file, plus it probably ultimately belongs in a separate file), and then also has to wonder if there might be any other code path that leads to instantiating one of these without going through there.
(And indeed there are such code paths in tests. So if any of those could use an Account where one of these fields is null, then that could lead to puzzling behavior in a test somewhere.)
To mitigate that, how about putting an assertion to this effect up at the constructor? Then
- this comment can go at that assertion;
- the assertion helps give confidence that, whether or not the comment's reasoning is airtight, the invariant does hold in practice;
- here at this line, the reader only has to consult the constructor a few lines up, rather than think about anything wider.
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.
Or better: put the assertions at the top of PerAccountStore.fromInitialSnapshot, to go alongside the similar assertions we already have there.
(That's a little farther away from where we use the invariant here; but we already have other getters here relying on similar invariants checked there.)
lib/model/store.dart
Outdated
| /// Like [Uri.resolve], but on failure return null instead of throwing. | ||
| Uri? _tryResolveUrl(Uri baseUrl, Uri reference) { | ||
| try { | ||
| return baseUrl.resolveUri(reference); |
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.
| /// Like [Uri.resolve], but on failure return null instead of throwing. | |
| Uri? _tryResolveUrl(Uri baseUrl, Uri reference) { | |
| try { | |
| return baseUrl.resolveUri(reference); | |
| /// Like [Uri.resolveUri], but on failure return null instead of throwing. | |
| Uri? _tryResolveUrl(Uri baseUrl, Uri reference) { | |
| try { | |
| return baseUrl.resolveUri(reference); |
right?
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.
Also: can [Uri.resolveUri] actually throw?
Skimming the implementation, I think it can't (short of a bug in the implementation) — there's no explicit mention of throwing, and nothing that looks like it's meant to sometimes throw. And semantically, I think if the two URLs in question have both parsed successfully, I'd expect resolving one upon the other to always produce a result.
lib/model/store.dart
Outdated
| /// Resolve [realmIcon] as a URL relative to [realmUrl]. | ||
| /// | ||
| /// This returns null if resolving fails. | ||
| Uri? resolveRealmIconUrl() => _tryResolveUrl(realmUrl, realmIcon); |
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.
Seems incongruous to drop the "try" here, as well as to have no "try" in this name while the very similar neighboring method does say "try".
But see my other comment — I think the fix is probably to make this non-nullable.
lib/model/store.dart
Outdated
| // The `account` is populated with the `realmIcon` before | ||
| // PerAccountStore is created, so this should never be null. | ||
| // See `UpdateMachine.load`. | ||
| Uri get realmIcon => account.realmIcon!; |
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.
Do we ever want to call this getter without resolving the result? Probably not, right?
So that makes this getter a trap for the unwary, when exposed publicly. If we did need the getter in some places, we could address the trap by writing some dartdoc that very clearly says to consider using [resolveRealmIconUrl] instead. But since I think the latter is probably the only call site we actually want… we can do better still by just inlining this into there.
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.
bump
| /// The main-menu sheet. | ||
| /// | ||
| /// Figma link: | ||
| /// https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=143-10939&t=s7AS3nEgNgjyqHck-4 | ||
| class _MainMenu extends StatelessWidget { |
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.
Good idea splitting this out as a widget. I've been thinking we should do that more often for our various showFoo functions — in particular so as to make Flutter's hot reload work for the things they show.
Let's make that split be its own NFC prep commit. That will simplify the diff somewhat for the main commit.
lib/widgets/home.dart
Outdated
| AvatarShape( | ||
| size: 28, | ||
| borderRadius: 4, | ||
| child: logo), |
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: let's minimize the number of different names for a given concept:
| child: logo), | |
| child: realmIcon), |
That way it's clearer to someone looking at this line what this is showing, without having to look around at the rest of this build method's logic.
lib/widgets/home.dart
Outdated
| class _MainMenuHeader extends StatelessWidget { | ||
| const _MainMenuHeader(); | ||
|
|
||
| void _onPressed(BuildContext context) { |
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:
| void _onPressed(BuildContext context) { | |
| void _handleSwitchAccount(BuildContext context) { |
Or some other specific name. When it's named just "on pressed", that sounds like it means something that gets called when this whole widget is pressed.
lib/widgets/home.dart
Outdated
| return child; | ||
| }, | ||
| errorBuilder: (_, e, st) { | ||
| assert(debugLog('$e\n$st')); // TODO(log) |
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 this isn't a TODO-log situation — this gets called if the app can't reach the server to fetch the image, and that's probably just the device not having network at the moment, so it's not something we'd want to send to a system like Sentry.
lib/widgets/home.dart
Outdated
| final placeholder = ColoredBox(color: designVariables.avatarPlaceholderBg); | ||
| final logo = realmIconUrl != null | ||
| ? RealmContentNetworkImage( | ||
| realmIconUrl, | ||
| frameBuilder: (_, child, frame, _) { | ||
| if (frame == null) return placeholder; | ||
| return child; | ||
| }, |
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.
Hmm interesting. What happens if we leave out frameBuilder? Is this piece of logic something we should be putting in AvatarImage?
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.
With the above frameBuilder logic, the placeholder will be displayed while the image is being downloaded, and then be replaced with the downloaded image. Without frameBuilder, the image will pop in when it has completed downloading (though without any funky layout changes as we control the sizing via AvatarShape here).
It could be an improvement for AvatarImage 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.
What's the difference between showing the placeholder until the download completes, and having the image pop in when it has completed downloading?
I.e., how does the placeholder look different from what we show without it?
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.
Without placeholder the image pops into the whitespace, see:
no-placeholder.mp4
placeholder.mp4
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, thanks.
It's not clear to me that that's an improvement. If the images take a second to appear, I feel like having a gray box there, then a transition from that to the icon, is likely to be more conspicuous than a blank space.
Let's leave that logic out from this PR. Similarly errorBuilder — if the icon doesn't load, let's leave its space blank the same way we do for other comparable images like avatars.
lib/widgets/home.dart
Outdated
| assert(debugLog('$e\n$st')); // TODO(log) | ||
| return placeholder; | ||
| }) | ||
| : placeholder; |
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: put a short fallback like this right next to the error/exception condition that causes it
ea2d43d to
0444640
Compare
|
Thanks for the review @gnprice! Pushed an update, PTAL. |
gnprice
left a comment
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 for the revision! Here's comments just on the parts I previously reviewed.
lib/model/store.dart
Outdated
| // The `account` is populated with the `realmIcon` before | ||
| // PerAccountStore is created, so this should never be null. | ||
| // See `UpdateMachine.load`. | ||
| Uri get realmIcon => account.realmIcon!; |
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.
bump
lib/widgets/home.dart
Outdated
| final placeholder = ColoredBox(color: designVariables.avatarPlaceholderBg); | ||
| final logo = realmIconUrl != null | ||
| ? RealmContentNetworkImage( | ||
| realmIconUrl, | ||
| frameBuilder: (_, child, frame, _) { | ||
| if (frame == null) return placeholder; | ||
| return child; | ||
| }, |
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 difference between showing the placeholder until the download completes, and having the image pop in when it has completed downloading?
I.e., how does the placeholder look different from what we show without it?
test/widgets/home_test.dart
Outdated
| debugNetworkImageHttpClientProvider = null; | ||
| }); | ||
|
|
||
| testWidgets('_MainMenuHeader', (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: put before the buttons, since the header appears before the buttons in the code (and also in the UI)
|
Playing with this a bit, it feels like something of a UX regression to have "switch account" become such a small touch target. That feels like it risks exacerbating the annoyance we've heard from a number of users about it being more annoying than it should to switch between accounts (e.g. #1162). How about if we make the whole header be the touch target for that? It looks like we changed that in a previous revision because the touch feedback didn't look right (#1937 (comment)); but it should be possible to give it different touch feedback. |
0444640 to
6460549
Compare
|
I could imagine using the "minimal neutral button small" from the Figma, which our https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=6092-50794&m=dev
|
|
Hm, I don't love that -- I feel like it's drawing attention to an action that for many users wouldn't be super common. We could just label is as "Switch", but I'd prefer to explore @gnprice 's suggestion to make the whole header be the touch target. |
6460549 to
d9beb1a
Compare
|
Thanks for the review @gnprice! Pushed an update, PTAL. Also current revision now makes the whole header a touch target. See: switch-account.mp4cc @alya @chrisbobbe |
gnprice
left a comment
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 for the revision! Just one comment below. I've now read the remaining parts of the commits, too.
lib/widgets/home.dart
Outdated
| final placeholder = ColoredBox(color: designVariables.avatarPlaceholderBg); | ||
| final logo = realmIconUrl != null | ||
| ? RealmContentNetworkImage( | ||
| realmIconUrl, | ||
| frameBuilder: (_, child, frame, _) { | ||
| if (frame == null) return placeholder; | ||
| return child; | ||
| }, |
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, thanks.
It's not clear to me that that's an improvement. If the images take a second to appear, I feel like having a gray box there, then a transition from that to the icon, is likely to be more conspicuous than a blank space.
Let's leave that logic out from this PR. Similarly errorBuilder — if the icon doesn't load, let's leave its space blank the same way we do for other comparable images like avatars.
d9beb1a to
8abf61c
Compare
|
Thanks for the review @gnprice! Pushed an update, PTAL. |
Additionally, add a bit of dartdoc for `realmName` and `realmIcon`.
We almost always need the full resolved realm icon URL, so provide a getter that does that.
|
Thanks! Looks good; merging. |
8abf61c to
603edcd
Compare









Figma: https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=2940-48987&p=f&m=dev
Fixes: #1037