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();
clearSelectedEvents();
MatrixState.pAnyState.closeOverlay();
showToolbarStream.close();
//Pangea#
super.dispose();
}
@ -1666,6 +1667,15 @@ class ChatController extends State<ChatPageWithRoom>
});
// #Pangea
String? get buttonEventID => timeline!.events
.firstWhereOrNull(
(event) => event.isVisibleInGui && event.senderId != room.client.userID,
)
?.eventId;
final StreamController<String> showToolbarStream =
StreamController.broadcast();
void showToolbar(
Event event, {
PangeaMessageEvent? pangeaMessageEvent,
@ -1704,23 +1714,28 @@ class ChatController extends State<ChatPageWithRoom>
return;
}
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);
showToolbarStream.add(event.eventId);
if (!kIsWeb) {
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 = [];

@ -191,6 +191,7 @@ class ChatEventList extends StatelessWidget {
// #Pangea
immersionMode: controller.choreographer.immersionMode,
controller: controller,
isButton: event.eventId == controller.buttonEventID,
// Pangea#
selected: controller.selectedEvents
.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/widgets/chat/message_buttons.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/string_color.dart';
import 'package:fluffychat/widgets/avatar.dart';
@ -41,6 +42,7 @@ class Message extends StatelessWidget {
final bool immersionMode;
final ChatController controller;
final MessageOverlayController? overlayController;
final bool isButton;
// Pangea#
final Color? avatarPresenceBackgroundColor;
@ -65,6 +67,7 @@ class Message extends StatelessWidget {
required this.immersionMode,
required this.controller,
this.overlayController,
this.isButton = false,
// Pangea#
super.key,
});
@ -353,182 +356,215 @@ class Message extends StatelessWidget {
: 1,
duration: FluffyThemes.animationDuration,
curve: FluffyThemes.animationCurve,
child: Material(
color:
noBubble ? Colors.transparent : color,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: borderRadius,
child:
// #Pangea
PressableButton(
triggerAnimation: controller
.showToolbarStream.stream
.where(
(eventID) => eventID == event.eventId,
),
// #Pangea
child: CompositedTransformTarget(
link: overlayController != null
? LayerLinkAndKey('overlay_msg')
.link
: MatrixState.pAnyState
.layerLinkAndKey(event.eventId)
.link,
child: Container(
key: overlayController != null
depressed: !isButton,
borderRadius: borderRadius,
onPressed: () {
showToolbar(pangeaMessageEvent);
},
color: color,
child:
// Pangea#
Material(
color: noBubble
? Colors.transparent
: color,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: borderRadius,
),
// #Pangea
child: CompositedTransformTarget(
link: overlayController != null
? LayerLinkAndKey('overlay_msg')
.key
.link
: MatrixState.pAnyState
.layerLinkAndKey(
event.eventId,
)
.key,
// Pangea#
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
AppConfig.borderRadius,
.link,
child: Container(
key: overlayController != null
? LayerLinkAndKey('overlay_msg')
.key
: MatrixState.pAnyState
.layerLinkAndKey(
event.eventId,
)
.key,
// Pangea#
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(
AppConfig.borderRadius,
),
),
),
padding: noBubble || noPadding
? EdgeInsets.zero
: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
constraints: const BoxConstraints(
maxWidth:
FluffyThemes.columnWidth * 1.5,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
if (event.relationshipType ==
RelationshipTypes.reply)
FutureBuilder<Event?>(
future: event
.getReplyEvent(timeline),
builder: (
BuildContext context,
snapshot,
) {
final replyEvent = snapshot
.hasData
? snapshot.data!
: Event(
eventId: event
.relationshipEventId!,
content: {
'msgtype':
'm.text',
'body': '...',
},
senderId:
event.senderId,
type:
'm.room.message',
room: event.room,
status: EventStatus
.sent,
originServerTs:
DateTime.now(),
);
return Padding(
padding:
const EdgeInsets.only(
bottom: 4.0,
),
child: InkWell(
borderRadius:
ReplyContent
.borderRadius,
onTap: () =>
scrollToEventId(
replyEvent.eventId,
padding: noBubble || noPadding
? EdgeInsets.zero
: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
constraints: const BoxConstraints(
maxWidth:
FluffyThemes.columnWidth *
1.5,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
if (event.relationshipType ==
RelationshipTypes.reply)
FutureBuilder<Event?>(
future: event.getReplyEvent(
timeline,
),
builder: (
BuildContext context,
snapshot,
) {
final replyEvent =
snapshot.hasData
? snapshot.data!
: Event(
eventId: event
.relationshipEventId!,
content: {
'msgtype':
'm.text',
'body':
'...',
},
senderId: event
.senderId,
type:
'm.room.message',
room: event
.room,
status:
EventStatus
.sent,
originServerTs:
DateTime
.now(),
);
return Padding(
padding:
const EdgeInsets
.only(
bottom: 4.0,
),
child: AbsorbPointer(
child: ReplyContent(
replyEvent,
ownMessage:
ownMessage,
timeline: timeline,
child: InkWell(
borderRadius:
ReplyContent
.borderRadius,
onTap: () =>
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(
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
.withAlpha(164),
size: 14,
),
Text(
' - ${displayEvent.originServerTs.localizedTimeShort(context)}',
style: TextStyle(
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(
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
.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 Widget child;
final void Function()? onPressed;
final Stream? triggerAnimation;
const PressableButton({
required this.borderRadius,
@ -21,6 +22,7 @@ class PressableButton extends StatefulWidget {
this.buttonHeight = 5,
this.enabled = true,
this.depressed = false,
this.triggerAnimation,
super.key,
});
@ -33,6 +35,7 @@ class PressableButtonState extends State<PressableButton>
late AnimationController _controller;
late Animation<double> _tweenAnimation;
Completer<void>? _animationCompleter;
StreamSubscription? _triggerAnimationSubscription;
@override
void initState() {
@ -43,11 +46,23 @@ class PressableButtonState extends State<PressableButton>
);
_tweenAnimation =
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;
_animationCompleter = Completer<void>();
if (!mounted) return;
_animateUp();
}
void _animateUp() {
if (!mounted) return;
_controller.forward().then((_) {
_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;
widget.onPressed?.call();
await _animateDown();
}
Future<void> _animateDown() async {
if (_animationCompleter != null) {
await _animationCompleter!.future;
}
@ -75,6 +94,7 @@ class PressableButtonState extends State<PressableButton>
@override
void dispose() {
_controller.dispose();
_triggerAnimationSubscription?.cancel();
super.dispose();
}

Loading…
Cancel
Save