Fix theme toggle

This commit is contained in:
2024-10-10 22:51:51 +02:00
parent d29d402cf3
commit a8629bc793
7 changed files with 178 additions and 156 deletions

View File

@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import React from 'react';
import {
MantineProvider,
createTheme,
ColorSchemeScript,
AppShell,
Container,
} from '@mantine/core';
@@ -15,44 +15,42 @@ import '@mantine/core/styles.css';
import '@mantine/notifications/styles.css';
import './App.scss';
const mantineTheme = createTheme({
/** You can add your Mantine theme overrides here */
});
function AppContent() {
const { settings, loading } = useSettings();
const [opened, setOpened] = useState(false);
const { loading } = useSettings();
if (loading) {
return <div>Loading...</div>;
}
return (
<MantineProvider theme={mantineTheme} defaultColorScheme={settings.theme}>
<Notifications />
<ModalsProvider>
<AppShell header={{ height: 60 }} padding="md">
<AppShell.Header>
<Header />
</AppShell.Header>
<AppShell.Main>
<Container size="xl">
<MainContent />
</Container>
</AppShell.Main>
</AppShell>
</ModalsProvider>
</MantineProvider>
<AppShell header={{ height: 60 }} padding="md">
<AppShell.Header>
<Header />
</AppShell.Header>
<AppShell.Main>
<Container size="xl">
<MainContent />
</Container>
</AppShell.Main>
</AppShell>
);
}
function App() {
return (
<SettingsProvider>
<ModalProvider>
<AppContent />
</ModalProvider>
</SettingsProvider>
<>
<ColorSchemeScript defaultColorScheme="light" />
<MantineProvider defaultColorScheme="light">
<Notifications />
<ModalsProvider>
<SettingsProvider>
<ModalProvider>
<AppContent />
</ModalProvider>
</SettingsProvider>
</ModalsProvider>
</MantineProvider>
</>
);
}

View File

@@ -121,12 +121,14 @@ const MainContent = () => {
{renderBreadcrumbs()}
<Tabs value={activeTab} onChange={handleTabChange}>
<Tabs.List>
<Tabs.Tab value="source" leftSection={<IconCode size="0.8rem" />}>
Source
</Tabs.Tab>
<Tabs.Tab value="preview" leftSection={<IconEye size="0.8rem" />}>
Preview
</Tabs.Tab>
<Tabs.Tab
value="source"
leftSection={<IconCode size="0.8rem" />}
/>
<Tabs.Tab
value="preview"
leftSection={<IconEye size="0.8rem" />}
/>
</Tabs.List>
</Tabs>
</Flex>

View File

@@ -1,6 +1,5 @@
import React, { useReducer, useEffect, useCallback, useRef } from 'react';
import { Modal, Badge, Button, Group } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import { Modal, Badge, Button, Group, Title } from '@mantine/core';
import { notifications } from '@mantine/notifications';
import { useSettings } from '../contexts/SettingsContext';
import AppearanceSettings from './settings/AppearanceSettings';
@@ -51,11 +50,10 @@ function settingsReducer(state, action) {
}
const Settings = () => {
const { settings, updateSettings, updateTheme } = useSettings();
const { settings, updateSettings, colorScheme } = useSettings();
const { settingsModalVisible, setSettingsModalVisible } = useModalContext();
const [state, dispatch] = useReducer(settingsReducer, initialState);
const isInitialMount = useRef(true);
const updateThemeTimeoutRef = useRef(null);
useEffect(() => {
if (isInitialMount.current) {
@@ -64,22 +62,17 @@ const Settings = () => {
}
}, [settings]);
useEffect(() => {
dispatch({
type: 'UPDATE_LOCAL_SETTINGS',
payload: { theme: colorScheme },
});
}, [colorScheme]);
const handleInputChange = useCallback((key, value) => {
dispatch({ type: 'UPDATE_LOCAL_SETTINGS', payload: { [key]: value } });
}, []);
const handleThemeChange = useCallback(() => {
const newTheme = state.localSettings.theme === 'dark' ? 'light' : 'dark';
dispatch({ type: 'UPDATE_LOCAL_SETTINGS', payload: { theme: newTheme } });
if (updateThemeTimeoutRef.current) {
clearTimeout(updateThemeTimeoutRef.current);
}
updateThemeTimeoutRef.current = setTimeout(() => {
updateTheme(newTheme);
}, 0);
}, [state.localSettings.theme, updateTheme]);
const handleSubmit = async () => {
try {
await updateSettings(state.localSettings);
@@ -100,30 +93,16 @@ const Settings = () => {
const handleClose = useCallback(() => {
if (state.hasUnsavedChanges) {
updateTheme(state.initialSettings.theme);
dispatch({ type: 'RESET' });
}
setSettingsModalVisible(false);
}, [
state.hasUnsavedChanges,
state.initialSettings.theme,
updateTheme,
setSettingsModalVisible,
]);
useEffect(() => {
return () => {
if (updateThemeTimeoutRef.current) {
clearTimeout(updateThemeTimeoutRef.current);
}
};
}, []);
}, [state.hasUnsavedChanges, setSettingsModalVisible]);
return (
<Modal
opened={settingsModalVisible}
onClose={handleClose}
title="Settings"
title={<Title order={2}>Settings</Title>}
centered
size="lg"
>
@@ -134,7 +113,7 @@ const Settings = () => {
)}
<AppearanceSettings
themeSettings={state.localSettings.theme}
onThemeChange={handleThemeChange}
onThemeChange={(newTheme) => handleInputChange('theme', newTheme)}
/>
<EditorSettings
autoSave={state.localSettings.autoSave}

View File

@@ -1,18 +1,25 @@
import React from 'react';
import { Text, Switch, Stack } from '@mantine/core';
import { Text, Switch, Group, Box, Title } from '@mantine/core';
import { useSettings } from '../../contexts/SettingsContext';
const AppearanceSettings = ({ onThemeChange }) => {
const { colorScheme, toggleColorScheme } = useSettings();
const handleThemeChange = () => {
toggleColorScheme();
onThemeChange(colorScheme === 'dark' ? 'light' : 'dark');
};
const AppearanceSettings = ({ themeSettings, onThemeChange }) => {
return (
<Stack spacing="xs">
<Text fw={500} size="lg">
<Box mb="md">
<Title order={3} mb="md">
Appearance
</Text>
<Switch
label="Dark Mode"
checked={themeSettings === 'dark'}
onChange={onThemeChange}
/>
</Stack>
</Title>
<Group justify="space-between" align="center">
<Text size="sm">Dark Mode</Text>
<Switch checked={colorScheme === 'dark'} onChange={handleThemeChange} />
</Group>
</Box>
);
};

View File

@@ -1,21 +1,23 @@
import React from 'react';
import { Text, Switch, Stack, Tooltip } from '@mantine/core';
import { Text, Switch, Tooltip, Group, Box, Title } from '@mantine/core';
const EditorSettings = ({ autoSave, onAutoSaveChange }) => {
return (
<Stack spacing="xs" mt="md">
<Text fw={500} size="lg">
<Box mb="md">
<Title order={3} mb="md">
Editor
</Text>
</Title>
<Tooltip label="Auto Save feature is coming soon!" position="left">
<Switch
label="Auto Save"
checked={autoSave}
onChange={(event) => onAutoSaveChange(event.currentTarget.checked)}
disabled
/>
<Group justify="space-between" align="center">
<Text size="sm">Auto Save</Text>
<Switch
checked={autoSave}
onChange={(event) => onAutoSaveChange(event.currentTarget.checked)}
disabled
/>
</Group>
</Tooltip>
</Stack>
</Box>
);
};

View File

@@ -1,5 +1,14 @@
import React from 'react';
import { Text, Switch, TextInput, Stack, PasswordInput } from '@mantine/core';
import {
Text,
Switch,
TextInput,
Stack,
PasswordInput,
Group,
Box,
Title,
} from '@mantine/core';
const GitSettings = ({
gitEnabled,
@@ -11,55 +20,79 @@ const GitSettings = ({
onInputChange,
}) => {
return (
<Stack spacing="xs" mt="md">
<Text fw={500} size="lg">
Git Integration
</Text>
<Switch
label="Enable Git"
checked={gitEnabled}
onChange={(event) =>
onInputChange('gitEnabled', event.currentTarget.checked)
}
/>
<TextInput
label="Git URL"
value={gitUrl}
onChange={(event) => onInputChange('gitUrl', event.currentTarget.value)}
disabled={!gitEnabled}
/>
<TextInput
label="Git Username"
value={gitUser}
onChange={(event) =>
onInputChange('gitUser', event.currentTarget.value)
}
disabled={!gitEnabled}
/>
<PasswordInput
label="Git Token"
value={gitToken}
onChange={(event) =>
onInputChange('gitToken', event.currentTarget.value)
}
disabled={!gitEnabled}
/>
<Switch
label="Auto Commit"
checked={gitAutoCommit}
onChange={(event) =>
onInputChange('gitAutoCommit', event.currentTarget.checked)
}
disabled={!gitEnabled}
/>
<TextInput
label="Commit Message Template"
value={gitCommitMsgTemplate}
onChange={(event) =>
onInputChange('gitCommitMsgTemplate', event.currentTarget.value)
}
disabled={!gitEnabled}
/>
<Stack spacing="md">
<Title order={3}>Git Integration</Title>
<Group justify="space-between" align="center">
<Text size="sm">Enable Git</Text>
<Switch
checked={gitEnabled}
onChange={(event) =>
onInputChange('gitEnabled', event.currentTarget.checked)
}
/>
</Group>
<Box>
<Text size="sm" mb="xs">
Git URL
</Text>
<TextInput
value={gitUrl}
onChange={(event) =>
onInputChange('gitUrl', event.currentTarget.value)
}
disabled={!gitEnabled}
placeholder="Enter Git URL"
/>
</Box>
<Box>
<Text size="sm" mb="xs">
Git Username
</Text>
<TextInput
value={gitUser}
onChange={(event) =>
onInputChange('gitUser', event.currentTarget.value)
}
disabled={!gitEnabled}
placeholder="Enter Git username"
/>
</Box>
<Box>
<Text size="sm" mb="xs">
Git Token
</Text>
<PasswordInput
value={gitToken}
onChange={(event) =>
onInputChange('gitToken', event.currentTarget.value)
}
disabled={!gitEnabled}
placeholder="Enter Git token"
/>
</Box>
<Group justify="space-between" align="center">
<Text size="sm">Auto Commit</Text>
<Switch
checked={gitAutoCommit}
onChange={(event) =>
onInputChange('gitAutoCommit', event.currentTarget.checked)
}
disabled={!gitEnabled}
/>
</Group>
<Box>
<Text size="sm" mb="xs">
Commit Message Template
</Text>
<TextInput
value={gitCommitMsgTemplate}
onChange={(event) =>
onInputChange('gitCommitMsgTemplate', event.currentTarget.value)
}
disabled={!gitEnabled}
placeholder="Enter commit message template"
/>
</Box>
</Stack>
);
};

View File

@@ -1,10 +1,5 @@
import React, {
createContext,
useState,
useContext,
useEffect,
useMemo,
} from 'react';
import React, { createContext, useContext, useEffect, useMemo } from 'react';
import { useMantineColorScheme } from '@mantine/core';
import { fetchUserSettings, saveUserSettings } from '../services/api';
import { DEFAULT_SETTINGS } from '../utils/constants';
@@ -13,14 +8,16 @@ const SettingsContext = createContext();
export const useSettings = () => useContext(SettingsContext);
export const SettingsProvider = ({ children }) => {
const [settings, setSettings] = useState(DEFAULT_SETTINGS);
const [loading, setLoading] = useState(true);
const { colorScheme, setColorScheme } = useMantineColorScheme();
const [settings, setSettings] = React.useState(DEFAULT_SETTINGS);
const [loading, setLoading] = React.useState(true);
useEffect(() => {
const loadSettings = async () => {
try {
const userSettings = await fetchUserSettings(1);
setSettings(userSettings.settings);
setColorScheme(userSettings.settings.theme);
} catch (error) {
console.error('Failed to load user settings:', error);
} finally {
@@ -29,7 +26,7 @@ export const SettingsProvider = ({ children }) => {
};
loadSettings();
}, []);
}, [setColorScheme]);
const updateSettings = async (newSettings) => {
try {
@@ -38,27 +35,31 @@ export const SettingsProvider = ({ children }) => {
settings: newSettings,
});
setSettings(newSettings);
// Ensure the color scheme is updated when settings are saved
if (newSettings.theme) {
setColorScheme(newSettings.theme);
}
} catch (error) {
console.error('Failed to save settings:', error);
throw error;
}
};
const updateTheme = (newTheme) => {
setSettings((prevSettings) => ({
...prevSettings,
theme: newTheme,
}));
const toggleColorScheme = () => {
const newTheme = colorScheme === 'dark' ? 'light' : 'dark';
setColorScheme(newTheme);
updateSettings({ ...settings, theme: newTheme });
};
const contextValue = useMemo(
() => ({
settings,
updateSettings,
updateTheme,
toggleColorScheme,
loading,
colorScheme,
}),
[settings, loading]
[settings, loading, colorScheme]
);
return (