From ca75ad67218fcff72ef505d9d3d13d31d498920e Mon Sep 17 00:00:00 2001 From: Hachi-R Date: Sun, 16 Feb 2025 13:26:27 -0300 Subject: [PATCH 1/4] feat: improve theme import with dynamic author information --- .../settings/aparence/modals/import-theme-modal.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) 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 index eba51d6f..b22809ff 100644 --- a/src/renderer/src/pages/settings/aparence/modals/import-theme-modal.tsx +++ b/src/renderer/src/pages/settings/aparence/modals/import-theme-modal.tsx @@ -5,7 +5,7 @@ import "./modals.scss"; import { Theme } from "@types"; import { injectCustomCss, removeCustomCss } from "@renderer/helpers"; import { useToast } from "@renderer/hooks"; - +import { UserProfile } from "@types"; interface ImportThemeModalProps { visible: boolean; onClose: () => void; @@ -24,13 +24,18 @@ export const ImportThemeModal = ({ const { t } = useTranslation("settings"); const { showSuccessToast, showErrorToast } = useToast(); + let author: UserProfile | null = null; + window.electron.getUser(authorCode).then((user) => { + author = user; + }); + const handleImportTheme = async () => { const theme: Theme = { id: crypto.randomUUID(), name: themeName, isActive: false, - author: authorCode, - authorName: "spectre", + author: author?.id, + authorName: author?.displayName, code: `https://hydrathemes.shop/themes/${themeName}.css`, createdAt: new Date(), updatedAt: new Date(), From ef28337729e15ef12a6c6cca07eb21e94978b827 Mon Sep 17 00:00:00 2001 From: Hachi-R Date: Sun, 16 Feb 2025 15:17:11 -0300 Subject: [PATCH 2/4] feat: enhance theme import flow with redirect and modal --- src/main/index.ts | 15 ++++++---- .../src/context/settings/settings.context.tsx | 15 ++++++++-- .../settings/aparence/settings-appearance.tsx | 29 +++++++++++++++++++ 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/src/main/index.ts b/src/main/index.ts index 919e48c6..1678d207 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -10,6 +10,7 @@ import { PythonRPC } from "./services/python-rpc"; import { Aria2 } from "./services/aria2"; import { db, levelKeys } from "./level"; import { loadState } from "./main"; +import { sleep } from "./helpers"; const { autoUpdater } = updater; @@ -92,11 +93,15 @@ const handleDeepLinkPath = (uri?: string) => { const authorCode = url.searchParams.get("author"); if (themeName && authorCode) { - WindowManager.mainWindow?.webContents.send( - "import-theme", - themeName, - authorCode - ); + WindowManager.redirect(`settings?theme=true`); + + sleep(1000).then(() => { + WindowManager.mainWindow?.webContents.send( + "import-theme", + themeName, + authorCode + ); + }); } } } catch (error) { diff --git a/src/renderer/src/context/settings/settings.context.tsx b/src/renderer/src/context/settings/settings.context.tsx index c1031a0c..b93efa55 100644 --- a/src/renderer/src/context/settings/settings.context.tsx +++ b/src/renderer/src/context/settings/settings.context.tsx @@ -37,22 +37,33 @@ export function SettingsContextProvider({ }: Readonly) { const dispatch = useAppDispatch(); const [sourceUrl, setSourceUrl] = useState(null); + const [appearanceUrl, setAppearanceUrl] = useState(null); const [currentCategoryIndex, setCurrentCategoryIndex] = useState(0); const [blockedUsers, setBlockedUsers] = useState([]); const [searchParams] = useSearchParams(); const defaultSourceUrl = searchParams.get("urls"); - + const defaultAppearanceUrl = searchParams.get("theme"); useEffect(() => { if (sourceUrl) setCurrentCategoryIndex(2); }, [sourceUrl]); - + useEffect(() => { if (defaultSourceUrl) { setSourceUrl(defaultSourceUrl); } }, [defaultSourceUrl]); + + useEffect(() => { + if (appearanceUrl) setCurrentCategoryIndex(3); + }, [appearanceUrl]); + + useEffect(() => { + if (defaultAppearanceUrl) { + setAppearanceUrl(defaultAppearanceUrl); + } + }, [defaultAppearanceUrl]); const fetchBlockedUsers = useCallback(async () => { const blockedUsers = await window.electron.getBlockedUsers(12, 0); diff --git a/src/renderer/src/pages/settings/aparence/settings-appearance.tsx b/src/renderer/src/pages/settings/aparence/settings-appearance.tsx index 4a0ff317..7a11b46b 100644 --- a/src/renderer/src/pages/settings/aparence/settings-appearance.tsx +++ b/src/renderer/src/pages/settings/aparence/settings-appearance.tsx @@ -2,9 +2,16 @@ import { useEffect, useState } from "react"; import "./settings-appearance.scss"; import { ThemeActions, ThemeCard, ThemePlaceholder } from "./index"; import type { Theme } from "@types"; +import { ImportThemeModal } from "./modals/import-theme-modal"; export const SettingsAppearance = () => { const [themes, setThemes] = useState([]); + const [isImportThemeModalVisible, setIsImportThemeModalVisible] = + useState(false); + const [importTheme, setImportTheme] = useState<{ + theme: string; + author: string; + } | null>(null); const loadThemes = async () => { const themesList = await window.electron.getAllCustomThemes(); @@ -23,6 +30,15 @@ export const SettingsAppearance = () => { return () => unsubscribe(); }, []); + useEffect(() => { + const unsubscribe = window.electron.onImportTheme((theme, author) => { + setIsImportThemeModalVisible(true); + setImportTheme({ theme, author }); + }); + + return () => unsubscribe(); + }, []); + return (
@@ -46,6 +62,19 @@ export const SettingsAppearance = () => { )) )}
+ + {importTheme && ( + setIsImportThemeModalVisible(false)} + onThemeImported={() => { + setIsImportThemeModalVisible(false); + loadThemes(); + }} + themeName={importTheme.theme} + authorCode={importTheme.author} + /> + )} ); }; From 569c80cbf423a942521f21c1651b3e75a20c5955 Mon Sep 17 00:00:00 2001 From: Hachi-R Date: Sun, 16 Feb 2025 16:10:02 -0300 Subject: [PATCH 3/4] refactor: simplify theme import handling and remove redundant code --- src/main/events/index.ts | 1 - src/main/events/themes/import-theme.ts | 12 ------ src/main/index.ts | 11 +---- src/preload/index.ts | 9 ----- src/renderer/src/app.tsx | 28 +------------ .../src/context/settings/settings.context.tsx | 40 ++++++++++++------- src/renderer/src/declaration.d.ts | 3 -- .../settings/aparence/settings-appearance.tsx | 20 ++++++---- src/renderer/src/pages/settings/settings.tsx | 14 ++++++- 9 files changed, 53 insertions(+), 85 deletions(-) delete mode 100644 src/main/events/themes/import-theme.ts diff --git a/src/main/events/index.ts b/src/main/events/index.ts index d3f70c2d..3dd9c298 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -87,7 +87,6 @@ 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 "./themes/toggle-custom-theme"; import { isPortableVersion } from "@main/helpers"; diff --git a/src/main/events/themes/import-theme.ts b/src/main/events/themes/import-theme.ts deleted file mode 100644 index 50ef940f..00000000 --- a/src/main/events/themes/import-theme.ts +++ /dev/null @@ -1,12 +0,0 @@ -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 1678d207..7b4ee618 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 { sleep } from "./helpers"; const { autoUpdater } = updater; @@ -93,15 +92,7 @@ const handleDeepLinkPath = (uri?: string) => { const authorCode = url.searchParams.get("author"); if (themeName && authorCode) { - WindowManager.redirect(`settings?theme=true`); - - sleep(1000).then(() => { - WindowManager.mainWindow?.webContents.send( - "import-theme", - themeName, - authorCode - ); - }); + WindowManager.redirect(`settings?theme=${themeName}&author=${authorCode}`); } } } catch (error) { diff --git a/src/preload/index.ts b/src/preload/index.ts index 34e3b8fd..a7b7b753 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -362,15 +362,6 @@ contextBridge.exposeInMainWorld("electron", { getActiveCustomTheme: () => ipcRenderer.invoke("getActiveCustomTheme"), toggleCustomTheme: (themeId: string, isActive: boolean) => ipcRenderer.invoke("toggleCustomTheme", themeId, isActive), - 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 55a94e21..daa93a6b 100644 --- a/src/renderer/src/app.tsx +++ b/src/renderer/src/app.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useRef } from "react"; import achievementSound from "@renderer/assets/audio/achievement.wav"; import { Sidebar, BottomPanel, Header, Toast } from "@renderer/components"; @@ -30,7 +30,6 @@ 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; @@ -39,12 +38,6 @@ 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"); @@ -278,15 +271,6 @@ 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]); @@ -328,16 +312,6 @@ export function App() { /> )} - {importTheme && ( - setIsImportThemeModalVisible(false)} - onThemeImported={() => setIsImportThemeModalVisible(false)} - themeName={importTheme.theme} - authorCode={importTheme.author} - /> - )} -
diff --git a/src/renderer/src/context/settings/settings.context.tsx b/src/renderer/src/context/settings/settings.context.tsx index b93efa55..ca5af9a4 100644 --- a/src/renderer/src/context/settings/settings.context.tsx +++ b/src/renderer/src/context/settings/settings.context.tsx @@ -13,16 +13,20 @@ export interface SettingsContext { currentCategoryIndex: number; blockedUsers: UserBlocks["blocks"]; fetchBlockedUsers: () => Promise; + appearanceTheme: string | null; + appearanceAuthor: string | null; } export const settingsContext = createContext({ - updateUserPreferences: async () => {}, - setCurrentCategoryIndex: () => {}, - clearSourceUrl: () => {}, + updateUserPreferences: async () => { }, + setCurrentCategoryIndex: () => { }, + clearSourceUrl: () => { }, sourceUrl: null, currentCategoryIndex: 0, blockedUsers: [], - fetchBlockedUsers: async () => {}, + fetchBlockedUsers: async () => { }, + appearanceTheme: null, + appearanceAuthor: null, }); const { Provider } = settingsContext; @@ -37,33 +41,39 @@ export function SettingsContextProvider({ }: Readonly) { const dispatch = useAppDispatch(); const [sourceUrl, setSourceUrl] = useState(null); - const [appearanceUrl, setAppearanceUrl] = useState(null); + const [appearanceTheme, setAppearanceTheme] = useState(null); + const [appearanceAuthor, setAppearanceAuthor] = useState( + null + ); const [currentCategoryIndex, setCurrentCategoryIndex] = useState(0); const [blockedUsers, setBlockedUsers] = useState([]); const [searchParams] = useSearchParams(); const defaultSourceUrl = searchParams.get("urls"); - const defaultAppearanceUrl = searchParams.get("theme"); + const defaultAppearanceTheme = searchParams.get("theme"); + const defaultAppearanceAuthor = searchParams.get("author"); + useEffect(() => { if (sourceUrl) setCurrentCategoryIndex(2); }, [sourceUrl]); - + useEffect(() => { if (defaultSourceUrl) { setSourceUrl(defaultSourceUrl); } }, [defaultSourceUrl]); - + useEffect(() => { - if (appearanceUrl) setCurrentCategoryIndex(3); - }, [appearanceUrl]); - + if (appearanceTheme) setCurrentCategoryIndex(3); + }, [appearanceTheme]); + useEffect(() => { - if (defaultAppearanceUrl) { - setAppearanceUrl(defaultAppearanceUrl); + if (defaultAppearanceTheme && defaultAppearanceAuthor) { + setAppearanceTheme(defaultAppearanceTheme); + setAppearanceAuthor(defaultAppearanceAuthor); } - }, [defaultAppearanceUrl]); + }, [defaultAppearanceTheme, defaultAppearanceAuthor]); const fetchBlockedUsers = useCallback(async () => { const blockedUsers = await window.electron.getBlockedUsers(12, 0); @@ -93,6 +103,8 @@ export function SettingsContextProvider({ currentCategoryIndex, sourceUrl, blockedUsers, + appearanceTheme, + appearanceAuthor, }} > {children} diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index ab36b5c8..2deffb05 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -290,9 +290,6 @@ declare global { getCustomThemeById: (themeId: string) => Promise; getActiveCustomTheme: () => Promise; toggleCustomTheme: (themeId: string, isActive: boolean) => Promise; - onImportTheme: ( - cb: (theme: string, author: string) => void - ) => () => Electron.IpcRenderer; /* Editor */ openEditorWindow: (themeId: string) => Promise; diff --git a/src/renderer/src/pages/settings/aparence/settings-appearance.tsx b/src/renderer/src/pages/settings/aparence/settings-appearance.tsx index 7a11b46b..4da0193f 100644 --- a/src/renderer/src/pages/settings/aparence/settings-appearance.tsx +++ b/src/renderer/src/pages/settings/aparence/settings-appearance.tsx @@ -4,7 +4,12 @@ import { ThemeActions, ThemeCard, ThemePlaceholder } from "./index"; import type { Theme } from "@types"; import { ImportThemeModal } from "./modals/import-theme-modal"; -export const SettingsAppearance = () => { +interface SettingsAppearanceProps { + appearanceTheme: string | null; + appearanceAuthor: string | null; +} + +export const SettingsAppearance = ({ appearanceTheme, appearanceAuthor }: SettingsAppearanceProps) => { const [themes, setThemes] = useState([]); const [isImportThemeModalVisible, setIsImportThemeModalVisible] = useState(false); @@ -31,13 +36,14 @@ export const SettingsAppearance = () => { }, []); useEffect(() => { - const unsubscribe = window.electron.onImportTheme((theme, author) => { + if (appearanceTheme && appearanceAuthor) { setIsImportThemeModalVisible(true); - setImportTheme({ theme, author }); - }); - - return () => unsubscribe(); - }, []); + setImportTheme({ + theme: appearanceTheme, + author: appearanceAuthor + }); + } + }, [appearanceTheme, appearanceAuthor]); return (
diff --git a/src/renderer/src/pages/settings/settings.tsx b/src/renderer/src/pages/settings/settings.tsx index f28cf6e5..4fa7821c 100644 --- a/src/renderer/src/pages/settings/settings.tsx +++ b/src/renderer/src/pages/settings/settings.tsx @@ -61,7 +61,12 @@ export default function Settings() { return ( - {({ currentCategoryIndex, setCurrentCategoryIndex }) => { + {({ + currentCategoryIndex, + setCurrentCategoryIndex, + appearanceTheme, + appearanceAuthor, + }) => { const renderCategory = () => { if (currentCategoryIndex === 0) { return ; @@ -76,7 +81,12 @@ export default function Settings() { } if (currentCategoryIndex === 3) { - return ; + return ( + + ); } if (currentCategoryIndex === 4) { From 4cfecf849310e061a176e1e8191bc06ff48491b2 Mon Sep 17 00:00:00 2001 From: Hachi-R Date: Sun, 16 Feb 2025 16:10:14 -0300 Subject: [PATCH 4/4] lint --- src/main/index.ts | 4 +++- .../src/context/settings/settings.context.tsx | 12 +++++------- .../pages/settings/aparence/settings-appearance.tsx | 7 +++++-- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/main/index.ts b/src/main/index.ts index 7b4ee618..3665c2b7 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -92,7 +92,9 @@ const handleDeepLinkPath = (uri?: string) => { const authorCode = url.searchParams.get("author"); if (themeName && authorCode) { - WindowManager.redirect(`settings?theme=${themeName}&author=${authorCode}`); + WindowManager.redirect( + `settings?theme=${themeName}&author=${authorCode}` + ); } } } catch (error) { diff --git a/src/renderer/src/context/settings/settings.context.tsx b/src/renderer/src/context/settings/settings.context.tsx index ca5af9a4..95c8623f 100644 --- a/src/renderer/src/context/settings/settings.context.tsx +++ b/src/renderer/src/context/settings/settings.context.tsx @@ -18,13 +18,13 @@ export interface SettingsContext { } export const settingsContext = createContext({ - updateUserPreferences: async () => { }, - setCurrentCategoryIndex: () => { }, - clearSourceUrl: () => { }, + updateUserPreferences: async () => {}, + setCurrentCategoryIndex: () => {}, + clearSourceUrl: () => {}, sourceUrl: null, currentCategoryIndex: 0, blockedUsers: [], - fetchBlockedUsers: async () => { }, + fetchBlockedUsers: async () => {}, appearanceTheme: null, appearanceAuthor: null, }); @@ -42,9 +42,7 @@ export function SettingsContextProvider({ const dispatch = useAppDispatch(); const [sourceUrl, setSourceUrl] = useState(null); const [appearanceTheme, setAppearanceTheme] = useState(null); - const [appearanceAuthor, setAppearanceAuthor] = useState( - null - ); + const [appearanceAuthor, setAppearanceAuthor] = useState(null); const [currentCategoryIndex, setCurrentCategoryIndex] = useState(0); const [blockedUsers, setBlockedUsers] = useState([]); diff --git a/src/renderer/src/pages/settings/aparence/settings-appearance.tsx b/src/renderer/src/pages/settings/aparence/settings-appearance.tsx index 4da0193f..d14b4fe7 100644 --- a/src/renderer/src/pages/settings/aparence/settings-appearance.tsx +++ b/src/renderer/src/pages/settings/aparence/settings-appearance.tsx @@ -9,7 +9,10 @@ interface SettingsAppearanceProps { appearanceAuthor: string | null; } -export const SettingsAppearance = ({ appearanceTheme, appearanceAuthor }: SettingsAppearanceProps) => { +export const SettingsAppearance = ({ + appearanceTheme, + appearanceAuthor, +}: SettingsAppearanceProps) => { const [themes, setThemes] = useState([]); const [isImportThemeModalVisible, setIsImportThemeModalVisible] = useState(false); @@ -40,7 +43,7 @@ export const SettingsAppearance = ({ appearanceTheme, appearanceAuthor }: Settin setIsImportThemeModalVisible(true); setImportTheme({ theme: appearanceTheme, - author: appearanceAuthor + author: appearanceAuthor, }); } }, [appearanceTheme, appearanceAuthor]);