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;
|
2024-05-30 15:14:29 +00:00
|
|
|
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,
|
2024-05-30 15:14:29 +00:00
|
|
|
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) {
|
2024-05-30 15:14:29 +00:00
|
|
|
if (isContentPreviewing) {
|
2024-06-23 12:18:55 +00:00
|
|
|
return buildContent();
|
2024-05-30 15:14:29 +00:00
|
|
|
} 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
|
|
|
}
|