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

RecentDmConversationsPage: Add #249

Merged
merged 11 commits into from
Aug 10, 2023

Conversation

chrisbobbe
Copy link
Collaborator

Fixes: #119


This includes my current revisions of #246, #247, and #248.

@chrisbobbe chrisbobbe requested a review from gnprice August 2, 2023 21:58
@chrisbobbe
Copy link
Collaborator Author

To add to the exclusions mentioned in #119 (e.g., that we don't show presence data), I should mention that this also doesn't yet show the number of unreads in each conversation, since we don't have a data structure for that yet. 🙂

@chrisbobbe chrisbobbe force-pushed the pr-recent-dm-conversations-ui branch from f02d4e3 to 257af55 Compare August 2, 2023 23:41
@chrisbobbe
Copy link
Collaborator Author

Just updated with a revision that's meant to follow Vlad's design for the new screen's content area (so, excluding the app bar, for now), instead of Material Design.

@chrisbobbe chrisbobbe force-pushed the pr-recent-dm-conversations-ui branch from 257af55 to bc361d8 Compare August 3, 2023 00:07
@chrisbobbe
Copy link
Collaborator Author

chrisbobbe commented Aug 3, 2023

(And another update with some small tweaks.)

It's meant to follow Vlad's Figma, so tagging @terpimost; feedback is appreciated. 🙂

Design things excluded here, to be done later:

  • The top app bar (which will take more work with navigation, etc.)
  • The bottom nav bar
  • The interactive "Message" box at the bottom
  • Users' presence
  • Conversation unread counts

Screenshot from the "Zulip test device" account:

EDIT: An earlier version of this PR and the screenshot had some of the users' avatars missing.

@gnprice
Copy link
Member

gnprice commented Aug 3, 2023

Design things excluded here, to be done later:

Just to expand on this part: the reason this version leaves out the design's top and bottom app bars and "Message" box is because the various UI elements there lead to a variety of screens that aren't yet implemented. When we've implemented more of the screens, we can circle back and build a good way to navigate among them.

And the reason it leaves out the presence indicators, unread counts, and some of the avatars, is because of various features not yet implemented on the data side. That's

@chrisbobbe chrisbobbe force-pushed the pr-recent-dm-conversations-ui branch from bc361d8 to 4b5889c Compare August 3, 2023 01:19
@chrisbobbe
Copy link
Collaborator Author

(Updated to fill in the missing avatars.)

@chrisbobbe
Copy link
Collaborator Author

New screenshot from this same PR revision (ending in 4b5889c), this time with:

  • A conversation with many names, so they wrap
  • A conversation with even more names, so they wrap and get ellipsized ("…")

(These are real examples from CZO; both conversations include two users with the same name, "Chris Bobbe (Test Account)".)

@chrisbobbe chrisbobbe force-pushed the pr-recent-dm-conversations-ui branch from 4b5889c to caeca7e Compare August 3, 2023 20:57
@chrisbobbe
Copy link
Collaborator Author

Updated now that #246 is merged, and incorporating my updated revision of #247.

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 building this! Comments below.

Comment on lines 14 to 18
'client_gravatar': false, // TODO turn on
'client_capabilities': {
'notification_settings_null': true,
'bulk_message_deletion': true,
'user_avatar_url_field_optional': true,
'user_avatar_url_field_optional': false, // TODO turn on
Copy link
Member

Choose a reason for hiding this comment

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

Let's include references to the issues (that I filed yesterday) in these TODO comments.

final user = store.users[userId]!;

final resolvedUrl = switch (user.avatarUrl) {
null => null, // TODO handle computing gravatars
Copy link
Member

Choose a reason for hiding this comment

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

Can also add an issue reference to this comment, while we're here.

Comment on lines 817 to 819
/// To set the size and clip the corners to be round, pass [size].
/// If [size] is not passed, the caller takes responsibility
/// for doing that with its own square wrapper.
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 caller that's leaving out size is doing the same thing as this would, except with a different border radius. I think it's probably cleanest for this widget to keep responsibility for applying a size and shape, and just add a double borderRadius field that callers have to specify.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sure, I can do that.

On the other hand, that'll mean reciting the same size and border radius again for the group-DM icon/avatar, so there are two places to update if we want to change them. Or maybe I can put them in variables. 🤔

Comment on lines 46 to 49
final recipientsSansSelf = allRecipientIds
.whereNot((id) => id == selfUser.userId)
.map((id) => store.users[id]!)
.toList();
Copy link
Member

Choose a reason for hiding this comment

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

This is [DmNarrow.otherRecipientIds], right?

Ah, plus I guess it's mapping the store.users lookup. But better to push that down inside the cases, I think — that way we don't need to allocate a list in the 1:1 case.

Comment on lines 57 to 58
break;
}
Copy link
Member

Choose a reason for hiding this comment

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

Comment on lines 59 to 60
case 1: {
final otherUser = recipientsSansSelf.single;
Copy link
Member

Choose a reason for hiding this comment

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

Can switch on recipientsSansSelf itself rather than its length, and then this can say

      case [var otherUser]:

(Or combined with my other comment above, it'd say var otherUserId and then look that up in store.users.)

final selfUser = store.users[store.account.userId]!;
final recipientsSansSelf = allRecipientIds
.whereNot((id) => id == selfUser.userId)
.map((id) => store.users[id]!)
Copy link
Member

Choose a reason for hiding this comment

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

This ! — so that we crash if a user appears which we don't know about — reflects how we'd write this sort of logic in zulip-mobile, but I'd prefer to soften these failures while we're rewriting things here.

In particular since we're planning a product feature where guests (for example) might not know about all the users in the realm, it'll start becoming a lot more likely that the server could have a bug, or even perhaps an edge case that's hard to eliminate even when we fully understand it, where we end up with a user in a list like this that isn't a user we know about. We'll want to try to avoid those situations, but it'd be good to avoid crashing in them.

See [MessageListAppBarTitle] for an example of handling that. Basically we'll end up with things like ?? '(unknown user)' sprinkled around.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah, okay.

Comment on lines 89 to 98
child: DefaultTextStyle(
style: const TextStyle(
fontFamily: 'Source Sans 3',
fontSize: 17,
height: (20 / 17),
color: Color(0xFF222222),
).merge(weightVariableTextStyle(context)),
maxLines: 2,
overflow: TextOverflow.ellipsis,
child: title),
Copy link
Member

Choose a reason for hiding this comment

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

This title widget is always a Text, and so the DefaultTextStyle is just applying to that, right?

In that case it seems more straightforward to just have the Text widget here. The switch above can compute a final String title instead of a Widget.

child: title),
)),
const SizedBox(width: 8),
// TODO: Unread count
Copy link
Member

Choose a reason for hiding this comment

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

can include the issue reference

});
}

Widget _buildItem(BuildContext context, DmNarrow narrow) {
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 it's probably a bit more idiomatic to break this out as its own widget.

There's some discussion of this in the StatelessWidget docs:
https://api.flutter.dev/flutter/widgets/StatelessWidget-class.html
complete with a video "Widgets vs Helper Methods". I think the reasons there why one sometimes really strongly wants separate widgets don't apply here, but it's still an indication of the direction in which to lean.

@chrisbobbe chrisbobbe force-pushed the pr-recent-dm-conversations-ui branch from caeca7e to 4de84ae Compare August 4, 2023 00:21
@chrisbobbe
Copy link
Collaborator Author

Thanks for the review! Revision pushed.

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! Comments below.

Let's also get some unit tests for this before merge. Partly that's useful because, as we learned at #219 (comment) , having some widget tests for a screen can let us know when we introduce something that causes Flex overflows or other layout errors on that screen.

Some good things to test would be:

  • for each of a self-1:1, another 1:1, and a group thread, test the page builds, that it has the relevant users' names, and that tapping the name navigates to the message list
  • have a group thread where names are long, and check the height is right and it's ellipsized (or if ellipsized is hard to check, then just make the names super long so that if it got the right height it must have cut off the text somehow, and didn't throw)
  • have a 1:1 thread and a group thread with unknown users, and check the phrase "unknown user" appears but mostly that it didn't throw
  • have 30 items in the list so it can't all fit on screen (and check the bottom item doesn't exist, just to make sure); drag to fling-scroll; check the bottom item is now visible

Comment on lines 57 to 58
final _avatarSize = 32.0;
final _avatarBorderRadius = 3.0;
Copy link
Member

Choose a reason for hiding this comment

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

Because these are the same for every widget of this type, let's make them static and const.

// TODO(i18n): List formatting, like you can do in JavaScript:
// new Intl.ListFormat('ja').format(['Chris', 'Greg', 'Alya'])
// // 'Chris、Greg、Alya'
title = narrow.otherRecipientIds.map((id) => store.users[id]!.fullName).join(', ');
Copy link
Member

Choose a reason for hiding this comment

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

another user-!

Comment on lines 82 to 86
avatar = SizedBox.square(
dimension: _avatarSize,
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(_avatarBorderRadius)),
clipBehavior: Clip.antiAlias,
Copy link
Member

Choose a reason for hiding this comment

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

Hmm yeah, I see, this is a bit annoying.

Still I think this is better than having Avatar sometimes take responsibility for its shape, and sometimes not. That feels like having that widget live at two different conceptual layers depending on the arguments it's constructed with.

I think the Flutter-upstream-style solution here would probably be to decompose Avatar into two widgets:

  • one named like AvatarImage that just builds the RealmContentNetworkImage (consulting the store in order to do so);
  • one named like AvatarShape that is very much like the _AvatarWrapper in your previous revision, but taking size and borderRadius as parameters;
  • and then also perhaps one named Avatar that is just a convenience composing those two.

Then this switch would compute a Widget avatar that's either an AvatarImage or an Icon, and then in a single spot afterward this build method would enclose that in an AvatarShape.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That makes sense! I can go for that in my next revision.

Comment on lines 104 to 105
child: Text(
title,
Copy link
Member

Choose a reason for hiding this comment

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

nit: I think the natural analogue of putting a widget's child last is to put a Text widget's positional String argument last.

This only became possible with a relatively recent version of Dart, circa 2022, so there may be a lot of code even in the Flutter tree that doesn't. But I think it's the natural thing to do now that one can.

super.key,
required this.userId,
required this.size,
this.borderRadius = 4, // TODO vary default value with [size]?
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 probably actually cleanest to make this argument required too. That way the value chosen for it is always evident in the source right next to the chosen size.

super.key,
required this.userId,
required this.size,
this.borderRadius = 4, // TODO vary default value with [size]?
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 probably actually cleanest to make this argument required too. That way the value chosen for it is always evident in the source right next to the chosen size.

chrisbobbe added a commit to chrisbobbe/zulip-flutter that referenced this pull request Aug 8, 2023
chrisbobbe added a commit to chrisbobbe/zulip-flutter that referenced this pull request Aug 8, 2023
@chrisbobbe chrisbobbe force-pushed the pr-recent-dm-conversations-ui branch from 4de84ae to c79cd7c Compare August 8, 2023 20:58
@chrisbobbe
Copy link
Collaborator Author

chrisbobbe commented Aug 8, 2023

Thanks for the review! Revision pushed, with a first stab at all those unit tests you mentioned.

Some things to flag as maybe non-optimal:

  • Setting the test device size doesn't seem to do anything when running with flutter run vs. flutter test: Inconsistent sizes reported when running a test with flutter run vs flutter test flutter/flutter#124071 , so with flutter run some tests fail. This problem also affects existing tests in test/widgets/message_list_test.dart.
  • Possible over-reliance on implementation details, like checking specifically for RecentDmConversationsItem, AvatarShape, AvatarImage, and Text. (?)
  • To check for correct navigation on tapping an item, I changed application code, to add an identifying RouteSettings to the message-list route:
    class MessageListPage extends StatefulWidget {
      const MessageListPage({super.key, required this.narrow});
    
      static Route<void> buildRoute({required BuildContext context, required Narrow narrow}) {
        return MaterialAccountPageRoute(context: context,
          settings: RouteSettings(name: 'message_list', arguments: narrow), // for testing
          builder: (context) => MessageListPage(narrow: narrow));
      }
  • As discussed, I didn't find an ideal way to check truncation of the items' titles, for example by checking for the position of a "…" character. Here, I just make the title super-long and assume that it must have been truncated properly if the text doesn't exceed two lines.

chrisbobbe added a commit to chrisbobbe/zulip-flutter that referenced this pull request Aug 9, 2023
chrisbobbe added a commit to chrisbobbe/zulip-flutter that referenced this pull request Aug 9, 2023
@chrisbobbe chrisbobbe force-pushed the pr-recent-dm-conversations-ui branch from c79cd7c to 0b71040 Compare August 9, 2023 19:45
@chrisbobbe
Copy link
Collaborator Author

(Just rebased.)

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 looks great; a few comments below.

  • Setting the test device size doesn't seem to do anything when running with flutter run vs. flutter test:

(See reply in an inline comment below.)

  • Possible over-reliance on implementation details, like checking specifically for RecentDmConversationsItem, AvatarShape, AvatarImage, and Text. (?)

I think these are fine, except the inspection of Text can be made more flexible using find.text instead as described in a comment below.

In particular RecentDmConversationsItem reflects the fact that this screen fundamentally is going to be some kind of list — its job is to show a list of the recent DM conversations — and so we're naturally going to have some kind of widget for an item in the list. The name of that widget type, or its exact interface, could change, so there's a bit of exposure here to having to change the tests in the event of a refactor, but I don't think it's too bad.

  • To check for correct navigation on tapping an item, I changed application code, to add an identifying RouteSettings to the message-list route:

Yeah, I think editing that application code (to make the target page more inspectable, where currently it's expressed only in the form of a builder function) is quite reasonable. I don't love the details of the RouteSettings solution, but can experiment with alternatives post-merge as discussed below.

  • As discussed, I didn't find an ideal way to check truncation of the items' titles, for example by checking for the position of a "…" character. Here, I just make the title super-long and assume that it must have been truncated properly if the text doesn't exceed two lines.

👍

Comment on lines 855 to 856
if (user == null) {
// TODO(log) log?
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
if (user == null) {
// TODO(log) log?
if (user == null) { // TODO(log)

Makes it a bit easier to see what the TODO is about when doing a grep.

Comment on lines 18 to 20
import 'content_checks.dart';


Future<void> setupPage(WidgetTester tester, {
Copy link
Member

Choose a reason for hiding this comment

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

nit:

Suggested change
import 'content_checks.dart';
Future<void> setupPage(WidgetTester tester, {
import 'content_checks.dart';
Future<void> setupPage(WidgetTester tester, {

super.key,
required this.userId,
required this.size,
required this.borderRadius,
Copy link
Member

Choose a reason for hiding this comment

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

The required borderRadius can be squashed into the "Pull out Avatar widget" commit, resolving the TODO in the latter.

Comment on lines +123 to +118
check(shape)
..size.equals(32)
..borderRadius.equals(3);
Copy link
Member

Choose a reason for hiding this comment

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

I'm lukewarm on checking this level of detail of the layout. It's the sort of test that's liable to end up acting just as another place we have to update when we make changes.

But because it's nicely centralized in this test helper, it is at least just one additional place to update, so it's a lot cheaper than the situation people often get into where tons of tests need to be updated for a small change in the code under test. So it's fine.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Makes sense. I'm not attached to it; I just saw it as an opportunity to be thorough. 🙂 I see what you mean about how this could get annoying if it weren't centralized in this helper.

await setupPage(tester, users: [], dmMessages: [message]);

checkAvatar(tester, DmNarrow.ofMessage(message, selfUserId: eg.selfUser.userId));
check(getTitle(tester).data).equals(eg.selfUser.fullName);
Copy link
Member

Choose a reason for hiding this comment

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

Instead of find.byType(Text) and this check on Text.data, this can use find.text.

That's a bit more flexible because it will also match if there's inline formatting on the text, or even if there isn't but the code uses Text.rich anyway (perhaps as part of some refactoring where the same code sometimes does apply inline formatting).

Comment on lines 149 to 153
void checkTitleHeight(WidgetTester tester, int expectedLines) {
final renderObject = tester.renderObject<RenderParagraph>(find.byWidget(getTitle(tester)));
check(renderObject.size.height).equals(
20.0 // line height
* expectedLines);
Copy link
Member

Choose a reason for hiding this comment

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

Ah, sorry, I was ambiguous when I said "check the height is right" above. I really just meant checking that the height of the whole item is right — that it's still 48, confirming that it hasn't been blown up taller to make room for lots of names.

This is fine now that it's done; it's just more work than I intended.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No problem 🙂—in fact I think this way makes it simpler to reason about what might be causing the height to blow up, whether it's that the truncation failed, or the system font size was increased (or possibly something else I'm not thinking of?). With a check on the whole item, we'd also have to think about the 4dp of vertical padding.

Comment on lines 23 to 25
return MaterialAccountPageRoute(context: context,
settings: RouteSettings(name: 'message_list', arguments: narrow), // for testing
builder: (context) => MessageListPage(narrow: narrow));
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.

This RouteSettings and the builder feel like they're expressing the same information twice. I'll try experimenting with ways to deduplicate that, and also to avoid us having to make up names like message_list for each of the pages we might want to test something navigating to. But that can be after merging this PR.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Interesting, yeah. Thanks!

Comment on lines +294 to +269
final pushedRoutes = <Route<dynamic>>[];
final testNavObserver = TestNavigatorObserver()
..onPushed = (route, prevRoute) => pushedRoutes.add(route);
Copy link
Member

Choose a reason for hiding this comment

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

Handy!

Comment on lines +3 to +5
// Inspired by test code in the Flutter tree:
// https://github.com/flutter/flutter/blob/53082f65b/packages/flutter/test/widgets/observer_tester.dart
// https://github.com/flutter/flutter/blob/53082f65b/packages/flutter/test/widgets/navigator_test.dart
Copy link
Member

Choose a reason for hiding this comment

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

Cool, good find.

Comment on lines 29 to 33

// N.B. this doesn't seem to work when running the tests with `flutter run`
// instead of `flutter test`, resulting in test failures. Upstream issue:
// https://github.com/flutter/flutter/issues/124071
tester.view.physicalSize = const Size(390, 844) * 3; // iPhone 13 Pro
Copy link
Member

Choose a reason for hiding this comment

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

That upstream issue is equally applicable if we don't set physicalSize ourselves, right? It does seem like an annoying issue. (I see in fact I'd already thumbs-upped it.)

I think it's a bit cleaner if we can avoid setting the physical size when there isn't a reason the tests specifically need to interact with it, and write the tests so that they work on the default flutter_test screen size. In particular, if all the tests use the same screen size except when the test case specifically mentions a different one, that makes it easier to learn a mental model of the size of it so that one can look at a given test and mentally picture how it's likely to lay out.

That default size is 800x600 logical pixels, so rather wider and a bit shorter than this. (I believe this line is written with the assumption of a 3x device pixel ratio, so that 390x844 is the size it gives in logical pixels.) For these tests, I think that means using longer names in the "long names" tests, and possibly no other changes.

One takeaway from that upstream issue is that it's another reason to keep the app working reasonably on desktop. As the upstream report shows, on desktop the issue has no effect on the reported size — it can affect the reported DPR and reported size in physical pixels, but those are something that far less code interacts with and so will have no effect at all on most tests. So that makes desktop a good target when using flutter run to investigate a test.

Copy link
Member

Choose a reason for hiding this comment

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

(I also left a comment on the upstream issue, because I think the original report didn't capture the full scope of the impact.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

As the upstream report shows, on desktop the issue has no effect on the reported size

This doesn't seem to hold in my environment: flutter/flutter#124071 (comment) and with this size-setting line removed, the "long name" tests are still giving different results for flutter test and flutter run. 😵

Copy link
Member

Choose a reason for hiding this comment

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

Awkward.

The thing is that this size-setting line isn't a great solution either — it's only helpful if one has an iPhone 13 Pro, or something with the same screen size.

(Or I guess if one is working on a Mac and can run a simulated iPhone 13 Pro. Though even then one might have wanted to run it on a physical device.)

I think if you make the name lists in the "very long name" tests sufficiently long, then they should work on a wide range of sizes including the default and any likely devices.

The intermediate-length "long name" tests are trickier, if they're trying to target reaching two lines and no longer. But as it is, it seems like they don't have a way to check that anyway — their checks are exactly the same as the "very long name" tests. So I think the "short name" and "very long name" versions together give us essentially all the same useful coverage.

chrisbobbe added a commit to chrisbobbe/zulip-flutter that referenced this pull request Aug 10, 2023
chrisbobbe added a commit to chrisbobbe/zulip-flutter that referenced this pull request Aug 10, 2023
@chrisbobbe chrisbobbe force-pushed the pr-recent-dm-conversations-ui branch from 0b71040 to 63f695f Compare August 10, 2023 01:31
@chrisbobbe
Copy link
Collaborator Author

Thanks for the review! Revision pushed.

@gnprice
Copy link
Member

gnprice commented Aug 10, 2023

Thanks for the revision!

This all LGTM. I think the intermediate-length "long name" tests may not be adding anything for us over the "short name" and "very long name" tests:
#249 (comment)
Please go ahead and merge after either removing those tests if you agree, or keeping them if you think they're useful.

@chrisbobbe chrisbobbe force-pushed the pr-recent-dm-conversations-ui branch from 63f695f to 80c725e Compare August 10, 2023 17:07
@chrisbobbe
Copy link
Collaborator Author

Yeah, agreed. Thanks for the many reviews! Merging, with those specific tests removed.

…ration

(And, since the linter complains of sized_box_for_whitespace
  <https://dart.dev/tools/linter-rules/sized_box_for_whitespace>
, swap out the Container for a SizedBox.)
We should implement these optimizations soon, like zulip-mobile
does, but the new "Direct messages" screen (coming next) would
otherwise be missing lots of avatars, which looks awkward.
…ting

This way, the Route object itself is distinguishable from other
Route objects. This will come in handy in widget tests for the
upcoming recent-DMs screen (zulip#119), where we'll want to assert that
tapping on a conversation brings up the appropriate message list.

While we're at it, add the usual
`extension FooChecks on Subject<Foo>` boilerplate that we'll use in
those tests.
The screen's content area (so, the list of conversations, but not
the app bar at the top) is built against Vlad's design:
  https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/design.3A.20DM-conversation.20list/near/1594654
except that some features that appear in that design are left
unimplemented for now, since we don't have data structures for them
yet:
- unread counts
- user presence

Fixes: zulip#119
@chrisbobbe chrisbobbe force-pushed the pr-recent-dm-conversations-ui branch from 80c725e to aa905d7 Compare August 10, 2023 17:09
@chrisbobbe chrisbobbe merged commit aa905d7 into zulip:main Aug 10, 2023
@chrisbobbe chrisbobbe deleted the pr-recent-dm-conversations-ui branch August 10, 2023 17:19
gnprice added a commit to gnprice/zulip-flutter that referenced this pull request Aug 14, 2023
As discussed when we added this first test of this kind:
  zulip#249 (comment)
chrisbobbe pushed a commit that referenced this pull request Aug 15, 2023
As discussed when we added this first test of this kind:
  #249 (comment)
chrisbobbe added a commit to chrisbobbe/zulip-flutter that referenced this pull request Nov 13, 2023
I missed this in zulip#249 and made the rows transparent, oops. So they
were taking on the scaffold background color, 0xfffafafa a.k.a.
ThemeData.canvasColor.

(If we were using Material 3, the scaffold background color would be
0xfffefbff a.k.a. ThemeData.colorScheme.background. Neither color is
correct for what the Figma actually specifies for the surface
underneath this screen's scrollable content. That's 0xfff6f6f6, and
we'll start using it soon.)

The ink effect on touch was being enabled by the scaffold's
underlying Material. To preserve the ink effect, use a Material here
instead of e.g. a ColoredBox.

See the design in Figma:
  https://www.figma.com/file/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=341%3A12378&mode=dev
chrisbobbe added a commit to chrisbobbe/zulip-flutter that referenced this pull request Nov 15, 2023
I missed this in zulip#249 and made the rows transparent, oops. So they
were taking on the scaffold background color, 0xfffafafa a.k.a.
ThemeData.canvasColor.

(If we were using Material 3, the scaffold background color would be
0xfffefbff a.k.a. ThemeData.colorScheme.background. Neither color is
correct for what the Figma actually specifies for the surface
underneath this screen's scrollable content. That's 0xfff6f6f6, and
we'll start using it soon.)

The ink effect on touch was being enabled by the scaffold's
underlying Material. To preserve the ink effect, use a Material here
instead of e.g. a ColoredBox.

See the design in Figma:
  https://www.figma.com/file/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=341%3A12378&mode=dev
chrisbobbe added a commit to chrisbobbe/zulip-flutter that referenced this pull request Nov 16, 2023
I missed this in zulip#249 and made the rows transparent, oops. So they
were taking on the scaffold background color, 0xfffafafa a.k.a.
ThemeData.canvasColor.

(If we were using Material 3, the scaffold background color would be
0xfffefbff a.k.a. ThemeData.colorScheme.background. Neither color is
correct for what the Figma actually specifies for the surface
underneath this screen's scrollable content. That's 0xfff6f6f6, and
we'll start using it soon.)

The ink effect on touch was being enabled by the scaffold's
underlying Material. To preserve the ink effect, use a Material here
instead of e.g. a ColoredBox.

See the design in Figma:
  https://www.figma.com/file/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=341%3A12378&mode=dev
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.

ui: Add list of recent DM conversations
2 participants