Skip to content

Comments

Swipe to reply#160

Draft
ChaoticLeah wants to merge 10 commits intozjs81:mainfrom
ChaoticLeah:enhancement/swipe-reply
Draft

Swipe to reply#160
ChaoticLeah wants to merge 10 commits intozjs81:mainfrom
ChaoticLeah:enhancement/swipe-reply

Conversation

@ChaoticLeah
Copy link
Contributor

@ChaoticLeah ChaoticLeah commented Feb 11, 2026

This PR allows you to swipe on a message to reply to it.

This has been tested on an android phone.

Closes #154

Copilot AI review requested due to automatic review settings February 11, 2026 21:03
@ChaoticLeah
Copy link
Contributor Author

Related issue: #154

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds “swipe to reply” interaction to the channel chat UI by tracking horizontal swipe gestures on individual message bubbles and showing a reply hint as feedback.

Changes:

  • Introduces per-message swipe tracking state (_swipeStartPosition, _swipeTrackingMessageId, _swipeOffset) and swipe handling helpers.
  • Wraps each message bubble with pointer listeners to translate the bubble during swipe and trigger reply on threshold.
  • Adds a reply hint UI displayed behind the message while swiping.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +131 to +143
void _handleSwipeEnd(
Offset position,
double threshold,
ChannelMessage message,
) {
if (_swipeStartPosition != null) {
final dx = position.dx - _swipeStartPosition!.dx;
if (dx.abs() >= threshold) {
_setReplyingTo(message);
HapticFeedback.selectionClick();
}
}
_resetSwipe();
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

_handleSwipeEnd doesn’t verify that the swipe being ended still corresponds to the message being handled (it doesn’t check _swipeTrackingMessageId and doesn’t track the pointer id). With multi-touch (or overlapping gestures), a second pointer can overwrite _swipeStartPosition/_swipeTrackingMessageId, and the first pointer’s onPointerUp can incorrectly trigger reply on the wrong message. Track PointerDownEvent.pointer and validate both pointer id and message id in update/end before applying the threshold / replying.

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

@446564 446564 left a comment

Choose a reason for hiding this comment

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

I think the robot makes some good points, just potential issues to avoid.

But otherwise this looks great and tested and working on my end.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings February 12, 2026 16:30
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


final double clamped = dx.clamp(-maxOffset, maxOffset).toDouble();
final adjusted = _applySwipeResistance(clamped, maxOffset);
if (adjusted != _swipeOffset) {
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

_handleSwipeUpdate calls setState on pointer-move events, which will rebuild the whole ChannelChatScreen (including the ListView) at gesture sampling rates and can cause jank on long chats. Consider moving the swipe offset into a per-message widget/state (e.g., a dedicated StatefulWidget for a bubble with its own setState/ValueNotifier) so only the swiped row repaints, or otherwise avoid screen-level rebuilds for pointer updates.

Suggested change
if (adjusted != _swipeOffset) {
// Avoid rebuilding the whole screen for tiny, imperceptible changes in offset.
// This throttles setState calls during pointer-move events to reduce jank.
if ((adjusted - _swipeOffset).abs() >= 1.0) {

Copilot uses AI. Check for mistakes.
Comment on lines +147 to +149
setState(() => _swipeOffset = 0);
_swipeStartPosition = null;
_swipeTrackingMessageId = null;
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

_resetSwipe always calls setState(() => _swipeOffset = 0) even when no swipe occurred (offset already 0). Since onPointerUp runs for every tap/scroll interaction on a message, this forces an extra rebuild for normal taps and scrolls. Guard the setState (only when _swipeOffset != 0 / swipe was active) and update _swipeStartPosition/_swipeTrackingMessageId inside the same setState to keep state mutations consistent.

Suggested change
setState(() => _swipeOffset = 0);
_swipeStartPosition = null;
_swipeTrackingMessageId = null;
// Avoid unnecessary rebuilds when there is no active swipe state.
if (_swipeOffset == 0 &&
_swipeStartPosition == null &&
_swipeTrackingMessageId == null) {
return;
}
setState(() {
_swipeOffset = 0;
_swipeStartPosition = null;
_swipeTrackingMessageId = null;
});

Copilot uses AI. Check for mistakes.
@446564
Copy link
Collaborator

446564 commented Feb 12, 2026

I've also noticed that you can't cancel the reply, if you start to swipe and then move back so the highlight + reply text goes away it still replies to the message.

Also it is very easy to trigger, so the same thing could solve both issues. If the event only fires if the state has changed to highlight with the text and then released, otherwise it should not fire.

Copy link
Collaborator

@446564 446564 left a comment

Choose a reason for hiding this comment

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

#160 (comment)

Please change from draft when ready

@446564 446564 marked this pull request as draft February 15, 2026 16:54
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.

Swipe to reply

2 participants