make first message not from the user in a chat a pressable button (#1175)

pull/1544/head
ggurdin 11 months ago committed by GitHub
parent 09031b1c0e
commit 2840a7dcfd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -572,6 +572,7 @@ class ChatController extends State<ChatPageWithRoom>
choreographer.dispose(); choreographer.dispose();
clearSelectedEvents(); clearSelectedEvents();
MatrixState.pAnyState.closeOverlay(); MatrixState.pAnyState.closeOverlay();
showToolbarStream.close();
//Pangea# //Pangea#
super.dispose(); super.dispose();
} }
@ -1666,6 +1667,15 @@ class ChatController extends State<ChatPageWithRoom>
}); });
// #Pangea // #Pangea
String? get buttonEventID => timeline!.events
.firstWhereOrNull(
(event) => event.isVisibleInGui && event.senderId != room.client.userID,
)
?.eventId;
final StreamController<String> showToolbarStream =
StreamController.broadcast();
void showToolbar( void showToolbar(
Event event, { Event event, {
PangeaMessageEvent? pangeaMessageEvent, PangeaMessageEvent? pangeaMessageEvent,
@ -1704,23 +1714,28 @@ class ChatController extends State<ChatPageWithRoom>
return; return;
} }
OverlayUtil.showOverlay( showToolbarStream.add(event.eventId);
context: context,
child: overlayEntry,
transformTargetId: "",
backgroundColor: Colors.black,
closePrevOverlay:
MatrixState.pangeaController.subscriptionController.isSubscribed,
position: OverlayPositionEnum.centered,
onDismiss: clearSelectedEvents,
blurBackground: true,
);
// select the message
onSelectMessage(event);
if (!kIsWeb) { if (!kIsWeb) {
HapticFeedback.mediumImpact(); HapticFeedback.mediumImpact();
} }
Future.delayed(
Duration(milliseconds: buttonEventID == event.eventId ? 200 : 0), () {
OverlayUtil.showOverlay(
context: context,
child: overlayEntry!,
transformTargetId: "",
backgroundColor: Colors.black,
closePrevOverlay:
MatrixState.pangeaController.subscriptionController.isSubscribed,
position: OverlayPositionEnum.centered,
onDismiss: clearSelectedEvents,
blurBackground: true,
);
// select the message
onSelectMessage(event);
});
} }
// final List<int> selectedTokenIndicies = []; // final List<int> selectedTokenIndicies = [];

@ -191,6 +191,7 @@ class ChatEventList extends StatelessWidget {
// #Pangea // #Pangea
immersionMode: controller.choreographer.immersionMode, immersionMode: controller.choreographer.immersionMode,
controller: controller, controller: controller,
isButton: event.eventId == controller.buttonEventID,
// Pangea# // Pangea#
selected: controller.selectedEvents selected: controller.selectedEvents
.any((e) => e.eventId == event.eventId), .any((e) => e.eventId == event.eventId),

@ -5,6 +5,7 @@ import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dar
import 'package:fluffychat/pangea/utils/any_state_holder.dart'; import 'package:fluffychat/pangea/utils/any_state_holder.dart';
import 'package:fluffychat/pangea/widgets/chat/message_buttons.dart'; import 'package:fluffychat/pangea/widgets/chat/message_buttons.dart';
import 'package:fluffychat/pangea/widgets/chat/message_selection_overlay.dart'; import 'package:fluffychat/pangea/widgets/chat/message_selection_overlay.dart';
import 'package:fluffychat/pangea/widgets/pressable_button.dart';
import 'package:fluffychat/utils/date_time_extension.dart'; import 'package:fluffychat/utils/date_time_extension.dart';
import 'package:fluffychat/utils/string_color.dart'; import 'package:fluffychat/utils/string_color.dart';
import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/avatar.dart';
@ -41,6 +42,7 @@ class Message extends StatelessWidget {
final bool immersionMode; final bool immersionMode;
final ChatController controller; final ChatController controller;
final MessageOverlayController? overlayController; final MessageOverlayController? overlayController;
final bool isButton;
// Pangea# // Pangea#
final Color? avatarPresenceBackgroundColor; final Color? avatarPresenceBackgroundColor;
@ -65,6 +67,7 @@ class Message extends StatelessWidget {
required this.immersionMode, required this.immersionMode,
required this.controller, required this.controller,
this.overlayController, this.overlayController,
this.isButton = false,
// Pangea# // Pangea#
super.key, super.key,
}); });
@ -353,182 +356,215 @@ class Message extends StatelessWidget {
: 1, : 1,
duration: FluffyThemes.animationDuration, duration: FluffyThemes.animationDuration,
curve: FluffyThemes.animationCurve, curve: FluffyThemes.animationCurve,
child: Material( child:
color: // #Pangea
noBubble ? Colors.transparent : color, PressableButton(
clipBehavior: Clip.antiAlias, triggerAnimation: controller
shape: RoundedRectangleBorder( .showToolbarStream.stream
borderRadius: borderRadius, .where(
(eventID) => eventID == event.eventId,
), ),
// #Pangea depressed: !isButton,
child: CompositedTransformTarget( borderRadius: borderRadius,
link: overlayController != null onPressed: () {
? LayerLinkAndKey('overlay_msg') showToolbar(pangeaMessageEvent);
.link },
: MatrixState.pAnyState color: color,
.layerLinkAndKey(event.eventId) child:
.link, // Pangea#
child: Container( Material(
key: overlayController != null color: noBubble
? Colors.transparent
: color,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: borderRadius,
),
// #Pangea
child: CompositedTransformTarget(
link: overlayController != null
? LayerLinkAndKey('overlay_msg') ? LayerLinkAndKey('overlay_msg')
.key .link
: MatrixState.pAnyState : MatrixState.pAnyState
.layerLinkAndKey( .layerLinkAndKey(
event.eventId, event.eventId,
) )
.key, .link,
// Pangea# child: Container(
decoration: BoxDecoration( key: overlayController != null
borderRadius: BorderRadius.circular( ? LayerLinkAndKey('overlay_msg')
AppConfig.borderRadius, .key
: MatrixState.pAnyState
.layerLinkAndKey(
event.eventId,
)
.key,
// Pangea#
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(
AppConfig.borderRadius,
),
), ),
), padding: noBubble || noPadding
padding: noBubble || noPadding ? EdgeInsets.zero
? EdgeInsets.zero : const EdgeInsets.symmetric(
: const EdgeInsets.symmetric( horizontal: 16,
horizontal: 16, vertical: 8,
vertical: 8, ),
), constraints: const BoxConstraints(
constraints: const BoxConstraints( maxWidth:
maxWidth: FluffyThemes.columnWidth *
FluffyThemes.columnWidth * 1.5, 1.5,
), ),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: crossAxisAlignment:
CrossAxisAlignment.start, CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
if (event.relationshipType == if (event.relationshipType ==
RelationshipTypes.reply) RelationshipTypes.reply)
FutureBuilder<Event?>( FutureBuilder<Event?>(
future: event future: event.getReplyEvent(
.getReplyEvent(timeline), timeline,
builder: ( ),
BuildContext context, builder: (
snapshot, BuildContext context,
) { snapshot,
final replyEvent = snapshot ) {
.hasData final replyEvent =
? snapshot.data! snapshot.hasData
: Event( ? snapshot.data!
eventId: event : Event(
.relationshipEventId!, eventId: event
content: { .relationshipEventId!,
'msgtype': content: {
'm.text', 'msgtype':
'body': '...', 'm.text',
}, 'body':
senderId: '...',
event.senderId, },
type: senderId: event
'm.room.message', .senderId,
room: event.room, type:
status: EventStatus 'm.room.message',
.sent, room: event
originServerTs: .room,
DateTime.now(), status:
); EventStatus
return Padding( .sent,
padding: originServerTs:
const EdgeInsets.only( DateTime
bottom: 4.0, .now(),
), );
child: InkWell( return Padding(
borderRadius: padding:
ReplyContent const EdgeInsets
.borderRadius, .only(
onTap: () => bottom: 4.0,
scrollToEventId(
replyEvent.eventId,
), ),
child: AbsorbPointer( child: InkWell(
child: ReplyContent( borderRadius:
replyEvent, ReplyContent
ownMessage: .borderRadius,
ownMessage, onTap: () =>
timeline: timeline, scrollToEventId(
replyEvent.eventId,
),
child: AbsorbPointer(
child: ReplyContent(
replyEvent,
ownMessage:
ownMessage,
timeline:
timeline,
),
), ),
), ),
), );
); },
},
),
MessageContent(
displayEvent,
textColor: textColor,
onInfoTab: onInfoTab,
borderRadius: borderRadius,
// #Pangea
pangeaMessageEvent:
pangeaMessageEvent,
immersionMode: immersionMode,
overlayController:
overlayController,
controller: controller,
nextEvent: nextEvent,
prevEvent: previousEvent,
// Pangea#
),
if (event.hasAggregatedEvents(
timeline,
RelationshipTypes.edit,
)
// #Pangea
||
(pangeaMessageEvent
?.showUseType ??
false)
// Pangea#
)
Padding(
padding:
const EdgeInsets.only(
top: 4.0,
), ),
child: Row( MessageContent(
mainAxisSize: displayEvent,
MainAxisSize.min, textColor: textColor,
children: [ onInfoTab: onInfoTab,
// #Pangea borderRadius: borderRadius,
if (pangeaMessageEvent // #Pangea
?.showUseType ?? pangeaMessageEvent:
false) ...[ pangeaMessageEvent,
pangeaMessageEvent! immersionMode: immersionMode,
.msgUseType overlayController:
.iconView( overlayController,
context, controller: controller,
textColor nextEvent: nextEvent,
.withAlpha(164), prevEvent: previousEvent,
), // Pangea#
const SizedBox( ),
width: 4, if (event.hasAggregatedEvents(
), timeline,
], RelationshipTypes
if (event .edit,
.hasAggregatedEvents( )
timeline, // #Pangea
RelationshipTypes.edit, ||
)) ...[ (pangeaMessageEvent
// Pangea# ?.showUseType ??
Icon( false)
Icons.edit_outlined, // Pangea#
color: textColor )
.withAlpha(164), Padding(
size: 14, padding:
), const EdgeInsets.only(
Text( top: 4.0,
' - ${displayEvent.originServerTs.localizedTimeShort(context)}', ),
style: TextStyle( child: Row(
mainAxisSize:
MainAxisSize.min,
children: [
// #Pangea
if (pangeaMessageEvent
?.showUseType ??
false) ...[
pangeaMessageEvent!
.msgUseType
.iconView(
context,
textColor
.withAlpha(164),
),
const SizedBox(
width: 4,
),
],
if (event
.hasAggregatedEvents(
timeline,
RelationshipTypes
.edit,
)) ...[
// Pangea#
Icon(
Icons.edit_outlined,
color: textColor color: textColor
.withAlpha(164), .withAlpha(164),
fontSize: 12, size: 14,
), ),
), Text(
' - ${displayEvent.originServerTs.localizedTimeShort(context)}',
style: TextStyle(
color: textColor
.withAlpha(
164,
),
fontSize: 12,
),
),
],
], ],
], ),
), ),
), ],
], ),
), ),
), ),
), ),

@ -12,6 +12,7 @@ class PressableButton extends StatefulWidget {
final Color color; final Color color;
final Widget child; final Widget child;
final void Function()? onPressed; final void Function()? onPressed;
final Stream? triggerAnimation;
const PressableButton({ const PressableButton({
required this.borderRadius, required this.borderRadius,
@ -21,6 +22,7 @@ class PressableButton extends StatefulWidget {
this.buttonHeight = 5, this.buttonHeight = 5,
this.enabled = true, this.enabled = true,
this.depressed = false, this.depressed = false,
this.triggerAnimation,
super.key, super.key,
}); });
@ -33,6 +35,7 @@ class PressableButtonState extends State<PressableButton>
late AnimationController _controller; late AnimationController _controller;
late Animation<double> _tweenAnimation; late Animation<double> _tweenAnimation;
Completer<void>? _animationCompleter; Completer<void>? _animationCompleter;
StreamSubscription? _triggerAnimationSubscription;
@override @override
void initState() { void initState() {
@ -43,11 +46,23 @@ class PressableButtonState extends State<PressableButton>
); );
_tweenAnimation = _tweenAnimation =
Tween<double>(begin: widget.buttonHeight, end: 0).animate(_controller); Tween<double>(begin: widget.buttonHeight, end: 0).animate(_controller);
if (widget.enabled) {
_triggerAnimationSubscription = widget.triggerAnimation?.listen((_) {
_animationCompleter = Completer<void>();
_animateUp();
_animateDown();
});
}
} }
void _onTapDown(TapDownDetails details) { void _onTapDown(TapDownDetails? details) {
if (!widget.enabled) return; if (!widget.enabled) return;
_animationCompleter = Completer<void>(); _animationCompleter = Completer<void>();
if (!mounted) return;
_animateUp();
}
void _animateUp() {
if (!mounted) return; if (!mounted) return;
_controller.forward().then((_) { _controller.forward().then((_) {
_animationCompleter?.complete(); _animationCompleter?.complete();
@ -55,9 +70,13 @@ class PressableButtonState extends State<PressableButton>
}); });
} }
Future<void> _onTapUp(TapUpDetails details) async { Future<void> _onTapUp(TapUpDetails? details) async {
if (!widget.enabled || widget.depressed) return; if (!widget.enabled || widget.depressed) return;
widget.onPressed?.call(); widget.onPressed?.call();
await _animateDown();
}
Future<void> _animateDown() async {
if (_animationCompleter != null) { if (_animationCompleter != null) {
await _animationCompleter!.future; await _animationCompleter!.future;
} }
@ -75,6 +94,7 @@ class PressableButtonState extends State<PressableButton>
@override @override
void dispose() { void dispose() {
_controller.dispose(); _controller.dispose();
_triggerAnimationSubscription?.cancel();
super.dispose(); super.dispose();
} }

Loading…
Cancel
Save