From 3e2d7a751c4bafe0720f5d37746a7d80841a5bff Mon Sep 17 00:00:00 2001 From: Hachi-R Date: Fri, 24 Jan 2025 15:29:07 -0300 Subject: [PATCH] feat: use leveldb instead of localStorage --- .../aparence/components/theme-actions.tsx | 68 +++++++--- .../aparence/components/theme-card.tsx | 126 +++++++++--------- .../aparence/components/theme-placeholder.tsx | 38 ++++-- .../aparence/modals/add-theme-modal.tsx | 53 ++++++-- .../modals/delete-all-themes-modal.tsx | 10 +- .../aparence/modals/delete-theme-modal.tsx | 10 +- .../settings/aparence/settings-appearance.tsx | 31 ++++- 7 files changed, 232 insertions(+), 104 deletions(-) diff --git a/src/renderer/src/pages/settings/aparence/components/theme-actions.tsx b/src/renderer/src/pages/settings/aparence/components/theme-actions.tsx index 18e14afe..589559f6 100644 --- a/src/renderer/src/pages/settings/aparence/components/theme-actions.tsx +++ b/src/renderer/src/pages/settings/aparence/components/theme-actions.tsx @@ -2,31 +2,63 @@ import { GlobeIcon, TrashIcon } from "@primer/octicons-react"; import { PlusIcon } from "@primer/octicons-react"; import { Button } from "@renderer/components/button/button"; import { useTranslation } from "react-i18next"; +import { AddThemeModal, DeleteAllThemesModal } from "../index"; import "./theme-actions.scss"; +import { useState } from "react"; -export const ThemeActions = () => { +interface ThemeActionsProps { + onListUpdated: () => void; +} + +export const ThemeActions = ({ onListUpdated }: ThemeActionsProps) => { const { t } = useTranslation(); + const [addThemeModalVisible, setAddThemeModalVisible] = useState(false); + const [deleteAllThemesModalVisible, setDeleteAllThemesModalVisible] = + useState(false); + return ( -
-
- + <> + setAddThemeModalVisible(false)} + onThemeAdded={onListUpdated} + /> - -
+ setDeleteAllThemesModalVisible(false)} + onThemesDeleted={onListUpdated} + /> -
- +
+
+ + + +
+ +
+ +
-
+ ); }; diff --git a/src/renderer/src/pages/settings/aparence/components/theme-card.tsx b/src/renderer/src/pages/settings/aparence/components/theme-card.tsx index 80c37f51..545aca8a 100644 --- a/src/renderer/src/pages/settings/aparence/components/theme-card.tsx +++ b/src/renderer/src/pages/settings/aparence/components/theme-card.tsx @@ -1,84 +1,90 @@ import { PencilIcon, TrashIcon } from "@primer/octicons-react"; import { useTranslation } from "react-i18next"; import { Button } from "@renderer/components/button/button"; -import type { Theme } from "../themes-manager"; +import type { Theme } from "@types"; import { useNavigate } from "react-router-dom"; import "./theme-card.scss"; +import { useState } from "react"; +import { DeleteThemeModal } from "../modals/delete-theme-modal"; interface ThemeCardProps { theme: Theme; - handleSetTheme: (themeId: string) => void; - handleDeleteTheme: (themeId: string) => void; + onListUpdated: () => void; } -export const ThemeCard = ({ - theme, - handleSetTheme, - handleDeleteTheme, -}: ThemeCardProps) => { +export const ThemeCard = ({ theme, onListUpdated }: ThemeCardProps) => { const { t } = useTranslation(); const navigate = useNavigate(); + const [deleteThemeModalVisible, setDeleteThemeModalVisible] = useState(false); + return ( -
-
-
{theme.name}
+ <> + setDeleteThemeModalVisible(false)} + onThemeDeleted={onListUpdated} + themeId={theme.id} + /> -
- {Object.entries(theme.colors).map(([key, color]) => ( -
+
+
{theme.name}
+ +
+ {Object.entries(theme.colors).map(([key, color]) => ( +
+ {/* color circle */} +
+ ))} +
+
+ + {theme.author && theme.author && ( +

+ {t("by")} + + navigate(`/profile/${theme.author}`)} > - {/* color circle */} -

- ))} -
-
+ {theme.author} + +

+ )} - {theme.author && theme.authorId && ( -

- {t("by")} +

+
+ {theme.isActive ? ( + + ) : ( + + )} +
- navigate(`/profile/${theme.authorId}`)} - > - {theme.author} - -

- )} - -
-
- {theme.isActive ? ( - - ) : ( - - )} -
-
- - - + +
-
+ ); }; diff --git a/src/renderer/src/pages/settings/aparence/components/theme-placeholder.tsx b/src/renderer/src/pages/settings/aparence/components/theme-placeholder.tsx index 2f10877e..7904bfe4 100644 --- a/src/renderer/src/pages/settings/aparence/components/theme-placeholder.tsx +++ b/src/renderer/src/pages/settings/aparence/components/theme-placeholder.tsx @@ -1,26 +1,36 @@ import { AlertIcon } from "@primer/octicons-react"; import { useTranslation } from "react-i18next"; import "./theme-placeholder.scss"; +import { AddThemeModal } from "../modals/add-theme-modal"; +import { useState } from "react"; interface ThemePlaceholderProps { - setAddThemeModalVisible: (visible: boolean) => void; + onListUpdated: () => void; } -export const ThemePlaceholder = ({ - setAddThemeModalVisible, -}: ThemePlaceholderProps) => { - const { t } = useTranslation(); +export const ThemePlaceholder = ({ onListUpdated }: ThemePlaceholderProps) => { + const { t } = useTranslation("settings"); + + const [addThemeModalVisible, setAddThemeModalVisible] = useState(false); return ( - + + ); }; diff --git a/src/renderer/src/pages/settings/aparence/modals/add-theme-modal.tsx b/src/renderer/src/pages/settings/aparence/modals/add-theme-modal.tsx index 7f7d0440..c1005510 100644 --- a/src/renderer/src/pages/settings/aparence/modals/add-theme-modal.tsx +++ b/src/renderer/src/pages/settings/aparence/modals/add-theme-modal.tsx @@ -1,19 +1,54 @@ import { Modal } from "@renderer/components/modal/modal"; import { TextField } from "@renderer/components/text-field/text-field"; -import { useTranslation } from "react-i18next"; -import "./modals.scss"; import { Button } from "@renderer/components/button/button"; +import { useTranslation } from "react-i18next"; import { useState } from "react"; +import "./modals.scss"; interface AddThemeModalProps { visible: boolean; onClose: () => void; + onThemeAdded: () => void; } -export const AddThemeModal = ({ visible, onClose }: AddThemeModalProps) => { +export const AddThemeModal = ({ + visible, + onClose, + onThemeAdded, +}: AddThemeModalProps) => { const { t } = useTranslation("settings"); + const [name, setName] = useState(""); + const [error, setError] = useState(""); - const [themeName, setThemeName] = useState(""); + const handleKeyDown = (event: React.KeyboardEvent) => { + if (event.key === "Enter") { + handleSubmit(); + } + }; + + const handleSubmit = async () => { + if (!name || name.length < 3) { + setError(t("theme_name_error_hint")); + return; + } + + const theme = { + id: crypto.randomUUID(), + name, + isActive: false, + colors: { + accent: "#c0c1c7", + background: "#1c1c1c", + surface: "#151515", + }, + }; + + await window.electron.addCustomTheme(theme); + setName(""); + setError(""); + onThemeAdded(); + onClose(); + }; return ( { setThemeName(e.target.value)} + value={name} + onChange={(e) => setName(e.target.value)} + hint={error} + error={!!error} + onKeyDown={handleKeyDown} /> -
diff --git a/src/renderer/src/pages/settings/aparence/modals/delete-all-themes-modal.tsx b/src/renderer/src/pages/settings/aparence/modals/delete-all-themes-modal.tsx index 48c17efb..47c58221 100644 --- a/src/renderer/src/pages/settings/aparence/modals/delete-all-themes-modal.tsx +++ b/src/renderer/src/pages/settings/aparence/modals/delete-all-themes-modal.tsx @@ -6,14 +6,22 @@ import "./modals.scss"; interface DeleteAllThemesModalProps { visible: boolean; onClose: () => void; + onThemesDeleted: () => void; } export const DeleteAllThemesModal = ({ visible, onClose, + onThemesDeleted, }: DeleteAllThemesModalProps) => { const { t } = useTranslation("settings"); + const handleDeleteAllThemes = async () => { + await window.electron.deleteAllCustomThemes(); + onClose(); + onThemesDeleted(); + }; + return (
- diff --git a/src/renderer/src/pages/settings/aparence/modals/delete-theme-modal.tsx b/src/renderer/src/pages/settings/aparence/modals/delete-theme-modal.tsx index 4b0f8be4..0a339627 100644 --- a/src/renderer/src/pages/settings/aparence/modals/delete-theme-modal.tsx +++ b/src/renderer/src/pages/settings/aparence/modals/delete-theme-modal.tsx @@ -7,14 +7,22 @@ interface DeleteThemeModalProps { visible: boolean; onClose: () => void; themeId: string; + onThemeDeleted: () => void; } export const DeleteThemeModal = ({ visible, onClose, + themeId, + onThemeDeleted, }: DeleteThemeModalProps) => { const { t } = useTranslation("settings"); + const handleDeleteTheme = async () => { + await window.electron.deleteCustomTheme(themeId); + onThemeDeleted(); + }; + return (
- diff --git a/src/renderer/src/pages/settings/aparence/settings-appearance.tsx b/src/renderer/src/pages/settings/aparence/settings-appearance.tsx index d0ca8e2d..636a424a 100644 --- a/src/renderer/src/pages/settings/aparence/settings-appearance.tsx +++ b/src/renderer/src/pages/settings/aparence/settings-appearance.tsx @@ -1,12 +1,39 @@ +import { useEffect, useState } from "react"; import "./settings-appearance.scss"; -import { ThemeActions } from "./index"; +import { ThemeActions, ThemeCard, ThemePlaceholder } from "./index"; +import type { Theme } from "@types"; export const SettingsAppearance = () => { + const [themes, setThemes] = useState([]); + + const loadThemes = async () => { + const themesList = await window.electron.getAllCustomThemes(); + setThemes(themesList); + }; + + useEffect(() => { + loadThemes(); + }, []); + return (

Appearance

- + + +
+ {!themes.length ? ( + + ) : ( + themes.map((theme) => ( + + )) + )} +
); };