mirror of
https://github.com/lordmathis/lemma.git
synced 2025-11-06 07:54:22 +00:00
Fix theme toggle
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
MantineProvider,
|
MantineProvider,
|
||||||
createTheme,
|
ColorSchemeScript,
|
||||||
AppShell,
|
AppShell,
|
||||||
Container,
|
Container,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
@@ -15,22 +15,14 @@ import '@mantine/core/styles.css';
|
|||||||
import '@mantine/notifications/styles.css';
|
import '@mantine/notifications/styles.css';
|
||||||
import './App.scss';
|
import './App.scss';
|
||||||
|
|
||||||
const mantineTheme = createTheme({
|
|
||||||
/** You can add your Mantine theme overrides here */
|
|
||||||
});
|
|
||||||
|
|
||||||
function AppContent() {
|
function AppContent() {
|
||||||
const { settings, loading } = useSettings();
|
const { loading } = useSettings();
|
||||||
const [opened, setOpened] = useState(false);
|
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <div>Loading...</div>;
|
return <div>Loading...</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MantineProvider theme={mantineTheme} defaultColorScheme={settings.theme}>
|
|
||||||
<Notifications />
|
|
||||||
<ModalsProvider>
|
|
||||||
<AppShell header={{ height: 60 }} padding="md">
|
<AppShell header={{ height: 60 }} padding="md">
|
||||||
<AppShell.Header>
|
<AppShell.Header>
|
||||||
<Header />
|
<Header />
|
||||||
@@ -41,18 +33,24 @@ function AppContent() {
|
|||||||
</Container>
|
</Container>
|
||||||
</AppShell.Main>
|
</AppShell.Main>
|
||||||
</AppShell>
|
</AppShell>
|
||||||
</ModalsProvider>
|
|
||||||
</MantineProvider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<ColorSchemeScript defaultColorScheme="light" />
|
||||||
|
<MantineProvider defaultColorScheme="light">
|
||||||
|
<Notifications />
|
||||||
|
<ModalsProvider>
|
||||||
<SettingsProvider>
|
<SettingsProvider>
|
||||||
<ModalProvider>
|
<ModalProvider>
|
||||||
<AppContent />
|
<AppContent />
|
||||||
</ModalProvider>
|
</ModalProvider>
|
||||||
</SettingsProvider>
|
</SettingsProvider>
|
||||||
|
</ModalsProvider>
|
||||||
|
</MantineProvider>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -121,12 +121,14 @@ const MainContent = () => {
|
|||||||
{renderBreadcrumbs()}
|
{renderBreadcrumbs()}
|
||||||
<Tabs value={activeTab} onChange={handleTabChange}>
|
<Tabs value={activeTab} onChange={handleTabChange}>
|
||||||
<Tabs.List>
|
<Tabs.List>
|
||||||
<Tabs.Tab value="source" leftSection={<IconCode size="0.8rem" />}>
|
<Tabs.Tab
|
||||||
Source
|
value="source"
|
||||||
</Tabs.Tab>
|
leftSection={<IconCode size="0.8rem" />}
|
||||||
<Tabs.Tab value="preview" leftSection={<IconEye size="0.8rem" />}>
|
/>
|
||||||
Preview
|
<Tabs.Tab
|
||||||
</Tabs.Tab>
|
value="preview"
|
||||||
|
leftSection={<IconEye size="0.8rem" />}
|
||||||
|
/>
|
||||||
</Tabs.List>
|
</Tabs.List>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import React, { useReducer, useEffect, useCallback, useRef } from 'react';
|
import React, { useReducer, useEffect, useCallback, useRef } from 'react';
|
||||||
import { Modal, Badge, Button, Group } from '@mantine/core';
|
import { Modal, Badge, Button, Group, Title } from '@mantine/core';
|
||||||
import { useDisclosure } from '@mantine/hooks';
|
|
||||||
import { notifications } from '@mantine/notifications';
|
import { notifications } from '@mantine/notifications';
|
||||||
import { useSettings } from '../contexts/SettingsContext';
|
import { useSettings } from '../contexts/SettingsContext';
|
||||||
import AppearanceSettings from './settings/AppearanceSettings';
|
import AppearanceSettings from './settings/AppearanceSettings';
|
||||||
@@ -51,11 +50,10 @@ function settingsReducer(state, action) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Settings = () => {
|
const Settings = () => {
|
||||||
const { settings, updateSettings, updateTheme } = useSettings();
|
const { settings, updateSettings, colorScheme } = useSettings();
|
||||||
const { settingsModalVisible, setSettingsModalVisible } = useModalContext();
|
const { settingsModalVisible, setSettingsModalVisible } = useModalContext();
|
||||||
const [state, dispatch] = useReducer(settingsReducer, initialState);
|
const [state, dispatch] = useReducer(settingsReducer, initialState);
|
||||||
const isInitialMount = useRef(true);
|
const isInitialMount = useRef(true);
|
||||||
const updateThemeTimeoutRef = useRef(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isInitialMount.current) {
|
if (isInitialMount.current) {
|
||||||
@@ -64,22 +62,17 @@ const Settings = () => {
|
|||||||
}
|
}
|
||||||
}, [settings]);
|
}, [settings]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch({
|
||||||
|
type: 'UPDATE_LOCAL_SETTINGS',
|
||||||
|
payload: { theme: colorScheme },
|
||||||
|
});
|
||||||
|
}, [colorScheme]);
|
||||||
|
|
||||||
const handleInputChange = useCallback((key, value) => {
|
const handleInputChange = useCallback((key, value) => {
|
||||||
dispatch({ type: 'UPDATE_LOCAL_SETTINGS', payload: { [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 () => {
|
const handleSubmit = async () => {
|
||||||
try {
|
try {
|
||||||
await updateSettings(state.localSettings);
|
await updateSettings(state.localSettings);
|
||||||
@@ -100,30 +93,16 @@ const Settings = () => {
|
|||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
if (state.hasUnsavedChanges) {
|
if (state.hasUnsavedChanges) {
|
||||||
updateTheme(state.initialSettings.theme);
|
|
||||||
dispatch({ type: 'RESET' });
|
dispatch({ type: 'RESET' });
|
||||||
}
|
}
|
||||||
setSettingsModalVisible(false);
|
setSettingsModalVisible(false);
|
||||||
}, [
|
}, [state.hasUnsavedChanges, setSettingsModalVisible]);
|
||||||
state.hasUnsavedChanges,
|
|
||||||
state.initialSettings.theme,
|
|
||||||
updateTheme,
|
|
||||||
setSettingsModalVisible,
|
|
||||||
]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
if (updateThemeTimeoutRef.current) {
|
|
||||||
clearTimeout(updateThemeTimeoutRef.current);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
opened={settingsModalVisible}
|
opened={settingsModalVisible}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
title="Settings"
|
title={<Title order={2}>Settings</Title>}
|
||||||
centered
|
centered
|
||||||
size="lg"
|
size="lg"
|
||||||
>
|
>
|
||||||
@@ -134,7 +113,7 @@ const Settings = () => {
|
|||||||
)}
|
)}
|
||||||
<AppearanceSettings
|
<AppearanceSettings
|
||||||
themeSettings={state.localSettings.theme}
|
themeSettings={state.localSettings.theme}
|
||||||
onThemeChange={handleThemeChange}
|
onThemeChange={(newTheme) => handleInputChange('theme', newTheme)}
|
||||||
/>
|
/>
|
||||||
<EditorSettings
|
<EditorSettings
|
||||||
autoSave={state.localSettings.autoSave}
|
autoSave={state.localSettings.autoSave}
|
||||||
|
|||||||
@@ -1,18 +1,25 @@
|
|||||||
import React from 'react';
|
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 (
|
return (
|
||||||
<Stack spacing="xs">
|
<Box mb="md">
|
||||||
<Text fw={500} size="lg">
|
<Title order={3} mb="md">
|
||||||
Appearance
|
Appearance
|
||||||
</Text>
|
</Title>
|
||||||
<Switch
|
<Group justify="space-between" align="center">
|
||||||
label="Dark Mode"
|
<Text size="sm">Dark Mode</Text>
|
||||||
checked={themeSettings === 'dark'}
|
<Switch checked={colorScheme === 'dark'} onChange={handleThemeChange} />
|
||||||
onChange={onThemeChange}
|
</Group>
|
||||||
/>
|
</Box>
|
||||||
</Stack>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,23 @@
|
|||||||
import React from 'react';
|
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 }) => {
|
const EditorSettings = ({ autoSave, onAutoSaveChange }) => {
|
||||||
return (
|
return (
|
||||||
<Stack spacing="xs" mt="md">
|
<Box mb="md">
|
||||||
<Text fw={500} size="lg">
|
<Title order={3} mb="md">
|
||||||
Editor
|
Editor
|
||||||
</Text>
|
</Title>
|
||||||
<Tooltip label="Auto Save feature is coming soon!" position="left">
|
<Tooltip label="Auto Save feature is coming soon!" position="left">
|
||||||
|
<Group justify="space-between" align="center">
|
||||||
|
<Text size="sm">Auto Save</Text>
|
||||||
<Switch
|
<Switch
|
||||||
label="Auto Save"
|
|
||||||
checked={autoSave}
|
checked={autoSave}
|
||||||
onChange={(event) => onAutoSaveChange(event.currentTarget.checked)}
|
onChange={(event) => onAutoSaveChange(event.currentTarget.checked)}
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
|
</Group>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Stack>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
import React from 'react';
|
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 = ({
|
const GitSettings = ({
|
||||||
gitEnabled,
|
gitEnabled,
|
||||||
@@ -11,55 +20,79 @@ const GitSettings = ({
|
|||||||
onInputChange,
|
onInputChange,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<Stack spacing="xs" mt="md">
|
<Stack spacing="md">
|
||||||
<Text fw={500} size="lg">
|
<Title order={3}>Git Integration</Title>
|
||||||
Git Integration
|
<Group justify="space-between" align="center">
|
||||||
</Text>
|
<Text size="sm">Enable Git</Text>
|
||||||
<Switch
|
<Switch
|
||||||
label="Enable Git"
|
|
||||||
checked={gitEnabled}
|
checked={gitEnabled}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
onInputChange('gitEnabled', event.currentTarget.checked)
|
onInputChange('gitEnabled', event.currentTarget.checked)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
</Group>
|
||||||
|
<Box>
|
||||||
|
<Text size="sm" mb="xs">
|
||||||
|
Git URL
|
||||||
|
</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Git URL"
|
|
||||||
value={gitUrl}
|
value={gitUrl}
|
||||||
onChange={(event) => onInputChange('gitUrl', event.currentTarget.value)}
|
onChange={(event) =>
|
||||||
|
onInputChange('gitUrl', event.currentTarget.value)
|
||||||
|
}
|
||||||
disabled={!gitEnabled}
|
disabled={!gitEnabled}
|
||||||
|
placeholder="Enter Git URL"
|
||||||
/>
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text size="sm" mb="xs">
|
||||||
|
Git Username
|
||||||
|
</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Git Username"
|
|
||||||
value={gitUser}
|
value={gitUser}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
onInputChange('gitUser', event.currentTarget.value)
|
onInputChange('gitUser', event.currentTarget.value)
|
||||||
}
|
}
|
||||||
disabled={!gitEnabled}
|
disabled={!gitEnabled}
|
||||||
|
placeholder="Enter Git username"
|
||||||
/>
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text size="sm" mb="xs">
|
||||||
|
Git Token
|
||||||
|
</Text>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
label="Git Token"
|
|
||||||
value={gitToken}
|
value={gitToken}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
onInputChange('gitToken', event.currentTarget.value)
|
onInputChange('gitToken', event.currentTarget.value)
|
||||||
}
|
}
|
||||||
disabled={!gitEnabled}
|
disabled={!gitEnabled}
|
||||||
|
placeholder="Enter Git token"
|
||||||
/>
|
/>
|
||||||
|
</Box>
|
||||||
|
<Group justify="space-between" align="center">
|
||||||
|
<Text size="sm">Auto Commit</Text>
|
||||||
<Switch
|
<Switch
|
||||||
label="Auto Commit"
|
|
||||||
checked={gitAutoCommit}
|
checked={gitAutoCommit}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
onInputChange('gitAutoCommit', event.currentTarget.checked)
|
onInputChange('gitAutoCommit', event.currentTarget.checked)
|
||||||
}
|
}
|
||||||
disabled={!gitEnabled}
|
disabled={!gitEnabled}
|
||||||
/>
|
/>
|
||||||
|
</Group>
|
||||||
|
<Box>
|
||||||
|
<Text size="sm" mb="xs">
|
||||||
|
Commit Message Template
|
||||||
|
</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Commit Message Template"
|
|
||||||
value={gitCommitMsgTemplate}
|
value={gitCommitMsgTemplate}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
onInputChange('gitCommitMsgTemplate', event.currentTarget.value)
|
onInputChange('gitCommitMsgTemplate', event.currentTarget.value)
|
||||||
}
|
}
|
||||||
disabled={!gitEnabled}
|
disabled={!gitEnabled}
|
||||||
|
placeholder="Enter commit message template"
|
||||||
/>
|
/>
|
||||||
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
import React, {
|
import React, { createContext, useContext, useEffect, useMemo } from 'react';
|
||||||
createContext,
|
import { useMantineColorScheme } from '@mantine/core';
|
||||||
useState,
|
|
||||||
useContext,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
} from 'react';
|
|
||||||
import { fetchUserSettings, saveUserSettings } from '../services/api';
|
import { fetchUserSettings, saveUserSettings } from '../services/api';
|
||||||
import { DEFAULT_SETTINGS } from '../utils/constants';
|
import { DEFAULT_SETTINGS } from '../utils/constants';
|
||||||
|
|
||||||
@@ -13,14 +8,16 @@ const SettingsContext = createContext();
|
|||||||
export const useSettings = () => useContext(SettingsContext);
|
export const useSettings = () => useContext(SettingsContext);
|
||||||
|
|
||||||
export const SettingsProvider = ({ children }) => {
|
export const SettingsProvider = ({ children }) => {
|
||||||
const [settings, setSettings] = useState(DEFAULT_SETTINGS);
|
const { colorScheme, setColorScheme } = useMantineColorScheme();
|
||||||
const [loading, setLoading] = useState(true);
|
const [settings, setSettings] = React.useState(DEFAULT_SETTINGS);
|
||||||
|
const [loading, setLoading] = React.useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadSettings = async () => {
|
const loadSettings = async () => {
|
||||||
try {
|
try {
|
||||||
const userSettings = await fetchUserSettings(1);
|
const userSettings = await fetchUserSettings(1);
|
||||||
setSettings(userSettings.settings);
|
setSettings(userSettings.settings);
|
||||||
|
setColorScheme(userSettings.settings.theme);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load user settings:', error);
|
console.error('Failed to load user settings:', error);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -29,7 +26,7 @@ export const SettingsProvider = ({ children }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
loadSettings();
|
loadSettings();
|
||||||
}, []);
|
}, [setColorScheme]);
|
||||||
|
|
||||||
const updateSettings = async (newSettings) => {
|
const updateSettings = async (newSettings) => {
|
||||||
try {
|
try {
|
||||||
@@ -38,27 +35,31 @@ export const SettingsProvider = ({ children }) => {
|
|||||||
settings: newSettings,
|
settings: newSettings,
|
||||||
});
|
});
|
||||||
setSettings(newSettings);
|
setSettings(newSettings);
|
||||||
|
// Ensure the color scheme is updated when settings are saved
|
||||||
|
if (newSettings.theme) {
|
||||||
|
setColorScheme(newSettings.theme);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to save settings:', error);
|
console.error('Failed to save settings:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateTheme = (newTheme) => {
|
const toggleColorScheme = () => {
|
||||||
setSettings((prevSettings) => ({
|
const newTheme = colorScheme === 'dark' ? 'light' : 'dark';
|
||||||
...prevSettings,
|
setColorScheme(newTheme);
|
||||||
theme: newTheme,
|
updateSettings({ ...settings, theme: newTheme });
|
||||||
}));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const contextValue = useMemo(
|
const contextValue = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
settings,
|
settings,
|
||||||
updateSettings,
|
updateSettings,
|
||||||
updateTheme,
|
toggleColorScheme,
|
||||||
loading,
|
loading,
|
||||||
|
colorScheme,
|
||||||
}),
|
}),
|
||||||
[settings, loading]
|
[settings, loading, colorScheme]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user