User channel profile cache

This commit is contained in:
LittleSheep 2025-03-04 23:35:28 +08:00
parent 5ac657e526
commit d40a6ca1c4
3 changed files with 70 additions and 9 deletions

View File

@ -8,6 +8,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:surface/database/database.dart'; import 'package:surface/database/database.dart';
import 'package:surface/logger.dart'; import 'package:surface/logger.dart';
import 'package:surface/providers/channel.dart';
import 'package:surface/providers/database.dart'; import 'package:surface/providers/database.dart';
import 'package:surface/providers/keypair.dart'; import 'package:surface/providers/keypair.dart';
import 'package:surface/providers/sn_attachment.dart'; import 'package:surface/providers/sn_attachment.dart';
@ -26,6 +27,7 @@ class ChatMessageController extends ChangeNotifier {
late final WebSocketProvider _ws; late final WebSocketProvider _ws;
late final SnAttachmentProvider _attach; late final SnAttachmentProvider _attach;
late final DatabaseProvider _dt; late final DatabaseProvider _dt;
late final ChatChannelProvider _ct;
late final KeyPairProvider _kp; late final KeyPairProvider _kp;
StreamSubscription? _wsSubscription; StreamSubscription? _wsSubscription;
@ -35,6 +37,7 @@ class ChatMessageController extends ChangeNotifier {
_ud = context.read<UserDirectoryProvider>(); _ud = context.read<UserDirectoryProvider>();
_ws = context.read<WebSocketProvider>(); _ws = context.read<WebSocketProvider>();
_attach = context.read<SnAttachmentProvider>(); _attach = context.read<SnAttachmentProvider>();
_ct = context.read<ChatChannelProvider>();
_dt = context.read<DatabaseProvider>(); _dt = context.read<DatabaseProvider>();
_kp = context.read<KeyPairProvider>(); _kp = context.read<KeyPairProvider>();
} }
@ -65,10 +68,7 @@ class ChatMessageController extends ChangeNotifier {
channel = chan; channel = chan;
// Fetch channel profile // Fetch channel profile
final resp = await _sn.client.get( profile = await _ct.getChannelProfile(channel!);
'/cgi/im/channels/${chan.keyPath}/me',
);
profile = SnChannelMember.fromJson(resp.data);
_wsSubscription = _ws.pk.stream.listen((event) { _wsSubscription = _ws.pk.stream.listen((event) {
switch (event.method) { switch (event.method) {

View File

@ -8,6 +8,7 @@ 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/sn_realm.dart';
import 'package:surface/providers/user_directory.dart'; import 'package:surface/providers/user_directory.dart';
import 'package:surface/providers/userinfo.dart';
import 'package:surface/types/chat.dart'; import 'package:surface/types/chat.dart';
class ChatChannelProvider extends ChangeNotifier { class ChatChannelProvider extends ChangeNotifier {
@ -15,12 +16,14 @@ class ChatChannelProvider extends ChangeNotifier {
late final SnNetworkProvider _sn; late final SnNetworkProvider _sn;
late final UserDirectoryProvider _ud; late final UserDirectoryProvider _ud;
late final UserProvider _ua;
late final DatabaseProvider _dt; late final DatabaseProvider _dt;
late final SnRealmProvider _rels; 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>();
_ua = context.read<UserProvider>();
_dt = context.read<DatabaseProvider>(); _dt = context.read<DatabaseProvider>();
_rels = context.read<SnRealmProvider>(); _rels = context.read<SnRealmProvider>();
} }
@ -149,4 +152,60 @@ class ChatChannelProvider extends ChangeNotifier {
await _ud.listAccount(out.map((ele) => ele.sender.accountId).toSet()); await _ud.listAccount(out.map((ele) => ele.sender.accountId).toSet());
return out; return out;
} }
Future<void> _saveMemberToLocal(Iterable<SnChannelMember> members) async {
final queries = members.map((ele) {
return _dt.db.snLocalChannelMember.insertOne(
SnLocalChannelMemberCompanion.insert(
id: Value(ele.id),
channelId: ele.channelId,
accountId: ele.accountId,
content: ele,
cacheExpiredAt: DateTime.now().add(const Duration(days: 7)),
),
onConflict: DoUpdate(
(_) => SnLocalChannelMemberCompanion.custom(
content: Constant(jsonEncode(ele.toJson())),
cacheExpiredAt:
Constant(DateTime.now().add(const Duration(days: 7))),
),
),
);
});
await Future.wait(queries);
}
Future<void> removeLocalChannel(SnChannel channel) async {
await _dt.db.transaction(() async {
await (_dt.db.snLocalChannelMember.delete()
..where((e) => e.channelId.equals(channel.id)))
.go();
await (_dt.db.snLocalChatChannel.delete()
..where((e) => e.id.equals(channel.id)))
.go();
await (_dt.db.snLocalChatMessage.delete()
..where((e) => e.channelId.equals(channel.id)))
.go();
});
}
Future<void> updateChannelProfile(SnChannelMember member) {
return _saveMemberToLocal([member]);
}
Future<SnChannelMember> getChannelProfile(SnChannel channel) async {
if (_ua.user == null) throw Exception('User not logged in');
final local = await (_dt.db.snLocalChannelMember.select()
..where((e) => e.channelId.equals(channel.id))
..where((e) => e.accountId.equals(_ua.user!.id)))
.getSingleOrNull();
if (local != null) {
return local.content;
}
final resp = await _sn.client.get('/cgi/im/channels/${channel.keyPath}/me');
final out = SnChannelMember.fromJson(resp.data);
_saveMemberToLocal([out]);
return out;
}
} }

View File

@ -57,11 +57,9 @@ class _ChannelDetailScreenState extends State<ChannelDetailScreen> {
setState(() => _isBusy = true); setState(() => _isBusy = true);
try { try {
final sn = context.read<SnNetworkProvider>(); final ct = context.read<ChatChannelProvider>();
final resp = final resp = await ct.getChannelProfile(_channel!);
await sn.client.get('/cgi/im/channels/${_channel!.keyPath}/me'); _notifyLevel = resp.notify;
_profile = SnChannelMember.fromJson(resp.data);
_notifyLevel = _profile!.notify;
if (!mounted) return; if (!mounted) return;
final ud = context.read<UserDirectoryProvider>(); final ud = context.read<UserDirectoryProvider>();
await ud.getAccount(_profile!.accountId); await ud.getAccount(_profile!.accountId);
@ -103,10 +101,12 @@ class _ChannelDetailScreenState extends State<ChannelDetailScreen> {
if (!mounted) return; if (!mounted) return;
try { try {
final ct = context.read<ChatChannelProvider>();
final sn = context.read<SnNetworkProvider>(); final sn = context.read<SnNetworkProvider>();
await sn.client.delete( await sn.client.delete(
'/cgi/im/channels/${_channel!.realm?.alias ?? 'global'}/${_channel!.alias}/me', '/cgi/im/channels/${_channel!.realm?.alias ?? 'global'}/${_channel!.alias}/me',
); );
await ct.removeLocalChannel(_channel!);
if (!mounted) return; if (!mounted) return;
Navigator.pop(context, false); Navigator.pop(context, false);
} catch (err) { } catch (err) {
@ -130,12 +130,14 @@ class _ChannelDetailScreenState extends State<ChannelDetailScreen> {
setState(() => _isUpdatingNotifyLevel = true); setState(() => _isUpdatingNotifyLevel = true);
try { try {
final ct = context.read<ChatChannelProvider>();
final sn = context.read<SnNetworkProvider>(); final sn = context.read<SnNetworkProvider>();
await sn.client.put( await sn.client.put(
'/cgi/im/channels/${_channel!.keyPath}/members/me/notify', '/cgi/im/channels/${_channel!.keyPath}/members/me/notify',
data: {'notify_level': value}, data: {'notify_level': value},
); );
_notifyLevel = value; _notifyLevel = value;
await ct.updateChannelProfile(_profile!);
if (!mounted) return; if (!mounted) return;
context.showSnackbar('channelNotifyLevelApplied'.tr()); context.showSnackbar('channelNotifyLevelApplied'.tr());
} catch (err) { } catch (err) {