Skip to content

Commit 79b68c1

Browse files
message_list: added pressedTint feature
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: #1142
1 parent 69f6cf1 commit 79b68c1

File tree

4 files changed

+264
-143
lines changed

4 files changed

+264
-143
lines changed

lib/widgets/action_sheet.dart

+5-4
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ import 'store.dart';
2828
import 'text.dart';
2929
import 'theme.dart';
3030

31-
void _showActionSheet(
31+
ModalStatus _showActionSheet(
3232
BuildContext context, {
3333
required List<Widget> optionButtons,
3434
}) {
35-
showModalBottomSheet<void>(
35+
final future = showModalBottomSheet<void>(
3636
context: context,
3737
// Clip.hardEdge looks bad; Clip.antiAliasWithSaveLayer looks pixel-perfect
3838
// on my iPhone 13 Pro but is marked as "much slower":
@@ -62,6 +62,7 @@ void _showActionSheet(
6262
const ActionSheetCancelButton(),
6363
])));
6464
});
65+
return ModalStatus(future);
6566
}
6667

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

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

391-
_showActionSheet(context, optionButtons: optionButtons);
392+
return _showActionSheet(context, optionButtons: optionButtons);
392393
}
393394

394395
abstract class MessageActionSheetMenuItemButton extends ActionSheetMenuItemButton {

lib/widgets/message_list.dart

+78-37
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import 'actions.dart';
1616
import 'app_bar.dart';
1717
import 'compose_box.dart';
1818
import 'content.dart';
19+
import 'dialog.dart';
1920
import 'emoji_reaction.dart';
2021
import 'icons.dart';
2122
import 'page.dart';
@@ -1274,22 +1275,45 @@ String formatHeaderDate(
12741275
// Design referenced from:
12751276
// - https://github.com/zulip/zulip-mobile/issues/5511
12761277
// - https://www.figma.com/file/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=538%3A20849&mode=dev
1277-
class MessageWithPossibleSender extends StatelessWidget {
1278+
class MessageWithPossibleSender extends StatefulWidget {
12781279
const MessageWithPossibleSender({super.key, required this.item});
12791280

12801281
final MessageListMessageItem item;
12811282

1283+
@override
1284+
State<MessageWithPossibleSender> createState() => _MessageWithPossibleSenderState();
1285+
}
1286+
1287+
class _MessageWithPossibleSenderState extends State<MessageWithPossibleSender> {
1288+
final WidgetStatesController statesController = WidgetStatesController();
1289+
1290+
@override
1291+
void initState() {
1292+
super.initState();
1293+
statesController.addListener(() {
1294+
setState(() {
1295+
// Force a rebuild to resolve background color
1296+
});
1297+
});
1298+
}
1299+
1300+
@override
1301+
void dispose() {
1302+
statesController.dispose();
1303+
super.dispose();
1304+
}
1305+
12821306
@override
12831307
Widget build(BuildContext context) {
12841308
final store = PerAccountStoreWidget.of(context);
12851309
final messageListTheme = MessageListTheme.of(context);
12861310
final designVariables = DesignVariables.of(context);
12871311

1288-
final message = item.message;
1312+
final message = widget.item.message;
12891313
final sender = store.users[message.senderId];
12901314

12911315
Widget? senderRow;
1292-
if (item.showSender) {
1316+
if (widget.item.showSender) {
12931317
final time = _kMessageTimestampFormat
12941318
.format(DateTime.fromMillisecondsSinceEpoch(1000 * message.timestamp));
12951319
senderRow = Row(
@@ -1347,40 +1371,57 @@ class MessageWithPossibleSender extends StatelessWidget {
13471371

13481372
return GestureDetector(
13491373
behavior: HitTestBehavior.translucent,
1350-
onLongPress: () => showMessageActionSheet(context: context, message: message),
1351-
child: Padding(
1352-
padding: const EdgeInsets.symmetric(vertical: 4),
1353-
child: Column(children: [
1354-
if (senderRow != null)
1355-
Padding(padding: const EdgeInsets.fromLTRB(16, 2, 16, 0),
1356-
child: senderRow),
1357-
Row(
1358-
crossAxisAlignment: CrossAxisAlignment.baseline,
1359-
textBaseline: localizedTextBaseline(context),
1360-
children: [
1361-
const SizedBox(width: 16),
1362-
Expanded(child: Column(
1363-
crossAxisAlignment: CrossAxisAlignment.stretch,
1364-
children: [
1365-
MessageContent(message: message, content: item.content),
1366-
if ((message.reactions?.total ?? 0) > 0)
1367-
ReactionChipsList(messageId: message.id, reactions: message.reactions!),
1368-
if (editStateText != null)
1369-
Text(editStateText,
1370-
textAlign: TextAlign.end,
1371-
style: TextStyle(
1372-
color: designVariables.labelEdited,
1373-
fontSize: 12,
1374-
height: (12 / 12),
1375-
letterSpacing: proportionalLetterSpacing(
1376-
context, 0.05, baseFontSize: 12))),
1377-
])),
1378-
SizedBox(width: 16,
1379-
child: message.flags.contains(MessageFlag.starred)
1380-
? Icon(ZulipIcons.star_filled, size: 16, color: designVariables.star)
1381-
: null),
1382-
]),
1383-
])));
1374+
onLongPress: () async {
1375+
statesController.update(WidgetState.selected, true);
1376+
ModalStatus status = showMessageActionSheet(context: context,
1377+
message: message);
1378+
await status.closed;
1379+
statesController.update(WidgetState.selected, false);
1380+
},
1381+
onLongPressDown: (_) => statesController.update(WidgetState.pressed, true),
1382+
onLongPressCancel: () => statesController.update(WidgetState.pressed, false),
1383+
onLongPressUp: () => statesController.update(WidgetState.pressed, false),
1384+
child: DecoratedBox(
1385+
decoration: BoxDecoration(
1386+
color: WidgetStateColor.fromMap({
1387+
WidgetState.pressed: designVariables.pressedTint,
1388+
WidgetState.selected: designVariables.pressedTint,
1389+
WidgetState.any: Colors.transparent,
1390+
}).resolve(statesController.value)
1391+
),
1392+
child: Padding(
1393+
padding: const EdgeInsets.symmetric(vertical: 4),
1394+
child: Column(children: [
1395+
if (senderRow != null)
1396+
Padding(padding: const EdgeInsets.fromLTRB(16, 2, 16, 0),
1397+
child: senderRow),
1398+
Row(
1399+
crossAxisAlignment: CrossAxisAlignment.baseline,
1400+
textBaseline: localizedTextBaseline(context),
1401+
children: [
1402+
const SizedBox(width: 16),
1403+
Expanded(child: Column(
1404+
crossAxisAlignment: CrossAxisAlignment.stretch,
1405+
children: [
1406+
MessageContent(message: message, content: widget.item.content),
1407+
if ((message.reactions?.total ?? 0) > 0)
1408+
ReactionChipsList(messageId: message.id, reactions: message.reactions!),
1409+
if (editStateText != null)
1410+
Text(editStateText,
1411+
textAlign: TextAlign.end,
1412+
style: TextStyle(
1413+
color: designVariables.labelEdited,
1414+
fontSize: 12,
1415+
height: (12 / 12),
1416+
letterSpacing: proportionalLetterSpacing(
1417+
context, 0.05, baseFontSize: 12))),
1418+
])),
1419+
SizedBox(width: 16,
1420+
child: message.flags.contains(MessageFlag.starred)
1421+
? Icon(ZulipIcons.star_filled, size: 16, color: designVariables.star)
1422+
: null),
1423+
]),
1424+
]))));
13841425
}
13851426
}
13861427

0 commit comments

Comments
 (0)