✨ Chat unread count
This commit is contained in:
parent
0424f98eb5
commit
78516abf2e
@ -6,9 +6,9 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:surface/database/database.dart';
|
import 'package:surface/database/database.dart';
|
||||||
import 'package:surface/providers/database.dart';
|
import 'package:surface/providers/database.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
|
import 'package:surface/providers/sn_realm.dart';
|
||||||
import 'package:surface/providers/user_directory.dart';
|
import 'package:surface/providers/user_directory.dart';
|
||||||
import 'package:surface/types/chat.dart';
|
import 'package:surface/types/chat.dart';
|
||||||
import 'package:surface/types/realm.dart';
|
|
||||||
|
|
||||||
class ChatChannelProvider extends ChangeNotifier {
|
class ChatChannelProvider extends ChangeNotifier {
|
||||||
static const kChatChannelBoxName = 'nex_chat_channels';
|
static const kChatChannelBoxName = 'nex_chat_channels';
|
||||||
@ -16,11 +16,13 @@ class ChatChannelProvider extends ChangeNotifier {
|
|||||||
late final SnNetworkProvider _sn;
|
late final SnNetworkProvider _sn;
|
||||||
late final UserDirectoryProvider _ud;
|
late final UserDirectoryProvider _ud;
|
||||||
late final DatabaseProvider _dt;
|
late final DatabaseProvider _dt;
|
||||||
|
late final SnRealmProvider _rels;
|
||||||
|
|
||||||
ChatChannelProvider(BuildContext context) {
|
ChatChannelProvider(BuildContext context) {
|
||||||
_sn = context.read<SnNetworkProvider>();
|
_sn = context.read<SnNetworkProvider>();
|
||||||
_ud = context.read<UserDirectoryProvider>();
|
_ud = context.read<UserDirectoryProvider>();
|
||||||
_dt = context.read<DatabaseProvider>();
|
_dt = context.read<DatabaseProvider>();
|
||||||
|
_rels = context.read<SnRealmProvider>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _saveChannelToLocal(Iterable<SnChannel> channels) async {
|
Future<void> _saveChannelToLocal(Iterable<SnChannel> channels) async {
|
||||||
@ -44,16 +46,9 @@ class ChatChannelProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<List<SnChannel>> _fetchChannelsFromServer({
|
Future<List<SnChannel>> _fetchChannelsFromServer({
|
||||||
String scope = 'global',
|
|
||||||
bool direct = false,
|
|
||||||
bool doNotSave = false,
|
bool doNotSave = false,
|
||||||
}) async {
|
}) async {
|
||||||
final resp = await _sn.client.get(
|
final resp = await _sn.client.get('/cgi/im/channels/me/available');
|
||||||
'/cgi/im/channels/$scope/me/available',
|
|
||||||
queryParameters: {
|
|
||||||
'direct': direct,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
final out = List<SnChannel>.from(
|
final out = List<SnChannel>.from(
|
||||||
resp.data?.map((e) => SnChannel.fromJson(e)) ?? [],
|
resp.data?.map((e) => SnChannel.fromJson(e)) ?? [],
|
||||||
);
|
);
|
||||||
@ -68,7 +63,10 @@ class ChatChannelProvider extends ChangeNotifier {
|
|||||||
final local = await (_dt.db.snLocalChatChannel.select()
|
final local = await (_dt.db.snLocalChatChannel.select()
|
||||||
..where((e) => e.alias.equals(key)))
|
..where((e) => e.alias.equals(key)))
|
||||||
.getSingleOrNull();
|
.getSingleOrNull();
|
||||||
if (local != null) return local.content;
|
if (local != null) {
|
||||||
|
final out = local.content;
|
||||||
|
return out.copyWith(realm: await _rels.getRealm(out.realmId!));
|
||||||
|
}
|
||||||
|
|
||||||
var resp =
|
var resp =
|
||||||
await _sn.client.get('/cgi/im/channels/${key.replaceAll(':', '/')}');
|
await _sn.client.get('/cgi/im/channels/${key.replaceAll(':', '/')}');
|
||||||
@ -76,8 +74,7 @@ class ChatChannelProvider extends ChangeNotifier {
|
|||||||
|
|
||||||
// Preload realm of the channel
|
// Preload realm of the channel
|
||||||
if (out.realmId != null) {
|
if (out.realmId != null) {
|
||||||
resp = await _sn.client.get('/cgi/id/realms/${out.realmId}');
|
out = out.copyWith(realm: await _rels.getRealm(out.realmId!));
|
||||||
out = out.copyWith(realm: SnRealm.fromJson(resp.data));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_saveChannelToLocal([out]);
|
_saveChannelToLocal([out]);
|
||||||
@ -98,44 +95,30 @@ class ChatChannelProvider extends ChangeNotifier {
|
|||||||
OrderingTerm(expression: e.createdAt, mode: OrderingMode.desc)
|
OrderingTerm(expression: e.createdAt, mode: OrderingMode.desc)
|
||||||
]))
|
]))
|
||||||
.get();
|
.get();
|
||||||
yield local.map((e) => e.content).toList();
|
final out = local.map((e) => e.content).toList();
|
||||||
|
for (var idx = 0; idx < out.length; idx++) {
|
||||||
|
final channel = out[idx];
|
||||||
|
if (channel.realmId != null) {
|
||||||
|
out[idx] = out[idx].copyWith(
|
||||||
|
realm: await _rels.getRealm(channel.realmId!),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yield out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (noRemote) return;
|
if (noRemote) return;
|
||||||
|
|
||||||
var resp = await _sn.client.get('/cgi/id/realms/me/available');
|
|
||||||
final realms = List<SnRealm>.from(
|
|
||||||
resp.data?.map((e) => SnRealm.fromJson(e)) ?? [],
|
|
||||||
);
|
|
||||||
final realmMap = {
|
|
||||||
for (final realm in realms) realm.alias: realm,
|
|
||||||
};
|
|
||||||
|
|
||||||
final scopeToFetch = {'global', ...realms.map((e) => e.alias)};
|
|
||||||
|
|
||||||
final List<SnChannel> result = List.empty(growable: true);
|
final List<SnChannel> result = List.empty(growable: true);
|
||||||
final directMessages = await _fetchChannelsFromServer(
|
final channels = await _fetchChannelsFromServer();
|
||||||
scope: scopeToFetch.first,
|
for (var idx = 0; idx < channels.length; idx++) {
|
||||||
direct: true,
|
final channel = channels[idx];
|
||||||
);
|
if (channel.realmId != null) {
|
||||||
result.addAll(directMessages);
|
channels[idx] = channels[idx].copyWith(
|
||||||
|
realm: await _rels.getRealm(channel.realmId!),
|
||||||
final nonBelongsChannels = await _fetchChannelsFromServer(
|
);
|
||||||
scope: scopeToFetch.first,
|
}
|
||||||
direct: false,
|
|
||||||
);
|
|
||||||
result.addAll(nonBelongsChannels);
|
|
||||||
|
|
||||||
for (final scope in scopeToFetch.skip(1)) {
|
|
||||||
final channel = await _fetchChannelsFromServer(
|
|
||||||
scope: scope,
|
|
||||||
direct: false,
|
|
||||||
doNotSave: true,
|
|
||||||
);
|
|
||||||
final out = channel.map((ele) => ele.copyWith(realm: realmMap[scope]));
|
|
||||||
_saveChannelToLocal(out);
|
|
||||||
result.addAll(out);
|
|
||||||
}
|
}
|
||||||
|
result.addAll(channels);
|
||||||
|
|
||||||
yield result;
|
yield result;
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,16 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
|
|
||||||
List<SnChannel>? _channels;
|
List<SnChannel>? _channels;
|
||||||
Map<int, SnChatMessage>? _lastMessages;
|
Map<int, SnChatMessage>? _lastMessages;
|
||||||
|
Map<int, int>? _unreadCounts;
|
||||||
|
|
||||||
|
Future<void> _fetchWhatsNew() async {
|
||||||
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
final resp = await sn.client.get('/cgi/im/whats-new');
|
||||||
|
final List<dynamic> out = resp.data;
|
||||||
|
setState(() {
|
||||||
|
_unreadCounts = {for (var v in out) v['channel_id']: v['count']};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void _refreshChannels({bool noRemote = false}) {
|
void _refreshChannels({bool noRemote = false}) {
|
||||||
final ua = context.read<UserProvider>();
|
final ua = context.read<UserProvider>();
|
||||||
@ -117,6 +127,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_refreshChannels();
|
_refreshChannels();
|
||||||
|
_fetchWhatsNew();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -211,7 +222,10 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
context: context,
|
context: context,
|
||||||
removeTop: true,
|
removeTop: true,
|
||||||
child: RefreshIndicator(
|
child: RefreshIndicator(
|
||||||
onRefresh: () => Future.sync(() => _refreshChannels()),
|
onRefresh: () => Future.wait([
|
||||||
|
Future.sync(() => _refreshChannels()),
|
||||||
|
_fetchWhatsNew(),
|
||||||
|
]),
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
itemCount: _channels?.length ?? 0,
|
itemCount: _channels?.length ?? 0,
|
||||||
itemBuilder: (context, idx) {
|
itemBuilder: (context, idx) {
|
||||||
@ -226,10 +240,22 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(ud
|
title: Row(
|
||||||
.getAccountFromCache(otherMember?.accountId)
|
children: [
|
||||||
?.nick ??
|
Expanded(
|
||||||
channel.name),
|
child: Text(ud
|
||||||
|
.getAccountFromCache(
|
||||||
|
otherMember?.accountId)
|
||||||
|
?.nick ??
|
||||||
|
channel.name),
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
if (_unreadCounts?[channel.id] != null)
|
||||||
|
Badge(
|
||||||
|
label: Text('${_unreadCounts![channel.id]}'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
subtitle: lastMessage != null
|
subtitle: lastMessage != null
|
||||||
? Text(
|
? Text(
|
||||||
'${ud.getAccountFromCache(lastMessage.sender.accountId)?.nick}: ${lastMessage.body['text'] ?? 'Unable preview'}',
|
'${ud.getAccountFromCache(lastMessage.sender.accountId)?.nick}: ${lastMessage.body['text'] ?? 'Unable preview'}',
|
||||||
@ -237,9 +263,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
)
|
)
|
||||||
: Text(
|
: Text(
|
||||||
'channelDirectMessageDescription'.tr(args: [
|
channel.description,
|
||||||
'@${ud.getAccountFromCache(otherMember?.accountId)?.name}',
|
|
||||||
]),
|
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
@ -265,7 +289,16 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(channel.name),
|
title: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(child: Text(channel.name)),
|
||||||
|
const Gap(8),
|
||||||
|
if (_unreadCounts?[channel.id] != null)
|
||||||
|
Badge(
|
||||||
|
label: Text('${_unreadCounts![channel.id]}'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
subtitle: lastMessage != null
|
subtitle: lastMessage != null
|
||||||
? Text(
|
? Text(
|
||||||
'${ud.getAccountFromCache(lastMessage.sender.accountId)?.nick}: ${lastMessage.body['text'] ?? 'Unable preview'}',
|
'${ud.getAccountFromCache(lastMessage.sender.accountId)?.nick}: ${lastMessage.body['text'] ?? 'Unable preview'}',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user