beta v0.2 dualcheck
This commit is contained in:
1 parent
c137299016
commit
1ec68bacf5
14 files changed
+1591
-359
No files matched your search
@@ -1,8 +1,13 @@
|
||||
// 全局昵称设置
|
||||
// 持久化到 SharedPreferences / localStorage,所有工具共享同一个昵称
|
||||
// Web 平台自动监听 localStorage 变化 → 跨标签页实时同步
|
||||
import 'dart:async';
|
||||
import 'dart:html' as html show window, StorageEvent;
|
||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
const _keyNickname = 'toolbox.nickname';
|
||||
const _keyBrowserId = 'toolbox.browser_id';
|
||||
|
||||
class NicknameService {
|
||||
NicknameService._();
|
||||
@@ -10,8 +15,60 @@ class NicknameService {
|
||||
|
||||
SharedPreferences? _prefs;
|
||||
|
||||
/// 浏览器唯一 ID:同浏览器跨 tab 共享(localStorage 作用域),跨浏览器不同
|
||||
/// 用于后端识别"同一个人",避免昵称冲突时自动改名
|
||||
String? _browserId;
|
||||
|
||||
/// 昵称变化广播流:本地 setNickname 触发 + Web 上其他标签页修改也触发
|
||||
final _streamController = StreamController<String>.broadcast();
|
||||
Stream<String> get onChanged => _streamController.stream;
|
||||
|
||||
StreamSubscription? _webStorageSub;
|
||||
|
||||
Future<void> _ensureInit() async {
|
||||
_prefs ??= await SharedPreferences.getInstance();
|
||||
// 惰性生成 browserId:首次访问时若没有就生成 uuid 存起来
|
||||
if (_browserId == null) {
|
||||
final existing = _prefs!.getString(_keyBrowserId);
|
||||
if (existing != null && existing.isNotEmpty) {
|
||||
_browserId = existing;
|
||||
} else {
|
||||
_browserId = _genBrowserId();
|
||||
await _prefs!.setString(_keyBrowserId, _browserId!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取浏览器 ID(稳定值,同浏览器跨 tab 相同)
|
||||
Future<String> get browserId async {
|
||||
await _ensureInit();
|
||||
return _browserId!;
|
||||
}
|
||||
|
||||
/// 生成短 browserId:base62(0-9A-Za-z),去掉特殊字符方便 URL 使用
|
||||
/// 12 字符足够唯一(62^12 ≈ 3e21 种可能)
|
||||
String _genBrowserId() {
|
||||
const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
||||
final rand = DateTime.now().microsecond.hashCode;
|
||||
final buf = StringBuffer();
|
||||
int seed = rand ^ DateTime.now().millisecondsSinceEpoch;
|
||||
for (int i = 0; i < 12; i++) {
|
||||
seed = (seed * 1103515245 + 12345) & 0x7FFFFFFF;
|
||||
buf.write(chars[seed % chars.length]);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/// 开启跨标签页同步(仅 Web 生效;非 Web 空操作)
|
||||
/// 在 widget 的 initState 里调用一次即可
|
||||
void enableCrossTabSync() {
|
||||
if (!kIsWeb || _webStorageSub != null) return;
|
||||
_webStorageSub = html.window.onStorage.listen((html.StorageEvent event) async {
|
||||
if (event.key == _keyNickname) {
|
||||
final n = await nickname;
|
||||
_streamController.add(n);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// 获取当前昵称,未设置时返回默认昵称
|
||||
@@ -28,6 +85,12 @@ class NicknameService {
|
||||
if (trimmed.isEmpty || trimmed.length > 32) return false;
|
||||
await _ensureInit();
|
||||
await _prefs!.setString(_keyNickname, trimmed);
|
||||
_streamController.add(trimmed);
|
||||
return true;
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_webStorageSub?.cancel();
|
||||
_webStorageSub = null;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user