disable react button while last action is pending

This commit is contained in:
Henry Hiles 2026-04-12 15:07:49 -04:00
commit 3e8eba0872
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
3 changed files with 79 additions and 53 deletions

View file

@ -377,6 +377,7 @@ class RoomChatController extends AsyncNotifier<InMemoryChatController> {
"key": reaction, "key": reaction,
}, },
}, },
synchronous: true,
disableEncryption: true, disableEncryption: true,
), ),
); );

View file

@ -8,6 +8,7 @@ abstract class SendEventRequest with _$SendEventRequest {
required String roomId, required String roomId,
required String type, required String type,
required Map<String, dynamic> content, required Map<String, dynamic> content,
@Default(false) bool synchronous,
@Default(false) bool disableEncryption, @Default(false) bool disableEncryption,
}) = _SendEventRequest; }) = _SendEventRequest;

View file

@ -2,6 +2,7 @@ import "package:cross_cache/cross_cache.dart";
import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_chat_core/flutter_chat_core.dart";
import "package:flutter_hooks/flutter_hooks.dart";
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/client_state_controller.dart"; import "package:nexus/controllers/client_state_controller.dart";
import "package:nexus/controllers/cross_cache_controller.dart"; import "package:nexus/controllers/cross_cache_controller.dart";
@ -24,63 +25,86 @@ class ReactionRow extends ConsumerWidget {
runSpacing: 4, runSpacing: 4,
children: clientState?.homeserverUrl == null || message.reactions == null children: clientState?.homeserverUrl == null || message.reactions == null
? [] ? []
: message.reactions!.mapTo((reaction, reactors) { : message.reactions!
final selected = reactors.contains(clientState!.userId); .mapTo(
return SizedBox( (reaction, reactors) => HookBuilder(
child: Tooltip( builder: (context) {
message: reactors.join(", "), final enabled = useState(true);
child: ChoiceChip( final selected = reactors.contains(clientState!.userId);
showCheckmark: false, return SizedBox(
selected: selected, child: Tooltip(
label: Row( message: reactors.join(", "),
mainAxisSize: MainAxisSize.min, child: ChoiceChip(
spacing: 8, showCheckmark: false,
children: [ selected: selected,
reaction.startsWith("mxc://") label: Row(
? Image( mainAxisSize: MainAxisSize.min,
height: 20, spacing: 8,
image: CachedNetworkImage( children: [
headers: ref.headers, reaction.startsWith("mxc://")
Uri.parse(reaction) ? Image(
.mxcToHttps(clientState.homeserverUrl!) height: 20,
.toString(), image: CachedNetworkImage(
ref.watch(CrossCacheController.provider), headers: ref.headers,
), Uri.parse(reaction)
) .mxcToHttps(
: Text(reaction), clientState.homeserverUrl!,
Text(reactors.length.toString()), )
], .toString(),
), ref.watch(
onSelected: (value) async { CrossCacheController.provider,
final roomId = ref.watch( ),
SelectedRoomController.provider.select( ),
(value) => value?.metadata?.id, )
: Text(reaction),
Text(reactors.length.toString()),
],
),
onSelected: enabled.value
? (value) async {
enabled.value = false;
try {
final roomId = ref.watch(
SelectedRoomController.provider.select(
(value) => value?.metadata?.id,
),
);
if (roomId == null ||
clientState.userId == null) {
return;
}
final controller = ref.watch(
RoomChatController.provider(
roomId,
).notifier,
);
if (selected) {
await controller
.removeReaction(
reaction,
message,
clientState.userId!,
)
.onError(showError);
} else {
await controller
.sendReaction(reaction, message)
.onError(showError);
}
} finally {
enabled.value = true;
}
}
: null,
),
), ),
); );
if (roomId == null || clientState.userId == null) return;
final controller = ref.watch(
RoomChatController.provider(roomId).notifier,
);
if (selected) {
await controller
.removeReaction(
reaction,
message,
clientState.userId!,
)
.onError(showError);
} else {
await controller
.sendReaction(reaction, message)
.onError(showError);
}
}, },
), ),
), )
); .toList(),
}).toList(),
); );
} }
} }