Files
digital-pilates/store/tabBarConfigSlice.ts
richarjiang 3db2d39a58 perf(store): 优化 selector 性能并移除未使用代码
- 使用 createSelector 和 useMemo 优化 medications 和 tabBarConfig 的 selector,避免不必要的重渲染
- 添加空数组常量 EMPTY_RECORDS_ARRAY,减少对象创建开销
- 移除 _layout.tsx 中未使用的路由配置
- 删除过时的通知实现文档
- 移除 pushNotificationManager 中未使用的 token 刷新监听器
- 禁用开发环境的后台任务调试工具初始化
2025-11-24 11:11:29 +08:00

208 lines
5.6 KiB
TypeScript
Raw Permalink 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 AsyncStorage from '@/utils/kvStore';
import { logger } from '@/utils/logger';
import { createSelector, createSlice, PayloadAction } from '@reduxjs/toolkit';
import type { RootState } from './index';
// Tab 配置接口
export interface TabConfig {
id: string; // tab 标识符
icon: string; // SF Symbol 图标名
titleKey: string; // i18n 翻译 key
enabled: boolean; // 是否启用
canBeDisabled: boolean; // 是否可以被禁用
order: number; // 显示顺序
}
// State 接口
interface TabBarConfigState {
configs: TabConfig[];
isInitialized: boolean;
}
// 默认配置
export const DEFAULT_TAB_CONFIGS: TabConfig[] = [
{
id: 'statistics',
icon: 'chart.pie.fill',
titleKey: 'statistics.tabs.health',
enabled: true,
canBeDisabled: false,
order: 1,
},
{
id: 'medications',
icon: 'pills.fill',
titleKey: 'statistics.tabs.medications',
enabled: true,
canBeDisabled: false,
order: 2,
},
{
id: 'fasting',
icon: 'timer',
titleKey: 'statistics.tabs.fasting',
enabled: true,
canBeDisabled: true, // 只有断食可以被关闭
order: 3,
},
{
id: 'challenges',
icon: 'trophy.fill',
titleKey: 'statistics.tabs.challenges',
enabled: true,
canBeDisabled: false,
order: 4,
},
{
id: 'personal',
icon: 'person.fill',
titleKey: 'statistics.tabs.personal',
enabled: true,
canBeDisabled: false,
order: 5,
},
];
// AsyncStorage key
const STORAGE_KEY = 'tab_bar_config';
// 初始状态
const initialState: TabBarConfigState = {
configs: DEFAULT_TAB_CONFIGS,
isInitialized: false,
};
const tabBarConfigSlice = createSlice({
name: 'tabBarConfig',
initialState,
reducers: {
// 设置配置(用于从 AsyncStorage 恢复)
setConfigs: (state, action: PayloadAction<TabConfig[]>) => {
state.configs = action.payload;
state.isInitialized = true;
},
// 切换 tab 启用状态
toggleTabEnabled: (state, action: PayloadAction<string>) => {
const tabId = action.payload;
const config = state.configs.find(c => c.id === tabId);
if (config && config.canBeDisabled) {
config.enabled = !config.enabled;
// 自动持久化到 AsyncStorage
saveConfigsToStorage(state.configs);
}
},
// 更新 tab 顺序(拖拽后)
reorderTabs: (state, action: PayloadAction<TabConfig[]>) => {
// 更新顺序,同时保持其他属性不变
const newConfigs = action.payload.map((config, index) => ({
...config,
order: index + 1,
}));
state.configs = newConfigs;
// 自动持久化到 AsyncStorage
saveConfigsToStorage(newConfigs);
},
// 恢复默认配置
resetToDefault: (state) => {
state.configs = DEFAULT_TAB_CONFIGS;
// 持久化到 AsyncStorage
saveConfigsToStorage(DEFAULT_TAB_CONFIGS);
},
// 标记已初始化
markInitialized: (state) => {
state.isInitialized = true;
},
},
});
// 持久化配置到 AsyncStorage
const saveConfigsToStorage = async (configs: TabConfig[]) => {
try {
await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(configs));
logger.info('底部栏配置已保存');
} catch (error) {
logger.error('保存底部栏配置失败:', error);
}
};
// 从 AsyncStorage 加载配置
export const loadTabBarConfigs = () => async (dispatch: any) => {
try {
const stored = await AsyncStorage.getItem(STORAGE_KEY);
if (stored) {
const configs = JSON.parse(stored) as TabConfig[];
// 验证配置有效性
if (Array.isArray(configs) && configs.length > 0) {
// 合并默认配置,确保新增的 tab 也能显示
const mergedConfigs = mergeWithDefaults(configs);
dispatch(setConfigs(mergedConfigs));
logger.info('底部栏配置已加载');
return;
}
}
// 如果没有存储或无效,使用默认配置
dispatch(setConfigs(DEFAULT_TAB_CONFIGS));
dispatch(markInitialized());
} catch (error) {
logger.error('加载底部栏配置失败:', error);
// 出错时使用默认配置
dispatch(setConfigs(DEFAULT_TAB_CONFIGS));
dispatch(markInitialized());
}
};
// 合并存储的配置和默认配置
const mergeWithDefaults = (storedConfigs: TabConfig[]): TabConfig[] => {
const merged = [...storedConfigs];
// 检查是否有新增的默认 tab
DEFAULT_TAB_CONFIGS.forEach(defaultConfig => {
const exists = merged.find(c => c.id === defaultConfig.id);
if (!exists) {
// 新增的 tab添加到末尾
merged.push({
...defaultConfig,
order: merged.length + 1,
});
}
});
// 按 order 排序
return merged.sort((a, b) => a.order - b.order);
};
// Actions
export const {
setConfigs,
toggleTabEnabled,
reorderTabs,
resetToDefault,
markInitialized,
} = tabBarConfigSlice.actions;
// Selectors
export const selectTabBarConfigs = (state: RootState) => state.tabBarConfig.configs;
// ✅ 使用 createSelector 进行记忆化,避免不必要的重渲染
export const selectEnabledTabs = createSelector(
[selectTabBarConfigs],
(configs) => configs
.filter(config => config.enabled)
.sort((a, b) => a.order - b.order)
);
export const selectIsInitialized = (state: RootState) => state.tabBarConfig.isInitialized;
// 按 id 获取配置
export const selectTabConfigById = (tabId: string) => (state: RootState) =>
state.tabBarConfig.configs.find(config => config.id === tabId);
export default tabBarConfigSlice.reducer;