Reaction support, currently readonly
This commit is contained in:
parent
624127f3a8
commit
5f5ad911c2
3 changed files with 88 additions and 3 deletions
|
|
@ -2,9 +2,11 @@ import "package:collection/collection.dart";
|
|||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||
import "package:flutter_chat_core/flutter_chat_core.dart";
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:nexus/controllers/client_controller.dart";
|
||||
import "package:nexus/controllers/client_state_controller.dart";
|
||||
import "package:nexus/helpers/extensions/mxc_to_https.dart";
|
||||
import "package:nexus/models/configs/message_config.dart";
|
||||
import "package:nexus/models/requests/get_related_events_request.dart";
|
||||
|
||||
class MessageController extends AsyncNotifier<Message?> {
|
||||
final MessageConfig config;
|
||||
|
|
@ -13,7 +15,8 @@ class MessageController extends AsyncNotifier<Message?> {
|
|||
@override
|
||||
Future<Message?> build() async {
|
||||
try {
|
||||
if (config.event.relationType == "m.replace" && !config.includeEdits) {
|
||||
if ((config.event.relationType == "m.replace" && !config.includeEdits) ||
|
||||
config.room.metadata == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -65,10 +68,35 @@ class MessageController extends AsyncNotifier<Message?> {
|
|||
final replyId =
|
||||
config.event.content["m.relates_to"]?["m.in_reply_to"]?["event_id"];
|
||||
|
||||
final reactionEvents = await ref
|
||||
.watch(ClientController.provider.notifier)
|
||||
.getRelatedEvents(
|
||||
GetRelatedEventsRequest(
|
||||
roomId: config.room.metadata!.id,
|
||||
eventId: config.event.eventId,
|
||||
relationType: "m.annotation",
|
||||
),
|
||||
);
|
||||
|
||||
final reactions = reactionEvents
|
||||
?.fold<IMap<String, IList<String>>>(IMap(), (acc, event) {
|
||||
final key = event.content["m.relates_to"]?["key"];
|
||||
if (key == null) return acc;
|
||||
|
||||
return acc.update(
|
||||
key,
|
||||
(list) => list.add(event.authorId),
|
||||
ifAbsent: () => IList([event.authorId]),
|
||||
);
|
||||
})
|
||||
.map((key, value) => MapEntry(key, value.unlock))
|
||||
.unlock;
|
||||
|
||||
final asText =
|
||||
Message.text(
|
||||
metadata: metadata,
|
||||
id: config.event.eventId,
|
||||
reactions: reactions,
|
||||
authorId: event.authorId,
|
||||
text: content["formatted_body"] ?? content["body"] ?? "",
|
||||
replyToMessageId: replyId,
|
||||
|
|
@ -80,6 +108,7 @@ class MessageController extends AsyncNotifier<Message?> {
|
|||
Message toSystemMessage(String content) => Message.system(
|
||||
metadata: {...metadata, "body": content},
|
||||
id: config.event.eventId,
|
||||
reactions: reactions,
|
||||
authorId: event.authorId,
|
||||
deliveredAt: config.event.timestamp,
|
||||
text: content,
|
||||
|
|
@ -104,6 +133,7 @@ class MessageController extends AsyncNotifier<Message?> {
|
|||
null || "m.image" => Message.image(
|
||||
id: config.event.eventId,
|
||||
authorId: event.authorId,
|
||||
reactions: reactions,
|
||||
source: source,
|
||||
replyToMessageId: replyId,
|
||||
metadata: metadata,
|
||||
|
|
@ -116,6 +146,7 @@ class MessageController extends AsyncNotifier<Message?> {
|
|||
size: content["info"]["size"],
|
||||
metadata: metadata,
|
||||
id: config.event.eventId,
|
||||
reactions: reactions,
|
||||
authorId: event.authorId,
|
||||
source: source,
|
||||
replyToMessageId: replyId,
|
||||
|
|
@ -159,6 +190,7 @@ class MessageController extends AsyncNotifier<Message?> {
|
|||
// ignore: dead_code
|
||||
? Message.unsupported(
|
||||
metadata: metadata,
|
||||
reactions: reactions,
|
||||
id: config.event.eventId,
|
||||
authorId: event.authorId,
|
||||
replyToMessageId: replyId,
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ class RoomChatController extends AsyncNotifier<InMemoryChatController> {
|
|||
ref.onDispose(
|
||||
ref.listen(NewEventsController.provider(roomId), (_, next) async {
|
||||
for (final event in next) {
|
||||
// TODO: Handle new reactions
|
||||
if (event.type == "m.room.redaction") {
|
||||
final controller = await future;
|
||||
final message = controller.messages.firstWhereOrNull(
|
||||
|
|
|
|||
|
|
@ -1,19 +1,28 @@
|
|||
import "package:cross_cache/cross_cache.dart";
|
||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_chat_core/flutter_chat_core.dart";
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:nexus/controllers/client_state_controller.dart";
|
||||
import "package:nexus/controllers/cross_cache_controller.dart";
|
||||
import "package:nexus/helpers/extensions/get_headers.dart";
|
||||
import "package:nexus/helpers/extensions/mxc_to_https.dart";
|
||||
import "package:nexus/widgets/chat_page/lazy_loading/message_avatar.dart";
|
||||
import "package:nexus/widgets/chat_page/lazy_loading/message_displayname.dart";
|
||||
import "package:timeago/timeago.dart";
|
||||
|
||||
class MessageWrapper extends StatelessWidget {
|
||||
class MessageWrapper extends ConsumerWidget {
|
||||
final Message message;
|
||||
final Widget child;
|
||||
final MessageGroupStatus? groupStatus;
|
||||
const MessageWrapper(this.message, this.child, this.groupStatus, {super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = Theme.of(context);
|
||||
final error = message.metadata?["error"];
|
||||
final clientState = ref.watch(ClientStateController.provider);
|
||||
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||
child: AnimatedContainer(
|
||||
|
|
@ -69,6 +78,49 @@ class MessageWrapper extends StatelessWidget {
|
|||
color: theme.colorScheme.error,
|
||||
),
|
||||
),
|
||||
Wrap(
|
||||
spacing: 4,
|
||||
runSpacing: 4,
|
||||
children:
|
||||
clientState?.homeserverUrl == null ||
|
||||
message.reactions == null
|
||||
? []
|
||||
: message.reactions!.mapTo((reaction, reactors) {
|
||||
final selected = reactors.contains(
|
||||
clientState!.userId,
|
||||
);
|
||||
return SizedBox(
|
||||
child: ChoiceChip(
|
||||
showCheckmark: false,
|
||||
selected: selected,
|
||||
label: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 8,
|
||||
children: [
|
||||
reaction.startsWith("mxc://")
|
||||
? Image(
|
||||
height: 20,
|
||||
image: CachedNetworkImage(
|
||||
headers: ref.headers,
|
||||
Uri.parse(reaction)
|
||||
.mxcToHttps(
|
||||
clientState.homeserverUrl!,
|
||||
)
|
||||
.toString(),
|
||||
ref.watch(
|
||||
CrossCacheController.provider,
|
||||
),
|
||||
),
|
||||
)
|
||||
: Text(reaction),
|
||||
Text(reactors.length.toString()),
|
||||
],
|
||||
),
|
||||
onSelected: (value) {}, // TODO
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue