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

Redesigned stream/topic recipient headers #416

Merged
merged 10 commits into from
Nov 29, 2023

Conversation

sirpengi
Copy link
Contributor

@sirpengi sirpengi commented Nov 24, 2023

Design from the following two sources:

This PR updates the stream/topic recipient headers to the new design and a PR to #373 will follow this up very shortly. The style of dates were updated, but the format of the text itself is still left for #411

This fixes #372 and fixes #220.
I made an attempt to tackle #282 using combinations of Row/Expanded/Flexible but wasn't able to succeed. I suspect we will need to implement some custom layout code to handle that.

Simulator Screenshot - iPhone 14 - 2023-11-24 at 18 16 28

Simulator Screenshot - iPhone 14 - 2023-11-24 at 18 19 59

@sirpengi
Copy link
Contributor Author

@gnprice ready for review!

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 @sirpengi! Generally this looks good. Comments below, mostly small.

@@ -46,13 +47,30 @@ class MessageListPage extends StatefulWidget {
State<MessageListPage> createState() => _MessageListPageState();
}

final _kFallbackStreamColorSwatch = StreamColorSwatch(0xfff6f6f6);
Copy link
Member

Choose a reason for hiding this comment

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

In the screenshot for "All messages", the app bar looks quite a bit darker than it does in the Figma design. Can you describe where this #f6f6f6 color came from?

Should it perhaps be Color(0xfff6f6f6) itself, rather than the StreamColorSwatch.barBackground that we get from treating that color as if it were a stream color?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I traced back my usage of #f6f6f6 from https://www.figma.com/file/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=132%3A9617&mode=dev but a lot of other screens show #f5f5f5 (https://www.figma.com/file/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=538%3A20850&mode=dev which I believe are more up to date) so I'll make that small tweak.

Although bigger picture is, yes, this should be the end color, and not the swatch that gets passed in! I'll update so we get the right fallback colors.

final message = this.message;
return switch (message) {
StreamMessage() => StreamTopicRecipientHeader(message: message),
StreamMessage() => StreamMessageRecipientHeader(message: message, showStream: narrow is AllMessagesNarrow),
Copy link
Member

Choose a reason for hiding this comment

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

nit: break line

class StreamTopicRecipientHeader extends StatelessWidget {
const StreamTopicRecipientHeader({super.key, required this.message});
class StreamMessageRecipientHeader extends StatelessWidget {
const StreamMessageRecipientHeader({super.key, required this.message, required this.showStream});
Copy link
Member

Choose a reason for hiding this comment

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

nit: break line

height: (18 / 16),
).merge(weightVariableTextStyle(context, wght: 600, wghtIfPlatformRequestsBold: 900));

final streamWidget = (showStream)
Copy link
Member

Choose a reason for hiding this comment

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

nit: redundant parens

Suggested change
final streamWidget = (showStream)
final streamWidget = showStream

(stream != null) ? iconDataForStream(stream) : null)),
Padding(
padding: const EdgeInsets.symmetric(vertical: 11),
child: Text(stream?.name ?? message.displayRecipient, // TODO(log) if missing
Copy link
Member

Choose a reason for hiding this comment

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

This gets a bit crowded. Imagine in particular what it'll look like after we fulfill that TODO(log) — we'll then definitely want a bit of explicit statement-level control flow. But I think even at this point, forcing all of this inside an expression is already a bit crowded.

Instead of using the ?: operator, this can look like:

  final Widget streamWidget;
  if (!showStream) {
    streamWidget = SizedBox(width: 16);
  } else {
    // … now there's room for local variables here, and conditionals, then …
    streamWidget = GestureDetector(
      // …

That way there can be a streamName local, just as there is in main.

Comment on lines 572 to 573
// Figma shows 5px spacing but 6px here matches better visually.
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 11),
Copy link
Member

Choose a reason for hiding this comment

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

Hmm interesting. What does it make a better match with — does it better match the result to what you see in Figma, or match this to another part of the design, or something else?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Adjusted the comment; it matches what we see in Figma more.

Copy link
Member

Choose a reason for hiding this comment

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

Curious. I wonder what the discrepancy is, then — in principle if we match the numbers in the Figma spec, we should get the same visual results as the Figma preview.

Given the Figma bug we just discovered about opacity, it wouldn't be shocking if this was a bug in Figma where its preview didn't interpret its spec correctly. But it could also be a bug in our translation of the spec.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

On further examination here it looks like the icon is given a size of 18px but the icon itself has width/height of 16px here (there is 1px of space inside the icon!) and so there are 6 pixels of whitespace from the left border:
figma_stream_icon

Another result of this examination is I adjusted the icon size to be 16px instead.

Copy link
Member

Choose a reason for hiding this comment

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

Hmm I see — and our hash_sign.svg has the tips of the strokes actually touching the edge of the canvas:
Screenshot from 2023-11-28 15-57-18

so it corresponds to that 16x16 area in the middle. That's the discrepancy, then — glad to have an explanation for it. @terpimost FYI.

onTap: () => Navigator.push(context,
MessageListPage.buildRoute(context: context,
narrow: StreamNarrow(message.streamId))),
child: Row(children: [
Copy link
Member

Choose a reason for hiding this comment

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

This needs CrossAxisAlignment.baseline, just like the other Row below. Otherwise the vertical alignment gets messed up — it's most conspicuous if you crank up your text size in system settings:
image

// A null [Icon.icon] makes a blank space.
(stream != null) ? iconDataForStream(stream) : null)),
Padding(
padding: const EdgeInsets.symmetric(vertical: 11),
Copy link
Member

Choose a reason for hiding this comment

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

What's the origin of the number 11 for vertical padding? I'm not finding that in the Figma design.

Also, it seems like it'd be simpler to apply the 11px vertical padding to the entire Row. Is there a reason I'm missing why that wouldn't have the right effect?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is reverse engineered from a 40px height given the icon height (18p) for this element, and was also applied to the text widget that has 18px line height. The chevron has 16 height and thus given vertical: 12. I don't think it's clear that wrapping the whole row is an improvement as the chevron would need padding as well.

I'll take a closer look at the behavior of cranking up text size in the system settings. In your screenshot it seems that this doesn't affect icon sizes, in which case it might make sense to apply a uniform padding on text elements and vertically center the icons another way.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After our call today I've settled on applying a CrossAxisAlignment.center to all the row elements and applying a vertical: 11 padding to the text elements. I've changed the date line-height to match the text elements so they all visually appear to have the same baseline and let the icons settle on the center of the vertical (having also had to tweak the stream icon a bit).
The text elements maintain their alignment even if the system requests they be rendered larger, and the icons don't look terrible where they are placed:
screen_enlarged_text

Copy link
Member

Choose a reason for hiding this comment

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

Cool, that solution sounds good to me.

If Flutter's Row accepted an option like CrossAxisAlignment.baselineCenter, so that it aligned to the baseline but fell back to center instead of to start, then that would be cleaner because it'd let us get the same effect as you have there but without having to mess with the line-height. But alas.

Alternatively if there were a widget that were akin to IgnoreBaseline but it moved the baseline around, rather than erase it, then we could use CrossAxisAlignment.baseline and use that widget to adjust where the icons sit vertically. That'd be a slightly different result, which might be better or worse. If that need comes up again, I may write such a widget — it'll require writing a render object, but shouldn't be a lot of code.

padding: const EdgeInsets.symmetric(vertical: 11),
child: Text(stream?.name ?? message.displayRecipient, // TODO(log) if missing
style: textStyle,
maxLines: 1,
Copy link
Member

Choose a reason for hiding this comment

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

Does this maxLines line do anything? It seems like it doesn't.

Comment on lines 264 to 266
final stream = eg.stream(isWebPublic: false, inviteOnly: false);
await setupMessageListPage(tester,
messages: [message], streams: [stream]);
Copy link
Member

Choose a reason for hiding this comment

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

This test is fragile because it relies on message being to the stream stream, and that's true only by accident. (It's true only because eg.stream currently uses a constant stream ID every time by default, whereas we ought to instead make it generate a distinct random ID every time.)

Can instead write messages: [eg.streamMessage(stream: stream)].

@sirpengi sirpengi force-pushed the pr-new-stream-headers branch 3 times, most recently from cd65a2b to c32e5ff Compare November 28, 2023 23:19
@sirpengi
Copy link
Contributor Author

@gnprice Ready for review again!

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 @sirpengi for the revision! This all now looks good. Just a couple of small comments.

Comment on lines 635 to 638
// TODO: Give a way to see the whole topic (maybe a
// long-press interaction?)
style: textStyle,
overflow: TextOverflow.ellipsis))),
Copy link
Member

Choose a reason for hiding this comment

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

This comment is about the overflow parameter, so swapping style and overflow separates the comment from what it's about.

There's no change to the overflow value, so can instead just leave it in place and only touch the style line.

Comment on lines +704 to +708
// In Figma this has a line-height of 19, but using 18
// here to match the stream/topic text widgets helps
// to align all the text to the same baseline.
height: (18 / 16),
Copy link
Member

Choose a reason for hiding this comment

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

This lgtm; I believe the line-height doesn't matter here (on the datestamp) except as a means to get it properly vertically aligned, and this seems like a good way to do that. @terpimost FYI.

// A null [Icon.icon] makes a blank space.
(stream != null) ? iconDataForStream(stream) : null)),
Padding(
padding: const EdgeInsets.symmetric(vertical: 11),
Copy link
Member

Choose a reason for hiding this comment

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

Cool, that solution sounds good to me.

If Flutter's Row accepted an option like CrossAxisAlignment.baselineCenter, so that it aligned to the baseline but fell back to center instead of to start, then that would be cleaner because it'd let us get the same effect as you have there but without having to mess with the line-height. But alas.

Alternatively if there were a widget that were akin to IgnoreBaseline but it moved the baseline around, rather than erase it, then we could use CrossAxisAlignment.baseline and use that widget to adjust where the icons sit vertically. That'd be a slightly different result, which might be better or worse. If that need comes up again, I may write such a widget — it'll require writing a render object, but shouldn't be a lot of code.

Comment on lines 572 to 573
// Figma shows 5px spacing but 6px here matches better visually.
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 11),
Copy link
Member

Choose a reason for hiding this comment

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

Hmm I see — and our hash_sign.svg has the tips of the strokes actually touching the edge of the canvas:
Screenshot from 2023-11-28 15-57-18

so it corresponds to that 16x16 area in the middle. That's the discrepancy, then — glad to have an explanation for it. @terpimost FYI.

@sirpengi sirpengi force-pushed the pr-new-stream-headers branch from c32e5ff to 5464033 Compare November 29, 2023 09:50
@sirpengi
Copy link
Contributor Author

@gnprice ready again!

First in a series of changes to update stream/topic
recipient headers. Stream name is now only displayed
in `AllMessagesNarrow` and clicking this name navigates
to the stream. Renamed `StreamTopicRecipientHeader`
to `StreamMessageRecipientHeader` to reflect this
conceptual change. Also adjusted existing test for
unknown streams to explicitly use `AllMessagesNarrow`.

Added doc links to design specs and upcoming
commits will incrementally bring these headers closer to
the desired design.
Design specifies 16px horizontal padding in recipient headers,
see zulip/zulip-mobile#5511 .
Also adjusted message timestamps so they line up with the
new headers.
Also prepares header dates to receive a color from its
parent so it can be changed depending on context.
Switch from colored background with chevron shaped border
to inline chevron icon.
@sirpengi sirpengi force-pushed the pr-new-stream-headers branch from 5464033 to b9df04e Compare November 29, 2023 15:27
@gnprice
Copy link
Member

gnprice commented Nov 29, 2023

Thanks for the revision!

Merging, with one tweak to make the comment about the icon spacing reflect the explanation we found in #416 (comment) :

-              // Figma specifies 5px horizontal spacing but 6px matches
-              // more accurately what we see in Figma.
+              // Figma specifies 5px horizontal spacing around an icon that's
+              // 18x18 and includes 1px padding.  The icon SVG is flush with
+              // the edges, so make it 16x16 with 6px horizontal padding.

@gnprice gnprice force-pushed the pr-new-stream-headers branch from b9df04e to 32ce11a Compare November 29, 2023 19:41
@gnprice gnprice merged commit 32ce11a into zulip:main Nov 29, 2023
@gnprice
Copy link
Member

gnprice commented Nov 29, 2023

(Also tweaked the PR description so it's marked as closing #220.)

@sirpengi sirpengi deleted the pr-new-stream-headers branch January 23, 2024 14:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

New-style stream/topic recipient bars Show stream privacy icons in recipient headers
2 participants