Added selection features to toolbar overlay

pull/1384/head
Kelrap 1 year ago
parent 668b9dd65f
commit 622384036b

@ -165,7 +165,9 @@ class ChatEventList extends StatelessWidget {
), ),
highlightMarker: highlightMarker:
controller.scrollToEventIdMarker == event.eventId, controller.scrollToEventIdMarker == event.eventId,
onSelect: controller.onSelectMessage, // #Pangea
// onSelect: controller.onSelectMessage,
// Pangea#
scrollToEventId: (String eventId) => scrollToEventId: (String eventId) =>
controller.scrollToEventId(eventId), controller.scrollToEventId(eventId),
longPressSelect: controller.selectedEvents.isNotEmpty, longPressSelect: controller.selectedEvents.isNotEmpty,

@ -7,7 +7,6 @@ import 'package:fluffychat/pages/chat/chat_emoji_picker.dart';
import 'package:fluffychat/pages/chat/chat_event_list.dart'; import 'package:fluffychat/pages/chat/chat_event_list.dart';
import 'package:fluffychat/pages/chat/chat_input_row.dart'; import 'package:fluffychat/pages/chat/chat_input_row.dart';
import 'package:fluffychat/pages/chat/pinned_events.dart'; import 'package:fluffychat/pages/chat/pinned_events.dart';
import 'package:fluffychat/pages/chat/reactions_picker.dart';
import 'package:fluffychat/pages/chat/reply_display.dart'; import 'package:fluffychat/pages/chat/reply_display.dart';
import 'package:fluffychat/pangea/choreographer/widgets/it_bar.dart'; import 'package:fluffychat/pangea/choreographer/widgets/it_bar.dart';
import 'package:fluffychat/pangea/choreographer/widgets/start_igc_button.dart'; import 'package:fluffychat/pangea/choreographer/widgets/start_igc_button.dart';
@ -35,6 +34,21 @@ class ChatView extends StatelessWidget {
const ChatView(this.controller, {super.key}); const ChatView(this.controller, {super.key});
// #Pangea
List<Widget> _editedAppBarActions(BuildContext context) {
if (!controller.selectMode) {
return [
ChatSettingsPopupMenu(
controller.room,
(!controller.room.isDirectChat && !controller.room.isArchived),
),
];
} else {
return [];
}
}
// Pangea#
List<Widget> _appBarActions(BuildContext context) { List<Widget> _appBarActions(BuildContext context) {
if (controller.selectMode) { if (controller.selectMode) {
return [ return [
@ -197,26 +211,33 @@ class ChatView extends StatelessWidget {
? null ? null
: Theme.of(context).colorScheme.primary, : Theme.of(context).colorScheme.primary,
), ),
leading: controller.selectMode leading:
? IconButton( // #Pangea
icon: const Icon(Icons.close), // controller.selectMode
onPressed: controller.clearSelectedEvents, // ? IconButton(
tooltip: L10n.of(context)!.close, // icon: const Icon(Icons.close),
color: Theme.of(context).colorScheme.primary, // onPressed: controller.clearSelectedEvents,
) // tooltip: L10n.of(context)!.close,
: UnreadRoomsBadge( // color: Theme.of(context).colorScheme.primary,
filter: (r) => // )
r.id != controller.roomId // :
// #Pangea // Pangea#
&& UnreadRoomsBadge(
!r.isAnalyticsRoom, filter: (r) =>
// Pangea# r.id != controller.roomId
badgePosition: BadgePosition.topEnd(end: 8, top: 4), // #Pangea
child: const Center(child: BackButton()), &&
), !r.isAnalyticsRoom,
// Pangea#
badgePosition: BadgePosition.topEnd(end: 8, top: 4),
child: const Center(child: BackButton()),
),
titleSpacing: 0, titleSpacing: 0,
title: ChatAppBarTitle(controller), title: ChatAppBarTitle(controller),
actions: _appBarActions(context), // #Pangea
// actions: _appBarActions(context),
actions: _editedAppBarActions(context),
// Pangea#
bottom: PreferredSize( bottom: PreferredSize(
preferredSize: Size.fromHeight(appbarBottomHeight), preferredSize: Size.fromHeight(appbarBottomHeight),
child: Column( child: Column(
@ -497,7 +518,6 @@ class ChatView extends StatelessWidget {
ITBar( ITBar(
choreographer: controller.choreographer, choreographer: controller.choreographer,
), ),
ReactionsPicker(controller),
ReplyDisplay(controller), ReplyDisplay(controller),
ChatInputRow(controller), ChatInputRow(controller),
ChatEmojiPicker(controller), ChatEmojiPicker(controller),

@ -9,7 +9,6 @@ import 'package:fluffychat/utils/string_color.dart';
import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:swipe_to_action/swipe_to_action.dart'; import 'package:swipe_to_action/swipe_to_action.dart';
@ -26,7 +25,6 @@ class Message extends StatelessWidget {
final Event? nextEvent; final Event? nextEvent;
final Event? previousEvent; final Event? previousEvent;
final bool displayReadMarker; final bool displayReadMarker;
final void Function(Event) onSelect;
final void Function(Event) onAvatarTab; final void Function(Event) onAvatarTab;
final void Function(Event) onInfoTab; final void Function(Event) onInfoTab;
final void Function(String) scrollToEventId; final void Function(String) scrollToEventId;
@ -38,6 +36,7 @@ class Message extends StatelessWidget {
final bool animateIn; final bool animateIn;
final void Function()? resetAnimateIn; final void Function()? resetAnimateIn;
// #Pangea // #Pangea
// final void Function(Event) onSelect;
final bool immersionMode; final bool immersionMode;
final bool definitions; final bool definitions;
final ChatController controller; final ChatController controller;
@ -50,7 +49,9 @@ class Message extends StatelessWidget {
this.previousEvent, this.previousEvent,
this.displayReadMarker = false, this.displayReadMarker = false,
this.longPressSelect = false, this.longPressSelect = false,
required this.onSelect, // #Pangea
// required this.onSelect,
// Pangea#
required this.onInfoTab, required this.onInfoTab,
required this.onAvatarTab, required this.onAvatarTab,
required this.scrollToEventId, required this.scrollToEventId,
@ -203,8 +204,10 @@ class Message extends StatelessWidget {
left: 0, left: 0,
right: 0, right: 0,
child: InkWell( child: InkWell(
onTap: () => onSelect(event), // #Pangea
onLongPress: () => onSelect(event), // onTap: () => onSelect(event),
// onLongPress: () => onSelect(event),
// Pangea#
borderRadius: borderRadius:
BorderRadius.circular(AppConfig.borderRadius / 2), BorderRadius.circular(AppConfig.borderRadius / 2),
child: Material( child: Material(
@ -228,17 +231,20 @@ class Message extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: rowMainAxisAlignment, mainAxisAlignment: rowMainAxisAlignment,
children: [ children: [
if (longPressSelect) // #Pangea
SizedBox( // if (longPressSelect)
height: 32, // SizedBox(
width: Avatar.defaultSize, // height: 32,
child: Checkbox.adaptive( // width: Avatar.defaultSize,
value: selected, // child: Checkbox.adaptive(
shape: const CircleBorder(), // value: selected,
onChanged: (_) => onSelect(event), // shape: const CircleBorder(),
), // onChanged: (_) => onSelect(event),
) // ),
else if (nextEventSameSender || ownMessage) // )
// else
// Pangea#
if (nextEventSameSender || ownMessage)
SizedBox( SizedBox(
width: Avatar.defaultSize, width: Avatar.defaultSize,
child: Center( child: Center(
@ -319,13 +325,13 @@ class Message extends StatelessWidget {
), ),
onDoubleTap: () => onDoubleTap: () =>
toolbarController?.showToolbar(context), toolbarController?.showToolbar(context),
// onLongPress: longPressSelect
// ? null
// : () {
// HapticFeedback.heavyImpact();
// onSelect(event);
// },
// Pangea# // Pangea#
onLongPress: longPressSelect
? null
: () {
HapticFeedback.heavyImpact();
onSelect(event);
},
child: AnimatedOpacity( child: AnimatedOpacity(
opacity: animateIn opacity: animateIn
? 0 ? 0

@ -317,9 +317,11 @@ class MessageContent extends StatelessWidget {
pangeaMessageEvent != null && pangeaMessageEvent != null &&
!(toolbarController!.highlighted) && !(toolbarController!.highlighted) &&
!selected) { !selected) {
toolbarController!.controller.onSelectMessage( // #Pangea
pangeaMessageEvent!.event, // toolbarController!.controller.onSelectMessage(
); // pangeaMessageEvent!.event,
// );
// Pangea#
return; return;
} }
toolbarController?.toolbar?.textSelection toolbarController?.toolbar?.textSelection

@ -27,6 +27,7 @@ class OverlayUtil {
Alignment? followerAnchor, Alignment? followerAnchor,
bool closePrevOverlay = true, bool closePrevOverlay = true,
bool targetScreen = false, bool targetScreen = false,
Function? onDismiss,
}) { }) {
try { try {
if (closePrevOverlay) { if (closePrevOverlay) {
@ -44,6 +45,7 @@ class OverlayUtil {
if (backDropToDismiss) if (backDropToDismiss)
TransparentBackdrop( TransparentBackdrop(
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
onDismiss: onDismiss,
), ),
Positioned( Positioned(
width: width, width: width,
@ -194,8 +196,10 @@ class OverlayUtil {
class TransparentBackdrop extends StatelessWidget { class TransparentBackdrop extends StatelessWidget {
final Color? backgroundColor; final Color? backgroundColor;
final Function? onDismiss;
const TransparentBackdrop({ const TransparentBackdrop({
super.key, super.key,
this.onDismiss,
this.backgroundColor, this.backgroundColor,
}); });
@ -211,6 +215,9 @@ class TransparentBackdrop extends StatelessWidget {
focusColor: Colors.transparent, focusColor: Colors.transparent,
highlightColor: Colors.transparent, highlightColor: Colors.transparent,
onTap: () { onTap: () {
if (onDismiss != null) {
onDismiss!();
}
MatrixState.pAnyState.closeOverlay(); MatrixState.pAnyState.closeOverlay();
}, },
child: Container( child: Container(

@ -12,6 +12,8 @@ import 'package:fluffychat/pangea/widgets/chat/message_speech_to_text_card.dart'
import 'package:fluffychat/pangea/widgets/chat/message_text_selection.dart'; import 'package:fluffychat/pangea/widgets/chat/message_text_selection.dart';
import 'package:fluffychat/pangea/widgets/chat/message_translation_card.dart'; import 'package:fluffychat/pangea/widgets/chat/message_translation_card.dart';
import 'package:fluffychat/pangea/widgets/chat/message_unsubscribed_card.dart'; import 'package:fluffychat/pangea/widgets/chat/message_unsubscribed_card.dart';
import 'package:fluffychat/pangea/widgets/chat/overlay_footer.dart';
import 'package:fluffychat/pangea/widgets/chat/overlay_header.dart';
import 'package:fluffychat/pangea/widgets/chat/overlay_message.dart'; import 'package:fluffychat/pangea/widgets/chat/overlay_message.dart';
import 'package:fluffychat/pangea/widgets/igc/word_data_card.dart'; import 'package:fluffychat/pangea/widgets/igc/word_data_card.dart';
import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_card.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_card.dart';
@ -72,7 +74,7 @@ class ToolbarDisplayController {
immersionMode: immersionMode, immersionMode: immersionMode,
ownMessage: pangeaMessageEvent.ownMessage, ownMessage: pangeaMessageEvent.ownMessage,
toolbarController: this, toolbarController: this,
width: messageWidth, width: 300,
nextEvent: nextEvent, nextEvent: nextEvent,
previousEvent: previousEvent, previousEvent: previousEvent,
); );
@ -84,18 +86,33 @@ class ToolbarDisplayController {
Widget overlayEntry; Widget overlayEntry;
if (toolbar == null) return; if (toolbar == null) return;
try { try {
overlayEntry = Container( overlayEntry = Column(
constraints: mainAxisAlignment: MainAxisAlignment.spaceBetween,
BoxConstraints(maxHeight: MediaQuery.sizeOf(context).height * .72), mainAxisSize: MainAxisSize.max,
child: Column( children: [
mainAxisSize: MainAxisSize.min, OverlayHeader(controller: controller),
crossAxisAlignment: CrossAxisAlignment.center, const SizedBox(
children: [ height: 7,
toolbar!, ),
const SizedBox(height: 6), Container(
overlayMessage, constraints: BoxConstraints(
], maxHeight: MediaQuery.sizeOf(context).height * .72,
), ),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
toolbar!,
const SizedBox(height: 9),
overlayMessage,
],
),
),
const SizedBox(
height: 7,
),
OverlayFooter(controller: controller),
],
); );
} catch (err) { } catch (err) {
debugger(when: kDebugMode); debugger(when: kDebugMode);
@ -113,9 +130,10 @@ class ToolbarDisplayController {
closePrevOverlay: closePrevOverlay:
MatrixState.pangeaController.subscriptionController.isSubscribed, MatrixState.pangeaController.subscriptionController.isSubscribed,
targetScreen: true, targetScreen: true,
onDismiss: controller.clearSelectedEvents,
); );
// controller.onSelectMessage(pangeaMessageEvent.event); controller.onSelectMessage(pangeaMessageEvent.event);
if (MatrixState.pAnyState.entries.isNotEmpty) { if (MatrixState.pAnyState.entries.isNotEmpty) {
overlayId = MatrixState.pAnyState.entries.last.hashCode.toString(); overlayId = MatrixState.pAnyState.entries.last.hashCode.toString();

@ -0,0 +1,57 @@
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pages/chat/chat_input_row.dart';
import 'package:fluffychat/pages/chat/reactions_picker.dart';
import 'package:fluffychat/pages/chat/reply_display.dart';
import 'package:fluffychat/pangea/choreographer/widgets/it_bar.dart';
import 'package:fluffychat/widgets/connection_status_header.dart';
import 'package:flutter/material.dart';
enum _EventContextAction { info, report }
class OverlayFooter extends StatelessWidget {
ChatController controller;
OverlayFooter({
required this.controller,
super.key,
});
@override
Widget build(BuildContext context) {
final bottomSheetPadding = FluffyThemes.isColumnMode(context) ? 18.0 : 10.0;
return Container(
margin: EdgeInsets.only(
bottom: bottomSheetPadding,
left: bottomSheetPadding,
right: bottomSheetPadding,
),
constraints: const BoxConstraints(
maxWidth: FluffyThemes.columnWidth * 2.5,
),
alignment: Alignment.center,
child: Material(
clipBehavior: Clip.hardEdge,
color: Theme.of(context).colorScheme.surfaceContainerHighest,
borderRadius: const BorderRadius.all(
Radius.circular(24),
),
child: Column(
children: [
const ConnectionStatusHeader(),
ITBar(
choreographer: controller.choreographer,
),
ReactionsPicker(controller),
ReplyDisplay(controller),
ChatInputRow(controller),
SizedBox(
height: (FluffyThemes.isColumnMode(context) ? 16.0 : 8.0),
),
],
),
),
);
}
}

@ -0,0 +1,112 @@
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pages/chat/chat_app_bar_title.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart';
enum _EventContextAction { info, report }
class OverlayHeader extends StatelessWidget {
ChatController controller;
OverlayHeader({
required this.controller,
super.key,
});
@override
Widget build(BuildContext context) {
final Event selectedEvent = controller.selectedEvents.single;
return AppBar(
actionsIconTheme: IconThemeData(
color: Theme.of(context).colorScheme.primary,
),
leading: IconButton(
icon: const Icon(Icons.close),
onPressed: MatrixState.pAnyState.closeOverlay,
tooltip: L10n.of(context)!.close,
color: Theme.of(context).colorScheme.primary,
),
titleSpacing: 0,
title: ChatAppBarTitle(controller),
actions: [
if (controller.canEditSelectedEvents)
IconButton(
icon: const Icon(Icons.edit_outlined),
tooltip: L10n.of(context)!.edit,
onPressed: controller.editSelectedEventAction,
),
if (selectedEvent.messageType == MessageTypes.Text)
IconButton(
icon: const Icon(Icons.copy_outlined),
tooltip: L10n.of(context)!.copy,
onPressed: controller.copyEventsAction,
),
if (controller.canSaveSelectedEvent)
// Use builder context to correctly position the share dialog on iPad
Builder(
builder: (context) => IconButton(
icon: Icon(Icons.adaptive.share),
tooltip: L10n.of(context)!.share,
onPressed: () => controller.saveSelectedEvent(context),
),
),
if (controller.canPinSelectedEvents)
IconButton(
icon: const Icon(Icons.push_pin_outlined),
onPressed: controller.pinEvent,
tooltip: L10n.of(context)!.pinMessage,
),
if (controller.canRedactSelectedEvents)
IconButton(
icon: const Icon(Icons.delete_outlined),
tooltip: L10n.of(context)!.redactMessage,
onPressed: controller.redactEventsAction,
),
PopupMenuButton<_EventContextAction>(
onSelected: (action) {
switch (action) {
case _EventContextAction.info:
controller.showEventInfo();
controller.clearSelectedEvents();
break;
case _EventContextAction.report:
controller.reportEventAction();
break;
}
},
itemBuilder: (context) => [
PopupMenuItem(
value: _EventContextAction.info,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.info_outlined),
const SizedBox(width: 12),
Text(L10n.of(context)!.messageInfo),
],
),
),
if (selectedEvent.status.isSent)
PopupMenuItem(
value: _EventContextAction.report,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.shield_outlined,
color: Colors.red,
),
const SizedBox(width: 12),
Text(L10n.of(context)!.reportMessage),
],
),
),
],
),
],
);
}
}

@ -143,9 +143,9 @@ class PangeaRichTextState extends State<PangeaRichText> {
(e) => e.eventId == widget.pangeaMessageEvent.eventId, (e) => e.eventId == widget.pangeaMessageEvent.eventId,
) ?? ) ??
false)) { false)) {
widget.toolbarController?.controller.onSelectMessage( // widget.toolbarController?.controller.onSelectMessage(
widget.pangeaMessageEvent.event, // widget.pangeaMessageEvent.event,
); // );
return; return;
} }
widget.toolbarController?.toolbar?.textSelection widget.toolbarController?.toolbar?.textSelection

Loading…
Cancel
Save