forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import {type ReactNode, useMemo} from 'react'
2import {createContext, useContext} from 'react'
3import {type TextStyle, type ViewStyle} from 'react-native'
4import {type ThemeName} from '@bsky.app/alf'
5
6import {useThemePrefs} from '#/state/shell/color-mode'
7import {hueShifter, type SchemeType, selectScheme} from '#/alf'
8import {themes} from '#/alf/themes'
9import {darkTheme, defaultTheme, dimTheme} from './themes'
10
11export type ColorScheme = 'light' | 'dark'
12
13export type PaletteColorName =
14 | 'default'
15 | 'primary'
16 | 'secondary'
17 | 'inverted'
18 | 'error'
19export type PaletteColor = {
20 background: string
21 backgroundLight: string
22 text: string
23 textLight: string
24 textInverted: string
25 link: string
26 border: string
27 borderDark: string
28 icon: string
29 [k: string]: string
30}
31export type Palette = Record<PaletteColorName, PaletteColor>
32
33export type ShapeName = 'button' | 'bigButton' | 'smallButton'
34export type Shapes = Record<ShapeName, ViewStyle>
35
36/**
37 * @deprecated use typography atoms from `#/alf`
38 */
39export type TypographyVariant =
40 | '2xl-thin'
41 | '2xl'
42 | '2xl-medium'
43 | '2xl-bold'
44 | '2xl-heavy'
45 | 'xl-thin'
46 | 'xl'
47 | 'xl-medium'
48 | 'xl-bold'
49 | 'xl-heavy'
50 | 'lg-thin'
51 | 'lg'
52 | 'lg-medium'
53 | 'lg-bold'
54 | 'lg-heavy'
55 | 'md-thin'
56 | 'md'
57 | 'md-medium'
58 | 'md-bold'
59 | 'md-heavy'
60 | 'sm-thin'
61 | 'sm'
62 | 'sm-medium'
63 | 'sm-bold'
64 | 'sm-heavy'
65 | 'xs-thin'
66 | 'xs'
67 | 'xs-medium'
68 | 'xs-bold'
69 | 'xs-heavy'
70 | 'title-2xl'
71 | 'title-xl'
72 | 'title-lg'
73 | 'title'
74 | 'title-sm'
75 | 'post-text-lg'
76 | 'post-text'
77 | 'button'
78 | 'button-lg'
79 | 'mono'
80export type Typography = Record<TypographyVariant, TextStyle>
81
82export interface Theme {
83 colorScheme: ColorScheme
84 palette: Palette
85 shapes: Shapes
86 typography: Typography
87}
88
89export interface ThemeProviderProps {
90 children?: ReactNode
91 theme: ThemeName
92}
93
94export const ThemeContext = createContext<Theme>(
95 defaultTheme({
96 lightPalette: themes.lightPalette,
97 darkPalette: themes.darkPalette,
98 }),
99)
100ThemeContext.displayName = 'ThemeContext'
101
102export const useTheme = () => useContext(ThemeContext)
103
104function getTheme(themeName: ThemeName, scheme: SchemeType) {
105 const paletteOptions = {
106 lightPalette: scheme.lightPalette,
107 darkPalette: scheme.darkPalette,
108 dimPalette: scheme.dimPalette,
109 }
110
111 switch (themeName) {
112 case 'light':
113 return defaultTheme(paletteOptions)
114 case 'dim':
115 return dimTheme(paletteOptions)
116 case 'dark':
117 return darkTheme(paletteOptions)
118 default:
119 return defaultTheme(paletteOptions)
120 }
121}
122
123export const ThemeProvider: React.FC<ThemeProviderProps> = ({
124 theme,
125 children,
126}) => {
127 const {colorScheme, hue} = useThemePrefs()
128
129 const themeValue = useMemo(() => {
130 const currentScheme = hueShifter(selectScheme(colorScheme), hue)
131 return getTheme(theme, currentScheme)
132 }, [theme, colorScheme, hue])
133
134 return (
135 <ThemeContext.Provider value={themeValue}>{children}</ThemeContext.Provider>
136 )
137}