From c5a40702b9de7a3bf8b260173b9c84c83b133161 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Wed, 4 Dec 2024 00:17:11 +0800 Subject: [PATCH] :sparkles: Ordering and show the last message in channel --- lib/providers/channel.dart | 23 +++++++++++++++++++++++ lib/screens/chat.dart | 37 +++++++++++++++++++++++++++++++------ 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/lib/providers/channel.dart b/lib/providers/channel.dart index d91b854..5c8ad7e 100644 --- a/lib/providers/channel.dart +++ b/lib/providers/channel.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:provider/provider.dart'; +import 'package:surface/controllers/chat_message_controller.dart'; import 'package:surface/providers/sn_network.dart'; +import 'package:surface/providers/user_directory.dart'; import 'package:surface/types/chat.dart'; import 'package:surface/types/realm.dart'; @@ -9,11 +11,13 @@ class ChatChannelProvider extends ChangeNotifier { static const kChatChannelBoxName = 'nex_chat_channels'; late final SnNetworkProvider _sn; + late final UserDirectoryProvider _ud; Box? get _channelBox => Hive.box(kChatChannelBoxName); ChatChannelProvider(BuildContext context) { _sn = context.read(); + _ud = context.read(); _initializeLocalData(); } @@ -113,6 +117,25 @@ class ChatChannelProvider extends ChangeNotifier { yield result; } + Future> getLastMessages( + Iterable channels, + ) async { + final result = List.empty(growable: true); + for (final channel in channels) { + final channelBox = await Hive.openBox( + '${ChatMessageController.kChatMessageBoxPrefix}${channel.id}', + ); + final lastMessage = channelBox.isNotEmpty + ? channelBox.values + .reduce((a, b) => a.createdAt.isAfter(b.createdAt) ? a : b) + : null; + if (lastMessage != null) result.add(lastMessage); + channelBox.close(); + } + await _ud.listAccount(result.map((ele) => ele.sender.accountId).toSet()); + return result; + } + @override void dispose() { _channelBox?.close(); diff --git a/lib/screens/chat.dart b/lib/screens/chat.dart index a42e593..1e07cfc 100644 --- a/lib/screens/chat.dart +++ b/lib/screens/chat.dart @@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:provider/provider.dart'; import 'package:surface/providers/channel.dart'; +import 'package:surface/providers/user_directory.dart'; import 'package:surface/types/chat.dart'; import 'package:surface/widgets/account/account_image.dart'; import 'package:surface/widgets/dialog.dart'; @@ -20,10 +21,25 @@ class _ChatScreenState extends State { bool _isBusy = true; List? _channels; + Map? _lastMessages; void _refreshChannels() { final chan = context.read(); - chan.fetchChannels().listen((channels) { + chan.fetchChannels().listen((channels) async { + final lastMessages = await chan.getLastMessages(channels); + _lastMessages = {for (final val in lastMessages) val.channelId: val}; + channels.sort((a, b) { + if (_lastMessages!.containsKey(a.id) && + _lastMessages!.containsKey(b.id)) { + return _lastMessages![b.id]! + .createdAt + .compareTo(_lastMessages![a.id]!.createdAt); + } + if (_lastMessages!.containsKey(a.id)) return -1; + if (_lastMessages!.containsKey(b.id)) return 1; + return 0; + }); + if (mounted) setState(() => _channels = channels); }) ..onError((err) { @@ -45,6 +61,8 @@ class _ChatScreenState extends State { @override Widget build(BuildContext context) { + final ud = context.read(); + return Scaffold( appBar: AppBar( title: Text('screenChat').tr(), @@ -67,13 +85,20 @@ class _ChatScreenState extends State { itemCount: _channels?.length ?? 0, itemBuilder: (context, idx) { final channel = _channels![idx]; + final lastMessage = _lastMessages?[channel.id]; return ListTile( title: Text(channel.name), - subtitle: Text( - channel.description, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), + subtitle: lastMessage != null + ? Text( + '${ud.getAccountFromCache(lastMessage.sender.accountId)?.nick}: ${lastMessage.body['text'] ?? 'Unable preview'}', + maxLines: 1, + overflow: TextOverflow.ellipsis, + ) + : Text( + channel.description, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), contentPadding: const EdgeInsets.symmetric(horizontal: 16), leading: AccountImage( content: null,