Skip to content

Conversation

chrisbobbe
Copy link
Collaborator

For manual testing, ideally use a real device, to see the fling and drag responses faithfully to what most users will experience :)

Screenshots coming soon.

Selected commit messages:


ead_receipts: Improve UX by making the sheet draggable-scrollable

The Figma asks that the sheet be expandable to fill the screen:
https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=11367-21131&m=dev
and that's implemented here.

Compare f8ddff2, where we removed an earlier implementation. I
hadn't tried to bring that back yet because I wanted to support
triggering resize from a drag handle at the top, and I couldn't
figure out how to do that. IIRC I could only get the drag handle to
respond to drag-down gestures (via enableDrag: true) by shifting
the sheet's position downward. That worked as a way to dismiss the
sheet, but it was frustratingly different from the gesture handling
on the scrollable list:

  • The slide-to-dismiss looked different from the shrink-and-dismiss,
    an awkward inconsistency
  • The list would respond to upward drags, too (by growing and
    showing more content)

I've managed it here, modulo with a header instead of a drag handle,
by making sure the scrollable area extends through the top of the
sheet. (Done with a CustomScrollView, pinning the header to the
viewport top.)


emoji_reaction: Use DraggableScrollableModalBottomSheet for view-reactions

Like we did for read-receipts in a recent commit. I think this
behavior is implied in the Figma, but it's not as explicit:
https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=5878-19207&m=dev
...anyway, helpful to be consistent with read-receipts, I think,
which is similarly a read-only view that might have a long list to
show.

I thought this would be more complicated than it turned out to be --
thanks to SliverSemantics, I can actually wrap the list of voters in
a labeled container node, because
DraggableScrollableModalBottomSheet's API takes a sliver for the
content.

@chrisbobbe chrisbobbe added the maintainer review PR ready for review by Zulip maintainers label Aug 14, 2025
@chrisbobbe
Copy link
Collaborator Author

Finger down on header, move up and down

Before:

ScreenRecording_08-14-2025.12-55-18_1.mov

After:

ScreenRecording_08-14-2025.12-57-41_1.mov

Finger down in list, move up and down

Before:

ScreenRecording_08-14-2025.12-56-03_1.mov

After:

ScreenRecording_08-14-2025.12-58-02_1.mov

@chrisbobbe
Copy link
Collaborator Author

I can record the view-reactions behavior too, but it's similar, and anyway the best way to see the behavior is to try it yourself :)

Copy link
Member

@rajveermalviya rajveermalviya 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 working on this @chrisbobbe! This works great on my device, and LGTM.

@rajveermalviya rajveermalviya added integration review Added by maintainers when PR may be ready for integration and removed maintainer review PR ready for review by Zulip maintainers labels Aug 15, 2025
@rajveermalviya rajveermalviya requested a review from gnprice August 15, 2025 18:30
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.

Neat, thanks for working this out.

Generally this all looks good; just small comments.

Comment on lines 230 to 232
/// Simply a [BottomSheetEmptyContentPlaceholder] wrapped in
/// [SliverToBoxAdapter].
class SliverBottomSheetEmptyContentPlaceholder extends StatelessWidget {
Copy link
Member

Choose a reason for hiding this comment

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

Seems like this might be clearer inlined, then — less than a line's worth of extra code, and more transparent.

Comment on lines 291 to 272
// The "inset shadow" effect in Figma is a bit awkwardly
// implemented and there might be a better factoring:
// 1. This effect leans on the abstraction that [contentSliver]
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
// The "inset shadow" effect in Figma is a bit awkwardly
// implemented and there might be a better factoring:
// 1. This effect leans on the abstraction that [contentSliver]
// The "inset shadow" effect in Figma is a bit awkwardly
// implemented here and there might be a better factoring:
// 1. This effect leans on the abstraction that [contentSliver]

Is that the intended meaning? My first thought was this line was about what's in Figma.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Eep, yeah, thanks for flagging.

physics: ClampingScrollPhysics(),
controller: controller,
slivers: [
PinnedHeaderSliver(child: headerWithShadow),
Copy link
Member

Choose a reason for hiding this comment

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

I've managed it here, modulo with a header instead of a drag handle,
by making sure the scrollable area extends through the top of the
sheet. (Done with a CustomScrollView, pinning the header to the
viewport top.)

Neat!

Comment on lines 763 to 784
final userIds = message?.reactions?.aggregated.firstWhereOrNull(
(x) => x.reactionType == reactionType && x.emojiCode == emojiCode
)?.userIds.toList();

Widget contentSliver;
if (reactionType != null && emojiCode != null && userIds != null) {
assert(emojiName != null);

// (No filtering of muted or deactivated users.
// Muted users will be shown as muted.)

contentSliver = SliverList.builder(
itemCount: userIds.length,
itemBuilder: (_, index) => ViewReactionsUserItem(userId: userIds[index]));

contentSliver = SliverSemantics(
identifier: ViewReactions.semanticsIdentifier, // See note on `controlsNodes` on the tab.
label: zulipLocalizations.seeWhoReactedSheetUserListLabel(emojiName!, userIds.length),
role: SemanticsRole.tabPanel,
container: true,
explicitChildNodes: true,
sliver: contentSliver);
Copy link
Member

Choose a reason for hiding this comment

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

This part feels like it would be useful to continue to have in its own widget, like ViewReactionsUserList.

Could put "sliver" in the name of the widget to make clear its render object will be a sliver rather than a box.

@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! Looks good; just a couple of nits.

final backgroundColor = Theme.of(context).bottomSheetTheme.backgroundColor!;

// The "inset shadow" effect in Figma is a bit awkwardly
// implemented here and there might be a better factoring:
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
// implemented here and there might be a better factoring:
// implemented here, and there might be a better factoring:

I know I suggested this wording myself 😅 yesterday above, but I misparsed this on first reading today, with the phrase "here and there".

Comment on lines 988 to 998
if (userIds == null) {
// This reaction lost all its votes, or the message was deleted.
return SizedBox.shrink();
return SliverPadding(padding: EdgeInsets.zero);
Copy link
Member

Choose a reason for hiding this comment

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

I guess this is also now the condition that handles the case where reactionType and/or emojiCode is null.

It'd be good to make that explicit, at least by editing the comment. Might be clearer still to put an explicit condition for it at the top of the build method.

@chrisbobbe chrisbobbe force-pushed the pr-draggable-scrollable-sheet-again branch from a9b5745 to a2774bb Compare August 20, 2025 22:44
@chrisbobbe
Copy link
Collaborator Author

Thanks! Revision pushed.

There are other localization classes, like MaterialLocalizations,
and in any case it seems right to have a consistent naming pattern.
The Figma asks that the sheet be expandable to fill the screen:
  https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=11367-21131&m=dev
and that's implemented here.

Compare f8ddff2, where we removed an earlier implementation. I
hadn't tried to bring that back yet because I wanted to support
triggering resize from a drag handle at the top, and I couldn't
figure out how to do that. IIRC I could only get the drag handle to
respond to drag-down gestures (via `enableDrag: true`) by shifting
the sheet's position downward. That worked as a way to dismiss the
sheet, but it was frustratingly different from the gesture handling
on the scrollable list:
- The slide-to-dismiss looked different from the shrink-and-dismiss,
  an awkward inconsistency
- The list would respond to upward drags, too (by growing and
  showing more content)
I've managed it here, modulo with a header instead of a drag handle,
by making sure the scrollable area extends through the top of the
sheet. (Done with a CustomScrollView, pinning the header to the
viewport top.)
This fixes a test failure when we make an upcoming refactor. (The
container node's label would apparently have its children's labels
appended to it...I haven't seen that behavior in manual testing on
my iPhone with VoiceOver, before or after that refactor, but, shrug;
this change does seem correct anyway.)
…tions

Like we did for read-receipts in a recent commit. I *think* this
behavior is implied in the Figma, but it's not as explicit:
  https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=5878-19207&m=dev
...anyway, helpful to be consistent with read-receipts, I think,
which is similarly a read-only view that might have a long list to
show.

I thought this would be more complicated than it turned out to be --
thanks to SliverSemantics, I can actually wrap the list of voters in
a labeled container node, because
DraggableScrollableModalBottomSheet's API takes a sliver for the
content.
@gnprice gnprice force-pushed the pr-draggable-scrollable-sheet-again branch from a2774bb to d9ab075 Compare August 20, 2025 23:33
@gnprice
Copy link
Member

gnprice commented Aug 20, 2025

Thanks! Looks good; merging.

@gnprice gnprice merged commit d9ab075 into zulip:main Aug 20, 2025
1 check passed
@chrisbobbe chrisbobbe deleted the pr-draggable-scrollable-sheet-again branch August 21, 2025 00:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
integration review Added by maintainers when PR may be ready for integration
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants