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": {} "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": "Mute chat",
"@muteChat": { "@muteChat": {
"type": "text", "type": "text",

View File

@ -110,6 +110,8 @@ class ChatController extends State<Chat> {
List<Event> selectedEvents = []; List<Event> selectedEvents = [];
bool actionsDisabled = false;
final Set<String> unfolded = {}; final Set<String> unfolded = {};
Event? replyEvent; Event? replyEvent;
@ -259,7 +261,7 @@ class ChatController extends State<Chat> {
if (sendController.text.trim().isEmpty) return; if (sendController.text.trim().isEmpty) return;
var parseCommands = true; var parseCommands = true;
final commandMatch = RegExp(r'^\/(\w+)').firstMatch(sendController.text); final commandMatch = RegExp(r'^/(\w+)').firstMatch(sendController.text);
if (commandMatch != null && if (commandMatch != null &&
!room!.client.commands.keys.contains(commandMatch[1]!.toLowerCase())) { !room!.client.commands.keys.contains(commandMatch[1]!.toLowerCase())) {
final l10n = L10n.of(context)!; final l10n = L10n.of(context)!;
@ -549,10 +551,29 @@ class ChatController extends State<Chat> {
) == ) ==
OkCancelResult.ok; OkCancelResult.ok;
if (!confirmed) return; 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) { for (final event in selectedEvents) {
await showFutureLoadingDialog( try {
context: context,
future: () async {
if (event.status.isSent) { if (event.status.isSent) {
if (event.canRedact) { if (event.canRedact) {
await event.redactEvent(); await event.redactEvent();
@ -569,11 +590,24 @@ class ChatController extends State<Chat> {
} else { } else {
await event.remove(); 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(() { setState(() {
showEmojiPicker = false; actionsDisabled = false;
selectedEvents.clear();
}); });
} }
@ -823,6 +857,7 @@ class ChatController extends State<Chat> {
} }
void onSelectMessage(Event event) { void onSelectMessage(Event event) {
if (actionsDisabled) return;
if (!event.redacted) { if (!event.redacted) {
if (selectedEvents.contains(event)) { if (selectedEvents.contains(event)) {
setState( setState(

View File

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