Add quickactions for chats

pull/2269/head
Steven Lageveen 2 weeks ago
parent 50aa402ef2
commit 789fa6efbd

@ -835,6 +835,53 @@ class ChatController extends State<ChatPageWithRoom>
}
}
void redactEventAction(Event event) async {
final reasonInput = event.status.isSent
? await showTextInputDialog(
context: context,
title: L10n.of(context).redactMessage,
message: L10n.of(context).redactMessageDescription,
isDestructive: true,
hintText: L10n.of(context).optionalRedactReason,
maxLength: 255,
maxLines: 3,
minLines: 1,
okLabel: L10n.of(context).remove,
cancelLabel: L10n.of(context).cancel,
)
: null;
if (reasonInput == null) return;
final reason = reasonInput.isEmpty ? null : reasonInput;
await showFutureLoadingDialog(
context: context,
futureWithProgress: (onProgress) async {
if (event.status.isSent) {
if (event.canRedact) {
await event.redactEvent(reason: reason);
} else {
final client = currentRoomBundle.firstWhere(
(cl) => selectedEvents.first.senderId == cl!.userID,
orElse: () => null,
);
if (client == null) {
return;
}
final room = client.getRoomById(roomId)!;
await Event.fromJson(event.toJson(), room).redactEvent(
reason: reason,
);
}
} else {
await event.cancelSend();
}
},
);
setState(() {
showEmojiPicker = false;
selectedEvents.clear();
});
}
void redactEventsAction() async {
final reasonInput = selectedEvents.any((event) => event.status.isSent)
? await showTextInputDialog(
@ -925,6 +972,20 @@ class ChatController extends State<ChatPageWithRoom>
.any((cl) => selectedEvents.first.senderId == cl!.userID);
}
void forwardEventAction(Event event) async {
final timeline = this.timeline;
if (timeline == null) return;
final items = [ContentShareItem(event.getDisplayEvent(timeline).content)];
await showScaffoldDialog(
context: context,
builder: (context) => ShareScaffoldDialog(
items: items,
),
);
if (!mounted) return;
}
void forwardEventsAction() async {
if (selectedEvents.isEmpty) return;
final timeline = this.timeline;
@ -1066,6 +1127,28 @@ class ChatController extends State<ChatPageWithRoom>
}
}
void editEventAction(Event event) {
final client = currentRoomBundle.firstWhere(
(cl) => event.senderId == cl!.userID,
orElse: () => null,
);
if (client == null) {
return;
}
setSendingClient(client);
setState(() {
pendingText = sendController.text;
editEvent = event;
sendController.text =
editEvent!.getDisplayEvent(timeline!).calcLocalizedBodyFallback(
MatrixLocals(L10n.of(context)),
withSenderNamePrefix: false,
hideReply: true,
);
});
inputFocus.requestFocus();
}
void editSelectedEventAction() {
final client = currentRoomBundle.firstWhere(
(cl) => selectedEvents.first.senderId == cl!.userID,
@ -1266,7 +1349,20 @@ class ChatController extends State<ChatPageWithRoom>
}
}
void pinEvent() {
void pinEvent(Event event) {
final pinnedEventIds = room.pinnedEventIds;
if (pinnedEventIds.contains(event.eventId)) {
pinnedEventIds.remove(event.eventId);
} else {
pinnedEventIds.add(event.eventId);
}
showFutureLoadingDialog(
context: context,
future: () => room.setPinnedEvents(pinnedEventIds),
);
}
void pinSelectedEvent() {
final pinnedEventIds = room.pinnedEventIds;
final selectedEventIds = selectedEvents.map((e) => e.eventId).toSet();
final unpin = selectedEventIds.length == 1 &&

@ -141,6 +141,10 @@ class ChatEventList extends StatelessWidget {
controller.animateInEventIndex = null;
},
onReply: () => controller.replyAction(replyTo: event),
onForward: () => controller.forwardEventAction(event),
onPin: () => controller.pinEvent(event),
onRedact: () => controller.redactEventAction(event),
onEdit: () => controller.editEventAction(event),
onInfoTab: controller.showEventInfo,
onMention: () => controller.sendController.text +=
'${event.senderFromMemoryOrFallback.mention} ',
@ -155,7 +159,6 @@ class ChatEventList extends StatelessWidget {
singleSelected:
controller.selectedEvents.singleOrNull?.eventId ==
event.eventId,
onEdit: () => controller.editSelectedEventAction(),
timeline: timeline,
displayReadMarker:
i > 0 && controller.readMarkerEventId == event.eventId,

@ -53,7 +53,7 @@ class ChatView extends StatelessWidget {
if (controller.canPinSelectedEvents)
IconButton(
icon: const Icon(Icons.push_pin_outlined),
onPressed: controller.pinEvent,
onPressed: controller.pinSelectedEvent,
tooltip: L10n.of(context).pinMessage,
),
if (controller.canRedactSelectedEvents)

@ -31,6 +31,9 @@ class Message extends StatelessWidget {
final void Function(Event) onInfoTab;
final void Function(String) scrollToEventId;
final void Function() onReply;
final void Function() onForward;
final void Function() onPin;
final void Function() onRedact;
final void Function() onMention;
final void Function() onEdit;
final bool longPressSelect;
@ -56,6 +59,9 @@ class Message extends StatelessWidget {
required this.onInfoTab,
required this.scrollToEventId,
required this.onReply,
required this.onForward,
required this.onPin,
required this.onRedact,
this.selected = false,
required this.onEdit,
required this.singleSelected,
@ -72,6 +78,37 @@ class Message extends StatelessWidget {
super.key,
});
Future<void> _showContextMenu(BuildContext context, Offset anchor) async {
final result = await showMenu<String>(
context: context,
position:
RelativeRect.fromLTRB(anchor.dx, anchor.dy, anchor.dx, anchor.dy),
items: const [
PopupMenuItem(value: 'reply', child: Text('Reply')),
PopupMenuItem(value: 'copy', child: Text('Copy')),
PopupMenuItem(value: 'forward', child: Text('Forward')),
PopupMenuItem(value: 'pin', child: Text('Pin')),
PopupMenuItem(value: 'edit', child: Text('Edit')),
PopupMenuItem(value: 'redact', child: Text('Redact')),
],
);
switch (result) {
case 'reply':
onReply();
break;
case 'forward':
onForward();
break;
case 'pin':
onPin();
break;
case 'edit':
onEdit();
break;
// handle others
}
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
@ -285,9 +322,17 @@ class Message extends StatelessWidget {
: null,
enableFeedback: !selected,
onLongPress: () => onSelect(event),
onTap: longPressSelect
? () => onSelect(event)
: null,
onTapUp: longPressSelect
? (_) => onSelect(event)
: (details) => _showContextMenu(
context,
details.globalPosition,
),
onSecondaryTapUp: (details) =>
_showContextMenu(
context,
details.globalPosition,
),
borderRadius: BorderRadius.circular(
AppConfig.borderRadius / 2,
),

@ -26,7 +26,8 @@ extension LocalNotificationsExtension on MatrixState {
void showLocalNotification(Event event) async {
Logs().v(
'[Notifications] event received for ${event.room.id} (${event.type})');
'[Notifications] event received for ${event.room.id} (${event.type})',
);
final roomId = event.room.id;
if (activeRoomId == roomId) {
if (kIsWeb && webHasFocus) return;

@ -295,8 +295,6 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
c.onNotification.stream.listen(showLocalNotification);
});
} else if (PlatformInfos.isLinux || PlatformInfos.isMacOS) {
Logs().v(
'[Notifications] Subscribing desktop listener for ${c.clientName}');
onNotification[name] ??=
c.onNotification.stream.listen(showLocalNotification);
}

Loading…
Cancel
Save