// 全局昵称设置 // 持久化到 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._(); static final NicknameService instance = NicknameService._(); SharedPreferences? _prefs; /// 浏览器唯一 ID:同浏览器跨 tab 共享(localStorage 作用域),跨浏览器不同 /// 用于后端识别"同一个人",避免昵称冲突时自动改名 String? _browserId; /// 昵称变化广播流:本地 setNickname 触发 + Web 上其他标签页修改也触发 final _streamController = StreamController.broadcast(); Stream get onChanged => _streamController.stream; StreamSubscription? _webStorageSub; Future _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 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); } }); } /// 获取当前昵称,未设置时返回默认昵称 Future get nickname async { await _ensureInit(); final n = _prefs!.getString(_keyNickname); if (n != null && n.trim().isNotEmpty) return n.trim(); return '用户'; } /// 设置昵称(1-32 字符,过短返回 false) Future setNickname(String value) async { final trimmed = value.trim(); 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; } }