mirror of
https://github.com/tooot-app/app
synced 2025-06-05 22:19:13 +02:00
Implemented new dark theme
This commit is contained in:
@ -1,21 +1,20 @@
|
||||
import { ContextsV0 } from './v0'
|
||||
import { ContextsV1 } from './v1'
|
||||
import { ContextsV2 } from './v2'
|
||||
|
||||
const contextsMigration = {
|
||||
1: (state: ContextsV0) => {
|
||||
return (state = {
|
||||
1: (state: ContextsV0): ContextsV1 => {
|
||||
return {
|
||||
...state,
|
||||
// @ts-ignore
|
||||
mePage: {
|
||||
lists: { shown: false },
|
||||
announcements: { shown: false, unread: 0 }
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
2: (state: ContextsV1) => {
|
||||
// @ts-ignore
|
||||
delete state.mePage
|
||||
return state
|
||||
2: (state: ContextsV1): ContextsV2 => {
|
||||
const { mePage, ...rest } = state
|
||||
return rest
|
||||
}
|
||||
}
|
||||
|
||||
|
13
src/utils/migrations/contexts/v2.ts
Normal file
13
src/utils/migrations/contexts/v2.ts
Normal file
@ -0,0 +1,13 @@
|
||||
export type ContextsV2 = {
|
||||
storeReview: {
|
||||
context: Readonly<number>
|
||||
current: number
|
||||
shown: boolean
|
||||
}
|
||||
publicRemoteNotice: {
|
||||
context: Readonly<number>
|
||||
current: number
|
||||
hidden: boolean
|
||||
}
|
||||
previousTab: 'Tab-Local' | 'Tab-Public' | 'Tab-Notifications' | 'Tab-Me'
|
||||
}
|
@ -2,15 +2,15 @@ import { InstanceV3 } from './v3'
|
||||
import { InstanceV4 } from './v4'
|
||||
import { InstanceV5 } from './v5'
|
||||
import { InstanceV6 } from './v6'
|
||||
import { InstanceV7 } from './v7'
|
||||
|
||||
const instancesMigration = {
|
||||
4: (state: InstanceV3) => {
|
||||
4: (state: InstanceV3): InstanceV4 => {
|
||||
return {
|
||||
instances: state.local.instances.map((instance, index) => {
|
||||
// @ts-ignore
|
||||
delete instance.notification
|
||||
const { notification, ...rest } = instance
|
||||
return {
|
||||
...instance,
|
||||
...rest,
|
||||
active: state.local.activeIndex === index,
|
||||
push: {
|
||||
global: { loading: false, value: false },
|
||||
@ -28,35 +28,42 @@ const instancesMigration = {
|
||||
})
|
||||
}
|
||||
},
|
||||
5: (state: InstanceV4) => {
|
||||
5: (state: InstanceV4): InstanceV5 => {
|
||||
// @ts-ignore
|
||||
if (state.instances.length && !state.instances[0].notifications_filter) {
|
||||
return {
|
||||
// @ts-ignore
|
||||
instances: state.instances.map(instance => {
|
||||
// @ts-ignore
|
||||
instance.notifications_filter = {
|
||||
follow: true,
|
||||
favourite: true,
|
||||
reblog: true,
|
||||
mention: true,
|
||||
poll: true,
|
||||
follow_request: true
|
||||
return {
|
||||
...instance,
|
||||
notifications_filter: {
|
||||
follow: true,
|
||||
favourite: true,
|
||||
reblog: true,
|
||||
mention: true,
|
||||
poll: true,
|
||||
follow_request: true
|
||||
}
|
||||
}
|
||||
return instance
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// @ts-ignore
|
||||
return state
|
||||
}
|
||||
},
|
||||
6: (state: InstanceV5) => {
|
||||
6: (state: InstanceV5): InstanceV6 => {
|
||||
return {
|
||||
// @ts-ignore
|
||||
instances: state.instances.map(instance => {
|
||||
return { ...instance, configuration: undefined }
|
||||
return {
|
||||
...instance,
|
||||
configuration: undefined
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
7: (state: InstanceV6) => {
|
||||
7: (state: InstanceV6): InstanceV7 => {
|
||||
return {
|
||||
instances: state.instances.map(instance => {
|
||||
return {
|
||||
|
77
src/utils/migrations/instances/v7.ts
Normal file
77
src/utils/migrations/instances/v7.ts
Normal file
@ -0,0 +1,77 @@
|
||||
import { ComposeStateDraft } from '@screens/Compose/utils/types'
|
||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||
|
||||
type Instance = {
|
||||
active: boolean
|
||||
appData: {
|
||||
clientId: string
|
||||
clientSecret: string
|
||||
}
|
||||
url: string
|
||||
token: string
|
||||
uri: Mastodon.Instance['uri']
|
||||
urls: Mastodon.Instance['urls']
|
||||
account: {
|
||||
id: Mastodon.Account['id']
|
||||
acct: Mastodon.Account['acct']
|
||||
avatarStatic: Mastodon.Account['avatar_static']
|
||||
preferences: Mastodon.Preferences
|
||||
}
|
||||
max_toot_chars?: number // To be deprecated in v4
|
||||
configuration?: Mastodon.Instance['configuration']
|
||||
filters: Mastodon.Filter[]
|
||||
notifications_filter: {
|
||||
follow: boolean
|
||||
favourite: boolean
|
||||
reblog: boolean
|
||||
mention: boolean
|
||||
poll: boolean
|
||||
follow_request: boolean
|
||||
}
|
||||
push: {
|
||||
global: { loading: boolean; value: boolean }
|
||||
decode: { loading: boolean; value: boolean }
|
||||
alerts: {
|
||||
follow: {
|
||||
loading: boolean
|
||||
value: Mastodon.PushSubscription['alerts']['follow']
|
||||
}
|
||||
favourite: {
|
||||
loading: boolean
|
||||
value: Mastodon.PushSubscription['alerts']['favourite']
|
||||
}
|
||||
reblog: {
|
||||
loading: boolean
|
||||
value: Mastodon.PushSubscription['alerts']['reblog']
|
||||
}
|
||||
mention: {
|
||||
loading: boolean
|
||||
value: Mastodon.PushSubscription['alerts']['mention']
|
||||
}
|
||||
poll: {
|
||||
loading: boolean
|
||||
value: Mastodon.PushSubscription['alerts']['poll']
|
||||
}
|
||||
}
|
||||
keys: {
|
||||
auth?: string
|
||||
public?: string // legacy
|
||||
private?: string // legacy
|
||||
}
|
||||
}
|
||||
timelinesLookback?: {
|
||||
[key: string]: {
|
||||
queryKey: QueryKeyTimeline
|
||||
ids: Mastodon.Status['id'][]
|
||||
}
|
||||
}
|
||||
mePage: {
|
||||
lists: { shown: boolean }
|
||||
announcements: { shown: boolean; unread: number }
|
||||
}
|
||||
drafts: ComposeStateDraft[]
|
||||
}
|
||||
|
||||
export type InstanceV7 = {
|
||||
instances: Instance[]
|
||||
}
|
13
src/utils/migrations/settings/migration.ts
Normal file
13
src/utils/migrations/settings/migration.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { SettingsV0 } from './v0'
|
||||
import { SettingsV1 } from './v1'
|
||||
|
||||
const settingsMigration = {
|
||||
1: (state: SettingsV0): SettingsV1 => {
|
||||
return {
|
||||
...state,
|
||||
darkTheme: 'lighter'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default settingsMigration
|
7
src/utils/migrations/settings/v0.ts
Normal file
7
src/utils/migrations/settings/v0.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export type SettingsV0 = {
|
||||
fontsize: -1 | 0 | 1 | 2 | 3
|
||||
language: string
|
||||
theme: 'light' | 'dark' | 'auto'
|
||||
browser: 'internal' | 'external'
|
||||
analytics: boolean
|
||||
}
|
8
src/utils/migrations/settings/v1.ts
Normal file
8
src/utils/migrations/settings/v1.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export type SettingsV1 = {
|
||||
fontsize: -1 | 0 | 1 | 2 | 3
|
||||
language: string
|
||||
theme: 'light' | 'dark' | 'auto'
|
||||
darkTheme: 'lighter' | 'darker'
|
||||
browser: 'internal' | 'external'
|
||||
analytics: boolean
|
||||
}
|
@ -17,7 +17,7 @@ export interface Params {
|
||||
|
||||
const pushUseConnect = ({ t, instances }: Params) => {
|
||||
const dispatch = useDispatch()
|
||||
const { mode } = useTheme()
|
||||
const { theme } = useTheme()
|
||||
|
||||
return useEffect(() => {
|
||||
const connect = async () => {
|
||||
@ -36,7 +36,7 @@ const pushUseConnect = ({ t, instances }: Params) => {
|
||||
}).catch(error => {
|
||||
if (error.status == 404) {
|
||||
displayMessage({
|
||||
mode,
|
||||
theme,
|
||||
type: 'error',
|
||||
duration: 'long',
|
||||
message: t('pushError.message'),
|
||||
|
@ -2,6 +2,7 @@ import apiInstance from '@api/instance'
|
||||
import haptics from '@components/haptics'
|
||||
import { displayMessage } from '@components/Message'
|
||||
import queryClient from '@helpers/queryClient'
|
||||
import { Theme } from '@utils/styles/themes'
|
||||
import { AxiosError } from 'axios'
|
||||
import i18next from 'i18next'
|
||||
import { RefObject } from 'react'
|
||||
@ -51,7 +52,7 @@ type MutationVarsProfileBase =
|
||||
}
|
||||
|
||||
type MutationVarsProfile = MutationVarsProfileBase & {
|
||||
mode: 'light' | 'dark'
|
||||
theme: Theme
|
||||
messageRef: RefObject<FlashMessage>
|
||||
message: {
|
||||
text: string
|
||||
@ -133,7 +134,7 @@ const useProfileMutation = () => {
|
||||
type: i18next.t(`screenTabs:${variables.message.text}`)
|
||||
}),
|
||||
...(err && { description: err.message }),
|
||||
mode: variables.mode,
|
||||
theme: variables.theme,
|
||||
type: 'error'
|
||||
})
|
||||
}
|
||||
@ -146,7 +147,7 @@ const useProfileMutation = () => {
|
||||
message: i18next.t('screenTabs:me.profile.feedback.succeed', {
|
||||
type: i18next.t(`screenTabs:${variables.message.text}`)
|
||||
}),
|
||||
mode: variables.mode,
|
||||
theme: variables.theme,
|
||||
type: 'success'
|
||||
})
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ export type SettingsState = {
|
||||
fontsize: -1 | 0 | 1 | 2 | 3
|
||||
language: string
|
||||
theme: 'light' | 'dark' | 'auto'
|
||||
darkTheme: 'lighter' | 'darker'
|
||||
browser: 'internal' | 'external'
|
||||
analytics: boolean
|
||||
}
|
||||
@ -34,6 +35,7 @@ export const settingsInitialState = {
|
||||
)[0]
|
||||
: 'en',
|
||||
theme: 'auto',
|
||||
darkTheme: 'lighter',
|
||||
browser: 'internal',
|
||||
analytics: true
|
||||
}
|
||||
@ -60,6 +62,12 @@ const settingsSlice = createSlice({
|
||||
) => {
|
||||
state.theme = action.payload
|
||||
},
|
||||
changeDarkTheme: (
|
||||
state,
|
||||
action: PayloadAction<NonNullable<SettingsState['darkTheme']>>
|
||||
) => {
|
||||
state.darkTheme = action.payload
|
||||
},
|
||||
changeBrowser: (
|
||||
state,
|
||||
action: PayloadAction<NonNullable<SettingsState['browser']>>
|
||||
@ -78,10 +86,17 @@ export const getSettingsFontsize = (state: RootState) =>
|
||||
state.settings.fontsize || 0
|
||||
export const getSettingsLanguage = (state: RootState) => state.settings.language
|
||||
export const getSettingsTheme = (state: RootState) => state.settings.theme
|
||||
export const getSettingsDarkTheme = (state: RootState) =>
|
||||
state.settings.darkTheme
|
||||
export const getSettingsBrowser = (state: RootState) => state.settings.browser
|
||||
export const getSettingsAnalytics = (state: RootState) =>
|
||||
state.settings.analytics
|
||||
|
||||
export const { changeFontsize, changeLanguage, changeTheme, changeBrowser } =
|
||||
settingsSlice.actions
|
||||
export const {
|
||||
changeFontsize,
|
||||
changeLanguage,
|
||||
changeTheme,
|
||||
changeDarkTheme,
|
||||
changeBrowser
|
||||
} = settingsSlice.actions
|
||||
export default settingsSlice.reducer
|
||||
|
@ -1,20 +1,30 @@
|
||||
import React, { createContext, useContext, useEffect, useState } from 'react'
|
||||
import React, {
|
||||
createContext,
|
||||
useContext,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState
|
||||
} from 'react'
|
||||
import { Appearance } from 'react-native'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { ColorDefinitions, getTheme } from '@utils/styles/themes'
|
||||
import { getSettingsTheme } from '@utils/slices/settingsSlice'
|
||||
import { ColorDefinitions, getColors, Theme } from '@utils/styles/themes'
|
||||
import {
|
||||
getSettingsDarkTheme,
|
||||
getSettingsTheme,
|
||||
SettingsState
|
||||
} from '@utils/slices/settingsSlice'
|
||||
import { throttle } from 'lodash'
|
||||
|
||||
type ContextType = {
|
||||
mode: 'light' | 'dark'
|
||||
theme: { [key in ColorDefinitions]: string }
|
||||
setTheme: (theme: 'light' | 'dark') => void
|
||||
theme: Theme
|
||||
colors: { [key in ColorDefinitions]: string }
|
||||
}
|
||||
|
||||
const ManageThemeContext = createContext<ContextType>({
|
||||
mode: 'light',
|
||||
theme: getTheme('light'),
|
||||
setTheme: () => {}
|
||||
theme: 'light',
|
||||
colors: getColors('light')
|
||||
})
|
||||
|
||||
export const useTheme = () => useContext(ManageThemeContext)
|
||||
@ -45,26 +55,54 @@ const useColorSchemeDelay = (delay = 500) => {
|
||||
return colorScheme
|
||||
}
|
||||
|
||||
const determineTheme = (
|
||||
osTheme: 'light' | 'dark' | null | undefined,
|
||||
userTheme: SettingsState['theme'],
|
||||
darkTheme: SettingsState['darkTheme']
|
||||
): 'light' | 'dark_lighter' | 'dark_darker' => {
|
||||
enum DarkTheme {
|
||||
lighter = 'dark_lighter',
|
||||
darker = 'dark_darker'
|
||||
}
|
||||
const determineDarkTheme = DarkTheme[darkTheme]
|
||||
switch (userTheme) {
|
||||
case 'auto':
|
||||
switch (osTheme) {
|
||||
case 'dark':
|
||||
return determineDarkTheme
|
||||
default:
|
||||
return 'light'
|
||||
}
|
||||
case 'light':
|
||||
return 'light'
|
||||
case 'dark':
|
||||
return determineDarkTheme
|
||||
}
|
||||
}
|
||||
|
||||
const ThemeManager: React.FC = ({ children }) => {
|
||||
const osTheme = useColorSchemeDelay()
|
||||
const userTheme = useSelector(getSettingsTheme)
|
||||
const currentMode =
|
||||
userTheme === 'auto' ? (osTheme as 'light' | 'dark') : userTheme
|
||||
const darkTheme = useSelector(getSettingsDarkTheme)
|
||||
|
||||
const [mode, setMode] = useState(currentMode)
|
||||
|
||||
const setTheme = (theme: 'light' | 'dark') => setMode(theme)
|
||||
const mode = useRef(userTheme === 'auto' ? osTheme || 'light' : userTheme)
|
||||
const [theme, setTheme] = useState<Theme>(
|
||||
determineTheme(osTheme, userTheme, darkTheme)
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
setMode(currentMode)
|
||||
}, [currentMode])
|
||||
mode.current = userTheme === 'auto' ? osTheme || 'light' : userTheme
|
||||
}, [userTheme])
|
||||
useEffect(() => {
|
||||
setTheme(determineTheme(osTheme, userTheme, darkTheme))
|
||||
}, [osTheme, userTheme, darkTheme])
|
||||
|
||||
return (
|
||||
<ManageThemeContext.Provider
|
||||
value={{
|
||||
mode: mode,
|
||||
theme: getTheme(mode),
|
||||
setTheme: setTheme
|
||||
mode: mode.current,
|
||||
theme,
|
||||
colors: getColors(theme)
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { DefaultTheme, DarkTheme } from '@react-navigation/native'
|
||||
|
||||
export type Theme = 'light' | 'dark_lighter' | 'dark_darker'
|
||||
|
||||
export type ColorDefinitions =
|
||||
| 'primaryDefault'
|
||||
| 'primaryOverlay'
|
||||
@ -20,82 +22,98 @@ export type ColorDefinitions =
|
||||
const themeColors: {
|
||||
[key in ColorDefinitions]: {
|
||||
light: string
|
||||
dark: string
|
||||
dark_lighter: string
|
||||
dark_darker: string
|
||||
}
|
||||
} = {
|
||||
primaryDefault: {
|
||||
light: 'rgb(18, 18, 18)',
|
||||
dark: 'rgb(180, 180, 180)'
|
||||
dark_lighter: 'rgb(250, 250, 250)',
|
||||
dark_darker: 'rgb(180, 180, 180)'
|
||||
},
|
||||
primaryOverlay: {
|
||||
light: 'rgb(250, 250, 250)',
|
||||
dark: 'rgb(200, 200, 200)'
|
||||
dark_lighter: 'rgb(200, 200, 200)',
|
||||
dark_darker: 'rgb(200, 200, 200)'
|
||||
},
|
||||
secondary: {
|
||||
light: 'rgb(135, 135, 135)',
|
||||
dark: 'rgb(130, 130, 130)'
|
||||
dark_lighter: 'rgb(160, 160, 160)',
|
||||
dark_darker: 'rgb(130, 130, 130)'
|
||||
},
|
||||
disabled: {
|
||||
light: 'rgb(200, 200, 200)',
|
||||
dark: 'rgb(66, 66, 66)'
|
||||
dark_lighter: 'rgb(120, 120, 120)',
|
||||
dark_darker: 'rgb(66, 66, 66)'
|
||||
},
|
||||
blue: {
|
||||
light: 'rgb(43, 144, 221)',
|
||||
dark: 'rgb(43, 144, 221)'
|
||||
dark_lighter: 'rgb(43, 144, 221)',
|
||||
dark_darker: 'rgb(43, 144, 221)'
|
||||
},
|
||||
red: {
|
||||
light: 'rgb(225, 45, 35)',
|
||||
dark: 'rgb(225, 78, 79)'
|
||||
dark_lighter: 'rgb(230, 60, 60)',
|
||||
dark_darker: 'rgb(225, 78, 79)'
|
||||
},
|
||||
green: {
|
||||
light: 'rgb(18, 158, 80)',
|
||||
dark: 'rgb(18, 158, 80)'
|
||||
dark_lighter: 'rgb(20, 185, 80)',
|
||||
dark_darker: 'rgb(18, 158, 80)'
|
||||
},
|
||||
yellow: {
|
||||
light: 'rgb(230, 166, 30)',
|
||||
dark: 'rgb(200, 145, 25)'
|
||||
dark_lighter: 'rgb(220, 160, 25)',
|
||||
dark_darker: 'rgb(200, 145, 25)'
|
||||
},
|
||||
|
||||
backgroundDefault: {
|
||||
light: 'rgb(250, 250, 250)',
|
||||
dark: 'rgb(18, 18, 18)'
|
||||
dark_lighter: 'rgb(44, 44, 44)',
|
||||
dark_darker: 'rgb(18, 18, 18)'
|
||||
},
|
||||
backgroundDefaultTransparent: {
|
||||
light: 'rgba(250, 250, 250, 0)',
|
||||
dark: 'rgba(18, 18, 18, 0)'
|
||||
dark_lighter: 'rgba(18, 18, 18, 0)',
|
||||
dark_darker: 'rgba(18, 18, 18, 0)'
|
||||
},
|
||||
backgroundOverlayDefault: {
|
||||
light: 'rgba(250, 250, 250, 0.5)',
|
||||
dark: 'rgba(0, 0, 0, 0.5)'
|
||||
dark_lighter: 'rgba(44, 44, 44, 0.5)',
|
||||
dark_darker: 'rgba(18, 18, 18, 0.5)'
|
||||
},
|
||||
backgroundOverlayInvert: {
|
||||
light: 'rgba(25, 25, 25, 0.5)',
|
||||
dark: 'rgba(0, 0, 0, 0.5)'
|
||||
dark_lighter: 'rgba(0, 0, 0, 0.5)',
|
||||
dark_darker: 'rgba(0, 0, 0, 0.5)'
|
||||
},
|
||||
|
||||
border: {
|
||||
light: 'rgba(25, 25, 25, 0.3)',
|
||||
dark: 'rgba(255, 255, 255, 0.3)'
|
||||
dark_lighter: 'rgba(255, 255, 255, 0.3)',
|
||||
dark_darker: 'rgba(255, 255, 255, 0.3)'
|
||||
},
|
||||
|
||||
shimmerDefault: {
|
||||
light: 'rgba(25, 25, 25, 0.05)',
|
||||
dark: 'rgba(250, 250, 250, 0.05)'
|
||||
dark_lighter: 'rgba(250, 250, 250, 0.05)',
|
||||
dark_darker: 'rgba(250, 250, 250, 0.05)'
|
||||
},
|
||||
shimmerHighlight: {
|
||||
light: 'rgba(25, 25, 25, 0.15)',
|
||||
dark: 'rgba(250, 250, 250, 0.15)'
|
||||
dark_lighter: 'rgba(250, 250, 250, 0.15)',
|
||||
dark_darker: 'rgba(250, 250, 250, 0.15)'
|
||||
}
|
||||
}
|
||||
|
||||
const getTheme = (mode: 'light' | 'dark') => {
|
||||
let Theme = {} as {
|
||||
const getColors = (theme: Theme): { [key in ColorDefinitions]: string } => {
|
||||
let colors = {} as {
|
||||
[key in ColorDefinitions]: string
|
||||
}
|
||||
const keys = Object.keys(themeColors) as ColorDefinitions[]
|
||||
keys.forEach(key => (Theme[key] = themeColors[key][mode]))
|
||||
keys.forEach(key => (colors[key] = themeColors[key][theme]))
|
||||
|
||||
return Theme
|
||||
return colors
|
||||
}
|
||||
|
||||
const themes = {
|
||||
@ -111,18 +129,30 @@ const themes = {
|
||||
notification: themeColors.red.light
|
||||
}
|
||||
},
|
||||
dark: {
|
||||
dark_lighter: {
|
||||
...DarkTheme,
|
||||
colors: {
|
||||
...DarkTheme.colors,
|
||||
primary: themeColors.primaryDefault.dark,
|
||||
background: themeColors.backgroundDefault.dark,
|
||||
card: themeColors.backgroundDefault.dark,
|
||||
text: themeColors.primaryDefault.dark,
|
||||
border: themeColors.border.dark,
|
||||
notification: themeColors.red.dark
|
||||
primary: themeColors.primaryDefault.dark_lighter,
|
||||
background: themeColors.backgroundDefault.dark_lighter,
|
||||
card: themeColors.backgroundDefault.dark_lighter,
|
||||
text: themeColors.primaryDefault.dark_lighter,
|
||||
border: themeColors.border.dark_lighter,
|
||||
notification: themeColors.red.dark_lighter
|
||||
}
|
||||
},
|
||||
dark_darker: {
|
||||
...DarkTheme,
|
||||
colors: {
|
||||
...DarkTheme.colors,
|
||||
primary: themeColors.primaryDefault.dark_darker,
|
||||
background: themeColors.backgroundDefault.dark_darker,
|
||||
card: themeColors.backgroundDefault.dark_darker,
|
||||
text: themeColors.primaryDefault.dark_darker,
|
||||
border: themeColors.border.dark_darker,
|
||||
notification: themeColors.red.dark_darker
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { themeColors, getTheme, themes }
|
||||
export { themeColors, getColors, themes }
|
||||
|
Reference in New Issue
Block a user