feat: use leveldb instead of localStorage

This commit is contained in:
Hachi-R 2025-01-24 15:29:07 -03:00
parent 58f63cab44
commit 3e2d7a751c
7 changed files with 232 additions and 104 deletions

View File

@ -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 (
<div className="settings-appearance__actions">
<div className="settings-appearance__actions-left">
<Button theme="primary" className="settings-appearance__button">
<GlobeIcon />
{t("web_store")}
</Button>
<>
<AddThemeModal
visible={addThemeModalVisible}
onClose={() => setAddThemeModalVisible(false)}
onThemeAdded={onListUpdated}
/>
<Button theme="danger" className="settings-appearance__button">
<TrashIcon />
{t("clear_themes")}
</Button>
</div>
<DeleteAllThemesModal
visible={deleteAllThemesModalVisible}
onClose={() => setDeleteAllThemesModalVisible(false)}
onThemesDeleted={onListUpdated}
/>
<div className="settings-appearance__actions-right">
<Button theme="outline" className="settings-appearance__button">
<PlusIcon />
{t("add_theme")}
</Button>
<div className="settings-appearance__actions">
<div className="settings-appearance__actions-left">
<Button theme="primary" className="settings-appearance__button">
<GlobeIcon />
{t("web_store")}
</Button>
<Button
theme="danger"
className="settings-appearance__button"
onClick={() => setDeleteAllThemesModalVisible(true)}
>
<TrashIcon />
{t("clear_themes")}
</Button>
</div>
<div className="settings-appearance__actions-right">
<Button
theme="outline"
className="settings-appearance__button"
onClick={() => setAddThemeModalVisible(true)}
>
<PlusIcon />
{t("add_theme")}
</Button>
</div>
</div>
</div>
</>
);
};

View File

@ -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 (
<div
className={`theme-card ${theme.isActive ? "theme-card--active" : ""}`}
key={theme.name}
>
<div className="theme-card__header">
<div className="theme-card__header__title">{theme.name}</div>
<>
<DeleteThemeModal
visible={deleteThemeModalVisible}
onClose={() => setDeleteThemeModalVisible(false)}
onThemeDeleted={onListUpdated}
themeId={theme.id}
/>
<div className="theme-card__header__colors">
{Object.entries(theme.colors).map(([key, color]) => (
<div
title={color}
style={{ backgroundColor: color }}
className="theme-card__header__colors__color"
key={key}
<div
className={`theme-card ${theme.isActive ? "theme-card--active" : ""}`}
key={theme.name}
>
<div className="theme-card__header">
<div className="theme-card__header__title">{theme.name}</div>
<div className="theme-card__header__colors">
{Object.entries(theme.colors).map(([key, color]) => (
<div
title={color}
style={{ backgroundColor: color }}
className="theme-card__header__colors__color"
key={key}
>
{/* color circle */}
</div>
))}
</div>
</div>
{theme.author && theme.author && (
<p className="theme-card__author">
{t("by")}
<span
className="theme-card__author__name"
onClick={() => navigate(`/profile/${theme.author}`)}
>
{/* color circle */}
</div>
))}
</div>
</div>
{theme.author}
</span>
</p>
)}
{theme.author && theme.authorId && (
<p className="theme-card__author">
{t("by")}
<div className="theme-card__actions">
<div className="theme-card__actions__left">
{theme.isActive ? (
<Button theme="dark">{t("unset_theme ")}</Button>
) : (
<Button theme="outline">{t("set_theme")}</Button>
)}
</div>
<span
className="theme-card__author__name"
onClick={() => navigate(`/profile/${theme.authorId}`)}
>
{theme.author}
</span>
</p>
)}
<div className="theme-card__actions">
<div className="theme-card__actions__left">
{theme.isActive ? (
<Button theme="dark">{t("unset_theme ")}</Button>
) : (
<Button onClick={() => handleSetTheme(theme.id)} theme="outline">
{t("set_theme")}
<div className="theme-card__actions__right">
<Button title={t("edit_theme")} theme="outline">
<PencilIcon />
</Button>
)}
</div>
<div className="theme-card__actions__right">
<Button title={t("edit_theme")} theme="outline">
<PencilIcon />
</Button>
<Button
onClick={() => handleDeleteTheme(theme.id)}
title={t("delete_theme")}
theme="outline"
>
<TrashIcon />
</Button>
<Button
onClick={() => setDeleteThemeModalVisible(true)}
title={t("delete_theme")}
theme="outline"
>
<TrashIcon />
</Button>
</div>
</div>
</div>
</div>
</>
);
};

View File

@ -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 (
<button
className="theme-placeholder"
onClick={() => setAddThemeModalVisible(true)}
>
<div className="theme-placeholder__icon">
<AlertIcon />
</div>
<>
<AddThemeModal
visible={addThemeModalVisible}
onClose={() => setAddThemeModalVisible(false)}
onThemeAdded={onListUpdated}
/>
<p className="theme-placeholder__text">{t("no_themes")}</p>
</button>
<button
className="theme-placeholder"
onClick={() => setAddThemeModalVisible(true)}
>
<div className="theme-placeholder__icon">
<AlertIcon />
</div>
<p className="theme-placeholder__text">{t("no_themes")}</p>
</button>
</>
);
};

View File

@ -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 (
<Modal
@ -26,12 +61,14 @@ export const AddThemeModal = ({ visible, onClose }: AddThemeModalProps) => {
<TextField
label={t("theme_name")}
placeholder={t("insert_theme_name")}
hint={t("theme_name_hint")}
value={themeName}
onChange={(e) => setThemeName(e.target.value)}
value={name}
onChange={(e) => setName(e.target.value)}
hint={error}
error={!!error}
onKeyDown={handleKeyDown}
/>
<Button theme="primary" onClick={onClose}>
<Button theme="primary" onClick={handleSubmit}>
{t("add_theme")}
</Button>
</div>

View File

@ -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 (
<Modal
visible={visible}
@ -22,7 +30,7 @@ export const DeleteAllThemesModal = ({
onClose={onClose}
>
<div className="delete-all-themes-modal__container">
<Button theme="outline" onClick={onClose}>
<Button theme="outline" onClick={handleDeleteAllThemes}>
{t("delete_all_themes")}
</Button>

View File

@ -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 (
<Modal
visible={visible}
@ -23,7 +31,7 @@ export const DeleteThemeModal = ({
onClose={onClose}
>
<div className="delete-all-themes-modal__container">
<Button theme="outline" onClick={onClose}>
<Button theme="outline" onClick={handleDeleteTheme}>
{t("delete_theme")}
</Button>

View File

@ -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<Theme[]>([]);
const loadThemes = async () => {
const themesList = await window.electron.getAllCustomThemes();
setThemes(themesList);
};
useEffect(() => {
loadThemes();
}, []);
return (
<div className="settings-appearance">
<p className="settings-appearance__description">Appearance</p>
<ThemeActions />
<ThemeActions onListUpdated={loadThemes} />
<div className="settings-appearance__themes">
{!themes.length ? (
<ThemePlaceholder onListUpdated={loadThemes} />
) : (
themes.map((theme) => (
<ThemeCard
key={theme.id}
theme={theme}
onListUpdated={loadThemes}
/>
))
)}
</div>
</div>
);
};