|  |  | @ -7,7 +7,7 @@ import 'package:flutter/material.dart'; | 
			
		
	
		
			
				
					|  |  |  | import '/models/messages.dart'; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | @immutable | 
			
		
	
		
			
				
					|  |  |  | class ConversationMessage extends StatelessWidget { | 
			
		
	
		
			
				
					|  |  |  | class ConversationMessage extends StatefulWidget { | 
			
		
	
		
			
				
					|  |  |  | const ConversationMessage({ | 
			
		
	
		
			
				
					|  |  |  | Key? key, | 
			
		
	
		
			
				
					|  |  |  | required this.message, | 
			
		
	
	
		
			
				
					|  |  | @ -19,18 +19,71 @@ class ConversationMessage extends StatelessWidget { | 
			
		
	
		
			
				
					|  |  |  | final MyProfile profile; | 
			
		
	
		
			
				
					|  |  |  | final int index; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | @override | 
			
		
	
		
			
				
					|  |  |  | _ConversationMessageState createState() => _ConversationMessageState(); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | class _ConversationMessageState extends State<ConversationMessage> { | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | List<PopupMenuEntry<String>> menuItems = []; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | Offset? _tapPosition; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | bool showDownloadButton = false; | 
			
		
	
		
			
				
					|  |  |  | bool showDeleteButton = false; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | @override | 
			
		
	
		
			
				
					|  |  |  | void initState() { | 
			
		
	
		
			
				
					|  |  |  | super.initState(); | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | showDownloadButton = widget.message.runtimeType == ImageMessage; | 
			
		
	
		
			
				
					|  |  |  | showDeleteButton = widget.message.senderId == widget.profile.id; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | if (showDownloadButton) { | 
			
		
	
		
			
				
					|  |  |  | menuItems.add(PopupMenuItem( | 
			
		
	
		
			
				
					|  |  |  | value: 'download', | 
			
		
	
		
			
				
					|  |  |  | child: Row( | 
			
		
	
		
			
				
					|  |  |  | children: const [ | 
			
		
	
		
			
				
					|  |  |  | Icon(Icons.download), | 
			
		
	
		
			
				
					|  |  |  | SizedBox( | 
			
		
	
		
			
				
					|  |  |  | width: 10, | 
			
		
	
		
			
				
					|  |  |  | ), | 
			
		
	
		
			
				
					|  |  |  | Text('Download') | 
			
		
	
		
			
				
					|  |  |  | ], | 
			
		
	
		
			
				
					|  |  |  | ), | 
			
		
	
		
			
				
					|  |  |  | )); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | if (showDeleteButton) { | 
			
		
	
		
			
				
					|  |  |  | menuItems.add(PopupMenuItem( | 
			
		
	
		
			
				
					|  |  |  | value: 'delete', | 
			
		
	
		
			
				
					|  |  |  | child: Row( | 
			
		
	
		
			
				
					|  |  |  | children: const [ | 
			
		
	
		
			
				
					|  |  |  | Icon(Icons.delete), | 
			
		
	
		
			
				
					|  |  |  | SizedBox( | 
			
		
	
		
			
				
					|  |  |  | width: 10, | 
			
		
	
		
			
				
					|  |  |  | ), | 
			
		
	
		
			
				
					|  |  |  | Text('Delete') | 
			
		
	
		
			
				
					|  |  |  | ], | 
			
		
	
		
			
				
					|  |  |  | ), | 
			
		
	
		
			
				
					|  |  |  | )); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | setState(() {}); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | @override | 
			
		
	
		
			
				
					|  |  |  | Widget build(BuildContext context) { | 
			
		
	
		
			
				
					|  |  |  | return Container( | 
			
		
	
		
			
				
					|  |  |  | padding: const EdgeInsets.only(left: 14,right: 14,top: 0,bottom: 0), | 
			
		
	
		
			
				
					|  |  |  | child: Align( | 
			
		
	
		
			
				
					|  |  |  | alignment: ( | 
			
		
	
		
			
				
					|  |  |  | message.senderUsername == profile.username ? | 
			
		
	
		
			
				
					|  |  |  | widget.message.senderId == widget.profile.id ? | 
			
		
	
		
			
				
					|  |  |  | Alignment.topRight : | 
			
		
	
		
			
				
					|  |  |  | Alignment.topLeft | 
			
		
	
		
			
				
					|  |  |  | ), | 
			
		
	
		
			
				
					|  |  |  | child: Column( | 
			
		
	
		
			
				
					|  |  |  | crossAxisAlignment: message.senderUsername == profile.username ? | 
			
		
	
		
			
				
					|  |  |  | crossAxisAlignment: widget.message.senderId == widget.profile.id ? | 
			
		
	
		
			
				
					|  |  |  | CrossAxisAlignment.end : | 
			
		
	
		
			
				
					|  |  |  | CrossAxisAlignment.start, | 
			
		
	
		
			
				
					|  |  |  | children: <Widget>[ | 
			
		
	
	
		
			
				
					|  |  | @ -40,26 +93,26 @@ class ConversationMessage extends StatelessWidget { | 
			
		
	
		
			
				
					|  |  |  | const SizedBox(height: 1.5), | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | Row( | 
			
		
	
		
			
				
					|  |  |  | mainAxisAlignment: message.senderUsername == profile.username ? | 
			
		
	
		
			
				
					|  |  |  | mainAxisAlignment: widget.message.senderId == widget.profile.id ? | 
			
		
	
		
			
				
					|  |  |  | MainAxisAlignment.end : | 
			
		
	
		
			
				
					|  |  |  | MainAxisAlignment.start, | 
			
		
	
		
			
				
					|  |  |  | children: <Widget>[ | 
			
		
	
		
			
				
					|  |  |  | const SizedBox(width: 10), | 
			
		
	
		
			
				
					|  |  |  | usernameOrFailedToSend(index), | 
			
		
	
		
			
				
					|  |  |  | usernameOrFailedToSend(), | 
			
		
	
		
			
				
					|  |  |  | ], | 
			
		
	
		
			
				
					|  |  |  | ), | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | const SizedBox(height: 1.5), | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | Row( | 
			
		
	
		
			
				
					|  |  |  | mainAxisAlignment: message.senderUsername == profile.username ? | 
			
		
	
		
			
				
					|  |  |  | mainAxisAlignment: widget.message.senderId == widget.profile.id ? | 
			
		
	
		
			
				
					|  |  |  | MainAxisAlignment.end : | 
			
		
	
		
			
				
					|  |  |  | MainAxisAlignment.start, | 
			
		
	
		
			
				
					|  |  |  | children: <Widget>[ | 
			
		
	
		
			
				
					|  |  |  | const SizedBox(width: 10), | 
			
		
	
		
			
				
					|  |  |  | Text( | 
			
		
	
		
			
				
					|  |  |  | convertToAgo(message.createdAt), | 
			
		
	
		
			
				
					|  |  |  | textAlign: message.senderUsername == profile.username ? | 
			
		
	
		
			
				
					|  |  |  | convertToAgo(widget.message.createdAt), | 
			
		
	
		
			
				
					|  |  |  | textAlign: widget.message.senderId == widget.profile.id ? | 
			
		
	
		
			
				
					|  |  |  | TextAlign.left : | 
			
		
	
		
			
				
					|  |  |  | TextAlign.right, | 
			
		
	
		
			
				
					|  |  |  | style: TextStyle( | 
			
		
	
	
		
			
				
					|  |  | @ -70,7 +123,7 @@ class ConversationMessage extends StatelessWidget { | 
			
		
	
		
			
				
					|  |  |  | ], | 
			
		
	
		
			
				
					|  |  |  | ), | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | index != 0 ? | 
			
		
	
		
			
				
					|  |  |  | widget.index != 0 ? | 
			
		
	
		
			
				
					|  |  |  | const SizedBox(height: 20) : | 
			
		
	
		
			
				
					|  |  |  | const SizedBox.shrink(), | 
			
		
	
		
			
				
					|  |  |  | ], | 
			
		
	
	
		
			
				
					|  |  | @ -79,22 +132,50 @@ class ConversationMessage extends StatelessWidget { | 
			
		
	
		
			
				
					|  |  |  | ); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | void _showCustomMenu() { | 
			
		
	
		
			
				
					|  |  |  | final Size overlay = MediaQuery.of(context).size; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | int addVerticalOffset = 75 * menuItems.length; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | // TODO: Implement download & delete methods | 
			
		
	
		
			
				
					|  |  |  | showMenu( | 
			
		
	
		
			
				
					|  |  |  | context: context, | 
			
		
	
		
			
				
					|  |  |  | items: menuItems, | 
			
		
	
		
			
				
					|  |  |  | position: RelativeRect.fromRect( | 
			
		
	
		
			
				
					|  |  |  | Offset(_tapPosition!.dx, (_tapPosition!.dy - addVerticalOffset)) & const Size(40, 40), | 
			
		
	
		
			
				
					|  |  |  | Offset.zero & overlay | 
			
		
	
		
			
				
					|  |  |  | ) | 
			
		
	
		
			
				
					|  |  |  | ) | 
			
		
	
		
			
				
					|  |  |  | .then<void>((String? delta) async { | 
			
		
	
		
			
				
					|  |  |  | if (delta == null) { | 
			
		
	
		
			
				
					|  |  |  | return; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | print(delta); | 
			
		
	
		
			
				
					|  |  |  | }); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | void _storePosition(TapDownDetails details) { | 
			
		
	
		
			
				
					|  |  |  | _tapPosition = details.globalPosition; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | Widget messageContent(BuildContext context) { | 
			
		
	
		
			
				
					|  |  |  | if (message.runtimeType == ImageMessage) { | 
			
		
	
		
			
				
					|  |  |  | if (widget.message.runtimeType == ImageMessage) { | 
			
		
	
		
			
				
					|  |  |  | return GestureDetector( | 
			
		
	
		
			
				
					|  |  |  | onTap: () { | 
			
		
	
		
			
				
					|  |  |  | Navigator.push(context, MaterialPageRoute(builder: (context) { | 
			
		
	
		
			
				
					|  |  |  | return ViewImage( | 
			
		
	
		
			
				
					|  |  |  | message: (message as ImageMessage) | 
			
		
	
		
			
				
					|  |  |  | message: (widget.message as ImageMessage) | 
			
		
	
		
			
				
					|  |  |  | ); | 
			
		
	
		
			
				
					|  |  |  | })); | 
			
		
	
		
			
				
					|  |  |  | }, | 
			
		
	
		
			
				
					|  |  |  | onLongPress: _showCustomMenu, | 
			
		
	
		
			
				
					|  |  |  | onTapDown: _storePosition, | 
			
		
	
		
			
				
					|  |  |  | child: ConstrainedBox( | 
			
		
	
		
			
				
					|  |  |  | constraints: const BoxConstraints(maxHeight: 350, maxWidth: 250), | 
			
		
	
		
			
				
					|  |  |  | child: ClipRRect( | 
			
		
	
		
			
				
					|  |  |  | borderRadius: BorderRadius.circular(20), | 
			
		
	
		
			
				
					|  |  |  | child: Image.file( | 
			
		
	
		
			
				
					|  |  |  | (message as ImageMessage).file, | 
			
		
	
		
			
				
					|  |  |  | borderRadius: BorderRadius.circular(20), child: Image.file( | 
			
		
	
		
			
				
					|  |  |  | (widget.message as ImageMessage).file, | 
			
		
	
		
			
				
					|  |  |  | fit: BoxFit.fill, | 
			
		
	
		
			
				
					|  |  |  | ), | 
			
		
	
		
			
				
					|  |  |  | ), | 
			
		
	
	
		
			
				
					|  |  | @ -102,32 +183,36 @@ class ConversationMessage extends StatelessWidget { | 
			
		
	
		
			
				
					|  |  |  | ); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | return Container( | 
			
		
	
		
			
				
					|  |  |  | decoration: BoxDecoration( | 
			
		
	
		
			
				
					|  |  |  | borderRadius: BorderRadius.circular(20), | 
			
		
	
		
			
				
					|  |  |  | color: ( | 
			
		
	
		
			
				
					|  |  |  | message.senderUsername == profile.username ? | 
			
		
	
		
			
				
					|  |  |  | Theme.of(context).colorScheme.primary : | 
			
		
	
		
			
				
					|  |  |  | Theme.of(context).colorScheme.tertiary | 
			
		
	
		
			
				
					|  |  |  | return GestureDetector( | 
			
		
	
		
			
				
					|  |  |  | onLongPress: _showCustomMenu, | 
			
		
	
		
			
				
					|  |  |  | onTapDown: _storePosition, | 
			
		
	
		
			
				
					|  |  |  | child: Container( | 
			
		
	
		
			
				
					|  |  |  | decoration: BoxDecoration( | 
			
		
	
		
			
				
					|  |  |  | borderRadius: BorderRadius.circular(20), | 
			
		
	
		
			
				
					|  |  |  | color: ( | 
			
		
	
		
			
				
					|  |  |  | widget.message.senderId == widget.profile.id ? | 
			
		
	
		
			
				
					|  |  |  | Theme.of(context).colorScheme.primary : | 
			
		
	
		
			
				
					|  |  |  | Theme.of(context).colorScheme.tertiary | 
			
		
	
		
			
				
					|  |  |  | ), | 
			
		
	
		
			
				
					|  |  |  | ), | 
			
		
	
		
			
				
					|  |  |  | ), | 
			
		
	
		
			
				
					|  |  |  | padding: const EdgeInsets.all(12), | 
			
		
	
		
			
				
					|  |  |  | child: Text( | 
			
		
	
		
			
				
					|  |  |  | message.getContent(), | 
			
		
	
		
			
				
					|  |  |  | style: TextStyle( | 
			
		
	
		
			
				
					|  |  |  | fontSize: 15, | 
			
		
	
		
			
				
					|  |  |  | color: message.senderUsername == profile.username ? | 
			
		
	
		
			
				
					|  |  |  | Theme.of(context).colorScheme.onPrimary : | 
			
		
	
		
			
				
					|  |  |  | Theme.of(context).colorScheme.onTertiary, | 
			
		
	
		
			
				
					|  |  |  | padding: const EdgeInsets.all(12), | 
			
		
	
		
			
				
					|  |  |  | child: Text( | 
			
		
	
		
			
				
					|  |  |  | widget.message.getContent(), | 
			
		
	
		
			
				
					|  |  |  | style: TextStyle( | 
			
		
	
		
			
				
					|  |  |  | fontSize: 15, | 
			
		
	
		
			
				
					|  |  |  | color: widget.message.senderId == widget.profile.id ? | 
			
		
	
		
			
				
					|  |  |  | Theme.of(context).colorScheme.onPrimary : | 
			
		
	
		
			
				
					|  |  |  | Theme.of(context).colorScheme.onTertiary, | 
			
		
	
		
			
				
					|  |  |  | ), | 
			
		
	
		
			
				
					|  |  |  | ), | 
			
		
	
		
			
				
					|  |  |  | ), | 
			
		
	
		
			
				
					|  |  |  | ); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | Widget usernameOrFailedToSend(int index) { | 
			
		
	
		
			
				
					|  |  |  | if (message.senderUsername != profile.username) { | 
			
		
	
		
			
				
					|  |  |  | Widget usernameOrFailedToSend() { | 
			
		
	
		
			
				
					|  |  |  | if (widget.message.senderId != widget.profile.id) { | 
			
		
	
		
			
				
					|  |  |  | return Text( | 
			
		
	
		
			
				
					|  |  |  | message.senderUsername, | 
			
		
	
		
			
				
					|  |  |  | widget.message.senderUsername, | 
			
		
	
		
			
				
					|  |  |  | style: TextStyle( | 
			
		
	
		
			
				
					|  |  |  | fontSize: 12, | 
			
		
	
		
			
				
					|  |  |  | color: Colors.grey[300], | 
			
		
	
	
		
			
				
					|  |  | @ -135,7 +220,7 @@ class ConversationMessage extends StatelessWidget { | 
			
		
	
		
			
				
					|  |  |  | ); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | if (message.failedToSend) { | 
			
		
	
		
			
				
					|  |  |  | if (widget.message.failedToSend) { | 
			
		
	
		
			
				
					|  |  |  | return Row( | 
			
		
	
		
			
				
					|  |  |  | mainAxisAlignment: MainAxisAlignment.end, | 
			
		
	
		
			
				
					|  |  |  | children: const <Widget>[ | 
			
		
	
	
		
			
				
					|  |  | 
 |