Files
MookNote/lib/utils/user_prefs.dart
DelLevin-Home 103afaa65e v0.2.9
2026-08-07 21:26:10 +08:00

340 lines
16 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:shared_preferences/shared_preferences.dart';
/// 用户偏好设置管理
class UserPrefs {
static final UserPrefs _instance = UserPrefs._internal();
static SharedPreferences? _prefs;
factory UserPrefs() => _instance;
UserPrefs._internal();
/// 初始化
static Future<void> init() async {
_prefs = await SharedPreferences.getInstance();
// 迁移旧版 isDarkMode 布尔值到新版 themeMode 三态值
if (_prefs!.containsKey('isDarkMode') && !_prefs!.containsKey('themeMode')) {
final oldValue = _prefs!.getBool('isDarkMode') ?? false;
await _prefs!.setInt('themeMode', oldValue ? 2 : 0);
}
// 记录首次使用日期
if (!_prefs!.containsKey('firstUseDate')) {
await _prefs!.setString('firstUseDate', DateTime.now().toIso8601String());
}
}
/// 获取实例
SharedPreferences get prefs {
if (_prefs == null) {
throw Exception('UserPrefs not initialized. Call UserPrefs.init() first.');
}
return _prefs!;
}
// ========== 用户信息 ==========
/// 首次使用日期
DateTime get firstUseDate {
final str = prefs.getString('firstUseDate');
if (str != null) return DateTime.tryParse(str)?.toLocal() ?? DateTime.now();
return DateTime.now();
}
/// 昵称
String get nickname => prefs.getString('nickname') ?? 'Mook';
Future<bool> setNickname(String value) => prefs.setString('nickname', value);
/// 座右铭
String get motto => prefs.getString('motto') ?? '好运不会眷顾一无所有之人。';
Future<bool> setMotto(String value) => prefs.setString('motto', value);
/// 头像路径
String? get avatarPath => prefs.getString('avatarPath');
Future<bool> setAvatarPath(String value) => prefs.setString('avatarPath', value);
Future<bool> clearAvatarPath() => prefs.remove('avatarPath');
// ========== 应用设置 ==========
/// 主题模式: 0=跟随系统, 1=浅色, 2=深色
int get themeMode => prefs.getInt('themeMode') ?? 0;
Future<bool> setThemeMode(int value) => prefs.setInt('themeMode', value);
/// 配色方案: 0=经典, 1=靛蓝, 2=薄荷, 3=琥珀, 4=玫瑰, 5=紫罗兰
int get colorSchemeIndex => prefs.getInt('colorSchemeIndex') ?? 0;
Future<bool> setColorSchemeIndex(int value) => prefs.setInt('colorSchemeIndex', value);
/// 字体: 空字符串=系统默认
String get fontFamily => prefs.getString('fontFamily') ?? '';
Future<bool> setFontFamily(String value) => prefs.setString('fontFamily', value);
/// 上映日期显示到日true/ 显示到月false
bool get showExactReleaseDate => prefs.getBool('showExactReleaseDate') ?? true;
Future<bool> setShowExactReleaseDate(bool value) => prefs.setBool('showExactReleaseDate', value);
/// 是否首次启动
bool get isFirstLaunch => prefs.getBool('isFirstLaunch') ?? true;
Future<bool> setFirstLaunch(bool value) => prefs.setBool('isFirstLaunch', value);
// ========== 详情页样式 ==========
/// 详情页展示样式: 0=标准(封面顶部), 1=叠层(封面+毛玻璃卡片)
int get detailPageStyle => prefs.getInt('detailPageStyle') ?? 0;
Future<bool> setDetailPageStyle(int value) => prefs.setInt('detailPageStyle', value);
// ========== 封面位置 ==========
/// 获取封面偏移量(-1.0 到 1.00 = 居中)
double getCoverOffset(String itemId) => prefs.getDouble('coverOffset_$itemId') ?? 0.0;
Future<bool> setCoverOffset(String itemId, double value) => prefs.setDouble('coverOffset_$itemId', value);
// ========== 主界面显示设置 ==========
/// 是否启用底部导航栏滚动隐藏(默认开启)
bool get hideBottomNavOnScroll => prefs.getBool('hideBottomNavOnScroll') ?? true;
Future<bool> setHideBottomNavOnScroll(bool value) => prefs.setBool('hideBottomNavOnScroll', value);
/// 是否显示观影标签
bool get showMovieTab => prefs.getBool('showMovieTab') ?? true;
Future<bool> setShowMovieTab(bool value) => prefs.setBool('showMovieTab', value);
/// 是否显示阅读标签
bool get showBookTab => prefs.getBool('showBookTab') ?? true;
Future<bool> setShowBookTab(bool value) => prefs.setBool('showBookTab', value);
/// 是否显示笔记标签
bool get showNoteTab => prefs.getBool('showNoteTab') ?? true;
Future<bool> setShowNoteTab(bool value) => prefs.setBool('showNoteTab', value);
/// 是否显示游戏标签
bool get showGameTab => prefs.getBool('showGameTab') ?? false;
Future<bool> setShowGameTab(bool value) => prefs.setBool('showGameTab', value);
/// 默认启动标签 (-1: 主页, 0: 影视, 1: 阅读, 2: 笔记, 3: 游戏)
int get defaultMainTabIndex => prefs.getInt('defaultMainTabIndex') ?? -1;
Future<bool> setDefaultMainTabIndex(int value) => prefs.setInt('defaultMainTabIndex', value);
/// 桌面端是否显示主页标签
bool get showDesktopHomeTab => prefs.getBool('showDesktopHomeTab') ?? true;
Future<bool> setShowDesktopHomeTab(bool value) => prefs.setBool('showDesktopHomeTab', value);
// ─── 侧边栏功能开关 ───
bool get showSidebarHeatmap => prefs.getBool('showSidebarHeatmap') ?? true;
Future<bool> setShowSidebarHeatmap(bool value) => prefs.setBool('showSidebarHeatmap', value);
bool get showSidebarRecent => prefs.getBool('showSidebarRecent') ?? true;
Future<bool> setShowSidebarRecent(bool value) => prefs.setBool('showSidebarRecent', value);
bool get showSidebarEncounter => prefs.getBool('showSidebarEncounter') ?? true;
Future<bool> setShowSidebarEncounter(bool value) => prefs.setBool('showSidebarEncounter', value);
bool get showSidebarStroll => prefs.getBool('showSidebarStroll') ?? true;
Future<bool> setShowSidebarStroll(bool value) => prefs.setBool('showSidebarStroll', value);
bool get showSidebarReviewed => prefs.getBool('showSidebarReviewed') ?? true;
Future<bool> setShowSidebarReviewed(bool value) => prefs.setBool('showSidebarReviewed', value);
bool get showSidebarPlaylist => prefs.getBool('showSidebarPlaylist') ?? true;
Future<bool> setShowSidebarPlaylist(bool value) => prefs.setBool('showSidebarPlaylist', value);
bool get showSidebarCalendar => prefs.getBool('showSidebarCalendar') ?? true;
Future<bool> setShowSidebarCalendar(bool value) => prefs.setBool('showSidebarCalendar', value);
bool get showSidebarPerson => prefs.getBool('showSidebarPerson') ?? true;
Future<bool> setShowSidebarPerson(bool value) => prefs.setBool('showSidebarPerson', value);
bool get showSidebarTags => prefs.getBool('showSidebarTags') ?? true;
Future<bool> setShowSidebarTags(bool value) => prefs.setBool('showSidebarTags', value);
bool get showSidebarMdReader => prefs.getBool('showSidebarMdReader') ?? false;
Future<bool> setShowSidebarMdReader(bool value) => prefs.setBool('showSidebarMdReader', value);
bool get showSidebarEpub => prefs.getBool('showSidebarEpub') ?? true;
Future<bool> setShowSidebarEpub(bool value) => prefs.setBool('showSidebarEpub', value);
bool get showSidebarQuickActions => prefs.getBool('showSidebarQuickActions') ?? true;
Future<bool> setShowSidebarQuickActions(bool value) => prefs.setBool('showSidebarQuickActions', value);
/// 笔记布局样式 (0: 列表, 1: 瀑布流, 2: 时间线)
int get noteLayoutStyle => prefs.getInt('noteLayoutStyle') ?? 0;
Future<bool> setNoteLayoutStyle(int value) => prefs.setInt('noteLayoutStyle', value);
/// 笔记排序方式 (0: 更新时间, 1: 创建时间)
int get noteSortMode => prefs.getInt('noteSortMode') ?? 0;
Future<bool> setNoteSortMode(int value) => prefs.setInt('noteSortMode', value);
/// 书影日历日期模式 (0: 创建日期, 1: 观看/开始阅读日期)
int get calendarDateMode => prefs.getInt('calendarDateMode') ?? 0;
Future<bool> setCalendarDateMode(int value) => prefs.setInt('calendarDateMode', value);
/// 数据统计时间范围 (0: 周, 1: 月, 2: 年)
int get statsTimeRange => prefs.getInt('statsTimeRange') ?? 2;
Future<bool> setStatsTimeRange(int value) => prefs.setInt('statsTimeRange', value);
/// 影视排序方式 (0: 更新时间, 1: 创建时间, 2: 评分, 3: 观看日期, 4: 上映时间)
int get movieSortMode => prefs.getInt('movieSortMode') ?? 0;
Future<bool> setMovieSortMode(int value) => prefs.setInt('movieSortMode', value);
/// 书籍排序方式 (0: 更新时间, 1: 创建时间, 2: 评分, 3: 开始阅读时间, 4: 出版时间)
int get bookSortMode => prefs.getInt('bookSortMode') ?? 0;
Future<bool> setBookSortMode(int value) => prefs.setInt('bookSortMode', value);
/// 影视布局样式 (0: 海报网格, 1: 列表)
int get movieLayoutStyle => prefs.getInt('movieLayoutStyle') ?? 0;
Future<bool> setMovieLayoutStyle(int value) => prefs.setInt('movieLayoutStyle', value);
/// 影视显示模式 (0: 观看状态, 1: 分类状态)
int get movieDisplayMode => prefs.getInt('movieDisplayMode') ?? 0;
Future<bool> setMovieDisplayMode(int value) => prefs.setInt('movieDisplayMode', value);
/// 阅读布局样式 (0: 封面网格, 1: 列表)
int get bookLayoutStyle => prefs.getInt('bookLayoutStyle') ?? 0;
Future<bool> setBookLayoutStyle(int value) => prefs.setInt('bookLayoutStyle', value);
/// 影视墙模式(不显示分类,按创建时间排序)
bool get movieWallMode => prefs.getBool('movieWallMode') ?? false;
Future<bool> setMovieWallMode(bool value) => prefs.setBool('movieWallMode', value);
/// 书架模式(不显示分类,按创建时间排序)
bool get bookshelfMode => prefs.getBool('bookshelfMode') ?? false;
Future<bool> setBookshelfMode(bool value) => prefs.setBool('bookshelfMode', value);
/// 游戏排序方式 (0: 更新时间, 1: 创建时间, 2: 评分)
int get gameSortMode => prefs.getInt('gameSortMode') ?? 0;
Future<bool> setGameSortMode(int value) => prefs.setInt('gameSortMode', value);
/// 游戏布局样式 (0: 网格, 1: 列表, 2: 大图卡片)
int get gameLayoutStyle => prefs.getInt('gameLayoutStyle') ?? 0;
Future<bool> setGameLayoutStyle(int value) => prefs.setInt('gameLayoutStyle', value);
/// 游戏墙模式
bool get gameWallMode => prefs.getBool('gameWallMode') ?? false;
Future<bool> setGameWallMode(bool value) => prefs.setBool('gameWallMode', value);
/// 已阅页布局样式 (0: 列表, 1: 网格)
int get reviewedLayoutStyle => prefs.getInt('reviewedLayoutStyle') ?? 0;
Future<bool> setReviewedLayoutStyle(int value) => prefs.setInt('reviewedLayoutStyle', value);
/// 片单页布局样式 (0: 列表, 1: 网格)
int get playlistLayoutStyle => prefs.getInt('playlistLayoutStyle') ?? 0;
Future<bool> setPlaylistLayoutStyle(int value) => prefs.setInt('playlistLayoutStyle', value);
// ========== 应用图标设置 ==========
// ========== Markdown 阅读器 ==========
/// Markdown 阅读器最近选择的目录
String? get lastMdFolder => prefs.getString('lastMdFolder');
Future<bool> setLastMdFolder(String value) => prefs.setString('lastMdFolder', value);
/// 是否显示空目录(无 Markdown 文件的目录)
bool get showEmptyDirs => prefs.getBool('showEmptyDirs') ?? true;
Future<bool> setShowEmptyDirs(bool value) => prefs.setBool('showEmptyDirs', value);
/// 是否显示纯图片目录(只有图片、无 Markdown 文件的目录)
bool get showImageOnlyDirs => prefs.getBool('showImageOnlyDirs') ?? true;
Future<bool> setShowImageOnlyDirs(bool value) => prefs.setBool('showImageOnlyDirs', value);
// ========== 应用图标设置 ==========
/// 当前选中的应用图标名称(对应 assets/icon/ 下的文件名,不含扩展名)
String get appIconName => prefs.getString('appIconName') ?? 'app_icon';
Future<bool> setAppIconName(String value) => prefs.setString('appIconName', value);
// ========== 用户统计设置 ==========
/// 匿名设备标识(首次启动自动生成)
String get deviceId => prefs.getString('deviceId') ?? '';
Future<bool> setDeviceId(String value) => prefs.setString('deviceId', value);
// ========== 搜索历史 ==========
/// 搜索历史记录
List<String> get searchHistory => prefs.getStringList('searchHistory') ?? [];
Future<bool> setSearchHistory(List<String> value) => prefs.setStringList('searchHistory', value);
/// 添加搜索记录(最多 50 条)
Future<void> addSearchHistory(String keyword) async {
final list = searchHistory;
list.remove(keyword);
list.insert(0, keyword);
if (list.length > 50) { list.removeRange(50, list.length); }
await setSearchHistory(list);
}
/// 删除单条搜索记录
Future<void> removeSearchHistory(String keyword) async {
final list = searchHistory;
list.remove(keyword);
await setSearchHistory(list);
}
/// 清空搜索历史
Future<void> clearSearchHistory() => setSearchHistory([]);
// ========== 增强搜索 ==========
/// 是否开启增强搜索
bool get enhancedSearchEnabled => prefs.getBool('enhancedSearchEnabled') ?? false;
Future<bool> setEnhancedSearchEnabled(bool value) => prefs.setBool('enhancedSearchEnabled', value);
/// 影视增强搜索 Token
String get movieSearchToken => prefs.getString('movieSearchToken') ?? '';
Future<bool> setMovieSearchToken(String value) => prefs.setString('movieSearchToken', value);
/// 书籍增强搜索 Token
String get bookSearchToken => prefs.getString('bookSearchToken') ?? '';
Future<bool> setBookSearchToken(String value) => prefs.setString('bookSearchToken', value);
/// 上次搜索的 Tab: 0=影视, 1=书籍
int get lastSearchTab => prefs.getInt('lastSearchTab') ?? 0;
Future<bool> setLastSearchTab(int value) => prefs.setInt('lastSearchTab', value);
// ========== 版本更新 ==========
/// 已忽略的版本号(不再提示更新)
String get dismissedVersion => prefs.getString('dismissedVersion') ?? '';
Future<bool> setDismissedVersion(String value) => prefs.setString('dismissedVersion', value);
/// 更新提醒 snooze 到指定时间戳ms24 小时内不弹
int get dismissedUpdateUntil => prefs.getInt('dismissedUpdateUntil') ?? 0;
Future<bool> setDismissedUpdateUntil(int value) => prefs.setInt('dismissedUpdateUntil', value);
// ========== EPUB 阅读器 ==========
/// EPUB 阅读器字体大小
double get epubFontSize => prefs.getDouble('epubFontSize') ?? 18.0;
Future<bool> setEpubFontSize(double value) => prefs.setDouble('epubFontSize', value);
/// EPUB 书架视图模式: 0=宽松, 1=紧凑
int get epubViewMode => prefs.getInt('epubViewMode') ?? 0;
Future<bool> setEpubViewMode(int value) => prefs.setInt('epubViewMode', value);
/// EPUB 书架排序模式: 0=更新时间, 1=创建时间, 2=阅读进度, 3=书名
int get epubSortMode => prefs.getInt('epubSortMode') ?? 0;
Future<bool> setEpubSortMode(int value) => prefs.setInt('epubSortMode', value);
/// EPUB 句读列表视图模式: 0=瀑布流, 1=列表
int get highlightsViewMode => prefs.getInt('highlightsViewMode') ?? 0;
Future<bool> setHighlightsViewMode(int value) => prefs.setInt('highlightsViewMode', value);
// ========== 字体选择器 ==========
/// 字体选择器上次使用的目录路径
String? get lastFontDir => prefs.getString('lastFontDir');
Future<bool> setLastFontDir(String value) => prefs.setString('lastFontDir', value);
// ========== 本地自动备份 ==========
/// 是否启用本地自动备份
bool get localAutoBackupEnabled => prefs.getBool('localAutoBackupEnabled') ?? false;
Future<bool> setLocalAutoBackupEnabled(bool value) => prefs.setBool('localAutoBackupEnabled', value);
/// 本地自动备份间隔(小时),默认 24
int get localAutoBackupIntervalHours => prefs.getInt('localAutoBackupIntervalHours') ?? 24;
Future<bool> setLocalAutoBackupIntervalHours(int value) => prefs.setInt('localAutoBackupIntervalHours', value);
/// 上次本地自动备份时间ISO8601
String? get lastLocalAutoBackupTime => prefs.getString('lastLocalAutoBackupTime');
Future<bool> setLastLocalAutoBackupTime(String value) => prefs.setString('lastLocalAutoBackupTime', value);
}