Encrypted messaging app
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

245 lines
6.3 KiB

import 'package:flutter/material.dart';
import '/components/view_image.dart';
import '/database/models/image_message.dart';
import '/database/models/my_profile.dart';
import '/database/models/messages.dart';
import '/utils/time.dart';
@immutable
class ConversationMessage extends StatefulWidget {
const ConversationMessage({
Key? key,
required this.message,
required this.profile,
required this.index,
}) : super(key: key);
final Message message;
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: (
widget.message.senderId == widget.profile.id ?
Alignment.topRight :
Alignment.topLeft
),
child: Column(
crossAxisAlignment: widget.message.senderId == widget.profile.id ?
CrossAxisAlignment.end :
CrossAxisAlignment.start,
children: <Widget>[
messageContent(context),
const SizedBox(height: 1.5),
Row(
mainAxisAlignment: widget.message.senderId == widget.profile.id ?
MainAxisAlignment.end :
MainAxisAlignment.start,
children: <Widget>[
const SizedBox(width: 10),
usernameOrFailedToSend(),
],
),
const SizedBox(height: 1.5),
Row(
mainAxisAlignment: widget.message.senderId == widget.profile.id ?
MainAxisAlignment.end :
MainAxisAlignment.start,
children: <Widget>[
const SizedBox(width: 10),
Text(
convertToAgo(widget.message.createdAt),
textAlign: widget.message.senderId == widget.profile.id ?
TextAlign.left :
TextAlign.right,
style: TextStyle(
fontSize: 12,
color: Theme.of(context).hintColor,
),
),
],
),
widget.index != 0 ?
const SizedBox(height: 20) :
const SizedBox.shrink(),
],
)
),
);
}
void _showCustomMenu() {
if (menuItems.isEmpty) {
return;
}
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;
}
});
}
void _storePosition(TapDownDetails details) {
_tapPosition = details.globalPosition;
}
Widget messageContent(BuildContext context) {
if (widget.message.runtimeType == ImageMessage) {
return GestureDetector(
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return ViewImage(
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(
(widget.message as ImageMessage).file,
fit: BoxFit.fill,
),
),
),
);
}
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(
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() {
if (widget.message.senderId != widget.profile.id) {
return Text(
widget.message.senderUsername,
style: TextStyle(
fontSize: 12,
color: Theme.of(context).hintColor,
),
);
}
if (widget.message.failedToSend) {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: const <Widget>[
Icon(
Icons.warning_rounded,
color: Colors.red,
size: 20,
),
Text(
'Failed to send',
style: TextStyle(color: Colors.red, fontSize: 12),
textAlign: TextAlign.right,
),
],
);
}
return const SizedBox.shrink();
}
}