|
|
@ -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>[ |
|
|
|