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:
controller.scrollToEventIdMarker == event.eventId,
onSelect: controller.onSelectMessage,
// #Pangea
// onSelect: controller.onSelectMessage,
// Pangea#
scrollToEventId: (String eventId) =>
controller.scrollToEventId(eventId),
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_input_row.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/pangea/choreographer/widgets/it_bar.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});
// #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) {
if (controller.selectMode) {
return [
@ -197,26 +211,33 @@ class ChatView extends StatelessWidget {
? null
: Theme.of(context).colorScheme.primary,
),
leading: controller.selectMode
? IconButton(
icon: const Icon(Icons.close),
onPressed: controller.clearSelectedEvents,
tooltip: L10n.of(context)!.close,
color: Theme.of(context).colorScheme.primary,
)
: UnreadRoomsBadge(
filter: (r) =>
r.id != controller.roomId
// #Pangea
&&
!r.isAnalyticsRoom,
// Pangea#
badgePosition: BadgePosition.topEnd(end: 8, top: 4),
child: const Center(child: BackButton()),
),
leading:
// #Pangea
// controller.selectMode
// ? IconButton(
// icon: const Icon(Icons.close),
// onPressed: controller.clearSelectedEvents,
// tooltip: L10n.of(context)!.close,
// color: Theme.of(context).colorScheme.primary,
// )
// :
// Pangea#
UnreadRoomsBadge(
filter: (r) =>
r.id != controller.roomId
// #Pangea
&&
!r.isAnalyticsRoom,
// Pangea#
badgePosition: BadgePosition.topEnd(end: 8, top: 4),
child: const Center(child: BackButton()),
),
titleSpacing: 0,
title: ChatAppBarTitle(controller),
actions: _appBarActions(context),
// #Pangea
// actions: _appBarActions(context),
actions: _editedAppBarActions(context),
// Pangea#
bottom: PreferredSize(
preferredSize: Size.fromHeight(appbarBottomHeight),
child: Column(
@ -497,7 +518,6 @@ class ChatView extends StatelessWidget {
ITBar(
choreographer: controller.choreographer,
),
ReactionsPicker(controller),
ReplyDisplay(controller),
ChatInputRow(controller),
ChatEmojiPicker(controller),

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

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

@ -27,6 +27,7 @@ class OverlayUtil {
Alignment? followerAnchor,
bool closePrevOverlay = true,
bool targetScreen = false,
Function? onDismiss,
}) {
try {
if (closePrevOverlay) {
@ -44,6 +45,7 @@ class OverlayUtil {
if (backDropToDismiss)
TransparentBackdrop(
backgroundColor: backgroundColor,
onDismiss: onDismiss,
),
Positioned(
width: width,
@ -194,8 +196,10 @@ class OverlayUtil {
class TransparentBackdrop extends StatelessWidget {
final Color? backgroundColor;
final Function? onDismiss;
const TransparentBackdrop({
super.key,
this.onDismiss,
this.backgroundColor,
});
@ -211,6 +215,9 @@ class TransparentBackdrop extends StatelessWidget {
focusColor: Colors.transparent,
highlightColor: Colors.transparent,
onTap: () {
if (onDismiss != null) {
onDismiss!();
}
MatrixState.pAnyState.closeOverlay();
},
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_translation_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/igc/word_data_card.dart';
import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_card.dart';
@ -72,7 +74,7 @@ class ToolbarDisplayController {
immersionMode: immersionMode,
ownMessage: pangeaMessageEvent.ownMessage,
toolbarController: this,
width: messageWidth,
width: 300,
nextEvent: nextEvent,
previousEvent: previousEvent,
);
@ -84,18 +86,33 @@ class ToolbarDisplayController {
Widget overlayEntry;
if (toolbar == null) return;
try {
overlayEntry = Container(
constraints:
BoxConstraints(maxHeight: MediaQuery.sizeOf(context).height * .72),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
toolbar!,
const SizedBox(height: 6),
overlayMessage,
],
),
overlayEntry = Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
children: [
OverlayHeader(controller: controller),
const SizedBox(
height: 7,
),
Container(
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) {
debugger(when: kDebugMode);
@ -113,9 +130,10 @@ class ToolbarDisplayController {
closePrevOverlay:
MatrixState.pangeaController.subscriptionController.isSubscribed,
targetScreen: true,
onDismiss: controller.clearSelectedEvents,
);
// controller.onSelectMessage(pangeaMessageEvent.event);
controller.onSelectMessage(pangeaMessageEvent.event);
if (MatrixState.pAnyState.entries.isNotEmpty) {
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,
) ??
false)) {
widget.toolbarController?.controller.onSelectMessage(
widget.pangeaMessageEvent.event,
);
// widget.toolbarController?.controller.onSelectMessage(
// widget.pangeaMessageEvent.event,
// );
return;
}
widget.toolbarController?.toolbar?.textSelection

Loading…
Cancel
Save