diff --git a/src/main/events/index.ts b/src/main/events/index.ts index 80b02c35..f377cdba 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -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"); diff --git a/src/main/events/themes/deeplink.ts b/src/main/events/themes/deeplink.ts index ca561701..47197a4f 100644 --- a/src/main/events/themes/deeplink.ts +++ b/src/main/events/themes/deeplink.ts @@ -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, +// }); +// }; diff --git a/src/main/events/themes/import-theme.ts b/src/main/events/themes/import-theme.ts new file mode 100644 index 00000000..50ef940f --- /dev/null +++ b/src/main/events/themes/import-theme.ts @@ -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); diff --git a/src/main/index.ts b/src/main/index.ts index 18423ee8..919e48c6 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -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) { diff --git a/src/preload/index.ts b/src/preload/index.ts index 31ae002b..16415b89 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -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) => diff --git a/src/renderer/src/app.tsx b/src/renderer/src/app.tsx index daa93a6b..55a94e21 100644 --- a/src/renderer/src/app.tsx +++ b/src/renderer/src/app.tsx @@ -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(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 && ( + setIsImportThemeModalVisible(false)} + onThemeImported={() => setIsImportThemeModalVisible(false)} + themeName={importTheme.theme} + authorCode={importTheme.author} + /> + )} +
diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index fbb7fbe7..e8a6bf3c 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -289,6 +289,9 @@ declare global { updateCustomTheme: (themeId: string, theme: Theme) => Promise; getCustomThemeById: (themeId: string) => Promise; getActiveCustomTheme: () => Promise; + onImportTheme: ( + cb: (theme: string, author: string) => void + ) => () => Electron.IpcRenderer; /* Editor */ openEditorWindow: (themeId: string) => Promise; diff --git a/src/renderer/src/pages/settings/aparence/modals/import-theme-modal.tsx b/src/renderer/src/pages/settings/aparence/modals/import-theme-modal.tsx new file mode 100644 index 00000000..65eea7c3 --- /dev/null +++ b/src/renderer/src/pages/settings/aparence/modals/import-theme-modal.tsx @@ -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 ( + +
+ + + +
+
+ ); +};