feat: allow users to override color scheme (closes #138)
This commit is contained in:
3
src/assets/icons/checkmark.svg
Normal file
3
src/assets/icons/checkmark.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="17" height="18" viewBox="0 0 17 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.36719 17.2363C6.78711 17.2363 7.11914 17.0508 7.35352 16.6895L16.582 2.1582C16.7578 1.875 16.8262 1.66016 16.8262 1.43555C16.8262 0.898438 16.4746 0.546875 15.9375 0.546875C15.5469 0.546875 15.332 0.673828 15.0977 1.04492L6.32812 15.0195L1.77734 9.0625C1.5332 8.7207 1.28906 8.58398 0.9375 8.58398C0.380859 8.58398 0 8.96484 0 9.50195C0 9.72656 0.0976562 9.98047 0.283203 10.2148L5.35156 16.6699C5.64453 17.0508 5.94727 17.2363 6.36719 17.2363Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 563 B |
@@ -1,18 +1,18 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { PropsWithChildren, useEffect } from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import TrackPlayer, { Capability } from 'react-native-track-player';
|
||||
import { PersistGate } from 'redux-persist/integration/react';
|
||||
import Routes from '../screens';
|
||||
import store, { persistedStore } from 'store';
|
||||
import store, { persistedStore, useTypedSelector } from 'store';
|
||||
import {
|
||||
NavigationContainer,
|
||||
DefaultTheme,
|
||||
DarkTheme as BaseDarkTheme,
|
||||
} from '@react-navigation/native';
|
||||
import { useColorScheme } from 'react-native';
|
||||
import { ColorSchemeContext, themes } from './Colors';
|
||||
import { ColorSchemeProvider, themes } from './Colors';
|
||||
import DownloadManager from './DownloadManager';
|
||||
// import ErrorReportingAlert from 'utility/ErrorReportingAlert';
|
||||
import { useColorScheme } from 'react-native';
|
||||
import { ColorScheme } from 'store/settings/types';
|
||||
|
||||
const LightTheme = {
|
||||
...DefaultTheme,
|
||||
@@ -30,14 +30,29 @@ const DarkTheme = {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a convenience wrapper for NavigationContainer that ensures that the
|
||||
* right theme is selected based on OS color scheme settings along with user preferences.
|
||||
*/
|
||||
function ThemedNavigationContainer({ children }: PropsWithChildren<{}>) {
|
||||
const systemScheme = useColorScheme();
|
||||
const userScheme = useTypedSelector((state) => state.settings.colorScheme);
|
||||
const scheme = userScheme === ColorScheme.System ? systemScheme : userScheme;
|
||||
|
||||
return (
|
||||
<NavigationContainer
|
||||
theme={scheme === 'dark' ? DarkTheme : LightTheme}
|
||||
>
|
||||
{children}
|
||||
</NavigationContainer>
|
||||
);
|
||||
}
|
||||
|
||||
// Track whether the player has already been setup, so that we don't
|
||||
// accidentally do it twice.
|
||||
let hasSetupPlayer = false;
|
||||
|
||||
export default function App(): JSX.Element {
|
||||
const colorScheme = useColorScheme();
|
||||
const theme = themes[colorScheme || 'light'];
|
||||
|
||||
useEffect(() => {
|
||||
async function setupTrackPlayer() {
|
||||
await TrackPlayer.setupPlayer();
|
||||
@@ -63,14 +78,12 @@ export default function App(): JSX.Element {
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<PersistGate loading={null} persistor={persistedStore}>
|
||||
<ColorSchemeContext.Provider value={theme}>
|
||||
<NavigationContainer
|
||||
theme={colorScheme === 'dark' ? DarkTheme : LightTheme}
|
||||
>
|
||||
<ColorSchemeProvider>
|
||||
<ThemedNavigationContainer>
|
||||
<Routes />
|
||||
<DownloadManager />
|
||||
</NavigationContainer>
|
||||
</ColorSchemeContext.Provider>
|
||||
</ThemedNavigationContainer>
|
||||
</ColorSchemeProvider>
|
||||
</PersistGate>
|
||||
</Provider>
|
||||
);
|
||||
|
||||
@@ -3,6 +3,8 @@ import { THEME_COLOR } from 'CONSTANTS';
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
import { useContext } from 'react';
|
||||
import { ColorSchemeName, Platform, StyleSheet, View, useColorScheme } from 'react-native';
|
||||
import { useTypedSelector } from 'store';
|
||||
import { ColorScheme } from 'store/settings/types';
|
||||
|
||||
const majorPlatformVersion = typeof Platform.Version === 'string' ? parseInt(Platform.Version, 10) : Platform.Version;
|
||||
|
||||
@@ -77,6 +79,22 @@ export const themes: Record<'dark' | 'light', ReturnType<typeof generateStyles>>
|
||||
// Create context for supplying the theming information
|
||||
export const ColorSchemeContext = React.createContext(themes.dark);
|
||||
|
||||
/**
|
||||
* This provider contains the logic for settings the right theme on the ColorSchemeContext.
|
||||
*/
|
||||
export function ColorSchemeProvider({ children }: PropsWithChildren<{}>) {
|
||||
const systemScheme = useColorScheme();
|
||||
const userScheme = useTypedSelector((state) => state.settings.colorScheme);
|
||||
const scheme = userScheme === ColorScheme.System ? systemScheme : userScheme;
|
||||
const theme = themes[scheme || 'light'];
|
||||
|
||||
return (
|
||||
<ColorSchemeContext.Provider value={theme}>
|
||||
{children}
|
||||
</ColorSchemeContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the default styles object in hook form
|
||||
*/
|
||||
@@ -98,13 +116,15 @@ export function DefaultStylesProvider(props: DefaultStylesProviderProps) {
|
||||
}
|
||||
|
||||
export function ColoredBlurView(props: PropsWithChildren<BlurViewProps>) {
|
||||
const scheme = useColorScheme();
|
||||
const systemScheme = useColorScheme();
|
||||
const userScheme = useTypedSelector((state) => state.settings.colorScheme);
|
||||
const scheme = userScheme === ColorScheme.System ? systemScheme : userScheme;
|
||||
|
||||
return Platform.OS === 'ios' ? (
|
||||
<BlurView
|
||||
{...props}
|
||||
blurType={Platform.OS === 'ios' && majorPlatformVersion >= 13
|
||||
? 'material'
|
||||
? scheme === 'dark' ? 'materialDark' : 'materialLight'
|
||||
: scheme === 'dark' ? 'extraDark' : 'xlight'
|
||||
} />
|
||||
) : (
|
||||
|
||||
@@ -63,5 +63,10 @@
|
||||
"total-duration": "Total duration",
|
||||
"similar-albums": "Similar albums",
|
||||
"playback-reporting": "Playback Reporting",
|
||||
"playback-reporting-description": "With Playback Reporting, all your playback events are relayed back to Jellyfin. This allows you to track your most listened songs, particularly with Jellyfin plugins such as ListenBrainz."
|
||||
"playback-reporting-description": "With Playback Reporting, all your playback events are relayed back to Jellyfin. This allows you to track your most listened songs, particularly with Jellyfin plugins such as ListenBrainz.",
|
||||
"color-scheme": "Color Scheme",
|
||||
"color-scheme-description": "By default, Fintunes will follow your operating system's color scheme. You can however choose to override this to make sure Fintunes is always in dark mode or light mode.",
|
||||
"color-scheme-system": "System",
|
||||
"color-scheme-light": "Light Mode",
|
||||
"color-scheme-dark": "Dark Mode"
|
||||
}
|
||||
@@ -61,4 +61,9 @@ export type LocaleKeys = 'play-next'
|
||||
| 'total-duration'
|
||||
| 'similar-albums'
|
||||
| 'playback-reporting'
|
||||
| 'playback-reporting-description'
|
||||
| 'playback-reporting-description'
|
||||
| 'color-scheme'
|
||||
| 'color-scheme-description'
|
||||
| 'color-scheme-system'
|
||||
| 'color-scheme-light'
|
||||
| 'color-scheme-dark'
|
||||
8
src/screens/Settings/components/Container.tsx
Normal file
8
src/screens/Settings/components/Container.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import { SafeScrollView } from 'components/SafeNavigatorView';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Container = styled(SafeScrollView)`
|
||||
padding: 24px;
|
||||
`;
|
||||
|
||||
export default Container;
|
||||
11
src/screens/Settings/components/Input.tsx
Normal file
11
src/screens/Settings/components/Input.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import styled from 'styled-components/native';
|
||||
|
||||
export const InputContainer = styled.View`
|
||||
margin: 10px 0;
|
||||
`;
|
||||
|
||||
export const Input = styled.TextInput`
|
||||
padding: 15px;
|
||||
margin-top: 5px;
|
||||
border-radius: 5px;
|
||||
`;
|
||||
61
src/screens/Settings/components/Radio.tsx
Normal file
61
src/screens/Settings/components/Radio.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import styled from 'styled-components/native';
|
||||
import CheckmarkIcon from 'assets/icons/checkmark.svg';
|
||||
import { Text } from 'components/Typography';
|
||||
import useDefaultStyles from 'components/Colors';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import { Gap } from 'components/Utility';
|
||||
import { View } from 'react-native';
|
||||
|
||||
export const RadioList = styled.View`
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const RadioItemContainer = styled.Pressable<{ checked?: boolean }>`
|
||||
padding: 16px 24px 16px 16px;
|
||||
border-bottom: 1px solid #444;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
export interface RadioItemProps<T> {
|
||||
checked?: boolean;
|
||||
label?: string;
|
||||
value: T;
|
||||
onPress: (value: T) => void;
|
||||
last?: boolean;
|
||||
}
|
||||
|
||||
export function RadioItem<T>({
|
||||
checked,
|
||||
label,
|
||||
value,
|
||||
onPress,
|
||||
last
|
||||
}: RadioItemProps<T>) {
|
||||
const defaultStyles = useDefaultStyles();
|
||||
|
||||
const handlePress = useCallback(() => {
|
||||
onPress(value);
|
||||
}, [onPress, value]);
|
||||
|
||||
return (
|
||||
<View style={!last ? { borderBottomWidth: 1, borderBottomColor: defaultStyles.divider.backgroundColor } : undefined}>
|
||||
<RadioItemContainer
|
||||
onPress={handlePress}
|
||||
style={({ pressed }) => [
|
||||
{ backgroundColor: pressed
|
||||
? defaultStyles.activeBackground.backgroundColor
|
||||
: defaultStyles.button.backgroundColor
|
||||
}
|
||||
]}
|
||||
>
|
||||
{checked ? <CheckmarkIcon fill={THEME_COLOR} height={14} width={14} /> : <Gap size={14} />}
|
||||
<Gap size={8} />
|
||||
<Text style={checked && { color: THEME_COLOR }}>{label}</Text>
|
||||
</RadioItemContainer>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
13
src/screens/Settings/components/Switch.tsx
Normal file
13
src/screens/Settings/components/Switch.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Text } from 'components/Typography';
|
||||
import styled from 'styled-components/native';
|
||||
|
||||
export const SwitchContainer = styled.View`
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin: 16px 0;
|
||||
`;
|
||||
|
||||
export const SwitchLabel = styled(Text)`
|
||||
font-size: 16px;
|
||||
`;
|
||||
@@ -1,17 +1,20 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import Library from './components/Library';
|
||||
import Cache from './components/Cache';
|
||||
import useDefaultStyles, { ColoredBlurView } from 'components/Colors';
|
||||
import { t } from '@localisation';
|
||||
import { createStackNavigator } from '@react-navigation/stack';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import ListButton from 'components/ListButton';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import Sentry from './components/Sentry';
|
||||
import ListButton from 'components/ListButton';
|
||||
import useDefaultStyles, { ColoredBlurView } from 'components/Colors';
|
||||
|
||||
import { SettingsNavigationProp } from './types';
|
||||
|
||||
import Cache from './stacks/Cache';
|
||||
import Sentry from './stacks/Sentry';
|
||||
import Library from './stacks/Library';
|
||||
import ColorScheme from './stacks/ColorScheme';
|
||||
import PlaybackReporting from './stacks/PlaybackReporting';
|
||||
import { SafeScrollView } from 'components/SafeNavigatorView';
|
||||
import PlaybackReporting from './components/PlaybackReporting';
|
||||
|
||||
export function SettingsList() {
|
||||
const navigation = useNavigation<SettingsNavigationProp>();
|
||||
@@ -19,6 +22,7 @@ export function SettingsList() {
|
||||
const handleCacheClick = useCallback(() => { navigation.navigate('Cache'); }, [navigation]);
|
||||
const handleSentryClick = useCallback(() => { navigation.navigate('Sentry'); }, [navigation]);
|
||||
const handlePlaybackReportingClick = useCallback(() => { navigation.navigate('Playback Reporting'); }, [navigation]);
|
||||
const handleColorSchemeClick = useCallback(() => { navigation.navigate('Color Scheme'); }, [navigation]);
|
||||
|
||||
return (
|
||||
<SafeScrollView>
|
||||
@@ -26,6 +30,7 @@ export function SettingsList() {
|
||||
<ListButton onPress={handleCacheClick}>{t('setting-cache')}</ListButton>
|
||||
<ListButton onPress={handleSentryClick}>{t('error-reporting')}</ListButton>
|
||||
<ListButton onPress={handlePlaybackReportingClick}>{t('playback-reporting')}</ListButton>
|
||||
<ListButton onPress={handleColorSchemeClick}>{t('color-scheme')}</ListButton>
|
||||
</SafeScrollView>
|
||||
);
|
||||
}
|
||||
@@ -47,6 +52,7 @@ export default function Settings() {
|
||||
<Stack.Screen name="Cache" component={Cache} options={{ headerTitle: t('setting-cache') }} />
|
||||
<Stack.Screen name="Sentry" component={Sentry} options={{ headerTitle: t('error-reporting') }} />
|
||||
<Stack.Screen name="Playback Reporting" component={PlaybackReporting} options={{ headerTitle: t('playback-reporting')}} />
|
||||
<Stack.Screen name="Color Scheme" component={ColorScheme} options={{ headerTitle: t('color-scheme')}} />
|
||||
</Stack.Navigator>
|
||||
);
|
||||
}
|
||||
@@ -6,16 +6,12 @@ import Button from 'components/Button';
|
||||
import styled from 'styled-components/native';
|
||||
import { Paragraph } from 'components/Typography';
|
||||
import { useAppDispatch } from 'store';
|
||||
import { SafeScrollView } from 'components/SafeNavigatorView';
|
||||
import Container from '../components/Container';
|
||||
|
||||
const ClearCache = styled(Button)`
|
||||
margin-top: 16px;
|
||||
`;
|
||||
|
||||
const Container = styled(SafeScrollView)`
|
||||
padding: 24px;
|
||||
`;
|
||||
|
||||
export default function CacheSettings() {
|
||||
const dispatch = useAppDispatch();
|
||||
const handleClearCache = useCallback(() => {
|
||||
45
src/screens/Settings/stacks/ColorScheme.tsx
Normal file
45
src/screens/Settings/stacks/ColorScheme.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { Paragraph } from 'components/Typography';
|
||||
import Container from '../components/Container';
|
||||
import { t } from '@localisation';
|
||||
import { RadioItem, RadioList } from '../components/Radio';
|
||||
import { ColorScheme } from 'store/settings/types';
|
||||
import { useAppDispatch, useTypedSelector } from 'store';
|
||||
import { setColorScheme } from 'store/settings/actions';
|
||||
|
||||
export default function ColorSchemeSetting() {
|
||||
const dispatch = useAppDispatch();
|
||||
const scheme = useTypedSelector((state) => state.settings.colorScheme);
|
||||
|
||||
const handlePress = useCallback((value: ColorScheme) => {
|
||||
dispatch(setColorScheme(value));
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Paragraph>{t('color-scheme-description')}</Paragraph>
|
||||
<Paragraph />
|
||||
<RadioList>
|
||||
<RadioItem
|
||||
label={t('color-scheme-system')}
|
||||
value={ColorScheme.System}
|
||||
onPress={handlePress}
|
||||
checked={scheme === ColorScheme.System}
|
||||
/>
|
||||
<RadioItem
|
||||
label={t('color-scheme-light')}
|
||||
value={ColorScheme.Light}
|
||||
onPress={handlePress}
|
||||
checked={scheme === ColorScheme.Light}
|
||||
/>
|
||||
<RadioItem
|
||||
label={t('color-scheme-dark')}
|
||||
value={ColorScheme.Dark}
|
||||
onPress={handlePress}
|
||||
checked={scheme === ColorScheme.Dark}
|
||||
last
|
||||
/>
|
||||
</RadioList>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import styled from 'styled-components/native';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import React, { useCallback } from 'react';
|
||||
import useDefaultStyles from 'components/Colors';
|
||||
@@ -7,21 +6,8 @@ import { useTypedSelector } from 'store';
|
||||
import { t } from '@localisation';
|
||||
import Button from 'components/Button';
|
||||
import { Paragraph } from 'components/Typography';
|
||||
import { SafeScrollView } from 'components/SafeNavigatorView';
|
||||
|
||||
const InputContainer = styled.View`
|
||||
margin: 10px 0;
|
||||
`;
|
||||
|
||||
const Input = styled.TextInput`
|
||||
padding: 15px;
|
||||
margin-top: 5px;
|
||||
border-radius: 5px;
|
||||
`;
|
||||
|
||||
const Container = styled(SafeScrollView)`
|
||||
padding: 24px;
|
||||
`;
|
||||
import Container from '../components/Container';
|
||||
import { InputContainer, Input } from '../components/Input';
|
||||
|
||||
export default function LibrarySettings() {
|
||||
const defaultStyles = useDefaultStyles();
|
||||
@@ -1,26 +1,12 @@
|
||||
import { Paragraph, Text } from 'components/Typography';
|
||||
import { Paragraph } from 'components/Typography';
|
||||
import React, { useCallback } from 'react';
|
||||
import { Switch } from 'react-native-gesture-handler';
|
||||
import styled from 'styled-components/native';
|
||||
import { t } from '@localisation';
|
||||
import { SafeScrollView } from 'components/SafeNavigatorView';
|
||||
import { useAppDispatch, useTypedSelector } from 'store';
|
||||
import { setEnablePlaybackReporting } from 'store/settings/actions';
|
||||
|
||||
const Container = styled.View`
|
||||
padding: 24px;
|
||||
`;
|
||||
|
||||
const SwitchContainer = styled.View`
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin: 16px 0;
|
||||
`;
|
||||
|
||||
const Label = styled(Text)`
|
||||
font-size: 16px;
|
||||
`;
|
||||
import Container from '../components/Container';
|
||||
import { SwitchContainer, SwitchLabel } from '../components/Switch';
|
||||
|
||||
export default function PlaybackReporting() {
|
||||
const isEnabled = useTypedSelector((state) => state.settings.enablePlaybackReporting);
|
||||
@@ -35,7 +21,7 @@ export default function PlaybackReporting() {
|
||||
<Container>
|
||||
<Paragraph>{t('playback-reporting-description')}</Paragraph>
|
||||
<SwitchContainer>
|
||||
<Label>{t('playback-reporting')}</Label>
|
||||
<SwitchLabel>{t('playback-reporting')}</SwitchLabel>
|
||||
<Switch value={isEnabled} onValueChange={toggleSwitch} />
|
||||
</SwitchContainer>
|
||||
</Container>
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import Modal from 'components/Modal';
|
||||
import Sentry from 'screens/Settings/components/Sentry';
|
||||
import Sentry from 'screens/Settings/stacks/Sentry';
|
||||
|
||||
export default function ErrorReportingPopup() {
|
||||
return (
|
||||
|
||||
@@ -8,6 +8,7 @@ import settings from './settings';
|
||||
import music, { initialState as musicInitialState } from './music';
|
||||
import downloads, { initialState as downloadsInitialState } from './downloads';
|
||||
import { PersistState } from 'redux-persist/es/types';
|
||||
import { ColorScheme } from './settings/types';
|
||||
|
||||
const persistConfig: PersistConfig<Omit<AppState, '_persist'>> = {
|
||||
key: 'root',
|
||||
@@ -44,6 +45,16 @@ const persistConfig: PersistConfig<Omit<AppState, '_persist'>> = {
|
||||
}
|
||||
};
|
||||
},
|
||||
// @ts-expect-error migrations are poorly typed
|
||||
4: (state: AppState) => {
|
||||
return {
|
||||
...state,
|
||||
settings: {
|
||||
...state.settings,
|
||||
colorScheme: ColorScheme.System,
|
||||
}
|
||||
};
|
||||
},
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { createAction } from '@reduxjs/toolkit';
|
||||
import { ColorScheme } from './types';
|
||||
|
||||
export const setJellyfinCredentials = createAction<{ access_token: string, user_id: string, uri: string, device_id: string; }>('SET_JELLYFIN_CREDENTIALS');
|
||||
export const setBitrate = createAction<number>('SET_BITRATE');
|
||||
export const setOnboardingStatus = createAction<boolean>('SET_ONBOARDING_STATUS');
|
||||
export const setReceivedErrorReportingAlert = createAction<void>('SET_RECEIVED_ERROR_REPORTING_ALERT');
|
||||
export const setEnablePlaybackReporting = createAction<boolean>('SET_ENABLE_PLAYBACK_REPORTING');
|
||||
export const setEnablePlaybackReporting = createAction<boolean>('SET_ENABLE_PLAYBACK_REPORTING');
|
||||
export const setColorScheme = createAction<ColorScheme>('SET_COLOR_SCHEME');
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createReducer } from '@reduxjs/toolkit';
|
||||
import { setReceivedErrorReportingAlert, setBitrate, setJellyfinCredentials, setOnboardingStatus, setEnablePlaybackReporting } from './actions';
|
||||
import { setReceivedErrorReportingAlert, setBitrate, setJellyfinCredentials, setOnboardingStatus, setEnablePlaybackReporting, setColorScheme } from './actions';
|
||||
import { ColorScheme } from './types';
|
||||
|
||||
interface State {
|
||||
jellyfin?: {
|
||||
@@ -12,6 +13,7 @@ interface State {
|
||||
isOnboardingComplete: boolean;
|
||||
hasReceivedErrorReportingAlert: boolean;
|
||||
enablePlaybackReporting: boolean;
|
||||
colorScheme: ColorScheme;
|
||||
}
|
||||
|
||||
const initialState: State = {
|
||||
@@ -19,6 +21,7 @@ const initialState: State = {
|
||||
isOnboardingComplete: false,
|
||||
hasReceivedErrorReportingAlert: false,
|
||||
enablePlaybackReporting: true,
|
||||
colorScheme: ColorScheme.System,
|
||||
};
|
||||
|
||||
const settings = createReducer(initialState, builder => {
|
||||
@@ -42,6 +45,10 @@ const settings = createReducer(initialState, builder => {
|
||||
...state,
|
||||
enablePlaybackReporting: action.payload,
|
||||
}));
|
||||
builder.addCase(setColorScheme, (state, action) => ({
|
||||
...state,
|
||||
colorScheme: action.payload,
|
||||
}));
|
||||
});
|
||||
|
||||
export default settings;
|
||||
5
src/store/settings/types.ts
Normal file
5
src/store/settings/types.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export enum ColorScheme {
|
||||
System = 'system',
|
||||
Light = 'light',
|
||||
Dark = 'dark',
|
||||
}
|
||||
Reference in New Issue
Block a user