Skip to content

Commit

Permalink
clean up reaction actions UI and transform into message actions imple…
Browse files Browse the repository at this point in the history
…mentation
  • Loading branch information
gtalha07 committed Jan 28, 2025
1 parent 6975d09 commit 181bf49
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 33 deletions.
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import 'dart:ui';

import 'package:acter/features/chat_ng/widgets/message_actions_widget.dart';
import 'package:acter/features/chat_ng/widgets/reactions/reaction_selector.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

// reaction selector action on chat message
void reactionSelectionAction({
// message actions on chat message
void messageActions({
required BuildContext context,
required Widget messageWidget,
required bool isMe,
required bool canRedact,
required String roomId,
required String messageId,
}) {
final RenderBox box = context.findRenderObject() as RenderBox;
final Offset position = box.localToGlobal(Offset.zero);
final messageSize = box.size;
}) async {
// trigger vibration haptic
await HapticFeedback.heavyImpact();
if (!context.mounted) return;

showGeneralDialog(
context: context,
Expand All @@ -22,29 +25,35 @@ void reactionSelectionAction({
barrierColor: Colors.transparent,
transitionDuration: const Duration(milliseconds: 200),
pageBuilder: (context, animation, secondaryAnimation) {
return Stack(
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_ReactionOverlay(
_BlurOverlay(
animation: animation,
child: const SizedBox.expand(),
child: const SizedBox.shrink(),
),
Positioned(
left: position.dx,
top: position.dy,
width: messageSize.width,
child: messageWidget,
// Reaction Row
_AnimatedActionsContainer(
animation: animation,
tagId: messageId,
child: ReactionSelector(
isMe: isMe,
messageId: '$messageId-reactions',
roomId: roomId,
),
),
Positioned(
left: position.dx,
top: position.dy - 60,
child: _AnimatedReactionSelector(
animation: animation,
// Message
messageWidget,
// Message actions
_AnimatedActionsContainer(
animation: animation,
tagId: '$messageId-actions',
child: MessageActionsWidget(
isMe: isMe,
canRedact: canRedact,
messageId: messageId,
child: ReactionSelector(
isMe: isMe,
messageId: messageId,
roomId: roomId,
),
roomId: roomId,
),
),
],
Expand All @@ -53,11 +62,11 @@ void reactionSelectionAction({
);
}

class _ReactionOverlay extends StatelessWidget {
class _BlurOverlay extends StatelessWidget {
final Animation<double> animation;
final Widget child;

const _ReactionOverlay({
const _BlurOverlay({
required this.animation,
required this.child,
});
Expand Down Expand Up @@ -86,21 +95,21 @@ class _ReactionOverlay extends StatelessWidget {
}
}

class _AnimatedReactionSelector extends StatelessWidget {
class _AnimatedActionsContainer extends StatelessWidget {
final Animation<double> animation;
final Widget child;
final String messageId;
final String tagId;

const _AnimatedReactionSelector({
const _AnimatedActionsContainer({
required this.animation,
required this.child,
required this.messageId,
required this.tagId,
});

@override
Widget build(BuildContext context) {
return Hero(
tag: messageId,
tag: tagId,
child: Material(
color: Colors.transparent,
child: AnimatedBuilder(
Expand Down
4 changes: 4 additions & 0 deletions app/lib/features/chat_ng/widgets/events/chat_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ class ChatEvent extends ConsumerWidget {
final options = AvatarOptions.DM(avatarInfo, size: 14);
final myId = ref.watch(myUserIdStrProvider);
final messageId = msg.uniqueId();
// FIXME: should check canRedact permission from the room
final canRedact = item.sender() == myId;

final isMe = myId == item.sender();
// TODO: render a regular timeline event
return Row(
Expand All @@ -89,6 +92,7 @@ class ChatEvent extends ConsumerWidget {
messageId: messageId,
item: item,
isMe: isMe,
canRedact: canRedact,
isNextMessageInGroup: isNextMessageInGroup,
),
),
Expand Down
3 changes: 3 additions & 0 deletions app/lib/features/chat_ng/widgets/events/chat_event_item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ class ChatEventItem extends StatelessWidget {
final String messageId;
final RoomEventItem item;
final bool isMe;
final bool canRedact;
final bool isNextMessageInGroup;
const ChatEventItem({
super.key,
required this.roomId,
required this.messageId,
required this.item,
required this.isMe,
required this.canRedact,
required this.isNextMessageInGroup,
});

Expand All @@ -35,6 +37,7 @@ class ChatEventItem extends StatelessWidget {
messageId: messageId,
item: item,
isMe: isMe,
canRedact: canRedact,
isNextMessageInGroup: isNextMessageInGroup,
),
'm.room.redaction' => isMe
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:acter/features/chat/utils.dart';
import 'package:acter/features/chat_ng/actions/reaction_selection_action.dart';
import 'package:acter/features/chat_ng/dialogs/message_actions.dart';
import 'package:acter/features/chat_ng/widgets/chat_bubble.dart';
import 'package:acter/features/chat_ng/widgets/events/file_message_event.dart';
import 'package:acter/features/chat_ng/widgets/events/image_message_event.dart';
Expand All @@ -17,6 +17,7 @@ class MessageEventItem extends StatelessWidget {
final String messageId;
final RoomEventItem item;
final bool isMe;
final bool canRedact;
final bool isNextMessageInGroup;

const MessageEventItem({
Expand All @@ -25,6 +26,7 @@ class MessageEventItem extends StatelessWidget {
required this.messageId,
required this.item,
required this.isMe,
required this.canRedact,
required this.isNextMessageInGroup,
});

Expand Down Expand Up @@ -56,10 +58,11 @@ class MessageEventItem extends StatelessWidget {
);

return GestureDetector(
onLongPressStart: (_) => reactionSelectionAction(
onLongPressStart: (_) => messageActions(
context: context,
messageWidget: messageWidget,
isMe: isMe,
canRedact: canRedact,
roomId: roomId,
messageId: messageId,
),
Expand Down
106 changes: 106 additions & 0 deletions app/lib/features/chat_ng/widgets/message_actions_widget.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import 'package:atlas_icons/atlas_icons.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';

class MessageActionsWidget extends StatelessWidget {
final bool isMe;
final bool canRedact;
final String messageId;
final String roomId;
const MessageActionsWidget({
super.key,
required this.isMe,
required this.canRedact,
required this.messageId,
required this.roomId,
});

@override
Widget build(BuildContext context) {
final lang = L10n.of(context);
return Container(
constraints: const BoxConstraints(maxWidth: 200),
padding: const EdgeInsets.all(8.0),
margin: const EdgeInsets.only(top: 4),
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(5)),
color: Theme.of(context).colorScheme.surface.withOpacity(0.8),
),
child: Column(
children: menuItems(context, lang).map((e) => e).toList(),
),
);
}

List<Widget> menuItems(BuildContext context, L10n lang) => [
makeMenuItem(
pressed: () {},
text: Text(lang.reply),
icon: const Icon(Icons.reply_rounded, size: 18),
),
// if (isTextMessage)
// makeMenuItem(
// pressed: () => onCopyMessage(context, ref, message),
// text: Text(lang.copyMessage),
// icon: const Icon(
// Icons.copy_all_outlined,
// size: 14,
// ),
// ),
if (isMe)
makeMenuItem(
pressed: () {},
text: Text(lang.edit),
icon: const Icon(Atlas.pencil_box_bold, size: 14),
),
if (!isMe)
makeMenuItem(
pressed: () {},
text: Text(
lang.report,
style: TextStyle(color: Theme.of(context).colorScheme.error),
),
icon: Icon(
Icons.flag_outlined,
size: 14,
color: Theme.of(context).colorScheme.error,
),
),
if (canRedact)
makeMenuItem(
pressed: () {},
text: Text(
lang.delete,
style: TextStyle(color: Theme.of(context).colorScheme.error),
),
icon: Icon(
Atlas.trash_can_thin,
size: 14,
color: Theme.of(context).colorScheme.error,
),
),
];

Widget makeMenuItem({
required Widget text,
Icon? icon,
required void Function() pressed,
}) {
return InkWell(
onTap: pressed,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 3,
vertical: 5,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
text,
if (icon != null) icon,
],
),
),
);
}
}

0 comments on commit 181bf49

Please sign in to comment.