105 lines
4.0 KiB
Dart
105 lines
4.0 KiB
Dart
import 'package:flutter/material.dart';
|
||
|
||
import '../../../../api/marianumcloud/talk/chat/get_chat_response.dart';
|
||
import '../../../../api/marianumcloud/talk/delete_react_message/delete_react_message.dart';
|
||
import '../../../../api/marianumcloud/talk/delete_react_message/delete_react_message_params.dart';
|
||
import '../../../../api/marianumcloud/talk/react_message/react_message.dart';
|
||
import '../../../../api/marianumcloud/talk/react_message/react_message_params.dart';
|
||
import '../../../../api/marianumcloud/talk/room/get_room_response.dart';
|
||
import '../../../../widget/a11y/a11y_labels.dart';
|
||
import '../../../../widget/async_action_button.dart';
|
||
import '../../../../widget/demo_restricted.dart';
|
||
import '../../../../widget/emoji_text.dart';
|
||
|
||
/// Reactions wrap shown beneath a chat bubble. Tapping a reaction toggles
|
||
/// the user's own reaction via the Talk API and notifies via [onChanged].
|
||
class ChatBubbleReactions extends StatelessWidget {
|
||
final GetChatResponseObject bubbleData;
|
||
final GetRoomResponseObject chatData;
|
||
final bool isSender;
|
||
final void Function({bool renew}) onChanged;
|
||
|
||
const ChatBubbleReactions({
|
||
required this.bubbleData,
|
||
required this.chatData,
|
||
required this.isSender,
|
||
required this.onChanged,
|
||
super.key,
|
||
});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final reactions = bubbleData.reactions;
|
||
if (reactions == null) return const SizedBox.shrink();
|
||
return Transform.translate(
|
||
offset: const Offset(0, -10),
|
||
child: Container(
|
||
width: MediaQuery.of(context).size.width,
|
||
margin: const EdgeInsets.only(left: 15, right: 15),
|
||
child: Wrap(
|
||
alignment: isSender ? WrapAlignment.end : WrapAlignment.start,
|
||
crossAxisAlignment: WrapCrossAlignment.start,
|
||
children: reactions.entries.map<Widget>((e) {
|
||
final hasSelfReacted =
|
||
bubbleData.reactionsSelf?.contains(e.key) ?? false;
|
||
void toggle() {
|
||
if (guardDemoAction(context)) return;
|
||
runWithErrorDialog(context, () async {
|
||
if (hasSelfReacted) {
|
||
await DeleteReactMessage(
|
||
chatToken: chatData.token,
|
||
messageId: bubbleData.id,
|
||
params: DeleteReactMessageParams(e.key),
|
||
).run();
|
||
} else {
|
||
await ReactMessage(
|
||
chatToken: chatData.token,
|
||
messageId: bubbleData.id,
|
||
params: ReactMessageParams(e.key),
|
||
).run();
|
||
}
|
||
onChanged(renew: true);
|
||
});
|
||
}
|
||
|
||
// Ownership (eigene Reaktion) wird visuell nur über die
|
||
// Hintergrundfarbe transportiert – für den Screenreader ins Label.
|
||
final label = hasSelfReacted
|
||
? '${e.key} ${e.value}, ${A11yLabels.yourReaction}'
|
||
: '${e.key} ${e.value}';
|
||
return Container(
|
||
margin: const EdgeInsets.only(right: 2.5, left: 2.5),
|
||
child: Semantics(
|
||
button: true,
|
||
label: label,
|
||
onTap: toggle,
|
||
child: ExcludeSemantics(
|
||
child: ActionChip(
|
||
label: Row(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
EmojiText(e.key, size: EmojiText.sizeInline),
|
||
const SizedBox(width: 4),
|
||
Text('${e.value}'),
|
||
],
|
||
),
|
||
visualDensity: const VisualDensity(
|
||
vertical: VisualDensity.minimumDensity,
|
||
horizontal: VisualDensity.minimumDensity,
|
||
),
|
||
padding: EdgeInsets.zero,
|
||
backgroundColor: hasSelfReacted
|
||
? Theme.of(context).primaryColor
|
||
: null,
|
||
onPressed: toggle,
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}).toList(),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|