⬆️ 升级支持服务器的 Event Based Messages #2
@ -14,13 +14,23 @@ class ChatEventController {
|
|||||||
final RxBool isLoading = false.obs;
|
final RxBool isLoading = false.obs;
|
||||||
|
|
||||||
Channel? channel;
|
Channel? channel;
|
||||||
|
String? scope;
|
||||||
|
|
||||||
initialize() async {
|
initialize() async {
|
||||||
database = await createHistoryDb();
|
database = await createHistoryDb();
|
||||||
currentEvents.clear();
|
currentEvents.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<LocalEvent?> getEvent(int id) async {
|
||||||
|
if(channel == null || scope == null) return null;
|
||||||
|
|
||||||
|
return await database.getEvent(id, channel!, scope: scope!);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> getEvents(Channel channel, String scope) async {
|
Future<void> getEvents(Channel channel, String scope) async {
|
||||||
|
this.channel = channel;
|
||||||
|
this.scope = scope;
|
||||||
|
|
||||||
syncLocal(channel);
|
syncLocal(channel);
|
||||||
|
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
|
@ -149,6 +149,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
key: Key('m${item.uuid}'),
|
key: Key('m${item.uuid}'),
|
||||||
item: item,
|
item: item,
|
||||||
isMerged: isMerged,
|
isMerged: isMerged,
|
||||||
|
chatController: _chatController,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,7 +203,6 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
_chatController.initialize();
|
_chatController.initialize();
|
||||||
|
|
||||||
getChannel().then((_) {
|
getChannel().then((_) {
|
||||||
_chatController.channel = _channel!;
|
|
||||||
_chatController.getEvents(_channel!, widget.realm);
|
_chatController.getEvents(_channel!, widget.realm);
|
||||||
|
|
||||||
listenMessages();
|
listenMessages();
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:solian/controllers/chat_events_controller.dart';
|
||||||
import 'package:solian/models/event.dart';
|
import 'package:solian/models/event.dart';
|
||||||
import 'package:solian/widgets/account/account_avatar.dart';
|
import 'package:solian/widgets/account/account_avatar.dart';
|
||||||
import 'package:solian/widgets/account/account_profile_popup.dart';
|
import 'package:solian/widgets/account/account_profile_popup.dart';
|
||||||
@ -15,6 +18,8 @@ class ChatEvent extends StatelessWidget {
|
|||||||
final bool isMerged;
|
final bool isMerged;
|
||||||
final bool isHasMerged;
|
final bool isHasMerged;
|
||||||
|
|
||||||
|
final ChatEventController? chatController;
|
||||||
|
|
||||||
const ChatEvent({
|
const ChatEvent({
|
||||||
super.key,
|
super.key,
|
||||||
required this.item,
|
required this.item,
|
||||||
@ -22,8 +27,28 @@ class ChatEvent extends StatelessWidget {
|
|||||||
this.isMerged = false,
|
this.isMerged = false,
|
||||||
this.isHasMerged = false,
|
this.isHasMerged = false,
|
||||||
this.isQuote = false,
|
this.isQuote = false,
|
||||||
|
this.chatController,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Widget buildQuote() {
|
||||||
|
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() {
|
Widget buildContent() {
|
||||||
switch (item.type) {
|
switch (item.type) {
|
||||||
case 'messages.new':
|
case 'messages.new':
|
||||||
@ -40,6 +65,7 @@ class ChatEvent extends StatelessWidget {
|
|||||||
text: 'messageEditDesc'.trParams({'id': '#${item.id}'}),
|
text: 'messageEditDesc'.trParams({'id': '#${item.id}'}),
|
||||||
isMerged: isMerged,
|
isMerged: isMerged,
|
||||||
isHasMerged: isHasMerged,
|
isHasMerged: isHasMerged,
|
||||||
|
isQuote: isQuote,
|
||||||
);
|
);
|
||||||
case 'messages.delete':
|
case 'messages.delete':
|
||||||
return ChatEventMessageActionLog(
|
return ChatEventMessageActionLog(
|
||||||
@ -47,6 +73,7 @@ class ChatEvent extends StatelessWidget {
|
|||||||
text: 'messageDeleteDesc'.trParams({'id': '#${item.id}'}),
|
text: 'messageDeleteDesc'.trParams({'id': '#${item.id}'}),
|
||||||
isMerged: isMerged,
|
isMerged: isMerged,
|
||||||
isHasMerged: isHasMerged,
|
isHasMerged: isHasMerged,
|
||||||
|
isQuote: isQuote,
|
||||||
);
|
);
|
||||||
case 'system.changes':
|
case 'system.changes':
|
||||||
return ChatEventMessageActionLog(
|
return ChatEventMessageActionLog(
|
||||||
@ -54,6 +81,7 @@ class ChatEvent extends StatelessWidget {
|
|||||||
text: item.body['text'],
|
text: item.body['text'],
|
||||||
isMerged: isMerged,
|
isMerged: isMerged,
|
||||||
isHasMerged: isHasMerged,
|
isHasMerged: isHasMerged,
|
||||||
|
isQuote: isQuote,
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return ChatEventMessageActionLog(
|
return ChatEventMessageActionLog(
|
||||||
@ -61,31 +89,55 @@ class ChatEvent extends StatelessWidget {
|
|||||||
text: 'messageTypeUnsupported'.trParams({'type': item.type}),
|
text: 'messageTypeUnsupported'.trParams({'type': item.type}),
|
||||||
isMerged: isMerged,
|
isMerged: isMerged,
|
||||||
isHasMerged: isHasMerged,
|
isHasMerged: isHasMerged,
|
||||||
|
isQuote: isQuote,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildBody(BuildContext context) {
|
Widget buildBody(BuildContext context) {
|
||||||
if (isContentPreviewing || isMerged) {
|
if (isContentPreviewing || (isMerged && !isQuote)) {
|
||||||
return buildContent();
|
return Column(
|
||||||
} else if (isQuote) {
|
|
||||||
return Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
children: [
|
||||||
Transform.scale(
|
if (item.body['quote_event'] != null && chatController != null)
|
||||||
scaleX: -1,
|
buildQuote(),
|
||||||
child: const FaIcon(FontAwesomeIcons.reply, size: 14),
|
buildContent(),
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
AccountAvatar(content: item.sender.account.avatar, radius: 8),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
Text(
|
|
||||||
item.sender.account.nick,
|
|
||||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
Expanded(child: buildContent()),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
} else if (isQuote) {
|
||||||
|
return Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
const Opacity(
|
||||||
|
opacity: 0.75,
|
||||||
|
child: FaIcon(FontAwesomeIcons.quoteLeft, size: 14),
|
||||||
|
).paddingOnly(bottom: 4),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Expanded(
|
||||||
|
child: Card(
|
||||||
|
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),
|
||||||
|
],
|
||||||
|
).paddingSymmetric(horizontal: 12, vertical: 4),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingOnly(left: isMerged ? 66 : 14, right: 4);
|
||||||
} else {
|
} else {
|
||||||
return Column(
|
return Column(
|
||||||
key: Key('m${item.uuid}'),
|
key: Key('m${item.uuid}'),
|
||||||
@ -121,6 +173,9 @@ class ChatEvent extends StatelessWidget {
|
|||||||
Text(format(item.createdAt, locale: 'en_short'))
|
Text(format(item.createdAt, locale: 'en_short'))
|
||||||
],
|
],
|
||||||
).paddingSymmetric(horizontal: 12),
|
).paddingSymmetric(horizontal: 12),
|
||||||
|
if (item.body['quote_event'] != null &&
|
||||||
|
chatController != null)
|
||||||
|
buildQuote(),
|
||||||
buildContent(),
|
buildContent(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -1,16 +1,10 @@
|
|||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:solian/models/event.dart';
|
|
||||||
import 'package:solian/widgets/attachments/attachment_list.dart';
|
|
||||||
import 'package:timeago/timeago.dart' show format;
|
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
|
||||||
|
|
||||||
class ChatEventMessageActionLog extends StatelessWidget {
|
class ChatEventMessageActionLog extends StatelessWidget {
|
||||||
final Widget icon;
|
final Widget icon;
|
||||||
final String text;
|
final String text;
|
||||||
|
final bool isQuote;
|
||||||
final bool isMerged;
|
final bool isMerged;
|
||||||
final bool isHasMerged;
|
final bool isHasMerged;
|
||||||
|
|
||||||
@ -20,6 +14,7 @@ class ChatEventMessageActionLog extends StatelessWidget {
|
|||||||
required this.text,
|
required this.text,
|
||||||
this.isMerged = false,
|
this.isMerged = false,
|
||||||
this.isHasMerged = false,
|
this.isHasMerged = false,
|
||||||
|
this.isQuote = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -34,7 +29,7 @@ class ChatEventMessageActionLog extends StatelessWidget {
|
|||||||
Text(text),
|
Text(text),
|
||||||
],
|
],
|
||||||
).paddingOnly(
|
).paddingOnly(
|
||||||
left: isMerged ? 64 : 12,
|
left: isQuote ? 0 : (isMerged ? 64 : 12),
|
||||||
top: 2,
|
top: 2,
|
||||||
bottom: isHasMerged ? 2 : 0,
|
bottom: isHasMerged ? 2 : 0,
|
||||||
),
|
),
|
||||||
|
@ -23,6 +23,21 @@ class ChatEventMessage extends StatelessWidget {
|
|||||||
this.isQuote = false,
|
this.isQuote = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Widget buildAttachment(BuildContext context) {
|
||||||
|
final body = EventMessageBody.fromJson(item.body);
|
||||||
|
|
||||||
|
return SizedBox(
|
||||||
|
width: min(MediaQuery.of(context).size.width, 640),
|
||||||
|
child: AttachmentList(
|
||||||
|
key: Key('m${item.uuid}attachments'),
|
||||||
|
parentId: item.uuid,
|
||||||
|
attachmentsId: body.attachments ?? List.empty(),
|
||||||
|
divided: true,
|
||||||
|
viewport: 1,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget buildContent() {
|
Widget buildContent() {
|
||||||
final body = EventMessageBody.fromJson(item.body);
|
final body = EventMessageBody.fromJson(item.body);
|
||||||
final hasAttachment = body.attachments?.isNotEmpty ?? false;
|
final hasAttachment = body.attachments?.isNotEmpty ?? false;
|
||||||
@ -41,9 +56,9 @@ class ChatEventMessage extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
).paddingOnly(
|
).paddingOnly(
|
||||||
left: 12,
|
left: isQuote ? 0 : 12,
|
||||||
right: 12,
|
right: isQuote ? 0 : 12,
|
||||||
top: 2,
|
top: body.quoteEvent == null ? 2 : 0,
|
||||||
bottom: hasAttachment ? 4 : (isHasMerged ? 2 : 0),
|
bottom: hasAttachment ? 4 : (isHasMerged ? 2 : 0),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -51,18 +66,15 @@ class ChatEventMessage extends StatelessWidget {
|
|||||||
Widget buildBody(BuildContext context) {
|
Widget buildBody(BuildContext context) {
|
||||||
final body = EventMessageBody.fromJson(item.body);
|
final body = EventMessageBody.fromJson(item.body);
|
||||||
|
|
||||||
if (isContentPreviewing || isQuote) {
|
if (isContentPreviewing) {
|
||||||
return buildContent();
|
return buildContent();
|
||||||
} else if (isMerged) {
|
} else if (isMerged) {
|
||||||
return Column(
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
buildContent().paddingOnly(left: 52),
|
buildContent().paddingOnly(left: 52),
|
||||||
if (body.attachments?.isNotEmpty ?? false)
|
if (body.attachments?.isNotEmpty ?? false)
|
||||||
AttachmentList(
|
buildAttachment(context).paddingOnly(left: 52, bottom: 4),
|
||||||
key: Key('m${item.uuid}attachments'),
|
|
||||||
parentId: item.uuid,
|
|
||||||
attachmentsId: body.attachments ?? List.empty(),
|
|
||||||
).paddingSymmetric(vertical: 4),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -71,16 +83,7 @@ class ChatEventMessage extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
buildContent(),
|
buildContent(),
|
||||||
if (body.attachments?.isNotEmpty ?? false)
|
if (body.attachments?.isNotEmpty ?? false)
|
||||||
SizedBox(
|
buildAttachment(context).paddingOnly(bottom: 4),
|
||||||
width: min(MediaQuery.of(context).size.width, 640),
|
|
||||||
child: AttachmentList(
|
|
||||||
key: Key('m${item.uuid}attachments'),
|
|
||||||
parentId: item.uuid,
|
|
||||||
attachmentsId: body.attachments ?? List.empty(),
|
|
||||||
divided: true,
|
|
||||||
viewport: 1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user