fix: add reaction notifier to rebuild reaction picker and reaction display on reaction change (#5151)

This commit is contained in:
ggurdin 2026-01-09 10:49:36 -05:00 committed by GitHub
parent 33d8df3125
commit 2b118e8185
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 84 additions and 34 deletions

View file

@ -10,6 +10,7 @@ import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pages/chat/events/emoji_burst.dart';
import 'package:fluffychat/pages/chat/events/reaction_listener.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
@ -35,10 +36,10 @@ class PangeaMessageReactions extends StatefulWidget {
}
class _PangeaMessageReactionsState extends State<PangeaMessageReactions> {
StreamSubscription? _reactionSubscription;
Map<String, _ReactionEntry> _reactionMap = {};
Set<String> _newlyAddedReactions = {};
late Client client;
ReactionListener? _reactionListener;
@override
void initState() {
@ -48,23 +49,17 @@ class _PangeaMessageReactionsState extends State<PangeaMessageReactions> {
_setupReactionStream();
}
void _setupReactionStream() {
_reactionSubscription = widget.controller.room.client.onSync.stream.where(
(update) {
final room = widget.controller.room;
final timelineEvents = update.rooms?.join?[room.id]?.timeline?.events;
if (timelineEvents == null) return false;
@override
void dispose() {
_reactionListener?.dispose();
super.dispose();
}
final eventID = widget.event.eventId;
return timelineEvents.any(
(e) =>
e.type == EventTypes.Redaction ||
(e.type == EventTypes.Reaction &&
Event.fromMatrixEvent(e, room).relationshipEventId ==
eventID),
);
},
).listen(_onReactionUpdate);
void _setupReactionStream() {
_reactionListener = ReactionListener(
event: widget.event,
onUpdate: _onReactionUpdate,
);
}
void _onReactionUpdate(SyncUpdate update) {
@ -106,12 +101,6 @@ class _PangeaMessageReactionsState extends State<PangeaMessageReactions> {
_reactionMap = newReactionMap;
}
@override
void dispose() {
_reactionSubscription?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
final reactionList = _reactionMap.values.toList()

View file

@ -0,0 +1,34 @@
import 'dart:async';
import 'package:matrix/matrix.dart';
class ReactionListener {
final Event event;
final Function(SyncUpdate) onUpdate;
StreamSubscription? _reactionSub;
ReactionListener({required this.event, required this.onUpdate}) {
_reactionSub = event.room.client.onSync.stream.where(
(update) {
final room = event.room;
final timelineEvents = update.rooms?.join?[room.id]?.timeline?.events;
if (timelineEvents == null) return false;
final eventID = event.eventId;
return timelineEvents.any(
(e) =>
e.type == EventTypes.Redaction ||
(e.type == EventTypes.Reaction &&
Event.fromMatrixEvent(e, room).relationshipEventId ==
eventID),
);
},
).listen(onUpdate);
}
void dispose() {
_reactionSub?.cancel();
_reactionSub = null;
}
}

View file

@ -10,6 +10,7 @@ import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pages/chat/events/reaction_listener.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
@ -62,6 +63,9 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
PangeaMessageEvent get pangeaMessageEvent =>
widget.overlayController.pangeaMessageEvent;
ReactionListener? _reactionListener;
final ValueNotifier<double?> reactionNotifier = ValueNotifier(null);
@override
void initState() {
super.initState();
@ -76,10 +80,25 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
});
},
);
reactionNotifier.value = _reactionsWidth;
_reactionListener = ReactionListener(
event: widget.event,
onUpdate: (update) {
if (mounted) {
final newWidth = _reactionsWidth;
if (newWidth != reactionNotifier.value) {
reactionNotifier.value = newWidth;
}
}
},
);
}
@override
void dispose() {
reactionNotifier.dispose();
_reactionListener?.dispose();
scrollController?.dispose();
super.dispose();
}
@ -127,7 +146,7 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
return hasReactions ? 28.0 : 0.0;
}
double? get reactionsWidth {
double? get _reactionsWidth {
if (_reactionsRenderBox != null) {
return _reactionsRenderBox!.size.width;
}

View file

@ -67,7 +67,7 @@ class OverMessageOverlay extends StatelessWidget {
readingAssistanceMode: controller.readingAssistanceMode,
overlayKey:
'overlay_message_${controller.widget.event.eventId}',
reactionsWidth: controller.reactionsWidth,
reactionsWidth: controller.reactionNotifier,
);
},
),

View file

@ -25,7 +25,7 @@ class OverlayCenterContent extends StatelessWidget {
final double? messageWidth;
final bool hasReactions;
final double? reactionsWidth;
final ValueNotifier<double?> reactionsWidth;
final bool isTransitionAnimation;
final ReadingAssistanceMode? readingAssistanceMode;
@ -42,7 +42,7 @@ class OverlayCenterContent extends StatelessWidget {
required this.nextEvent,
required this.prevEvent,
required this.hasReactions,
this.reactionsWidth,
required this.reactionsWidth,
this.onChangeMessageSize,
this.sizeAnimation,
this.isTransitionAnimation = false,
@ -96,11 +96,14 @@ class OverlayCenterContent extends StatelessWidget {
top: 4.0,
left: ownMessage ? 0.0 : 4.0,
),
child: PangeaMessageReactions(
event,
chatController.timeline!,
chatController,
width: reactionsWidth,
child: ValueListenableBuilder(
valueListenable: reactionsWidth,
builder: (context, width, __) => PangeaMessageReactions(
event,
chatController.timeline!,
chatController,
width: width != null && width > 0 ? width : null,
),
),
),
],

View file

@ -145,6 +145,7 @@ class PracticeModeTransitionAnimationState
widget.controller.readingAssistanceMode,
overlayKey:
"overlay_transition_message_${widget.controller.widget.event.eventId}",
reactionsWidth: widget.controller.reactionNotifier,
),
);
},
@ -195,6 +196,7 @@ class CenteredMessage extends StatelessWidget {
overlayKey:
"overlay_center_message_${controller.widget.event.eventId}",
readingAssistanceMode: controller.readingAssistanceMode,
reactionsWidth: controller.reactionNotifier,
),
const SizedBox(
height: AppConfig.readingAssistanceInputBarHeight + 60.0,

View file

@ -24,8 +24,11 @@ class WordCardSwitcher extends StatelessWidget {
overlayController: controller.widget.overlayController,
)
: mode != SelectMode.emoji
? MessageReactionPicker(
chatController: controller.widget.chatController,
? ValueListenableBuilder(
valueListenable: controller.reactionNotifier,
builder: (context, _, __) => MessageReactionPicker(
chatController: controller.widget.chatController,
),
)
: const SizedBox.shrink(),
);