Solian/lib/widgets/chat/chat_message.dart

185 lines
5.6 KiB
Dart
Raw Normal View History

2024-06-23 12:18:55 +00:00
import 'dart:math';
2024-05-25 17:21:08 +00:00
import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
2024-06-04 15:29:05 +00:00
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
2024-05-25 17:21:08 +00:00
import 'package:get/get.dart';
import 'package:solian/models/message.dart';
import 'package:solian/widgets/account/account_avatar.dart';
2024-05-26 13:03:25 +00:00
import 'package:solian/widgets/attachments/attachment_list.dart';
2024-05-25 17:21:08 +00:00
import 'package:timeago/timeago.dart' show format;
2024-05-26 05:39:21 +00:00
import 'package:url_launcher/url_launcher_string.dart';
2024-05-25 17:21:08 +00:00
class ChatMessage extends StatelessWidget {
final Message item;
final bool isContentPreviewing;
2024-06-04 15:29:05 +00:00
final bool isReply;
2024-05-26 05:39:21 +00:00
final bool isMerged;
2024-05-29 16:05:39 +00:00
final bool isHasMerged;
2024-05-25 17:21:08 +00:00
2024-05-26 05:39:21 +00:00
const ChatMessage({
super.key,
required this.item,
this.isContentPreviewing = false,
2024-05-26 05:39:21 +00:00
this.isMerged = false,
2024-05-29 16:05:39 +00:00
this.isHasMerged = false,
2024-06-04 15:29:05 +00:00
this.isReply = false,
2024-05-26 05:39:21 +00:00
});
2024-05-25 17:21:08 +00:00
Future<String?> decodeContent(Map<String, dynamic> content) async {
String? text;
if (item.type == 'm.text') {
switch (content['algorithm']) {
case 'plain':
text = content['value'];
default:
throw Exception('Unsupported algorithm');
}
}
return text;
}
2024-05-26 05:39:21 +00:00
Widget buildContent() {
2024-05-25 17:21:08 +00:00
final hasAttachment = item.attachments?.isNotEmpty ?? false;
2024-05-26 05:39:21 +00:00
return FutureBuilder(
future: decodeContent(item.content),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Opacity(
opacity: 0.8,
child: Row(
children: [
const Icon(Icons.more_horiz),
const SizedBox(width: 4),
Text('messageDecoding'.tr)
],
2024-05-25 17:21:08 +00:00
),
2024-05-26 05:39:21 +00:00
).animate(onPlay: (c) => c.repeat()).fade(begin: 0, end: 1);
} else if (snapshot.hasError) {
return Opacity(
opacity: 0.9,
child: Row(
children: [
const Icon(Icons.close),
const SizedBox(width: 4),
Text(
'messageDecodeFailed'
.trParams({'message': snapshot.error.toString()}),
)
],
),
);
}
2024-06-23 12:18:55 +00:00
if (snapshot.data?.isNotEmpty ?? false) {
return Markdown(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
data: snapshot.data ?? '',
padding: const EdgeInsets.all(0),
onTapLink: (text, href, title) async {
if (href == null) return;
await launchUrlString(
href,
mode: LaunchMode.externalApplication,
);
},
).paddingOnly(
left: 12,
right: 12,
top: 2,
bottom: hasAttachment ? 4 : 0,
);
} else {
return const SizedBox();
}
2024-05-26 05:39:21 +00:00
},
2024-05-25 17:21:08 +00:00
);
}
2024-05-26 05:39:21 +00:00
2024-06-23 12:18:55 +00:00
Widget buildBody(BuildContext context) {
if (isContentPreviewing) {
2024-06-23 12:18:55 +00:00
return buildContent();
} else if (isMerged) {
2024-06-23 12:18:55 +00:00
return Column(
2024-06-09 15:00:11 +00:00
children: [
buildContent().paddingOnly(left: 52),
if (item.attachments?.isNotEmpty ?? false)
AttachmentList(
key: Key('m${item.uuid}attachments'),
parentId: item.uuid,
attachmentsId: item.attachments ?? List.empty(),
).paddingSymmetric(vertical: 4),
],
);
2024-06-04 15:29:05 +00:00
} else if (isReply) {
2024-06-23 12:18:55 +00:00
return Row(
2024-06-04 15:29:05 +00:00
mainAxisAlignment: MainAxisAlignment.center,
2024-05-26 05:39:21 +00:00
children: [
2024-06-04 15:29:05 +00:00
Transform.scale(
scaleX: -1,
child: const FaIcon(FontAwesomeIcons.reply, size: 14),
),
const SizedBox(width: 12),
2024-05-26 05:39:21 +00:00
AccountAvatar(content: item.sender.account.avatar, radius: 8),
2024-06-04 15:29:05 +00:00
const SizedBox(width: 4),
2024-05-26 05:39:21 +00:00
Text(
item.sender.account.nick,
style: const TextStyle(fontWeight: FontWeight.bold),
),
2024-06-04 15:29:05 +00:00
Expanded(child: buildContent()),
2024-05-26 05:39:21 +00:00
],
);
} else {
2024-06-23 12:18:55 +00:00
return Column(
2024-05-26 05:39:21 +00:00
key: Key('m${item.uuid}'),
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AccountAvatar(content: item.sender.account.avatar),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
item.sender.account.nick,
style: const TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(width: 4),
Text(format(item.createdAt, locale: 'en_short'))
],
).paddingSymmetric(horizontal: 12),
buildContent(),
2024-06-23 12:18:55 +00:00
if (item.attachments?.isNotEmpty ?? false)
SizedBox(
width: min(MediaQuery.of(context).size.width, 640),
child: AttachmentList(
key: Key('m${item.uuid}attachments'),
parentId: item.uuid,
attachmentsId: item.attachments ?? List.empty(),
divided: true,
viewport: 1,
),
),
2024-05-26 05:39:21 +00:00
],
),
),
],
2024-05-26 13:03:25 +00:00
).paddingSymmetric(horizontal: 12),
2024-05-26 05:39:21 +00:00
],
);
}
2024-06-23 12:18:55 +00:00
}
2024-05-26 05:39:21 +00:00
2024-06-23 12:18:55 +00:00
@override
Widget build(BuildContext context) {
return buildBody(context);
2024-05-26 05:39:21 +00:00
}
2024-05-25 17:21:08 +00:00
}