1import Config from '@/core/config'; 2 3import { 4 DarkTheme as _DarkTheme, 5 DefaultTheme as _DefaultTheme, 6} from '@react-navigation/native'; 7import {GlobalState} from '@/utils/stateMapper'; 8import {CustomizedColors} from '@/hooks/useColors'; 9import Color from 'color'; 10 11export const lightTheme = { 12 id: 'p-light', 13 ..._DefaultTheme, 14 colors: { 15 ..._DefaultTheme.colors, 16 background: 'transparent', 17 text: '#333333', 18 textSecondary: Color('#333333').alpha(0.7).toString(), 19 primary: '#f17d34', 20 pageBackground: '#fafafa', 21 shadow: '#000', 22 appBar: '#f17d34', 23 appBarText: '#fefefe', 24 musicBar: '#f2f2f2', 25 musicBarText: '#333333', 26 divider: 'rgba(0,0,0,0.1)', 27 listActive: 'rgba(0,0,0,0.1)', // 在手机上表现是ripple 28 mask: 'rgba(51,51,51,0.2)', 29 backdrop: '#f0f0f0', 30 tabBar: '#f0f0f0', 31 placeholder: '#eaeaea', 32 success: '#08A34C', 33 danger: '#FC5F5F', 34 info: '#0A95C8', 35 card: '#e0e0e033', 36 }, 37}; 38 39export const darkTheme = { 40 id: 'p-dark', 41 ..._DarkTheme, 42 colors: { 43 ..._DarkTheme.colors, 44 background: 'transparent', 45 text: '#fcfcfc', 46 textSecondary: Color('#fcfcfc').alpha(0.7).toString(), 47 primary: '#3FA3B5', 48 pageBackground: '#202020', 49 shadow: '#999', 50 appBar: '#262626', 51 appBarText: '#fcfcfc', 52 musicBar: '#262626', 53 musicBarText: '#fcfcfc', 54 divider: 'rgba(255,255,255,0.1)', 55 listActive: 'rgba(255,255,255,0.1)', // 在手机上表现是ripple 56 mask: 'rgba(33,33,33,0.8)', 57 backdrop: '#303030', 58 tabBar: '#303030', 59 placeholder: '#424242', 60 success: '#08A34C', 61 danger: '#FC5F5F', 62 info: '#0A95C8', 63 card: '#33333366', 64 }, 65}; 66 67interface IBackgroundInfo { 68 url?: string; 69 blur?: number; 70 opacity?: number; 71} 72 73const themeStore = new GlobalState(darkTheme); 74const backgroundStore = new GlobalState<IBackgroundInfo | null>(null); 75 76function setup() { 77 const currentTheme = Config.get('setting.theme.selectedTheme') ?? 'p-dark'; 78 79 if (currentTheme === 'p-dark') { 80 themeStore.setValue(darkTheme); 81 } else if (currentTheme === 'p-light') { 82 themeStore.setValue(lightTheme); 83 } else { 84 themeStore.setValue({ 85 id: currentTheme, 86 dark: true, 87 // @ts-ignore 88 colors: 89 (Config.get('setting.theme.colors') as CustomizedColors) ?? 90 darkTheme.colors, 91 }); 92 } 93 94 const bgUrl = Config.get('setting.theme.background'); 95 const bgBlur = Config.get('setting.theme.backgroundBlur'); 96 const bgOpacity = Config.get('setting.theme.backgroundOpacity'); 97 98 backgroundStore.setValue({ 99 url: bgUrl, 100 blur: bgBlur ?? 20, 101 opacity: bgOpacity ?? 0.6, 102 }); 103} 104 105function setTheme( 106 themeName: string, 107 extra?: { 108 colors?: Partial<CustomizedColors>; 109 background?: IBackgroundInfo; 110 }, 111) { 112 if (themeName === 'p-light') { 113 themeStore.setValue(lightTheme); 114 } else if (themeName === 'p-dark') { 115 themeStore.setValue(darkTheme); 116 } else { 117 themeStore.setValue({ 118 id: themeName, 119 dark: true, 120 colors: { 121 ...darkTheme.colors, 122 ...(extra?.colors ?? {}), 123 }, 124 }); 125 } 126 127 Config.set('setting.theme.selectedTheme', themeName); 128 Config.set('setting.theme.colors', themeStore.getValue().colors); 129 130 if (extra?.background) { 131 const currentBg = backgroundStore.getValue(); 132 let newBg: IBackgroundInfo = { 133 blur: 20, 134 opacity: 0.6, 135 ...(currentBg ?? {}), 136 url: undefined, 137 }; 138 if (typeof extra.background.blur === 'number') { 139 newBg.blur = extra.background.blur; 140 } 141 if (typeof extra.background.opacity === 'number') { 142 newBg.opacity = extra.background.opacity; 143 } 144 if (extra.background.url) { 145 newBg.url = extra.background.url; 146 } 147 148 Config.set('setting.theme.background', newBg.url); 149 Config.set('setting.theme.backgroundBlur', newBg.blur); 150 Config.set('setting.theme.backgroundOpacity', newBg.opacity); 151 152 backgroundStore.setValue(newBg); 153 } 154} 155 156function setColors(colors: Partial<CustomizedColors>) { 157 const currentTheme = themeStore.getValue(); 158 if (currentTheme.id !== 'p-light' && currentTheme.id !== 'p-dark') { 159 const newTheme = { 160 ...currentTheme, 161 colors: { 162 ...currentTheme.colors, 163 ...colors, 164 }, 165 }; 166 Config.set('setting.theme.customColors', newTheme.colors); 167 Config.set('setting.theme.colors', newTheme.colors); 168 themeStore.setValue(newTheme); 169 } 170} 171 172function setBackground(backgroundInfo: Partial<IBackgroundInfo>) { 173 const currentBackgroundInfo = backgroundStore.getValue(); 174 let newBgInfo = { 175 ...(currentBackgroundInfo ?? { 176 opacity: 0.6, 177 blur: 20, 178 }), 179 }; 180 if (typeof backgroundInfo.blur === 'number') { 181 Config.set('setting.theme.backgroundBlur', backgroundInfo.blur); 182 newBgInfo.blur = backgroundInfo.blur; 183 } 184 if (typeof backgroundInfo.opacity === 'number') { 185 Config.set('setting.theme.backgroundOpacity', backgroundInfo.opacity); 186 newBgInfo.opacity = backgroundInfo.opacity; 187 } 188 if (backgroundInfo.url !== undefined) { 189 Config.set('setting.theme.background', backgroundInfo.url); 190 newBgInfo.url = backgroundInfo.url; 191 } 192 backgroundStore.setValue(newBgInfo); 193} 194 195const configableColorKey: Array<keyof CustomizedColors> = [ 196 'primary', 197 'text', 198 'appBar', 199 'appBarText', 200 'musicBar', 201 'musicBarText', 202 'pageBackground', 203 'backdrop', 204 'card', 205 'placeholder', 206]; 207 208const colorDesc: Record<string, string> = { 209 text: '文字颜色', 210 primary: '主题色', 211 appBar: '标题栏背景色', 212 appBarText: '标题栏文字颜色', 213 musicBar: '音乐栏背景色', 214 musicBarText: '音乐栏文字颜色', 215 pageBackground: '页面背景色', 216 backdrop: '弹窗、浮层背景色', 217 card: '卡片背景色', 218 placeholder: '输入框背景色', 219}; 220 221const Theme = { 222 setup, 223 setTheme, 224 setBackground, 225 setColors, 226 useTheme: themeStore.useValue, 227 getTheme: themeStore.getValue, 228 useBackground: backgroundStore.useValue, 229 configableColorKey, 230 colorDesc, 231}; 232 233export default Theme; 234