Bluesky app fork with some witchin' additions 馃挮 witchsky.app
bluesky fork client
144
fork

Configure Feed

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

at main 286 lines 6.9 kB view raw
1import {createContext, useCallback, useContext, useMemo, useState} 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 catppuccinscheme, 17 cyanscheme, 18 deerscheme, 19 evergardenscheme, 20 kittyscheme, 21 type Palette, 22 reddwarfscheme, 23 themes, 24 witchskyscheme, 25 zeppelinscheme, 26} from '#/alf/themes' 27import {type Device} from '#/storage' 28import {getMaterial3Colors} from './util/material3Theme' 29import { 30 MaterialYouPaletteProvider, 31 useMaterialYouPalette, 32} from './util/materialYou' 33 34export { 35 type TextStyleProp, 36 type Theme, 37 utils, 38 type ViewStyleProp, 39} from '@bsky.app/alf' 40export {atoms} from '#/alf/atoms' 41export * from '#/alf/breakpoints' 42export * from '#/alf/fonts' 43export * as tokens from '#/alf/tokens' 44export * from '#/alf/util/flatten' 45export * from '#/alf/util/platform' 46export * from '#/alf/util/themeSelector' 47export * from '#/alf/util/useGutters' 48 49export type Alf = { 50 themeName: ThemeName 51 theme: Theme 52 themes: typeof themes 53 fonts: { 54 scale: Exclude<Device['fontScale'], undefined> 55 scaleMultiplier: number 56 family: Device['fontFamily'] 57 setFontScale: (fontScale: Exclude<Device['fontScale'], undefined>) => void 58 setFontFamily: (fontFamily: Device['fontFamily']) => void 59 } 60 /** 61 * Feature flags or other gated options 62 */ 63 flags: {} 64} 65 66/* 67 * Context 68 */ 69export const Context = createContext<Alf>({ 70 themeName: 'light', 71 theme: themes.light, 72 themes, 73 fonts: { 74 scale: getFontScale(), 75 scaleMultiplier: computeFontScaleMultiplier(getFontScale()), 76 family: getFontFamily(), 77 setFontScale: () => {}, 78 setFontFamily: () => {}, 79 }, 80 flags: {}, 81}) 82Context.displayName = 'AlfContext' 83 84export type SchemeType = typeof themes 85 86export function changeHue(colorStr: string, hueShift: number) { 87 if (!hueShift || hueShift === 0) return colorStr 88 89 const color = chroma(colorStr).oklch() 90 91 const newHue = (color[2] + hueShift + 360) % 360 92 93 return chroma.oklch(color[0], color[1], newHue).hex() 94} 95 96export function shiftPalette(palette: Palette, hueShift: number): Palette { 97 const newPalette = {...palette} 98 const keys = Object.keys(newPalette) as Array<keyof Palette> 99 100 keys.forEach(key => { 101 if ( 102 key.startsWith('positive_') || 103 key.startsWith('negative_') || 104 key === 'like' || 105 key === 'pink' || 106 key === 'yellow' 107 ) { 108 return 109 } 110 newPalette[key] = changeHue(newPalette[key], hueShift) 111 }) 112 113 return newPalette 114} 115 116export function hueShifter(scheme: SchemeType, hueShift: number): SchemeType { 117 if (!hueShift || hueShift === 0) { 118 return scheme 119 } 120 121 const lightPalette = shiftPalette(scheme.lightPalette, hueShift) 122 const darkPalette = shiftPalette(scheme.darkPalette, hueShift) 123 const dimPalette = shiftPalette(scheme.dimPalette, hueShift) 124 125 const light = createTheme({ 126 scheme: 'light', 127 name: 'light', 128 palette: lightPalette, 129 }) 130 131 const dark = createTheme({ 132 scheme: 'dark', 133 name: 'dark', 134 palette: darkPalette, 135 options: { 136 shadowOpacity: 0.4, 137 }, 138 }) 139 140 const dim = createTheme({ 141 scheme: 'dark', 142 name: 'dim', 143 palette: dimPalette, 144 options: { 145 shadowOpacity: 0.4, 146 }, 147 }) 148 149 return { 150 lightPalette, 151 darkPalette, 152 dimPalette, 153 light, 154 dark, 155 dim, 156 } 157} 158 159export function useScheme(): SchemeType { 160 const {hue, colorScheme} = useThemePrefs() 161 const palette = useMaterialYouPalette() 162 163 return useMemo(() => { 164 let currentScheme = themes 165 switch (colorScheme) { 166 case 'witchsky': 167 currentScheme = witchskyscheme 168 break 169 case 'bluesky': 170 currentScheme = blueskyscheme 171 break 172 case 'blacksky': 173 currentScheme = blackskyscheme 174 break 175 case 'deer': 176 currentScheme = deerscheme 177 break 178 case 'zeppelin': 179 currentScheme = zeppelinscheme 180 break 181 case 'kitty': 182 currentScheme = kittyscheme 183 break 184 case 'reddwarf': 185 currentScheme = reddwarfscheme 186 break 187 case 'catppuccin': 188 currentScheme = catppuccinscheme 189 break 190 case 'evergarden': 191 currentScheme = evergardenscheme 192 break 193 case 'cyan base': 194 currentScheme = cyanscheme 195 break 196 case 'material3': 197 currentScheme = getMaterial3Colors(palette).scheme 198 break 199 default: 200 currentScheme = themes 201 break 202 } 203 204 return hueShifter(currentScheme, hue) 205 }, [colorScheme, hue, palette]) 206} 207 208function ThemeProviderInner({ 209 children, 210 theme: themeName, 211}: React.PropsWithChildren<{theme: ThemeName}>) { 212 const currentScheme = useScheme() 213 const [fontScale, setFontScale] = useState<Alf['fonts']['scale']>(() => 214 getFontScale(), 215 ) 216 const [fontScaleMultiplier, setFontScaleMultiplier] = useState(() => 217 computeFontScaleMultiplier(fontScale), 218 ) 219 const setFontScaleAndPersist = useCallback<Alf['fonts']['setFontScale']>( 220 fs => { 221 setFontScale(fs) 222 persistFontScale(fs) 223 setFontScaleMultiplier(computeFontScaleMultiplier(fs)) 224 }, 225 [setFontScale], 226 ) 227 const [fontFamily, setFontFamily] = useState<Alf['fonts']['family']>(() => 228 getFontFamily(), 229 ) 230 const setFontFamilyAndPersist = useCallback<Alf['fonts']['setFontFamily']>( 231 ff => { 232 setFontFamily(ff) 233 persistFontFamily(ff) 234 }, 235 [setFontFamily], 236 ) 237 238 const value = useMemo<Alf>(() => { 239 return { 240 themes: currentScheme, 241 themeName: themeName, 242 theme: currentScheme[themeName], 243 fonts: { 244 scale: fontScale, 245 scaleMultiplier: fontScaleMultiplier, 246 family: fontFamily, 247 setFontScale: setFontScaleAndPersist, 248 setFontFamily: setFontFamilyAndPersist, 249 }, 250 flags: {}, 251 } 252 }, [ 253 currentScheme, 254 themeName, 255 fontScale, 256 fontScaleMultiplier, 257 fontFamily, 258 setFontScaleAndPersist, 259 setFontFamilyAndPersist, 260 ]) 261 262 return <Context.Provider value={value}>{children}</Context.Provider> 263} 264 265export function ThemeProvider({ 266 children, 267 theme: themeName, 268}: React.PropsWithChildren<{theme: ThemeName}>) { 269 const {material3Accent, material3Style} = useThemePrefs() 270 return ( 271 <MaterialYouPaletteProvider accent={material3Accent} style={material3Style}> 272 <ThemeProviderInner theme={themeName}>{children}</ThemeProviderInner> 273 </MaterialYouPaletteProvider> 274 ) 275} 276 277export function useAlf() { 278 return useContext(Context) 279} 280 281export function useTheme(theme?: ThemeName) { 282 const alf = useAlf() 283 return useMemo(() => { 284 return theme ? alf.themes[theme] : alf.theme 285 }, [theme, alf]) 286}