Skip to content

Commit

Permalink
message_list: added pressedTint feature
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Gaurav-Kushwaha-1225 committed Jan 21, 2025
1 parent 69f6cf1 commit a0aba57
Show file tree
Hide file tree
Showing 4 changed files with 267 additions and 141 deletions.
9 changes: 5 additions & 4 deletions lib/widgets/action_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ import 'store.dart';
import 'text.dart';
import 'theme.dart';

void _showActionSheet(
ModalStatus _showActionSheet(
BuildContext context, {
required List<Widget> optionButtons,
}) {
showModalBottomSheet<void>(
final future = showModalBottomSheet<void>(
context: context,
// Clip.hardEdge looks bad; Clip.antiAliasWithSaveLayer looks pixel-perfect
// on my iPhone 13 Pro but is marked as "much slower":
Expand Down Expand Up @@ -62,6 +62,7 @@ void _showActionSheet(
const ActionSheetCancelButton(),
])));
});
return ModalStatus(future);
}

abstract class ActionSheetMenuItemButton extends StatelessWidget {
Expand Down Expand Up @@ -361,7 +362,7 @@ class UserTopicUpdateButton extends ActionSheetMenuItemButton {
/// Show a sheet of actions you can take on a message in the message list.
///
/// Must have a [MessageListPage] ancestor.
void showMessageActionSheet({required BuildContext context, required Message message}) {
ModalStatus showMessageActionSheet({required BuildContext context, required Message message}) {
final store = PerAccountStoreWidget.of(context);

// The UI that's conditioned on this won't live-update during this appearance
Expand All @@ -388,7 +389,7 @@ void showMessageActionSheet({required BuildContext context, required Message mes
ShareButton(message: message, pageContext: context),
];

_showActionSheet(context, optionButtons: optionButtons);
return _showActionSheet(context, optionButtons: optionButtons);
}

abstract class MessageActionSheetMenuItemButton extends ActionSheetMenuItemButton {
Expand Down
114 changes: 79 additions & 35 deletions lib/widgets/message_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import 'actions.dart';
import 'app_bar.dart';
import 'compose_box.dart';
import 'content.dart';
import 'dialog.dart';
import 'emoji_reaction.dart';
import 'icons.dart';
import 'page.dart';
Expand Down Expand Up @@ -1274,22 +1275,44 @@ String formatHeaderDate(
// Design referenced from:
// - https://github.com/zulip/zulip-mobile/issues/5511
// - https://www.figma.com/file/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=538%3A20849&mode=dev
class MessageWithPossibleSender extends StatelessWidget {
class MessageWithPossibleSender extends StatefulWidget {
const MessageWithPossibleSender({super.key, required this.item});

final MessageListMessageItem item;

@override
State<MessageWithPossibleSender> createState() => _MessageWithPossibleSenderState();
}

class _MessageWithPossibleSenderState extends State<MessageWithPossibleSender> {
final WidgetStatesController statesController = WidgetStatesController();

@override
void initState() {
super.initState();
statesController.addListener(() {
setState(() {
});
});
}

@override
void dispose() {
statesController.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
final store = PerAccountStoreWidget.of(context);
final messageListTheme = MessageListTheme.of(context);
final designVariables = DesignVariables.of(context);

final message = item.message;
final message = widget.item.message;
final sender = store.users[message.senderId];

Widget? senderRow;
if (item.showSender) {
if (widget.item.showSender) {
final time = _kMessageTimestampFormat
.format(DateTime.fromMillisecondsSinceEpoch(1000 * message.timestamp));
senderRow = Row(
Expand Down Expand Up @@ -1347,40 +1370,61 @@ class MessageWithPossibleSender extends StatelessWidget {

return GestureDetector(
behavior: HitTestBehavior.translucent,
onLongPress: () => showMessageActionSheet(context: context, message: message),
onLongPress: () async {
statesController.update(WidgetState.selected, true);
if (context.mounted) {
ModalStatus status = showMessageActionSheet(
context: context,
message: message);
await status.closed;
statesController.update(WidgetState.selected, false);
}
},
onLongPressDown: (_) => statesController.update(WidgetState.pressed, true),
onLongPressCancel: () => statesController.update(WidgetState.pressed, false),
onLongPressUp: () => statesController.update(WidgetState.pressed, false),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Column(children: [
if (senderRow != null)
Padding(padding: const EdgeInsets.fromLTRB(16, 2, 16, 0),
child: senderRow),
Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: localizedTextBaseline(context),
children: [
const SizedBox(width: 16),
Expanded(child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
MessageContent(message: message, content: item.content),
if ((message.reactions?.total ?? 0) > 0)
ReactionChipsList(messageId: message.id, reactions: message.reactions!),
if (editStateText != null)
Text(editStateText,
textAlign: TextAlign.end,
style: TextStyle(
color: designVariables.labelEdited,
fontSize: 12,
height: (12 / 12),
letterSpacing: proportionalLetterSpacing(
context, 0.05, baseFontSize: 12))),
])),
SizedBox(width: 16,
child: message.flags.contains(MessageFlag.starred)
? Icon(ZulipIcons.star_filled, size: 16, color: designVariables.star)
: null),
]),
])));
child: DecoratedBox(
decoration: BoxDecoration(
color: WidgetStateColor.fromMap({
WidgetState.pressed: designVariables.pressedTint,
WidgetState.selected: designVariables.pressedTint,
WidgetState.any: Colors.transparent,
}).resolve(statesController.value)
),
child: Column(children: [
if (senderRow != null)
Padding(padding: const EdgeInsets.fromLTRB(16, 2, 16, 0),
child: senderRow),
Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: localizedTextBaseline(context),
children: [
const SizedBox(width: 16),
Expanded(child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
MessageContent(message: message, content: widget.item.content),
if ((message.reactions?.total ?? 0) > 0)
ReactionChipsList(messageId: message.id, reactions: message.reactions!),
if (editStateText != null)
Text(editStateText,
textAlign: TextAlign.end,
style: TextStyle(
color: designVariables.labelEdited,
fontSize: 12,
height: (12 / 12),
letterSpacing: proportionalLetterSpacing(
context, 0.05, baseFontSize: 12))),
])),
SizedBox(width: 16,
child: message.flags.contains(MessageFlag.starred)
? Icon(ZulipIcons.star_filled, size: 16, color: designVariables.star)
: null),
]),
]),
)));
}
}

Expand Down
Loading

0 comments on commit a0aba57

Please sign in to comment.