Skip to content

Commit

Permalink
refactor: better settings structure and some renaming
Browse files Browse the repository at this point in the history
  • Loading branch information
Charlie-XIAO committed Feb 1, 2025
1 parent 6571801 commit 3d209e1
Show file tree
Hide file tree
Showing 16 changed files with 164 additions and 130 deletions.
47 changes: 33 additions & 14 deletions crates/deskulpt-core/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,36 @@ use serde::{Deserialize, Serialize};
/// The settings file name in the persistence directory.
static SETTINGS_FILE: &str = "settings.bin";

/// Light/dark theme of the application.
#[derive(Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
enum Theme {
#[default]
Light,
Dark,
}

/// Keyboard shortcuts registered in the application.
///
/// A keyboard shortcut being `None` means that it is disabled, otherwise it is
/// a string parsable into [`Shortcut`](tauri_plugin_global_shortcut::Shortcut).
#[derive(Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Shortcuts {
/// For toggling canvas click-through.
pub toggle_canvas: Option<String>,
}

/// Application-wide settings.
#[derive(Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
struct AppSettings {
/// The application theme.
theme: Theme,
/// The keyboard shortcuts.
shortcuts: Shortcuts,
}

/// Per-widget settings.
///
/// Different from widget configurations, these are independent of the widget
Expand All @@ -25,25 +55,14 @@ struct WidgetSettings {
opacity: i32,
}

/// The light/dark theme appearance of the application.
#[derive(Default, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
enum Appearance {
#[default]
Light,
Dark,
}

/// Full settings of the application.
#[derive(Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Settings {
/// The theme appearance.
appearance: Appearance,
/// The keyboard shortcut for toggling canvas click-through.
toggle_shortcut: Option<String>,
/// Application-wide settings.
app: AppSettings,
/// The mapping from widget IDs to their respective settings.
widget_settings_map: HashMap<String, WidgetSettings>,
widgets: HashMap<String, WidgetSettings>,
}

impl Settings {
Expand Down
12 changes: 6 additions & 6 deletions src/canvas/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import useRenderWidgetListener from "./hooks/useRenderWidgetListener";
import useRemoveWidgetsListener from "./hooks/useRemoveWidgetsListener";
import useShowToastListener from "./hooks/useShowToastListener";
import { Toaster } from "sonner";
import { Theme } from "@radix-ui/themes";
import useAppearanceListener from "./hooks/useAppearanceListener";
import { Theme as RadixTheme } from "@radix-ui/themes";
import useThemeListener from "./hooks/useThemeListener";

/**
* The main component of the canvas window.
Expand All @@ -17,7 +17,7 @@ export default function App() {
const [canvasWidgetStates, setCanvasWidgetStates] = useState<
Record<string, CanvasWidgetState>
>({});
const appearance = useAppearanceListener();
const theme = useThemeListener();

useShowToastListener();
useRenderWidgetListener(canvasWidgetStates, setCanvasWidgetStates);
Expand All @@ -41,8 +41,8 @@ export default function App() {
}

return (
<Theme
appearance={appearance}
<RadixTheme
appearance={theme}
accentColor="indigo"
grayColor="slate"
hasBackground={false}
Expand Down Expand Up @@ -73,6 +73,6 @@ export default function App() {
</WidgetContainer>
),
)}
</Theme>
</RadixTheme>
);
}
28 changes: 0 additions & 28 deletions src/canvas/hooks/useAppearanceListener.ts

This file was deleted.

28 changes: 28 additions & 0 deletions src/canvas/hooks/useThemeListener.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useEffect, useState } from "react";
import { listenToSwitchTheme } from "../../events";

/**
* Handle the theme of the canvas.
*
* This hook works by listening to the "switch-theme" event and updating the
* theme state accordingly.
*
* @returns The current theme.
*/
export default function useAppearanceListener() {
const [theme, setTheme] = useState(
window.__DESKULPT_CANVAS_INTERNALS__.initialSettings.app.theme,
);

useEffect(() => {
const unlisten = listenToSwitchTheme((event) => {
setTheme(event.payload);
});

return () => {
unlisten.then((f) => f()).catch(console.error);
};
}, []);

return theme;
}
4 changes: 2 additions & 2 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ export function invokeRescanWidgets() {
* Invoke the `update_toggle_shortcut` command.
*/
export function invokeUpdateToggleShortcut(payload: {
oldShortcut?: string;
newShortcut?: string;
oldShortcut: string | null;
newShortcut: string | null;
}) {
return invoke<void>("update_toggle_shortcut", payload);
}
14 changes: 7 additions & 7 deletions src/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
RenderWidgetPayload,
UpdateSettingsPayload,
} from "./types/frontend";
import { Appearance, ShowToastPayload } from "./types/backend";
import { ShowToastPayload, Theme } from "./types/backend";

/**
* Emit the "render-widget" event to the canvas window.
Expand Down Expand Up @@ -79,22 +79,22 @@ export function listenToUpdateSettings(
}

/**
* Emit the "switch-theme-appearance" event to the canvas window.
* Emit the "switch-theme" event to the canvas window.
*
* @param payload The payload of the event.
*/
export async function emitSwitchAppearanceToCanvas(payload: Appearance) {
await emitTo("canvas", "switch-theme-appearance", payload);
export async function emitSwitchThemeToCanvas(payload: Theme) {
await emitTo("canvas", "switch-theme", payload);
}

/**
* Listen to the "switch-theme-appearance" event.
* Listen to the "switch-theme" event.
*
* @param handler The callback function to handle the event.
* @returns A promise that resolves to a function to unlisten to the event.
*/
export function listenToSwitchAppearance(handler: EventCallback<Appearance>) {
return listen("switch-theme-appearance", handler);
export function listenToSwitchTheme(handler: EventCallback<Theme>) {
return listen("switch-theme", handler);
}

/**
Expand Down
27 changes: 11 additions & 16 deletions src/manager/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,33 @@ import useExitAppListener from "./hooks/useExitAppListener";
import useToggleShortcut from "./hooks/useToggleShortcut";
import useManagerWidgetStates from "./hooks/useManagerWidgetStates";
import useUpdateSettingsListener from "./hooks/useUpdateSettingsListener";
import { Box, Tabs, Theme } from "@radix-ui/themes";
import { Box, Theme as RadixTheme, Tabs } from "@radix-ui/themes";
import ManagerToaster from "./components/ManagerToaster";
import AppearanceToggler from "./components/AppearanceToggler";
import ThemeToggler from "./components/ThemeToggler";

/**
* The main component of the manager window.
*/
export default function App() {
const [appearance, setAppearance] = useState(
window.__DESKULPT_MANAGER_INTERNALS__.initialSettings.appearance,
);
const { toggleShortcut, setToggleShortcut } = useToggleShortcut(
window.__DESKULPT_MANAGER_INTERNALS__.initialSettings.toggleShortcut,
const [theme, setTheme] = useState(
window.__DESKULPT_MANAGER_INTERNALS__.initialSettings.app.theme,
);
const { toggleShortcut, setToggleShortcut } = useToggleShortcut();
const { managerWidgetStates, setManagerWidgetStates, rescanAndRender } =
useManagerWidgetStates();

useExitAppListener(toggleShortcut, appearance, managerWidgetStates);
useExitAppListener(toggleShortcut, theme, managerWidgetStates);
useUpdateSettingsListener(setManagerWidgetStates);

return (
<Theme
appearance={appearance}
<RadixTheme
appearance={theme}
accentColor="indigo"
grayColor="slate"
css={{ height: "100vh" }}
>
<ManagerToaster appearance={appearance} />
<AppearanceToggler
appearance={appearance}
setAppearance={setAppearance}
/>
<ManagerToaster theme={theme} />
<ThemeToggler theme={theme} setTheme={setTheme} />
<Tabs.Root defaultValue="widgets" asChild>
<Box height="100%" p="2">
<Tabs.List>
Expand Down Expand Up @@ -72,6 +67,6 @@ export default function App() {
</Box>
</Box>
</Tabs.Root>
</Theme>
</RadixTheme>
);
}
10 changes: 5 additions & 5 deletions src/manager/components/ManagerToaster.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Toaster } from "sonner";
import { Appearance } from "../../types/backend";
import { Theme } from "../../types/backend";

export interface ManagerToasterProps {
/** The theme appearance. */
appearance: Appearance;
/** The theme. */
theme: Theme;
}

/**
Expand All @@ -12,11 +12,11 @@ export interface ManagerToasterProps {
* This is styled on top of [`Toaster`](https://sonner.emilkowal.ski/toaster), rendered
* in the bottom center of the manager window.
*/
export default function ManagerToaster({ appearance }: ManagerToasterProps) {
export default function ManagerToaster({ theme }: ManagerToasterProps) {
return (
<Toaster
position="bottom-center"
theme={appearance}
theme={theme}
gap={6}
toastOptions={{
style: {
Expand Down
4 changes: 2 additions & 2 deletions src/manager/components/SettingToggleShortcut.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import useKeyboardListener from "../hooks/useKeyboardListener";

export interface SettingToggleShortcutProps {
/** Setter for the toggle shortcut state. */
setToggleShortcut: Dispatch<SetStateAction<string | undefined>>;
setToggleShortcut: Dispatch<SetStateAction<string | null>>;
}

/**
Expand Down Expand Up @@ -162,7 +162,7 @@ export default function SettingToggleShortcut({
disabled={!disableShortcut && (!hasModifier || !hasKey)}
onClick={() => {
setToggleShortcut(
disableShortcut ? undefined : listenedShortcut.join("+"),
disableShortcut ? null : listenedShortcut.join("+"),
);
setPopoverOpen(false);
}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { Box, IconButton, Tooltip } from "@radix-ui/themes";
import { Appearance } from "../../types/backend";
import { Theme } from "../../types/backend";
import { MdOutlineDarkMode, MdOutlineLightMode } from "react-icons/md";
import { Dispatch, SetStateAction } from "react";
import { emitSwitchAppearanceToCanvas } from "../../events";
import { emitSwitchThemeToCanvas } from "../../events";

export interface AppearanceTogglerProps {
/** Theme appearance. */
appearance: Appearance;
/** State for theme appearance. */
setAppearance: Dispatch<SetStateAction<Appearance>>;
/** Theme. */
theme: Theme;
/** State for theme. */
setTheme: Dispatch<SetStateAction<Theme>>;
}

/**
Expand All @@ -19,23 +19,23 @@ export interface AppearanceTogglerProps {
* and dark mode. The tooltip and icon should reflect the current theme appearance.
*/
export default function AppearanceToggler({
appearance,
setAppearance,
theme,
setTheme,
}: AppearanceTogglerProps) {
const toggleAppearance = () => {
const newAppearance = appearance === "light" ? "dark" : "light";
setAppearance(newAppearance);
emitSwitchAppearanceToCanvas(newAppearance).catch(console.error);
const toggleTheme = () => {
const newTheme = theme === Theme.LIGHT ? Theme.DARK : Theme.LIGHT;
setTheme(newTheme);
emitSwitchThemeToCanvas(newTheme).catch(console.error);
};

return (
<Box position="absolute" right="3" top="4">
<Tooltip
side="left"
content={`Switch to ${appearance === "light" ? "dark" : "light"} mode`}
content={`Switch to ${theme === Theme.LIGHT ? Theme.DARK : Theme.LIGHT} mode`}
>
<IconButton variant="soft" size="1" onClick={toggleAppearance}>
{appearance === "light" ? (
<IconButton variant="soft" size="1" onClick={toggleTheme}>
{theme === Theme.LIGHT ? (
<MdOutlineLightMode />
) : (
<MdOutlineDarkMode />
Expand Down
Loading

0 comments on commit 3d209e1

Please sign in to comment.