76f266645d
含 Phase 1.1 IM seq 排序、断线重连、多端已读同步与微信式语音转文字 UI。 排除 node_modules、构建产物、安装包与 .env 密钥。 Co-authored-by: Cursor <cursoragent@cursor.com>
91 lines
2.8 KiB
Dart
91 lines
2.8 KiB
Dart
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
/// 会话级本地偏好:免打扰、置顶、背景、本地清空记录。
|
|
class ChatPrefs {
|
|
ChatPrefs._();
|
|
static SharedPreferences? _p;
|
|
|
|
static Future<void> ensure() async {
|
|
_p ??= await SharedPreferences.getInstance();
|
|
// 旧版本曾在滑动过程中误写 hidden,导致会话读完后看似“自动删除”。
|
|
// 升级时清理一次旧标记,之后只有确认“不显示/删除”才会再次隐藏。
|
|
if (_p!.getBool('im.hidden.migration.v3') != true) {
|
|
for (final key
|
|
in _p!.getKeys().where((k) => k.startsWith('im.hidden.'))) {
|
|
await _p!.remove(key);
|
|
}
|
|
await _p!.setBool('im.hidden.migration.v3', true);
|
|
}
|
|
}
|
|
|
|
static bool muted(String id) =>
|
|
id.isEmpty ? false : (_p?.getBool('im.mute.$id') ?? false);
|
|
static bool pinned(String id) =>
|
|
id.isEmpty ? false : (_p?.getBool('im.pin.$id') ?? false);
|
|
static bool hidden(String id) =>
|
|
id.isEmpty ? false : (_p?.getBool('im.hidden.$id') ?? false);
|
|
static int? bgColor(String id) => id.isEmpty ? null : _p?.getInt('im.bg.$id');
|
|
static DateTime? clearedAt(String id) {
|
|
if (id.isEmpty) return null;
|
|
final ms = _p?.getInt('im.cleared.$id');
|
|
if (ms == null || ms <= 0) return null;
|
|
return DateTime.fromMillisecondsSinceEpoch(ms);
|
|
}
|
|
|
|
static Future<void> setMuted(String id, bool v) async {
|
|
await ensure();
|
|
await _p!.setBool('im.mute.$id', v);
|
|
}
|
|
|
|
static Future<void> setPinned(String id, bool v) async {
|
|
await ensure();
|
|
await _p!.setBool('im.pin.$id', v);
|
|
}
|
|
|
|
static Future<void> setHidden(String id, bool v) async {
|
|
await ensure();
|
|
await _p!.setBool('im.hidden.$id', v);
|
|
}
|
|
|
|
static Future<void> setBg(String id, int color) async {
|
|
await ensure();
|
|
await _p!.setInt('im.bg.$id', color);
|
|
}
|
|
|
|
static Future<void> clearHistory(String id) async {
|
|
await ensure();
|
|
await _p!.setInt('im.cleared.$id', DateTime.now().millisecondsSinceEpoch);
|
|
}
|
|
|
|
static int fakeUnread(String id) =>
|
|
id.isEmpty ? 0 : (_p?.getInt('im.fakeUnread.$id') ?? 0);
|
|
|
|
static Future<void> setFakeUnread(String id, int n) async {
|
|
await ensure();
|
|
if (n <= 0) {
|
|
await _p!.remove('im.fakeUnread.$id');
|
|
} else {
|
|
await _p!.setInt('im.fakeUnread.$id', n);
|
|
}
|
|
}
|
|
|
|
/// 语音转文字结果,按消息 id 或 fileId 缓存。
|
|
static String? voiceText(String key) {
|
|
if (key.isEmpty) return null;
|
|
final v = _p?.getString('im.voiceText.$key');
|
|
if (v == null || v.isEmpty) return null;
|
|
return v;
|
|
}
|
|
|
|
static Future<void> setVoiceText(String key, String text) async {
|
|
if (key.isEmpty) return;
|
|
await ensure();
|
|
final t = text.trim();
|
|
if (t.isEmpty) {
|
|
await _p!.remove('im.voiceText.$key');
|
|
} else {
|
|
await _p!.setString('im.voiceText.$key', t);
|
|
}
|
|
}
|
|
}
|