mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-03-09 15:40:26 +00:00
Merge branch 'feature/custom-themes' of github.com:hydralauncher/hydra into feature/custom-themes
This commit is contained in:
commit
9449d7cdcd
8 changed files with 181 additions and 35 deletions
|
@ -87,6 +87,7 @@ import "./themes/get-custom-theme-by-id";
|
|||
import "./themes/get-active-custom-theme";
|
||||
import "./themes/css-injector";
|
||||
import "./themes/close-editor-window";
|
||||
import "./themes/import-theme";
|
||||
import { isPortableVersion } from "@main/helpers";
|
||||
|
||||
ipcMain.handle("ping", () => "pong");
|
||||
|
|
|
@ -1,38 +1,38 @@
|
|||
import { themes } from "@main/level/sublevels/themes";
|
||||
import { WindowManager } from "@main/services";
|
||||
import { Theme } from "@types";
|
||||
// import { themes } from "@main/level/sublevels/themes";
|
||||
// import { WindowManager } from "@main/services";
|
||||
// import { Theme } from "@types";
|
||||
|
||||
export const handleDeepLinkTheme = async (
|
||||
themeName: string,
|
||||
authorCode: string
|
||||
) => {
|
||||
const theme: Theme = {
|
||||
id: crypto.randomUUID(),
|
||||
name: themeName,
|
||||
isActive: false,
|
||||
author: authorCode,
|
||||
authorName: "spectre",
|
||||
code: `https://hydrathemes.shop/themes/${themeName}.css`,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
// export const handleDeepLinkTheme = async (
|
||||
// themeName: string,
|
||||
// authorCode: string
|
||||
// ) => {
|
||||
// const theme: Theme = {
|
||||
// id: crypto.randomUUID(),
|
||||
// name: themeName,
|
||||
// isActive: false,
|
||||
// author: authorCode,
|
||||
// authorName: "spectre",
|
||||
// code: `https://hydrathemes.shop/themes/${themeName}.css`,
|
||||
// createdAt: new Date(),
|
||||
// updatedAt: new Date(),
|
||||
// };
|
||||
|
||||
await themes.put(theme.id, theme);
|
||||
// await themes.put(theme.id, theme);
|
||||
|
||||
const allThemes = await themes.values().all();
|
||||
const activeTheme = allThemes.find((theme: Theme) => theme.isActive);
|
||||
// const allThemes = await themes.values().all();
|
||||
// const activeTheme = allThemes.find((theme: Theme) => theme.isActive);
|
||||
|
||||
if (activeTheme) {
|
||||
await themes.put(activeTheme.id, {
|
||||
...activeTheme,
|
||||
isActive: false,
|
||||
});
|
||||
}
|
||||
// if (activeTheme) {
|
||||
// await themes.put(activeTheme.id, {
|
||||
// ...activeTheme,
|
||||
// isActive: false,
|
||||
// });
|
||||
// }
|
||||
|
||||
WindowManager.mainWindow?.webContents.send("css-injected", theme.code);
|
||||
// WindowManager.mainWindow?.webContents.send("css-injected", theme.code);
|
||||
|
||||
await themes.put(theme.id, {
|
||||
...theme,
|
||||
isActive: true,
|
||||
});
|
||||
};
|
||||
// await themes.put(theme.id, {
|
||||
// ...theme,
|
||||
// isActive: true,
|
||||
// });
|
||||
// };
|
||||
|
|
12
src/main/events/themes/import-theme.ts
Normal file
12
src/main/events/themes/import-theme.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { registerEvent } from "../register-event";
|
||||
import { WindowManager } from "@main/services";
|
||||
|
||||
const importTheme = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
theme: string,
|
||||
author: string
|
||||
) => {
|
||||
WindowManager.mainWindow?.webContents.send("import-theme", theme, author);
|
||||
};
|
||||
|
||||
registerEvent("importTheme", importTheme);
|
|
@ -10,7 +10,6 @@ import { PythonRPC } from "./services/python-rpc";
|
|||
import { Aria2 } from "./services/aria2";
|
||||
import { db, levelKeys } from "./level";
|
||||
import { loadState } from "./main";
|
||||
import { handleDeepLinkTheme } from "./events/themes/deeplink";
|
||||
|
||||
const { autoUpdater } = updater;
|
||||
|
||||
|
@ -93,7 +92,11 @@ const handleDeepLinkPath = (uri?: string) => {
|
|||
const authorCode = url.searchParams.get("author");
|
||||
|
||||
if (themeName && authorCode) {
|
||||
handleDeepLinkTheme(themeName, authorCode);
|
||||
WindowManager.mainWindow?.webContents.send(
|
||||
"import-theme",
|
||||
themeName,
|
||||
authorCode
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
|
@ -360,6 +360,15 @@ contextBridge.exposeInMainWorld("electron", {
|
|||
getCustomThemeById: (themeId: string) =>
|
||||
ipcRenderer.invoke("getCustomThemeById", themeId),
|
||||
getActiveCustomTheme: () => ipcRenderer.invoke("getActiveCustomTheme"),
|
||||
onImportTheme: (cb: (theme: string, author: string) => void) => {
|
||||
const listener = (
|
||||
_event: Electron.IpcRendererEvent,
|
||||
theme: string,
|
||||
author: string
|
||||
) => cb(theme, author);
|
||||
ipcRenderer.on("import-theme", listener);
|
||||
return () => ipcRenderer.removeListener("import-theme", listener);
|
||||
},
|
||||
|
||||
/* Editor */
|
||||
openEditorWindow: (themeId: string) =>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { useCallback, useEffect, useRef } from "react";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import achievementSound from "@renderer/assets/audio/achievement.wav";
|
||||
import { Sidebar, BottomPanel, Header, Toast } from "@renderer/components";
|
||||
|
||||
|
@ -30,6 +30,7 @@ import { HydraCloudModal } from "./pages/shared-modals/hydra-cloud/hydra-cloud-m
|
|||
|
||||
import { injectCustomCss } from "./helpers";
|
||||
import "./app.scss";
|
||||
import { ImportThemeModal } from "./pages/settings/aparence/modals/import-theme-modal";
|
||||
|
||||
export interface AppProps {
|
||||
children: React.ReactNode;
|
||||
|
@ -38,6 +39,12 @@ export interface AppProps {
|
|||
export function App() {
|
||||
const contentRef = useRef<HTMLDivElement>(null);
|
||||
const { updateLibrary, library } = useLibrary();
|
||||
const [isImportThemeModalVisible, setIsImportThemeModalVisible] =
|
||||
useState(false);
|
||||
const [importTheme, setImportTheme] = useState<{
|
||||
theme: string;
|
||||
author: string;
|
||||
} | null>(null);
|
||||
|
||||
const { t } = useTranslation("app");
|
||||
|
||||
|
@ -271,6 +278,15 @@ export function App() {
|
|||
return () => unsubscribe();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = window.electron.onImportTheme((theme, author) => {
|
||||
setImportTheme({ theme, author });
|
||||
setIsImportThemeModalVisible(true);
|
||||
});
|
||||
|
||||
return () => unsubscribe();
|
||||
}, []);
|
||||
|
||||
const handleToastClose = useCallback(() => {
|
||||
dispatch(closeToast());
|
||||
}, [dispatch]);
|
||||
|
@ -312,6 +328,16 @@ export function App() {
|
|||
/>
|
||||
)}
|
||||
|
||||
{importTheme && (
|
||||
<ImportThemeModal
|
||||
visible={isImportThemeModalVisible}
|
||||
onClose={() => setIsImportThemeModalVisible(false)}
|
||||
onThemeImported={() => setIsImportThemeModalVisible(false)}
|
||||
themeName={importTheme.theme}
|
||||
authorCode={importTheme.author}
|
||||
/>
|
||||
)}
|
||||
|
||||
<main>
|
||||
<Sidebar />
|
||||
|
||||
|
|
3
src/renderer/src/declaration.d.ts
vendored
3
src/renderer/src/declaration.d.ts
vendored
|
@ -289,6 +289,9 @@ declare global {
|
|||
updateCustomTheme: (themeId: string, theme: Theme) => Promise<void>;
|
||||
getCustomThemeById: (themeId: string) => Promise<Theme | null>;
|
||||
getActiveCustomTheme: () => Promise<Theme | null>;
|
||||
onImportTheme: (
|
||||
cb: (theme: string, author: string) => void
|
||||
) => () => Electron.IpcRenderer;
|
||||
|
||||
/* Editor */
|
||||
openEditorWindow: (themeId: string) => Promise<void>;
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
import { Button } from "@renderer/components/button/button";
|
||||
import { Modal } from "@renderer/components/modal/modal";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import "./modals.scss";
|
||||
import { Theme } from "@types";
|
||||
import { injectCustomCss, removeCustomCss } from "@renderer/helpers";
|
||||
import { useToast } from "@renderer/hooks";
|
||||
|
||||
interface ImportThemeModalProps {
|
||||
visible: boolean;
|
||||
onClose: () => void;
|
||||
onThemeImported: () => void;
|
||||
themeName: string;
|
||||
authorCode: string;
|
||||
}
|
||||
|
||||
export const ImportThemeModal = ({
|
||||
visible,
|
||||
onClose,
|
||||
onThemeImported,
|
||||
themeName,
|
||||
authorCode,
|
||||
}: ImportThemeModalProps) => {
|
||||
const { t } = useTranslation("settings");
|
||||
const { showSuccessToast, showErrorToast } = useToast();
|
||||
|
||||
const handleImportTheme = async () => {
|
||||
const theme: Theme = {
|
||||
id: crypto.randomUUID(),
|
||||
name: themeName,
|
||||
isActive: false,
|
||||
author: authorCode,
|
||||
authorName: "spectre",
|
||||
code: `https://hydrathemes.shop/themes/${themeName}.css`,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
|
||||
try {
|
||||
await window.electron.addCustomTheme(theme);
|
||||
|
||||
const currentTheme = await window.electron.getCustomThemeById(theme.id);
|
||||
|
||||
if (!currentTheme) return;
|
||||
|
||||
const activeTheme = await window.electron.getActiveCustomTheme();
|
||||
|
||||
if (activeTheme) {
|
||||
removeCustomCss();
|
||||
await window.electron.updateCustomTheme(activeTheme.id, {
|
||||
...activeTheme,
|
||||
isActive: false,
|
||||
});
|
||||
}
|
||||
|
||||
if (currentTheme.code) {
|
||||
injectCustomCss(currentTheme.code);
|
||||
}
|
||||
|
||||
await window.electron.updateCustomTheme(currentTheme.id, {
|
||||
...currentTheme,
|
||||
isActive: true,
|
||||
});
|
||||
onThemeImported();
|
||||
showSuccessToast(t("theme_imported"));
|
||||
onClose();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
showErrorToast(t("error_importing_theme"));
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
visible={visible}
|
||||
title={t("import_theme")}
|
||||
description={t("import_theme_description")}
|
||||
onClose={onClose}
|
||||
>
|
||||
<div className="delete-all-themes-modal__container">
|
||||
<Button theme="outline" onClick={handleImportTheme}>
|
||||
{t("import_theme")}
|
||||
</Button>
|
||||
|
||||
<Button theme="primary" onClick={onClose}>
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue