simplified positioning of toolbar
parent
868e83709d
commit
0373d01f1b
@ -1,148 +1,92 @@
|
|||||||
import 'package:fluffychat/pages/chat/chat.dart';
|
import 'package:fluffychat/pages/chat/chat.dart';
|
||||||
import 'package:fluffychat/pages/chat/chat_app_bar_title.dart';
|
import 'package:fluffychat/pages/chat/chat_app_bar_title.dart';
|
||||||
import 'package:fluffychat/pangea/utils/overlay.dart';
|
import 'package:fluffychat/widgets/matrix.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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';
|
||||||
|
|
||||||
class OverlayHeader extends StatelessWidget {
|
class OverlayHeader extends StatelessWidget {
|
||||||
ChatController controller;
|
final ChatController controller;
|
||||||
Function closeToolbar;
|
|
||||||
|
|
||||||
OverlayHeader({
|
const OverlayHeader({
|
||||||
required this.controller,
|
required this.controller,
|
||||||
required this.closeToolbar,
|
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final Event selectedEvent = controller.selectedEvents.single;
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
return AppBar(
|
children: [
|
||||||
backgroundColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
AppBar(
|
||||||
actionsIconTheme: IconThemeData(
|
actionsIconTheme: IconThemeData(
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
),
|
|
||||||
leading: IconButton(
|
|
||||||
icon: const Icon(Icons.close),
|
|
||||||
onPressed: () => closeToolbar(),
|
|
||||||
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)
|
leading: IconButton(
|
||||||
IconButton(
|
icon: const Icon(Icons.close),
|
||||||
icon: const Icon(Icons.delete_outlined),
|
onPressed: () {
|
||||||
tooltip: L10n.of(context)!.redactMessage,
|
controller.clearSelectedEvents();
|
||||||
onPressed: controller.redactEventsAction,
|
MatrixState.pAnyState.closeAllOverlays();
|
||||||
|
},
|
||||||
|
tooltip: L10n.of(context)!.close,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
),
|
),
|
||||||
IconButton(
|
titleSpacing: 0,
|
||||||
padding: const EdgeInsets.only(bottom: 6),
|
title: ChatAppBarTitle(controller),
|
||||||
icon: Icon(
|
actions: [
|
||||||
Icons.more_horiz,
|
if (controller.canEditSelectedEvents)
|
||||||
color: Theme.of(context).colorScheme.onSurface,
|
IconButton(
|
||||||
),
|
icon: const Icon(Icons.edit_outlined),
|
||||||
onPressed: () => showPopup(context),
|
tooltip: L10n.of(context)!.edit,
|
||||||
),
|
onPressed: controller.editSelectedEventAction,
|
||||||
],
|
),
|
||||||
);
|
if (controller.selectedEvents.length == 1 &&
|
||||||
}
|
controller.selectedEvents.single.messageType ==
|
||||||
|
MessageTypes.Text)
|
||||||
void showPopup(BuildContext context) {
|
IconButton(
|
||||||
OverlayUtil.showOverlay(
|
icon: const Icon(Icons.copy_outlined),
|
||||||
context: context,
|
tooltip: L10n.of(context)!.copy,
|
||||||
child: SelectionPopup(controller: controller),
|
onPressed: controller.copyEventsAction,
|
||||||
transformTargetId: "",
|
),
|
||||||
targetAnchor: Alignment.center,
|
if (controller.canSaveSelectedEvent)
|
||||||
followerAnchor: Alignment.center,
|
// Use builder context to correctly position the share dialog on iPad
|
||||||
closePrevOverlay: false,
|
Builder(
|
||||||
position: OverlayEnum.topRight,
|
builder: (context) => IconButton(
|
||||||
);
|
icon: Icon(Icons.adaptive.share),
|
||||||
}
|
tooltip: L10n.of(context)!.share,
|
||||||
}
|
onPressed: () => controller.saveSelectedEvent(context),
|
||||||
|
),
|
||||||
class SelectionPopup extends StatelessWidget {
|
),
|
||||||
ChatController controller;
|
if (controller.canPinSelectedEvents)
|
||||||
|
IconButton(
|
||||||
SelectionPopup({
|
icon: const Icon(Icons.push_pin_outlined),
|
||||||
required this.controller,
|
onPressed: controller.pinEvent,
|
||||||
super.key,
|
tooltip: L10n.of(context)!.pinMessage,
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Material(
|
|
||||||
type: MaterialType.transparency,
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(5),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
|
||||||
borderRadius: const BorderRadius.all(
|
|
||||||
Radius.circular(20),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: <Widget>[
|
|
||||||
TextButton(
|
|
||||||
onPressed: controller.showEventInfo,
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.info_outlined),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Text(L10n.of(context)!.messageInfo),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
if (controller.canRedactSelectedEvents)
|
||||||
TextButton(
|
IconButton(
|
||||||
onPressed: controller.reportEventAction,
|
icon: const Icon(Icons.delete_outlined),
|
||||||
child: Row(
|
tooltip: L10n.of(context)!.redactMessage,
|
||||||
mainAxisSize: MainAxisSize.min,
|
onPressed: controller.redactEventsAction,
|
||||||
children: [
|
),
|
||||||
const Icon(
|
if (controller.selectedEvents.length == 1)
|
||||||
Icons.shield_outlined,
|
IconButton(
|
||||||
color: Colors.red,
|
icon: const Icon(Icons.info_outlined),
|
||||||
),
|
tooltip: L10n.of(context)!.messageInfo,
|
||||||
const SizedBox(width: 12),
|
onPressed: () {
|
||||||
Text(L10n.of(context)!.reportMessage),
|
controller.showEventInfo();
|
||||||
],
|
controller.clearSelectedEvents();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (controller.selectedEvents.length == 1)
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.shield_outlined),
|
||||||
|
tooltip: L10n.of(context)!.reportMessage,
|
||||||
|
onPressed: controller.reportEventAction,
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,188 +0,0 @@
|
|||||||
import 'package:fluffychat/config/themes.dart';
|
|
||||||
import 'package:fluffychat/pages/chat/events/message_content.dart';
|
|
||||||
import 'package:fluffychat/pangea/enum/use_type.dart';
|
|
||||||
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
|
|
||||||
import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart';
|
|
||||||
import 'package:fluffychat/utils/date_time_extension.dart';
|
|
||||||
import 'package:fluffychat/utils/platform_infos.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:matrix/matrix.dart';
|
|
||||||
|
|
||||||
import '../../../config/app_config.dart';
|
|
||||||
|
|
||||||
class OverlayMessage extends StatelessWidget {
|
|
||||||
final Event event;
|
|
||||||
final bool selected;
|
|
||||||
final Timeline timeline;
|
|
||||||
// final LanguageModel? selectedDisplayLang;
|
|
||||||
final bool immersionMode;
|
|
||||||
// final bool definitions;
|
|
||||||
final bool ownMessage;
|
|
||||||
final ToolbarDisplayController toolbarController;
|
|
||||||
final double? width;
|
|
||||||
final bool showDown;
|
|
||||||
|
|
||||||
const OverlayMessage(
|
|
||||||
this.event, {
|
|
||||||
this.selected = false,
|
|
||||||
required this.timeline,
|
|
||||||
required this.immersionMode,
|
|
||||||
required this.ownMessage,
|
|
||||||
required this.toolbarController,
|
|
||||||
required this.showDown,
|
|
||||||
this.width,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
if (event.type != EventTypes.Message ||
|
|
||||||
event.messageType == EventTypes.KeyVerificationRequest) {
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
|
|
||||||
var color = Theme.of(context).colorScheme.surfaceContainer;
|
|
||||||
final isLight = Theme.of(context).brightness == Brightness.light;
|
|
||||||
var lightness = isLight ? .05 : .2;
|
|
||||||
final textColor = ownMessage
|
|
||||||
? Theme.of(context).colorScheme.onPrimary
|
|
||||||
: Theme.of(context).colorScheme.onSurface;
|
|
||||||
|
|
||||||
const hardCorner = Radius.circular(4);
|
|
||||||
const roundedCorner = Radius.circular(AppConfig.borderRadius);
|
|
||||||
final borderRadius = BorderRadius.only(
|
|
||||||
topLeft: !showDown && !ownMessage ? hardCorner : roundedCorner,
|
|
||||||
topRight: !showDown && ownMessage ? hardCorner : roundedCorner,
|
|
||||||
bottomLeft: showDown && !ownMessage ? hardCorner : roundedCorner,
|
|
||||||
bottomRight: showDown && ownMessage ? hardCorner : roundedCorner,
|
|
||||||
);
|
|
||||||
|
|
||||||
final noBubble = {
|
|
||||||
MessageTypes.Video,
|
|
||||||
MessageTypes.Image,
|
|
||||||
MessageTypes.Sticker,
|
|
||||||
}.contains(event.messageType) &&
|
|
||||||
!event.redacted;
|
|
||||||
final noPadding = {
|
|
||||||
MessageTypes.File,
|
|
||||||
MessageTypes.Audio,
|
|
||||||
}.contains(event.messageType);
|
|
||||||
|
|
||||||
if (ownMessage) {
|
|
||||||
color = Theme.of(context).colorScheme.primary;
|
|
||||||
lightness = isLight ? .15 : .85;
|
|
||||||
}
|
|
||||||
// Make overlay a little darker/lighter than the message
|
|
||||||
color = Color.fromARGB(
|
|
||||||
color.alpha,
|
|
||||||
isLight || !ownMessage
|
|
||||||
? (color.red + lightness * (255 - color.red)).round()
|
|
||||||
: (color.red * lightness).round(),
|
|
||||||
isLight || !ownMessage
|
|
||||||
? (color.green + lightness * (255 - color.green)).round()
|
|
||||||
: (color.green * lightness).round(),
|
|
||||||
isLight || !ownMessage
|
|
||||||
? (color.blue + lightness * (255 - color.blue)).round()
|
|
||||||
: (color.blue * lightness).round(),
|
|
||||||
);
|
|
||||||
|
|
||||||
final double maxHeight = (MediaQuery.of(context).size.height -
|
|
||||||
(PlatformInfos.isWeb
|
|
||||||
? 228
|
|
||||||
: PlatformInfos.isIOS
|
|
||||||
? 258
|
|
||||||
: 198)) /
|
|
||||||
2 -
|
|
||||||
30;
|
|
||||||
|
|
||||||
final pangeaMessageEvent = PangeaMessageEvent(
|
|
||||||
event: event,
|
|
||||||
timeline: timeline,
|
|
||||||
ownMessage: ownMessage,
|
|
||||||
);
|
|
||||||
|
|
||||||
return Material(
|
|
||||||
color: noBubble ? Colors.transparent : color,
|
|
||||||
clipBehavior: Clip.antiAlias,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: borderRadius,
|
|
||||||
),
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(
|
|
||||||
AppConfig.borderRadius,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
padding: noBubble || noPadding
|
|
||||||
? EdgeInsets.zero
|
|
||||||
: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 16,
|
|
||||||
vertical: 8,
|
|
||||||
),
|
|
||||||
constraints: BoxConstraints(
|
|
||||||
maxWidth: width ?? FluffyThemes.columnWidth * 1.25,
|
|
||||||
maxHeight: maxHeight,
|
|
||||||
),
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Flexible(
|
|
||||||
child: MessageContent(
|
|
||||||
event.getDisplayEvent(timeline),
|
|
||||||
textColor: textColor,
|
|
||||||
borderRadius: borderRadius,
|
|
||||||
selected: selected,
|
|
||||||
pangeaMessageEvent: pangeaMessageEvent,
|
|
||||||
immersionMode: immersionMode,
|
|
||||||
toolbarController: toolbarController,
|
|
||||||
isOverlay: true,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (event.hasAggregatedEvents(
|
|
||||||
timeline,
|
|
||||||
RelationshipTypes.edit,
|
|
||||||
) ||
|
|
||||||
(pangeaMessageEvent.showUseType))
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
top: 4.0,
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
if (pangeaMessageEvent.showUseType) ...[
|
|
||||||
pangeaMessageEvent.msgUseType.iconView(
|
|
||||||
context,
|
|
||||||
textColor.withAlpha(164),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
],
|
|
||||||
if (event.hasAggregatedEvents(
|
|
||||||
timeline,
|
|
||||||
RelationshipTypes.edit,
|
|
||||||
)) ...[
|
|
||||||
Icon(
|
|
||||||
Icons.edit_outlined,
|
|
||||||
color: textColor.withAlpha(164),
|
|
||||||
size: 14,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
' - ${event.getDisplayEvent(timeline).originServerTs.localizedTimeShort(context)}',
|
|
||||||
style: TextStyle(
|
|
||||||
color: textColor.withAlpha(164),
|
|
||||||
fontSize: 12,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue