Solian/lib/widgets/chat/chat_event.dart

271 lines
9.0 KiB
Dart
Raw Normal View History

2024-06-27 16:59:11 +00:00
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:get/get.dart';
2024-06-27 18:49:28 +00:00
import 'package:solian/controllers/chat_events_controller.dart';
2024-06-27 16:59:11 +00:00
import 'package:solian/models/event.dart';
import 'package:solian/widgets/account/account_avatar.dart';
import 'package:solian/widgets/account/account_profile_popup.dart';
import 'package:solian/widgets/attachments/attachment_list.dart';
2024-06-27 16:59:11 +00:00
import 'package:solian/widgets/chat/chat_event_action_log.dart';
import 'package:solian/widgets/chat/chat_event_message.dart';
import 'package:timeago/timeago.dart' show format;
class ChatEvent extends StatelessWidget {
final Event item;
final bool isContentPreviewing;
final bool isQuote;
final bool isMerged;
final bool isHasMerged;
2024-06-27 18:49:28 +00:00
final ChatEventController? chatController;
2024-06-27 16:59:11 +00:00
const ChatEvent({
super.key,
required this.item,
this.isContentPreviewing = false,
this.isMerged = false,
this.isHasMerged = false,
this.isQuote = false,
2024-06-27 18:49:28 +00:00
this.chatController,
2024-06-27 16:59:11 +00:00
});
2024-06-27 20:34:35 +00:00
String _formatDuration(Duration duration) {
String negativeSign = duration.isNegative ? '-' : '';
String twoDigits(int n) => n.toString().padLeft(2, '0');
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60).abs());
String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60).abs());
return '$negativeSign${twoDigits(duration.inHours)}:$twoDigitMinutes:$twoDigitSeconds';
}
Widget _buildAttachment(BuildContext context) {
final attachments = item.body['attachments'] != null
? List<int>.from(item.body['attachments'].map((x) => x))
: List<int>.empty();
if (attachments.isEmpty) return const SizedBox();
return Container(
key: Key('m${item.uuid}attachments-box'),
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.only(top: isMerged ? 0 : 4),
constraints: const BoxConstraints(
maxHeight: 720,
),
child: AttachmentList(
key: Key('m${item.uuid}attachments'),
parentId: item.uuid,
attachmentsId: attachments,
viewport: 1,
),
);
}
Widget _buildQuote() {
2024-06-27 18:49:28 +00:00
return FutureBuilder(
future: chatController!.getEvent(
item.body['quote_event'],
),
builder: (context, snapshot) {
if (!snapshot.hasData || snapshot.data == null) {
return const SizedBox();
}
return ChatEvent(
item: snapshot.data!.data,
isMerged: false,
isQuote: true,
).paddingOnly(left: isMerged ? 52 : 0);
},
);
}
Widget _buildContent() {
2024-06-27 16:59:11 +00:00
switch (item.type) {
case 'messages.new':
return ChatEventMessage(
item: item,
isContentPreviewing: isContentPreviewing,
isMerged: isMerged,
isHasMerged: isHasMerged,
isQuote: isQuote,
);
case 'messages.edit':
return ChatEventMessageActionLog(
icon: const Icon(Icons.edit_note, size: 16),
text: 'messageEditDesc'.trParams({'id': '#${item.id}'}),
isMerged: isMerged,
isHasMerged: isHasMerged,
2024-06-27 18:49:28 +00:00
isQuote: isQuote,
2024-06-27 16:59:11 +00:00
);
case 'messages.delete':
return ChatEventMessageActionLog(
icon: const Icon(Icons.cancel_schedule_send, size: 16),
text: 'messageDeleteDesc'.trParams({'id': '#${item.id}'}),
isMerged: isMerged,
isHasMerged: isHasMerged,
2024-06-27 18:49:28 +00:00
isQuote: isQuote,
2024-06-27 16:59:11 +00:00
);
2024-06-27 20:34:35 +00:00
case 'calls.start':
return ChatEventMessageActionLog(
icon: const Icon(Icons.call_made, size: 16),
text: 'messageCallStartDesc'
.trParams({'user': '@${item.sender.account.name}'}),
isMerged: isMerged,
isHasMerged: isHasMerged,
isQuote: isQuote,
);
case 'calls.end':
return ChatEventMessageActionLog(
icon: const Icon(Icons.call_received, size: 16),
text: 'messageCallEndDesc'.trParams({
'duration': _formatDuration(
2024-06-28 11:36:48 +00:00
Duration(seconds: item.body['last']),
2024-06-27 20:34:35 +00:00
),
}),
isMerged: isMerged,
isHasMerged: isHasMerged,
isQuote: isQuote,
);
2024-06-27 16:59:11 +00:00
case 'system.changes':
return ChatEventMessageActionLog(
icon: const Icon(Icons.manage_history, size: 16),
text: item.body['text'],
isMerged: isMerged,
isHasMerged: isHasMerged,
2024-06-27 18:49:28 +00:00
isQuote: isQuote,
2024-06-27 16:59:11 +00:00
);
default:
return ChatEventMessageActionLog(
icon: const Icon(Icons.error, size: 16),
text: 'messageTypeUnsupported'.trParams({'type': item.type}),
isMerged: isMerged,
isHasMerged: isHasMerged,
2024-06-27 18:49:28 +00:00
isQuote: isQuote,
2024-06-27 16:59:11 +00:00
);
}
}
Widget _buildBody(BuildContext context) {
2024-06-27 18:49:28 +00:00
if (isContentPreviewing || (isMerged && !isQuote)) {
return Column(
2024-06-29 10:40:26 +00:00
crossAxisAlignment: CrossAxisAlignment.start,
2024-06-27 18:49:28 +00:00
children: [
if (item.body['quote_event'] != null && chatController != null)
_buildQuote(),
2024-06-29 10:40:26 +00:00
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(child: _buildContent()),
2024-06-29 10:40:26 +00:00
if (item.isPending)
const SizedBox(
width: 12,
height: 12,
child: CircularProgressIndicator(strokeWidth: 2),
),
],
).paddingOnly(right: 12),
_buildAttachment(context),
2024-06-27 18:49:28 +00:00
],
);
2024-06-27 16:59:11 +00:00
} else if (isQuote) {
2024-06-27 18:55:05 +00:00
return Card(
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
const Opacity(
opacity: 0.75,
child: FaIcon(FontAwesomeIcons.quoteLeft, size: 14),
).paddingOnly(bottom: 2.75),
const SizedBox(width: 4),
Expanded(
2024-06-27 18:49:28 +00:00
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
AccountAvatar(
content: item.sender.account.avatar, radius: 9),
const SizedBox(width: 5),
Text(
item.sender.account.nick,
style: const TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(width: 4),
Text(format(item.createdAt, locale: 'en_short')),
],
),
_buildContent().paddingOnly(left: 0.5),
2024-06-27 18:49:28 +00:00
],
2024-06-27 18:55:05 +00:00
),
2024-06-27 18:49:28 +00:00
),
2024-06-27 18:55:05 +00:00
],
).paddingOnly(left: 12, right: 12, top: 8, bottom: 4),
).paddingOnly(left: isMerged ? 52 : 0, right: 4);
2024-06-27 16:59:11 +00:00
} else {
return Column(
key: Key('m${item.uuid}'),
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GestureDetector(
child: AccountAvatar(content: item.sender.account.avatar),
onTap: () {
showModalBottomSheet(
useRootNavigator: true,
isScrollControlled: true,
backgroundColor: Theme.of(context).colorScheme.surface,
context: context,
builder: (context) => AccountProfilePopup(
account: item.sender.account,
),
);
},
),
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),
2024-06-27 18:49:28 +00:00
if (item.body['quote_event'] != null &&
chatController != null)
_buildQuote(),
2024-06-29 10:40:26 +00:00
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(child: _buildContent()),
2024-06-29 10:40:26 +00:00
if (item.isPending)
const SizedBox(
width: 12,
height: 12,
child: CircularProgressIndicator(strokeWidth: 2),
),
],
),
2024-06-27 16:59:11 +00:00
],
),
),
],
).paddingSymmetric(horizontal: 12),
_buildAttachment(context),
2024-06-27 16:59:11 +00:00
],
);
}
}
@override
Widget build(BuildContext context) {
return _buildBody(context);
2024-06-27 16:59:11 +00:00
}
}