chore: do not block UI while redacting events

Signed-off-by: TheOneWithTheBraid <the-one@with-the-braid.cf>
This commit is contained in:
TheOneWithTheBraid 2022-12-24 07:13:44 +01:00
parent 56b3297610
commit ccfd4ecae3
3 changed files with 88 additions and 31 deletions

View File

@ -1467,6 +1467,20 @@
"count": {}
}
},
"messagesRemoving": "{count,plural, =1{Message removing...} other{Messages removing...}}",
"@messagesRemoving": {
"type": "text",
"placeholders": {
"count": {}
}
},
"couldNotDeleteMessages": "{count,plural, =1{Could not delete the message.} other{Could not delete {count} message.}}",
"@couldNotDeleteMessages": {
"type": "text",
"placeholders": {
"count": {}
}
},
"muteChat": "Mute chat",
"@muteChat": {
"type": "text",

View File

@ -110,6 +110,8 @@ class ChatController extends State<Chat> {
List<Event> selectedEvents = [];
bool actionsDisabled = false;
final Set<String> unfolded = {};
Event? replyEvent;
@ -259,7 +261,7 @@ class ChatController extends State<Chat> {
if (sendController.text.trim().isEmpty) return;
var parseCommands = true;
final commandMatch = RegExp(r'^\/(\w+)').firstMatch(sendController.text);
final commandMatch = RegExp(r'^/(\w+)').firstMatch(sendController.text);
if (commandMatch != null &&
!room!.client.commands.keys.contains(commandMatch[1]!.toLowerCase())) {
final l10n = L10n.of(context)!;
@ -549,31 +551,63 @@ class ChatController extends State<Chat> {
) ==
OkCancelResult.ok;
if (!confirmed) return;
setState(() {
actionsDisabled = true;
showEmojiPicker = false;
});
ScaffoldFeatureController<MaterialBanner, MaterialBannerClosedReason>?
bannerController;
final banner = MaterialBanner(
content: Text(L10n.of(context)!.messagesRemoving(selectedEvents.length)),
actions: [
TextButton(
onPressed: () => bannerController?.close(),
child: Text(L10n.of(context)!.close),
),
],
);
bannerController = ScaffoldMessenger.of(context).showMaterialBanner(banner);
final Set<String> failedRedacts = {};
for (final event in selectedEvents) {
await showFutureLoadingDialog(
context: context,
future: () async {
if (event.status.isSent) {
if (event.canRedact) {
await event.redactEvent();
} 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();
}
} else {
await event.remove();
try {
if (event.status.isSent) {
if (event.canRedact) {
await event.redactEvent();
} 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();
}
} else {
await event.remove();
}
} catch (e) {
failedRedacts.add(event.eventId);
}
}
bannerController.close();
if (failedRedacts.isEmpty) {
selectedEvents.clear();
} else {
selectedEvents.removeWhere(
(element) => !failedRedacts.contains(element.eventId),
);
showAlertDialog(
context: context,
message: L10n.of(context)!.couldNotDeleteMessages(failedRedacts.length),
);
}
setState(() {
showEmojiPicker = false;
selectedEvents.clear();
actionsDisabled = false;
});
}
@ -823,6 +857,7 @@ class ChatController extends State<Chat> {
}
void onSelectMessage(Event event) {
if (actionsDisabled) return;
if (!event.redacted) {
if (selectedEvents.contains(event)) {
setState(

View File

@ -40,30 +40,38 @@ class ChatView extends StatelessWidget {
IconButton(
icon: const Icon(Icons.edit_outlined),
tooltip: L10n.of(context)!.edit,
onPressed: controller.editSelectedEventAction,
onPressed: controller.actionsDisabled
? null
: controller.editSelectedEventAction,
),
IconButton(
icon: const Icon(Icons.copy_outlined),
tooltip: L10n.of(context)!.copy,
onPressed: controller.copyEventsAction,
onPressed:
controller.actionsDisabled ? null : controller.copyEventsAction,
),
if (controller.canSaveSelectedEvent)
// Use builder context to correctly position the share dialog on iPad
Builder(
builder: (context) => IconButton(
icon: Icon(Icons.adaptive.share),
tooltip: L10n.of(context)!.share,
onPressed: () => controller.saveSelectedEvent(context),
)),
builder: (context) => IconButton(
icon: Icon(Icons.adaptive.share),
tooltip: L10n.of(context)!.share,
onPressed: controller.actionsDisabled
? null
: () => controller.saveSelectedEvent(context),
),
),
if (controller.canRedactSelectedEvents)
IconButton(
icon: const Icon(Icons.delete_outlined),
tooltip: L10n.of(context)!.redactMessage,
onPressed: controller.redactEventsAction,
onPressed: controller.actionsDisabled
? null
: controller.redactEventsAction,
),
IconButton(
icon: const Icon(Icons.push_pin_outlined),
onPressed: controller.pinEvent,
onPressed: controller.actionsDisabled ? null : controller.pinEvent,
tooltip: L10n.of(context)!.pinMessage,
),
if (controller.selectedEvents.length == 1)