Compare commits

..

2 Commits

Author SHA1 Message Date
6235e736b9 Sticker cache 2025-03-04 23:56:39 +08:00
e075804782 🐛 Bug fixes on channel member cache 2025-03-04 23:39:55 +08:00
3 changed files with 76 additions and 8 deletions

View File

@ -1,11 +1,17 @@
import 'dart:convert';
import 'package:drift/drift.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:surface/database/database.dart';
import 'package:surface/logger.dart'; import 'package:surface/logger.dart';
import 'package:surface/providers/database.dart';
import 'package:surface/providers/sn_network.dart'; import 'package:surface/providers/sn_network.dart';
import 'package:surface/types/attachment.dart'; import 'package:surface/types/attachment.dart';
class SnStickerProvider { class SnStickerProvider {
late final SnNetworkProvider _sn; late final SnNetworkProvider _sn;
late final DatabaseProvider _dt;
final Map<String, SnSticker?> _cache = {}; final Map<String, SnSticker?> _cache = {};
final Map<int, List<SnSticker>> stickersByPack = {}; final Map<int, List<SnSticker>> stickersByPack = {};
@ -15,6 +21,7 @@ class SnStickerProvider {
SnStickerProvider(BuildContext context) { SnStickerProvider(BuildContext context) {
_sn = context.read<SnNetworkProvider>(); _sn = context.read<SnNetworkProvider>();
_dt = context.read<DatabaseProvider>();
} }
bool hasNotSticker(String alias) { bool hasNotSticker(String alias) {
@ -31,22 +38,32 @@ class SnStickerProvider {
} }
} }
void putSticker(Iterable<SnSticker> sticker) { void putSticker(Iterable<SnSticker> stickers) {
for (final ele in sticker) { for (final ele in stickers) {
_cacheSticker(ele); _cacheSticker(ele);
} }
_saveStickerToLocal(stickers);
_saveStickerPackToLocal(stickers.map((ele) => ele.pack).toSet());
} }
Future<SnSticker?> lookupSticker(String alias) async { Future<SnSticker?> lookupSticker(String alias) async {
// In-memory cache
if (_cache.containsKey(alias)) { if (_cache.containsKey(alias)) {
return _cache[alias]; return _cache[alias];
} }
// On-disk cache
final localStickers = await (_dt.db.snLocalSticker.select()
..where((e) => e.fullAlias.equals(alias)))
.getSingleOrNull();
if (localStickers != null) {
_cache[alias] = localStickers.content;
return localStickers.content;
}
// Remote server
try { try {
final resp = await _sn.client.get('/cgi/uc/stickers/lookup/$alias'); final resp = await _sn.client.get('/cgi/uc/stickers/lookup/$alias');
final sticker = SnSticker.fromJson(resp.data); final sticker = SnSticker.fromJson(resp.data);
_cacheSticker(sticker); putSticker([sticker]);
return sticker; return sticker;
} catch (err) { } catch (err) {
_cache[alias] = null; _cache[alias] = null;
@ -57,6 +74,18 @@ class SnStickerProvider {
} }
Future<void> listSticker() async { Future<void> listSticker() async {
final localPacks = await _dt.db.snLocalStickerPack.select().get();
final localStickers = await _dt.db.snLocalSticker.select().get();
final local = localStickers.map((ele) {
return ele.content.copyWith(
pack: localPacks
.firstWhere((pk) => pk.content.id == ele.content.packId)
.content,
);
});
for (final sticker in local) {
_cacheSticker(sticker);
}
try { try {
final resp = await _sn.client.get('/cgi/uc/stickers'); final resp = await _sn.client.get('/cgi/uc/stickers');
final data = resp.data; final data = resp.data;
@ -69,4 +98,35 @@ class SnStickerProvider {
rethrow; rethrow;
} }
} }
Future<void> _saveStickerToLocal(Iterable<SnSticker> stickers) async {
await _dt.db.snLocalSticker.insertAll(
stickers.map(
(ele) => SnLocalStickerCompanion.insert(
id: Value(ele.id),
alias: ele.alias,
fullAlias: '${ele.pack.prefix}${ele.alias}',
content: ele,
createdAt: Value(ele.createdAt),
),
),
onConflict: DoNothing(),
);
}
Future<void> _saveStickerPackToLocal(Iterable<SnStickerPack> packs) async {
final queries = packs
.map(
(ele) => _dt.db.snLocalStickerPack.insertOne(
SnLocalStickerPackCompanion.insert(
id: Value(ele.id),
content: ele,
createdAt: Value(ele.createdAt),
),
onConflict: DoUpdate((_) => SnLocalStickerPackCompanion.custom(
content: Constant(jsonEncode(ele.toJson()))))),
)
.toList();
await Future.wait(queries);
}
} }

View File

@ -59,6 +59,7 @@ class _ChannelDetailScreenState extends State<ChannelDetailScreen> {
try { try {
final ct = context.read<ChatChannelProvider>(); final ct = context.read<ChatChannelProvider>();
final resp = await ct.getChannelProfile(_channel!); final resp = await ct.getChannelProfile(_channel!);
_profile = resp;
_notifyLevel = resp.notify; _notifyLevel = resp.notify;
if (!mounted) return; if (!mounted) return;
final ud = context.read<UserDirectoryProvider>(); final ud = context.read<UserDirectoryProvider>();
@ -132,10 +133,11 @@ class _ChannelDetailScreenState extends State<ChannelDetailScreen> {
try { try {
final ct = context.read<ChatChannelProvider>(); final ct = context.read<ChatChannelProvider>();
final sn = context.read<SnNetworkProvider>(); final sn = context.read<SnNetworkProvider>();
await sn.client.put( final resp = 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},
); );
_profile = SnChannelMember.fromJson(resp.data);
_notifyLevel = value; _notifyLevel = value;
await ct.updateChannelProfile(_profile!); await ct.updateChannelProfile(_profile!);
if (!mounted) return; if (!mounted) return;
@ -409,11 +411,14 @@ class _ChannelProfileDetailDialogState
setState(() => _isBusy = true); setState(() => _isBusy = true);
try { try {
final ct = context.read<ChatChannelProvider>();
final sn = context.read<SnNetworkProvider>(); final sn = context.read<SnNetworkProvider>();
await sn.client.put( final resp = await sn.client.put(
'/cgi/im/channels/${widget.channel.keyPath}/members/me', '/cgi/im/channels/${widget.channel.keyPath}/members/me',
data: {'nick': _nickController.text}, data: {'nick': _nickController.text},
); );
final out = SnChannelMember.fromJson(resp.data);
await ct.updateChannelProfile(out);
if (!mounted) return; if (!mounted) return;
Navigator.pop(context, true); Navigator.pop(context, true);
} catch (err) { } catch (err) {

View File

@ -284,7 +284,10 @@ class _StickerPackAddPopupState extends State<_StickerPackAddPopup> {
); );
if (!mounted) return; if (!mounted) return;
context.showSnackbar('stickersAdded'.tr()); context.showSnackbar('stickersAdded'.tr());
if (_pack?.stickers != null) stickers.putSticker(_pack!.stickers!); if (_pack?.stickers != null) {
stickers.putSticker(
_pack!.stickers!.map((ele) => ele.copyWith(pack: _pack!)));
}
Navigator.pop(context, true); Navigator.pop(context, true);
} catch (err) { } catch (err) {
if (!mounted) return; if (!mounted) return;