-
Notifications
You must be signed in to change notification settings - Fork 270
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
Message List from Same User is now Distinguishable via Surface Tint on Long Press #1152
base: main
Are you sure you want to change the base?
Message List from Same User is now Distinguishable via Surface Tint on Long Press #1152
Conversation
Please post screenshots of your changes in the PR description, as videos are hard to review. |
Alya means before/after screenshots of the app's UI. We can read your code easily without screenshots :) |
Also I see there's a failing check; please run tests locally ( |
Are those before/after scheenshots the same? The point is to show the differences from your PR's changes. |
@alya, |
Oh, I see it now. Is it happening in the dark theme screenshot? I don't see a difference, so if the effect is there, it's too subtle. |
It's also best to take screenshots that are identical other than the change your PR makes, to make them easy to compare. |
@alya |
Sure, please be mindful to review your own work carefully next time. Posting screenshots is one of the things that should help you identify any problems yourself, before asking someone else to take a look. |
Here, is the updated PR. Requirements according to Figma FileBefore and After Screenshots
Both |
Hi @Gaurav-Kushwaha-1225! Thanks for the updates. For the PR to be reviewable, we need tests for this: https://github.com/zulip/zulip-flutter?tab=readme-ov-file#submitting-a-pull-request Please also pay attention to the point on commit style guide from that link, and organize the commit history coherently. |
I see you're adding merge commits; Zulip doesn't use those. |
Hi @PIG208, I have added the necessary tests for these changes in |
Looks like this update still hasn't addressed my previous comment on the commit style:
As Chris said, we don't use merge commits. Could you please also review the commit style guide linked in our README? |
1b7e27d
to
4d26607
Compare
I’ve updated the commits, improving the commit style and making other enhancements. Please let me know if any further changes are required. |
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. Please also squash these two commits into one, so the tests land in the same commit as the code they're testing.
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.
In addition to Chris's comments above, one other high-level comment. (I've only done an initial skim of this PR, since it's still in maintainer review.)
Done. @Gaurav-Kushwaha-1225 As the first step when you start work on your next revision, be sure to pull down the updated version of this branch — use |
30f20c6
to
a0aba57
Compare
Hi @chrisbobbe, I have updated the commits as per your request.
video_2025-01-21_19-39-25.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.
Thanks! Small comments below.
a0aba57
to
79b68c1
Compare
Hi @chrisbobbe, Please have a look at it and let me know if anything else is required. |
Hi @chrisbobbe, Please take a review and let me know if something else is required. |
Thanks @Gaurav-Kushwaha-1225 and @chrisbobbe for your work on this so far! The issue this addresses is an M6 Post-launch issue, and we're now focusing more strictly on launch issues until the launch is ready. But given that a substantial amount of review work has gone into this and it seems like it's relatively close to reaching a state we're able to merge, this can be one of the exceptions that we continue work on nonetheless. |
Hi @Gaurav-Kushwaha-1225, GitHub is saying there are some merge conflicts; would you resolve those please? |
d4981ee
to
bd7396b
Compare
I've updated the commits, and there are no more merge conflicts. However, the current failing CI checks are due to a separate issue in This CI failure is android specific, we can ignore the |
Thanks! Ah, but I see now there are some new merge conflicts; would you resolve those please? |
bd7396b
to
92274ad
Compare
I've rebased the commits, and there are no more merge conflicts. |
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 another review, this time with a new thought about a case that needs test coverage.
@@ -1188,6 +1189,77 @@ void main() { | |||
|
|||
debugNetworkImageHttpClientProvider = null; | |||
}); | |||
|
|||
|
|||
group('action sheet visual feedback', () { |
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 visual feedback is also given on long-press gestures that are canceled and don't cause the action sheet to open. How about just 'background-color tint'?
test/widgets/message_list_test.dart
Outdated
return (decoratedBox.decoration as BoxDecoration).color; | ||
} | ||
|
||
testWidgets('starts with transparent background', (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.
Ah, there's another important case that we should test that isn't covered yet: the onLongPressCancel
case. The user puts their finger down, then quickly drags because they want to scroll instead of opening the action sheet. The UI recognizes this as cancelling the long-press gesture, and we should test that the feedback disappears at that time.
Rather than just adding another testWidgets
, I think a neat way to organize the test coverage is in just two tests, each with a clear storyline:
- 'long-press opens open action sheet':
- Check transparent
- Begin long-press
- Check tinted
- End long-press
- Check tinted (it didn't flicker to transparent here)
- wait through the bottom-sheet entrance animation (for examples, search for 'default duration of bottom-sheet enter animation')
- Check tinted
- Dismiss action sheet
- Check transparent
- 'long-press canceled':
- Check transparent
- Begin long-press
- Check tinted
- Cancel long-press, for example by dragging
- Check transparent
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.
Hi @chrisbobbe,
The above comment #1152 (comment) and this comment are referring to the same issue?
If yes, then np.
I have updated the PR with both the required tests and changes.
Thank You.
We'll soon use this for modal bottom sheets too, aka action sheets. Arguably that could mean it belongs in some other file, not specific to dialogs nor bottom sheets. But there isn't an obvious other file for it, and I think this is as good a home as any.
92274ad
to
b6406f0
Compare
It allows to see a tint color on the message when it is pressed. Moved `MessageWithPossibleSender` to `StatefulWidget` and used `ModalStatus` return type of `showMessageActionSheet` to check whether BottomSheet is open or not. Added `pressedTint` to `DesignVariables` for using it in `MessageWithPossibleSender`. Added tests too in `message_list_test.dart`. Fixes: zulip#1142
b6406f0
to
2610fa3
Compare
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, including some substantive comments on the tests.
testWidgets('long-press opens action sheet', (tester) async { | ||
await setupMessageListPage(tester, messages: [message]); | ||
|
||
check(getBackgroundColor(tester)).equals(Colors.transparent); | ||
|
||
await tester.longPress(find.byType(MessageWithPossibleSender)); | ||
await tester.pump(); | ||
|
||
final expectedTint = DesignVariables.of(tester.element(find.byType(MessageWithPossibleSender))) | ||
.pressedTint; | ||
|
||
check(getBackgroundColor(tester)).equals(expectedTint); | ||
|
||
await tester.pumpAndSettle(); | ||
|
||
check(getBackgroundColor(tester)).equals(expectedTint); | ||
|
||
await tester.tapAt(const Offset(0, 0)); | ||
await tester.pumpAndSettle(); | ||
|
||
check(getBackgroundColor(tester)).equals(Colors.transparent); | ||
}); |
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 is missing much of what I outlined at #1152 (comment) . In fact, if I intentionally remove onLongPressDown
and onLongPressUp
in the implementation, this test still passes, meaning it wouldn't catch user-facing bugs that might sneak in in the future.
Quoting what I outlined, with annotations:
'long-press opens open action sheet':
- Check transparent
- Begin long-press
- Check tinted ← This is missing; (a)
- End long-press
- Check tinted (it didn't flicker to transparent here) ← This isn't working as intended; (b)
- wait through the bottom-sheet entrance animation (for examples, search for 'default duration of bottom-sheet enter animation') ← We should explicitly wait for 250ms; please search for those examples I mentioned; (c)
- Check tinted
- Dismiss action sheet
- Check transparent
(a) should be possible by calling tester.longPress
and storing the returned Future in a variable instead of immediately await
ing it. Then pump for some duration that's shorter than would trigger a long-press. I see the following in the implementation of tester.longPress
:
await pump(kLongPressTimeout + kPressTimeout);
So how about awaiting a pump of half that duration? Then to proceed to the end of the long-press gesture, pump that duration again, and await
the stored Future.
(b) is subtle but also good to test. You can test whether this check is working by temporarily breaking the implementation in a way that should make the check fail, and confirm that the check does fail. Here's a way to do that (i.e. delaying the "selected" state by a frame):
--- lib/widgets/message_list.dart
+++ lib/widgets/message_list.dart
@@ -2,6 +2,7 @@ import 'dart:math';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart';
import 'package:flutter_color_models/flutter_color_models.dart';
import 'package:intl/intl.dart' hide TextDirection;
@@ -1428,7 +1429,8 @@ class _MessageWithPossibleSenderState extends State<MessageWithPossibleSender> {
return GestureDetector(
behavior: HitTestBehavior.translucent,
onLongPress: () async {
- statesController.update(WidgetState.selected, true);
+ SchedulerBinding.instance.addPostFrameCallback(
+ (_) => statesController.update(WidgetState.selected, true));
ModalStatus status = showMessageActionSheet(context: context,
message: message);
await status.closed;
It might be as easy as doing the check before instead of after a pump()
.
For (c), see existing tests, as mentioned.
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.
According to the above comments, I think this would be the updated version. I also added comments so that I can describe what I think is happening via those respective lines.
testWidgets('long-press opens action sheet', (tester) async {
await setupMessageListPage(tester, messages: [message]);
// Check starts as transparent
check(getBackgroundColor(tester)).equals(Colors.transparent);
// Begin long-press - part (a)
final gestureFuture = tester.longPress(find.byType(MessageWithPossibleSender));
await tester.pump(const Duration(milliseconds: 300));
final expectedTint = DesignVariables.of(tester.element(find.byType(MessageWithPossibleSender)))
.pressedTint;
// Check tinted after short pump
check(getBackgroundColor(tester)).equals(expectedTint);
// Complete long-press
await tester.pump(const Duration(milliseconds: 300));
await gestureFuture;
// Check tinted after long-press
// (part b - Not yet completed, as it is not failing a particular case when implemented that it should fail.)
check(getBackgroundColor(tester)).equals(expectedTint);
// Wait explicitly for bottom-sheet entrance animation (250ms)
await tester.pump(const Duration(milliseconds: 250));
// Check still tinted
check(getBackgroundColor(tester)).equals(expectedTint);
// Dismiss action sheet
await tester.tapAt(const Offset(0, 0));
await tester.pumpAndSettle();
// Check returns to transparent
check(getBackgroundColor(tester)).equals(Colors.transparent);
});
Breakdown of Changes
1. For part (a)
- It's now implemented as requested.
- Used 300 milliseconds because
await pump(kLongPressTimeout + kPressTimeout);
summed up to 600ms. - However, an error occurs while running the test because
await
should be there after a future method call:
The following assertion was thrown running a test:
Guarded function conflict.
You must use "await" with all Future-returning test APIs.
The guarded method "longPressAt" from class WidgetController was called from
Then, the "pump" method from class WidgetTester was called from
The first method (WidgetController.longPressAt) had not yet finished executing at the time that the
second method (WidgetTester.pump) was called. Since both are guarded, and the second was not a
nested call inside the first, the first must complete its execution before the second can be called.
Typically, this is achieved by putting an "await" statement in front of the call to the first.
2. For part (b)
- I completely understood the request, butt.
- This check isn't failing, but after making the suggested changes (in details part discussion link), as it should fail.
- I am trying to figure out where I am missing in this case of checking tint after long press.
3. For part (c)
- Used 250ms for waiting, as I couldn't find a default duration for bottom-sheet enter animation.
- The Flutter documentation states that the default value is null.
- References:
I will also ask regarding part (b) in the mobile-dev-help
group for help.
Thank You.
Changes Made
This issue resolved by:
MessageWithPossibleSender
with aContainer
isMessageActionSheetOpen
istrue
show the surface tint, else remainColors.transparent
.For this,
MessageWithPossibleSender
fromStatelessWidget
toStatefulWidget
.VoidCallback? onDismiss
method in parameters ofshowMessageActionSheet
or BottomSheet for updating the Surface Tint of message.Fixes #1142
Screenshots
video_2024-12-13_13-16-02.mp4
video_2024-12-13_13-16-08.mp4