mirror of https://github.com/tooot-app/app
Theme done
This commit is contained in:
parent
24d0681c9e
commit
1493e20962
35
App.tsx
35
App.tsx
|
@ -28,22 +28,25 @@ setConsole({
|
|||
const App: React.FC = () => {
|
||||
return (
|
||||
<AppearanceProvider>
|
||||
<ThemeManager>
|
||||
<ReactQueryCacheProvider queryCache={queryCache}>
|
||||
<Provider store={store}>
|
||||
<PersistGate persistor={persistor}>
|
||||
{bootstrapped => {
|
||||
if (bootstrapped) {
|
||||
require('src/i18n/i18n')
|
||||
return <Index />
|
||||
} else {
|
||||
return <></>
|
||||
}
|
||||
}}
|
||||
</PersistGate>
|
||||
</Provider>
|
||||
</ReactQueryCacheProvider>
|
||||
</ThemeManager>
|
||||
<ReactQueryCacheProvider queryCache={queryCache}>
|
||||
<Provider store={store}>
|
||||
<PersistGate persistor={persistor}>
|
||||
{bootstrapped => {
|
||||
if (bootstrapped) {
|
||||
console.log('Bootstrapped!')
|
||||
require('src/i18n/i18n')
|
||||
return (
|
||||
<ThemeManager>
|
||||
<Index />
|
||||
</ThemeManager>
|
||||
)
|
||||
} else {
|
||||
return <></>
|
||||
}
|
||||
}}
|
||||
</PersistGate>
|
||||
</Provider>
|
||||
</ReactQueryCacheProvider>
|
||||
</AppearanceProvider>
|
||||
)
|
||||
}
|
||||
|
|
116
src/Index.tsx
116
src/Index.tsx
|
@ -4,6 +4,7 @@ import { NavigationContainer } from '@react-navigation/native'
|
|||
import { enableScreens } from 'react-native-screens'
|
||||
|
||||
import React from 'react'
|
||||
import { StatusBar } from 'react-native'
|
||||
import Toast from 'react-native-toast-message'
|
||||
import { Feather } from '@expo/vector-icons'
|
||||
|
||||
|
@ -30,65 +31,72 @@ export type RootStackParamList = {
|
|||
|
||||
export const Index: React.FC = () => {
|
||||
const { mode, theme } = useTheme()
|
||||
enum barStyle {
|
||||
light = 'dark-content',
|
||||
dark = 'light-content'
|
||||
}
|
||||
|
||||
return (
|
||||
<NavigationContainer theme={themes[mode]}>
|
||||
<Tab.Navigator
|
||||
screenOptions={({ route }) => ({
|
||||
tabBarIcon: ({ focused, color, size }) => {
|
||||
let name: string
|
||||
switch (route.name) {
|
||||
case 'Screen-Local':
|
||||
name = 'home'
|
||||
break
|
||||
case 'Screen-Public':
|
||||
name = 'globe'
|
||||
break
|
||||
case 'Screen-Post':
|
||||
name = 'plus'
|
||||
break
|
||||
case 'Screen-Notifications':
|
||||
name = 'bell'
|
||||
break
|
||||
case 'Screen-Me':
|
||||
name = focused ? 'smile' : 'meh'
|
||||
break
|
||||
default:
|
||||
name = 'alert-octagon'
|
||||
break
|
||||
}
|
||||
return <Feather name={name} size={size} color={color} />
|
||||
}
|
||||
})}
|
||||
tabBarOptions={{
|
||||
activeTintColor: theme.primary,
|
||||
inactiveTintColor: theme.secondary,
|
||||
showLabel: false
|
||||
}}
|
||||
>
|
||||
<Tab.Screen name='Screen-Local' component={ScreenLocal} />
|
||||
<Tab.Screen name='Screen-Public' component={ScreenPublic} />
|
||||
<Tab.Screen
|
||||
name='Screen-Post'
|
||||
listeners={({ navigation, route }) => ({
|
||||
tabPress: e => {
|
||||
e.preventDefault()
|
||||
navigation.navigate(getCurrentTab(navigation), {
|
||||
screen: 'Screen-Shared-Compose'
|
||||
})
|
||||
<>
|
||||
<StatusBar barStyle={barStyle[mode]} />
|
||||
<NavigationContainer theme={themes[mode]}>
|
||||
<Tab.Navigator
|
||||
screenOptions={({ route }) => ({
|
||||
tabBarIcon: ({ focused, color, size }) => {
|
||||
let name: string
|
||||
switch (route.name) {
|
||||
case 'Screen-Local':
|
||||
name = 'home'
|
||||
break
|
||||
case 'Screen-Public':
|
||||
name = 'globe'
|
||||
break
|
||||
case 'Screen-Post':
|
||||
name = 'plus'
|
||||
break
|
||||
case 'Screen-Notifications':
|
||||
name = 'bell'
|
||||
break
|
||||
case 'Screen-Me':
|
||||
name = focused ? 'smile' : 'meh'
|
||||
break
|
||||
default:
|
||||
name = 'alert-octagon'
|
||||
break
|
||||
}
|
||||
return <Feather name={name} size={size} color={color} />
|
||||
}
|
||||
})}
|
||||
tabBarOptions={{
|
||||
activeTintColor: theme.primary,
|
||||
inactiveTintColor: theme.secondary,
|
||||
showLabel: false
|
||||
}}
|
||||
>
|
||||
{() => <></>}
|
||||
</Tab.Screen>
|
||||
<Tab.Screen
|
||||
name='Screen-Notifications'
|
||||
component={ScreenNotifications}
|
||||
/>
|
||||
<Tab.Screen name='Screen-Me' component={ScreenMe} />
|
||||
</Tab.Navigator>
|
||||
<Tab.Screen name='Screen-Local' component={ScreenLocal} />
|
||||
<Tab.Screen name='Screen-Public' component={ScreenPublic} />
|
||||
<Tab.Screen
|
||||
name='Screen-Post'
|
||||
listeners={({ navigation, route }) => ({
|
||||
tabPress: e => {
|
||||
e.preventDefault()
|
||||
navigation.navigate(getCurrentTab(navigation), {
|
||||
screen: 'Screen-Shared-Compose'
|
||||
})
|
||||
}
|
||||
})}
|
||||
>
|
||||
{() => <></>}
|
||||
</Tab.Screen>
|
||||
<Tab.Screen
|
||||
name='Screen-Notifications'
|
||||
component={ScreenNotifications}
|
||||
/>
|
||||
<Tab.Screen name='Screen-Me' component={ScreenMe} />
|
||||
</Tab.Navigator>
|
||||
|
||||
<Toast ref={(ref: any) => Toast.setRef(ref)} config={toastConfig} />
|
||||
</NavigationContainer>
|
||||
<Toast ref={(ref: any) => Toast.setRef(ref)} config={toastConfig} />
|
||||
</NavigationContainer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ export interface Props {
|
|||
|
||||
const Timelines: React.FC<Props> = ({ name, content }) => {
|
||||
const navigation = useNavigation()
|
||||
const { theme } = useTheme()
|
||||
const { mode, theme } = useTheme()
|
||||
const localRegistered = useSelector(getLocalUrl)
|
||||
const publicDomain = useSelector(getRemoteUrl)
|
||||
const [segment, setSegment] = useState(0)
|
||||
|
@ -107,6 +107,7 @@ const Timelines: React.FC<Props> = ({ name, content }) => {
|
|||
headerCenter: () => (
|
||||
<View style={styles.segmentsContainer}>
|
||||
<SegmentedControl
|
||||
appearance={mode}
|
||||
values={[content[0].title, content[1].title]}
|
||||
selectedIndex={segment}
|
||||
onChange={onChangeSegment}
|
||||
|
|
|
@ -88,6 +88,7 @@ const TimelineDefault: React.FC<Props> = ({ item, queryKey }) => {
|
|||
emojis={actualStatus.account.emojis}
|
||||
account={actualStatus.account.acct}
|
||||
created_at={item.created_at}
|
||||
visibility={item.visibility}
|
||||
application={item.application}
|
||||
/>
|
||||
{/* Can pass toot info to next page to speed up performance */}
|
||||
|
|
|
@ -98,6 +98,7 @@ export interface Props {
|
|||
emojis?: Mastodon.Emoji[]
|
||||
account: string
|
||||
created_at: string
|
||||
visibility: Mastodon.Status['visibility']
|
||||
application?: Mastodon.Application
|
||||
}
|
||||
|
||||
|
@ -109,6 +110,7 @@ const HeaderDefault: React.FC<Props> = ({
|
|||
emojis,
|
||||
account,
|
||||
created_at,
|
||||
visibility,
|
||||
application
|
||||
}) => {
|
||||
const { theme } = useTheme()
|
||||
|
@ -197,6 +199,14 @@ const HeaderDefault: React.FC<Props> = ({
|
|||
{since}
|
||||
</Text>
|
||||
</View>
|
||||
{visibility === 'private' && (
|
||||
<Feather
|
||||
name='lock'
|
||||
size={constants.FONT_SIZE_S}
|
||||
color={theme.secondary}
|
||||
style={styles.visibility}
|
||||
/>
|
||||
)}
|
||||
{application && application.name !== 'Web' && (
|
||||
<View>
|
||||
<Text
|
||||
|
@ -292,12 +302,16 @@ const styles = StyleSheet.create({
|
|||
},
|
||||
meta: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginTop: constants.SPACING_XS,
|
||||
marginBottom: constants.SPACING_S
|
||||
},
|
||||
created_at: {
|
||||
fontSize: constants.FONT_SIZE_S
|
||||
},
|
||||
visibility: {
|
||||
marginLeft: constants.SPACING_S
|
||||
},
|
||||
application: {
|
||||
fontSize: constants.FONT_SIZE_S,
|
||||
marginLeft: constants.SPACING_S
|
||||
|
|
|
@ -9,9 +9,8 @@ import {
|
|||
getSettingsLanguage
|
||||
} from 'src/utils/slices/settingsSlice'
|
||||
import { store } from 'src/store'
|
||||
console.log(store.getState())
|
||||
|
||||
if (!getSettingsLanguage(store.getState())) {
|
||||
console.log('No default locale of app')
|
||||
const deviceLocal = Localization.locale
|
||||
if (deviceLocal.startsWith('zh')) {
|
||||
store.dispatch(changeLanguage('zh'))
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
export default {
|
||||
common: require('./common').default,
|
||||
settings: require('./settings').default
|
||||
|
||||
meRoot: require('./screens/meRoot').default,
|
||||
meConversations: require('./screens/meConversations').default,
|
||||
meBookmarks: require('./screens/meBookmarks').default,
|
||||
meFavourites: require('./screens/meFavourites').default,
|
||||
meLists: require('./screens/meLists').default,
|
||||
meListsList: require('./screens/meListsList').default,
|
||||
meSettings: require('./screens/meSettings').default
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
export default {
|
||||
buttons: {
|
||||
cancel: '取消'
|
||||
},
|
||||
headers: {
|
||||
local: {
|
||||
segments: {
|
||||
|
@ -12,20 +15,6 @@ export default {
|
|||
right: '外站嘟嘟'
|
||||
}
|
||||
},
|
||||
notifications: '我的通知',
|
||||
me: {
|
||||
root: '我的长毛象',
|
||||
conversations: '私信',
|
||||
bookmarks: '书签',
|
||||
favourites: '喜欢',
|
||||
lists: {
|
||||
root: '列表',
|
||||
list: '列表 {{list}}'
|
||||
},
|
||||
settings: {
|
||||
root: '设置',
|
||||
language: '语言'
|
||||
}
|
||||
}
|
||||
notifications: '我的通知'
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
export default {
|
||||
heading: '书签',
|
||||
content: {}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
export default {
|
||||
heading: '私信',
|
||||
content: {}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
export default {
|
||||
heading: '收藏',
|
||||
content: {}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
export default {
|
||||
heading: '列表',
|
||||
content: {}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
export default {
|
||||
heading: '列表 {{list}}',
|
||||
content: {}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
export default {
|
||||
heading: '我的长毛象',
|
||||
content: {
|
||||
login: {},
|
||||
collections: {
|
||||
conversations: '$t(meConversations:heading)',
|
||||
bookmarks: '$t(meBookmarks:heading)',
|
||||
favourites: '$t(meFavourites:heading)',
|
||||
lists: '$t(meLists:heading)'
|
||||
},
|
||||
settings: '$t(meSettings:heading)',
|
||||
logout: {
|
||||
button: '退出当前账号',
|
||||
alert: {
|
||||
title: '确认退出登录?',
|
||||
message: '退出登录后,需要重新认证账号',
|
||||
buttons: {
|
||||
logout: '退出登录',
|
||||
cancel: '$t(common:buttons.cancel)'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
export default {
|
||||
heading: '设置',
|
||||
content: {
|
||||
language: {
|
||||
heading: '切换语言',
|
||||
options: {
|
||||
zh: '简体中文',
|
||||
en: 'English',
|
||||
cancel: '$t(common:buttons.cancel)'
|
||||
}
|
||||
},
|
||||
theme: {
|
||||
heading: '颜色模式',
|
||||
options: {
|
||||
auto: '跟随系统',
|
||||
light: '浅色模式',
|
||||
dark: '深色模式',
|
||||
cancel: '$t(common:buttons.cancel)'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
export default {
|
||||
content: {
|
||||
language: {
|
||||
title: '切换语言',
|
||||
options: {
|
||||
zh: '简体中文',
|
||||
en: 'English'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -41,42 +41,42 @@ const ScreenMe: React.FC = () => {
|
|||
name='Screen-Me-Conversations'
|
||||
component={ScreenMeConversations}
|
||||
options={{
|
||||
headerTitle: t('headers.me.conversations')
|
||||
headerTitle: t('meConversations:heading')
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name='Screen-Me-Bookmarks'
|
||||
component={ScreenMeBookmarks}
|
||||
options={{
|
||||
headerTitle: t('headers.me.bookmarks')
|
||||
headerTitle: t('meBookmarks:heading')
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name='Screen-Me-Favourites'
|
||||
component={ScreenMeFavourites}
|
||||
options={{
|
||||
headerTitle: t('headers.me.favourites')
|
||||
headerTitle: t('meFavourites:heading')
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name='Screen-Me-Lists'
|
||||
component={ScreenMeLists}
|
||||
options={{
|
||||
headerTitle: t('headers.me.lists.root')
|
||||
headerTitle: t('meLists:heading')
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name='Screen-Me-Lists-List'
|
||||
component={ScreenMeListsList}
|
||||
options={({ route }: any) => ({
|
||||
headerTitle: t('headers.me.lists.list', { list: route.params.title })
|
||||
headerTitle: t('meListsList:heading', { list: route.params.title })
|
||||
})}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name='Screen-Me-Settings'
|
||||
component={ScreenMeSettings}
|
||||
options={{
|
||||
headerTitle: t('headers.me.settings.root')
|
||||
headerTitle: t('meSettings:heading')
|
||||
}}
|
||||
/>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import { getLocalUrl } from 'src/utils/slices/instancesSlice'
|
|||
|
||||
import Login from './Root/Login'
|
||||
import MyInfo from './Root/MyInfo'
|
||||
import MyCollections from './Root/MyCollections'
|
||||
import Collections from './Root/Collections'
|
||||
import Settings from './Root/Settings'
|
||||
import Logout from './Root/Logout'
|
||||
|
||||
|
@ -16,7 +16,7 @@ const ScreenMeRoot: React.FC = () => {
|
|||
return (
|
||||
<ScrollView>
|
||||
{localRegistered ? <MyInfo /> : <Login />}
|
||||
{localRegistered && <MyCollections />}
|
||||
{localRegistered && <Collections />}
|
||||
<Settings />
|
||||
{localRegistered && <Logout />}
|
||||
</ScrollView>
|
||||
|
|
|
@ -4,34 +4,34 @@ import { useTranslation } from 'react-i18next'
|
|||
|
||||
import { MenuContainer, MenuItem } from 'src/components/Menu'
|
||||
|
||||
const MyInfo: React.FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const Collections: React.FC = () => {
|
||||
const { t } = useTranslation('meRoot')
|
||||
const navigation = useNavigation()
|
||||
|
||||
return (
|
||||
<MenuContainer>
|
||||
<MenuItem
|
||||
iconFront='mail'
|
||||
title={t('headers.me.conversations')}
|
||||
title={t('content.collections.conversations')}
|
||||
onPress={() => navigation.navigate('Screen-Me-Conversations')}
|
||||
/>
|
||||
<MenuItem
|
||||
iconFront='bookmark'
|
||||
title={t('headers.me.bookmarks')}
|
||||
title={t('content.collections.bookmarks')}
|
||||
onPress={() => navigation.navigate('Screen-Me-Bookmarks')}
|
||||
/>
|
||||
<MenuItem
|
||||
iconFront='star'
|
||||
title={t('headers.me.favourites')}
|
||||
title={t('content.collections.favourites')}
|
||||
onPress={() => navigation.navigate('Screen-Me-Favourites')}
|
||||
/>
|
||||
<MenuItem
|
||||
iconFront='list'
|
||||
title={t('headers.me.lists.root')}
|
||||
title={t('content.collections.lists')}
|
||||
onPress={() => navigation.navigate('Screen-Me-Lists')}
|
||||
/>
|
||||
</MenuContainer>
|
||||
)
|
||||
}
|
||||
|
||||
export default MyInfo
|
||||
export default Collections
|
|
@ -5,17 +5,19 @@ import { updateLocal } from 'src/utils/slices/instancesSlice'
|
|||
import MenuButton from 'src/components/Menu/Button'
|
||||
import { MenuContainer } from 'src/components/Menu'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const Logout: React.FC = () => {
|
||||
const { t } = useTranslation('meRoot')
|
||||
const dispatch = useDispatch()
|
||||
const navigation = useNavigation()
|
||||
|
||||
const alertOption = {
|
||||
title: '确认退出登录?',
|
||||
message: '退出登录后,需要重新认证账号',
|
||||
title: t('content.logout.alert.title'),
|
||||
message: t('content.logout.alert.message'),
|
||||
buttons: [
|
||||
{
|
||||
text: '退出登录',
|
||||
text: t('content.logout.alert.buttons.logout'),
|
||||
style: 'destructive' as const,
|
||||
onPress: () => {
|
||||
dispatch(updateLocal({}))
|
||||
|
@ -26,7 +28,7 @@ const Logout: React.FC = () => {
|
|||
}
|
||||
},
|
||||
{
|
||||
text: '取消',
|
||||
text: t('content.logout.alert.buttons.cancel'),
|
||||
style: 'cancel' as const
|
||||
}
|
||||
]
|
||||
|
@ -35,7 +37,7 @@ const Logout: React.FC = () => {
|
|||
return (
|
||||
<MenuContainer>
|
||||
<MenuButton
|
||||
text='退出当前账号'
|
||||
text={t('content.logout.button')}
|
||||
destructive={true}
|
||||
alertOption={alertOption}
|
||||
/>
|
||||
|
|
|
@ -5,14 +5,14 @@ import { useTranslation } from 'react-i18next'
|
|||
import { MenuContainer, MenuItem } from 'src/components/Menu'
|
||||
|
||||
const Settings: React.FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const { t } = useTranslation('meRoot')
|
||||
const navigation = useNavigation()
|
||||
|
||||
return (
|
||||
<MenuContainer>
|
||||
<MenuItem
|
||||
iconFront='settings'
|
||||
title={t('headers.me.settings.root')}
|
||||
title={t('content.settings')}
|
||||
onPress={() => navigation.navigate('Screen-Me-Settings')}
|
||||
/>
|
||||
</MenuContainer>
|
||||
|
|
|
@ -6,28 +6,32 @@ import { useDispatch, useSelector } from 'react-redux'
|
|||
import { MenuContainer, MenuItem } from 'src/components/Menu'
|
||||
import {
|
||||
changeLanguage,
|
||||
getSettingsLanguage
|
||||
changeTheme,
|
||||
getSettingsLanguage,
|
||||
getSettingsTheme
|
||||
} from 'src/utils/slices/settingsSlice'
|
||||
import { useTheme } from 'src/utils/styles/ThemeManager'
|
||||
|
||||
const ScreenMeSettings: React.FC = () => {
|
||||
const { t, i18n } = useTranslation('settings')
|
||||
const language = useSelector(getSettingsLanguage)
|
||||
const { t, i18n } = useTranslation('meSettings')
|
||||
const { setTheme } = useTheme()
|
||||
const settingsLanguage = useSelector(getSettingsLanguage)
|
||||
const settingsTheme = useSelector(getSettingsTheme)
|
||||
const dispatch = useDispatch()
|
||||
console.log(i18n.language)
|
||||
|
||||
return (
|
||||
<MenuContainer marginTop={true}>
|
||||
<MenuItem
|
||||
title={t('content.language.title')}
|
||||
content={t(`settings:content.language.options.${language}`)}
|
||||
title={t('content.language.heading')}
|
||||
content={t(`content.language.options.${settingsLanguage}`)}
|
||||
iconBack='chevron-right'
|
||||
onPress={() =>
|
||||
ActionSheetIOS.showActionSheetWithOptions(
|
||||
{
|
||||
options: [
|
||||
t('settings:content.language.options.zh'),
|
||||
t('settings:content.language.options.en'),
|
||||
'取消'
|
||||
t('content.language.options.zh'),
|
||||
t('content.language.options.en'),
|
||||
t('content.language.options.cancel')
|
||||
],
|
||||
cancelButtonIndex: 2
|
||||
},
|
||||
|
@ -46,6 +50,39 @@ const ScreenMeSettings: React.FC = () => {
|
|||
)
|
||||
}
|
||||
/>
|
||||
<MenuItem
|
||||
title={t('content.theme.heading')}
|
||||
content={t(`content.theme.options.${settingsTheme}`)}
|
||||
iconBack='chevron-right'
|
||||
onPress={() =>
|
||||
ActionSheetIOS.showActionSheetWithOptions(
|
||||
{
|
||||
options: [
|
||||
t('content.theme.options.auto'),
|
||||
t('content.theme.options.light'),
|
||||
t('content.theme.options.dark'),
|
||||
t('content.theme.options.cancel')
|
||||
],
|
||||
cancelButtonIndex: 3
|
||||
},
|
||||
buttonIndex => {
|
||||
switch (buttonIndex) {
|
||||
case 0:
|
||||
dispatch(changeTheme('auto'))
|
||||
break
|
||||
case 1:
|
||||
dispatch(changeTheme('light'))
|
||||
setTheme('light')
|
||||
break
|
||||
case 2:
|
||||
dispatch(changeTheme('dark'))
|
||||
setTheme('dark')
|
||||
break
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
/>
|
||||
</MenuContainer>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -5,10 +5,12 @@ import { RootState } from 'src/store'
|
|||
|
||||
export type SettingsState = {
|
||||
language: 'zh' | 'en' | undefined
|
||||
theme: 'light' | 'dark' | 'auto'
|
||||
}
|
||||
|
||||
const initialState = {
|
||||
language: undefined
|
||||
language: undefined,
|
||||
theme: 'auto'
|
||||
}
|
||||
|
||||
// export const updateLocal = createAsyncThunk(
|
||||
|
@ -62,6 +64,12 @@ const settingsSlice = createSlice({
|
|||
action: PayloadAction<NonNullable<SettingsState['language']>>
|
||||
) => {
|
||||
state.language = action.payload
|
||||
},
|
||||
changeTheme: (
|
||||
state,
|
||||
action: PayloadAction<NonNullable<SettingsState['theme']>>
|
||||
) => {
|
||||
state.theme = action.payload
|
||||
}
|
||||
}
|
||||
// extraReducers: builder => {
|
||||
|
@ -72,6 +80,7 @@ const settingsSlice = createSlice({
|
|||
})
|
||||
|
||||
export const getSettingsLanguage = (state: RootState) => state.settings.language
|
||||
export const getSettingsTheme = (state: RootState) => state.settings.theme
|
||||
|
||||
export const { changeLanguage } = settingsSlice.actions
|
||||
export const { changeLanguage, changeTheme } = settingsSlice.actions
|
||||
export default settingsSlice.reducer
|
||||
|
|
|
@ -1,39 +1,44 @@
|
|||
import React, { createContext, useContext, useEffect, useState } from 'react'
|
||||
import { Appearance, useColorScheme } from 'react-native-appearance'
|
||||
import { Appearance } from 'react-native-appearance'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { ColorDefinitions, getTheme } from 'src/utils/styles/themes'
|
||||
import { getSettingsTheme } from '../slices/settingsSlice'
|
||||
|
||||
const osTheme = Appearance.getColorScheme() as 'light' | 'dark'
|
||||
|
||||
export const ManageThemeContext: React.Context<{
|
||||
type ContextType = {
|
||||
mode: 'light' | 'dark'
|
||||
theme: { [key in ColorDefinitions]: string }
|
||||
toggle: () => void
|
||||
}> = createContext({
|
||||
mode: osTheme,
|
||||
theme: getTheme(osTheme),
|
||||
toggle: () => {}
|
||||
setTheme: (theme: 'light' | 'dark') => void
|
||||
}
|
||||
|
||||
export const ManageThemeContext = createContext<ContextType>({
|
||||
mode: 'light',
|
||||
theme: getTheme('light'),
|
||||
setTheme: () => {}
|
||||
})
|
||||
|
||||
export const useTheme = () => useContext(ManageThemeContext)
|
||||
|
||||
const ThemeManager: React.FC = ({ children }) => {
|
||||
const [mode, setMode] = useState(osTheme)
|
||||
const systemTheme = useColorScheme()
|
||||
const userTheme = useSelector(getSettingsTheme)
|
||||
const currentMode =
|
||||
userTheme === 'auto'
|
||||
? (Appearance.getColorScheme() as 'light' | 'dark')
|
||||
: userTheme
|
||||
|
||||
const toggleTheme = () => {
|
||||
mode === 'light' ? setMode('dark') : setMode('light')
|
||||
}
|
||||
const [mode, setMode] = useState(currentMode)
|
||||
|
||||
const setTheme = (theme: 'light' | 'dark') => setMode(theme)
|
||||
|
||||
useEffect(() => {
|
||||
setMode(systemTheme === 'no-preference' ? 'light' : systemTheme)
|
||||
}, [systemTheme])
|
||||
setMode(currentMode)
|
||||
}, [currentMode])
|
||||
|
||||
return (
|
||||
<ManageThemeContext.Provider
|
||||
value={{
|
||||
mode: mode,
|
||||
theme: getTheme(mode),
|
||||
toggle: toggleTheme
|
||||
setTheme: setTheme
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
|
Loading…
Reference in New Issue