Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 999e52ed2d5a2c8b2f7b8747dfcfd0e2017e5eb0 236 lines 5.5 kB view raw
1import React from 'react' 2import {createTheme, type Theme, type ThemeName} from '@bsky.app/alf' 3import chroma from 'chroma-js' 4 5import {useThemePrefs} from '#/state/shell/color-mode' 6import { 7 computeFontScaleMultiplier, 8 getFontFamily, 9 getFontScale, 10 setFontFamily as persistFontFamily, 11 setFontScale as persistFontScale, 12} from '#/alf/fonts' 13import { 14 blackskyscheme, 15 blueskyscheme, 16 deerscheme, 17 kittyscheme, 18 type Palette, 19 reddwarfscheme, 20 themes, 21 witchskyscheme, 22 zeppelinscheme, 23} from '#/alf/themes' 24import {type Device} from '#/storage' 25 26export { 27 type TextStyleProp, 28 type Theme, 29 utils, 30 type ViewStyleProp, 31} from '@bsky.app/alf' 32export {atoms} from '#/alf/atoms' 33export * from '#/alf/breakpoints' 34export * from '#/alf/fonts' 35export * as tokens from '#/alf/tokens' 36export * from '#/alf/util/flatten' 37export * from '#/alf/util/platform' 38export * from '#/alf/util/themeSelector' 39export * from '#/alf/util/useGutters' 40 41export type Alf = { 42 themeName: ThemeName 43 theme: Theme 44 themes: typeof themes 45 fonts: { 46 scale: Exclude<Device['fontScale'], undefined> 47 scaleMultiplier: number 48 family: Device['fontFamily'] 49 setFontScale: (fontScale: Exclude<Device['fontScale'], undefined>) => void 50 setFontFamily: (fontFamily: Device['fontFamily']) => void 51 } 52 /** 53 * Feature flags or other gated options 54 */ 55 flags: {} 56} 57 58/* 59 * Context 60 */ 61export const Context = React.createContext<Alf>({ 62 themeName: 'light', 63 theme: themes.light, 64 themes, 65 fonts: { 66 scale: getFontScale(), 67 scaleMultiplier: computeFontScaleMultiplier(getFontScale()), 68 family: getFontFamily(), 69 setFontScale: () => {}, 70 setFontFamily: () => {}, 71 }, 72 flags: {}, 73}) 74Context.displayName = 'AlfContext' 75 76export type SchemeType = typeof themes 77 78export function changeHue(colorStr: string, hueShift: number) { 79 if (!hueShift || hueShift === 0) return colorStr 80 81 const color = chroma(colorStr).oklch() 82 83 const newHue = (color[2] + hueShift + 360) % 360 84 85 return chroma.oklch(color[0], color[1], newHue).hex() 86} 87 88export function shiftPalette(palette: Palette, hueShift: number): Palette { 89 const newPalette = {...palette} 90 const keys = Object.keys(newPalette) as Array<keyof Palette> 91 92 keys.forEach(key => { 93 newPalette[key] = changeHue(newPalette[key], hueShift) 94 }) 95 96 return newPalette 97} 98 99export function hueShifter(scheme: SchemeType, hueShift: number): SchemeType { 100 if (!hueShift || hueShift === 0) { 101 return scheme 102 } 103 104 const lightPalette = shiftPalette(scheme.lightPalette, hueShift) 105 const darkPalette = shiftPalette(scheme.darkPalette, hueShift) 106 const dimPalette = shiftPalette(scheme.dimPalette, hueShift) 107 108 const light = createTheme({ 109 scheme: 'light', 110 name: 'light', 111 palette: lightPalette, 112 }) 113 114 const dark = createTheme({ 115 scheme: 'dark', 116 name: 'dark', 117 palette: darkPalette, 118 options: { 119 shadowOpacity: 0.4, 120 }, 121 }) 122 123 const dim = createTheme({ 124 scheme: 'dark', 125 name: 'dim', 126 palette: dimPalette, 127 options: { 128 shadowOpacity: 0.4, 129 }, 130 }) 131 132 return { 133 lightPalette, 134 darkPalette, 135 dimPalette, 136 light, 137 dark, 138 dim, 139 } 140} 141 142export function selectScheme(colorScheme: string | undefined): SchemeType { 143 switch (colorScheme) { 144 case 'witchsky': 145 return witchskyscheme 146 case 'bluesky': 147 return blueskyscheme 148 case 'blacksky': 149 return blackskyscheme 150 case 'deer': 151 return deerscheme 152 case 'zeppelin': 153 return zeppelinscheme 154 case 'kitty': 155 return kittyscheme 156 case 'reddwarf': 157 return reddwarfscheme 158 default: 159 return themes 160 } 161} 162 163export function ThemeProvider({ 164 children, 165 theme: themeName, 166}: React.PropsWithChildren<{theme: ThemeName}>) { 167 const {colorScheme, hue} = useThemePrefs() 168 const currentScheme = selectScheme(colorScheme) 169 const [fontScale, setFontScale] = React.useState<Alf['fonts']['scale']>(() => 170 getFontScale(), 171 ) 172 const [fontScaleMultiplier, setFontScaleMultiplier] = React.useState(() => 173 computeFontScaleMultiplier(fontScale), 174 ) 175 const setFontScaleAndPersist = React.useCallback< 176 Alf['fonts']['setFontScale'] 177 >( 178 fs => { 179 setFontScale(fs) 180 persistFontScale(fs) 181 setFontScaleMultiplier(computeFontScaleMultiplier(fs)) 182 }, 183 [setFontScale], 184 ) 185 const [fontFamily, setFontFamily] = React.useState<Alf['fonts']['family']>( 186 () => getFontFamily(), 187 ) 188 const setFontFamilyAndPersist = React.useCallback< 189 Alf['fonts']['setFontFamily'] 190 >( 191 ff => { 192 setFontFamily(ff) 193 persistFontFamily(ff) 194 }, 195 [setFontFamily], 196 ) 197 198 const value = React.useMemo<Alf>( 199 () => ({ 200 themes: hueShifter(currentScheme, hue), 201 themeName: themeName, 202 theme: hueShifter(currentScheme, hue)[themeName], 203 fonts: { 204 scale: fontScale, 205 scaleMultiplier: fontScaleMultiplier, 206 family: fontFamily, 207 setFontScale: setFontScaleAndPersist, 208 setFontFamily: setFontFamilyAndPersist, 209 }, 210 flags: {}, 211 }), 212 [ 213 currentScheme, 214 hue, 215 themeName, 216 fontScale, 217 fontScaleMultiplier, 218 fontFamily, 219 setFontScaleAndPersist, 220 setFontFamilyAndPersist, 221 ], 222 ) 223 224 return <Context.Provider value={value}>{children}</Context.Provider> 225} 226 227export function useAlf() { 228 return React.useContext(Context) 229} 230 231export function useTheme(theme?: ThemeName) { 232 const alf = useAlf() 233 return React.useMemo(() => { 234 return theme ? alf.themes[theme] : alf.theme 235 }, [theme, alf]) 236}