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
777f8573d2
6 changed files with 63 additions and 32 deletions
|
@ -328,7 +328,11 @@
|
||||||
"torbox_description": "TorBox is your premium seedbox service rivaling even the best servers on the market.",
|
"torbox_description": "TorBox is your premium seedbox service rivaling even the best servers on the market.",
|
||||||
"torbox_account_linked": "TorBox account linked",
|
"torbox_account_linked": "TorBox account linked",
|
||||||
"real_debrid_account_linked": "Real-Debrid account linked",
|
"real_debrid_account_linked": "Real-Debrid account linked",
|
||||||
"name_min_length": "Theme name must be at least 3 characters long"
|
"name_min_length": "Theme name must be at least 3 characters long",
|
||||||
|
"import_theme": "Import theme",
|
||||||
|
"import_theme_description": "You will import {{theme}} from the theme store",
|
||||||
|
"error_importing_theme": "Error importing theme",
|
||||||
|
"theme_imported": "Theme imported successfully"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"download_complete": "Download complete",
|
"download_complete": "Download complete",
|
||||||
|
|
|
@ -101,11 +101,12 @@ const handleDeepLinkPath = (uri?: string) => {
|
||||||
|
|
||||||
if (url.host === "install-theme") {
|
if (url.host === "install-theme") {
|
||||||
const themeName = url.searchParams.get("theme");
|
const themeName = url.searchParams.get("theme");
|
||||||
const authorCode = url.searchParams.get("author");
|
const authorId = url.searchParams.get("authorId");
|
||||||
|
const authorName = url.searchParams.get("authorName");
|
||||||
|
|
||||||
if (themeName && authorCode) {
|
if (themeName && authorId && authorName) {
|
||||||
WindowManager.redirect(
|
WindowManager.redirect(
|
||||||
`settings?theme=${themeName}&author=${authorCode}`
|
`settings?theme=${themeName}&authorId=${authorId}&authorName=${authorName}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,8 @@ export interface SettingsContext {
|
||||||
blockedUsers: UserBlocks["blocks"];
|
blockedUsers: UserBlocks["blocks"];
|
||||||
fetchBlockedUsers: () => Promise<void>;
|
fetchBlockedUsers: () => Promise<void>;
|
||||||
appearanceTheme: string | null;
|
appearanceTheme: string | null;
|
||||||
appearanceAuthor: string | null;
|
appearanceAuthorId: string | null;
|
||||||
|
appearanceAuthorName: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const settingsContext = createContext<SettingsContext>({
|
export const settingsContext = createContext<SettingsContext>({
|
||||||
|
@ -26,7 +27,8 @@ export const settingsContext = createContext<SettingsContext>({
|
||||||
blockedUsers: [],
|
blockedUsers: [],
|
||||||
fetchBlockedUsers: async () => {},
|
fetchBlockedUsers: async () => {},
|
||||||
appearanceTheme: null,
|
appearanceTheme: null,
|
||||||
appearanceAuthor: null,
|
appearanceAuthorId: null,
|
||||||
|
appearanceAuthorName: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { Provider } = settingsContext;
|
const { Provider } = settingsContext;
|
||||||
|
@ -42,7 +44,12 @@ export function SettingsContextProvider({
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const [sourceUrl, setSourceUrl] = useState<string | null>(null);
|
const [sourceUrl, setSourceUrl] = useState<string | null>(null);
|
||||||
const [appearanceTheme, setAppearanceTheme] = useState<string | null>(null);
|
const [appearanceTheme, setAppearanceTheme] = useState<string | null>(null);
|
||||||
const [appearanceAuthor, setAppearanceAuthor] = useState<string | null>(null);
|
const [appearanceAuthorId, setAppearanceAuthorId] = useState<string | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
const [appearanceAuthorName, setAppearanceAuthorName] = useState<
|
||||||
|
string | null
|
||||||
|
>(null);
|
||||||
const [currentCategoryIndex, setCurrentCategoryIndex] = useState(0);
|
const [currentCategoryIndex, setCurrentCategoryIndex] = useState(0);
|
||||||
|
|
||||||
const [blockedUsers, setBlockedUsers] = useState<UserBlocks["blocks"]>([]);
|
const [blockedUsers, setBlockedUsers] = useState<UserBlocks["blocks"]>([]);
|
||||||
|
@ -50,7 +57,8 @@ export function SettingsContextProvider({
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const defaultSourceUrl = searchParams.get("urls");
|
const defaultSourceUrl = searchParams.get("urls");
|
||||||
const defaultAppearanceTheme = searchParams.get("theme");
|
const defaultAppearanceTheme = searchParams.get("theme");
|
||||||
const defaultAppearanceAuthor = searchParams.get("author");
|
const defaultAppearanceAuthorId = searchParams.get("authorId");
|
||||||
|
const defaultAppearanceAuthorName = searchParams.get("authorName");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (sourceUrl) setCurrentCategoryIndex(2);
|
if (sourceUrl) setCurrentCategoryIndex(2);
|
||||||
|
@ -67,11 +75,20 @@ export function SettingsContextProvider({
|
||||||
}, [appearanceTheme]);
|
}, [appearanceTheme]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (defaultAppearanceTheme && defaultAppearanceAuthor) {
|
if (
|
||||||
|
defaultAppearanceTheme &&
|
||||||
|
defaultAppearanceAuthorId &&
|
||||||
|
defaultAppearanceAuthorName
|
||||||
|
) {
|
||||||
setAppearanceTheme(defaultAppearanceTheme);
|
setAppearanceTheme(defaultAppearanceTheme);
|
||||||
setAppearanceAuthor(defaultAppearanceAuthor);
|
setAppearanceAuthorId(defaultAppearanceAuthorId);
|
||||||
|
setAppearanceAuthorName(defaultAppearanceAuthorName);
|
||||||
}
|
}
|
||||||
}, [defaultAppearanceTheme, defaultAppearanceAuthor]);
|
}, [
|
||||||
|
defaultAppearanceTheme,
|
||||||
|
defaultAppearanceAuthorId,
|
||||||
|
defaultAppearanceAuthorName,
|
||||||
|
]);
|
||||||
|
|
||||||
const fetchBlockedUsers = useCallback(async () => {
|
const fetchBlockedUsers = useCallback(async () => {
|
||||||
const blockedUsers = await window.electron.getBlockedUsers(12, 0);
|
const blockedUsers = await window.electron.getBlockedUsers(12, 0);
|
||||||
|
@ -102,7 +119,8 @@ export function SettingsContextProvider({
|
||||||
sourceUrl,
|
sourceUrl,
|
||||||
blockedUsers,
|
blockedUsers,
|
||||||
appearanceTheme,
|
appearanceTheme,
|
||||||
appearanceAuthor,
|
appearanceAuthorId,
|
||||||
|
appearanceAuthorName,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Button } from "@renderer/components/button/button";
|
||||||
import { Modal } from "@renderer/components/modal/modal";
|
import { Modal } from "@renderer/components/modal/modal";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import "./modals.scss";
|
import "./modals.scss";
|
||||||
import { Theme, UserProfile } from "@types";
|
import { Theme } from "@types";
|
||||||
import { injectCustomCss, removeCustomCss } from "@renderer/helpers";
|
import { injectCustomCss, removeCustomCss } from "@renderer/helpers";
|
||||||
import { useToast } from "@renderer/hooks";
|
import { useToast } from "@renderer/hooks";
|
||||||
import { THEME_WEB_STORE_URL } from "@renderer/constants";
|
import { THEME_WEB_STORE_URL } from "@renderer/constants";
|
||||||
|
@ -13,7 +13,8 @@ interface ImportThemeModalProps {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onThemeImported: () => void;
|
onThemeImported: () => void;
|
||||||
themeName: string;
|
themeName: string;
|
||||||
authorCode: string;
|
authorId: string;
|
||||||
|
authorName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ImportThemeModal = ({
|
export const ImportThemeModal = ({
|
||||||
|
@ -21,23 +22,19 @@ export const ImportThemeModal = ({
|
||||||
onClose,
|
onClose,
|
||||||
onThemeImported,
|
onThemeImported,
|
||||||
themeName,
|
themeName,
|
||||||
authorCode,
|
authorId,
|
||||||
|
authorName,
|
||||||
}: ImportThemeModalProps) => {
|
}: ImportThemeModalProps) => {
|
||||||
const { t } = useTranslation("settings");
|
const { t } = useTranslation("settings");
|
||||||
const { showSuccessToast, showErrorToast } = useToast();
|
const { showSuccessToast, showErrorToast } = useToast();
|
||||||
|
|
||||||
let author: UserProfile | null = null;
|
|
||||||
window.electron.getUser(authorCode).then((user) => {
|
|
||||||
author = user;
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleImportTheme = async () => {
|
const handleImportTheme = async () => {
|
||||||
const theme: Theme = {
|
const theme: Theme = {
|
||||||
id: crypto.randomUUID(),
|
id: crypto.randomUUID(),
|
||||||
name: themeName,
|
name: themeName,
|
||||||
isActive: false,
|
isActive: false,
|
||||||
author: author?.id,
|
author: authorId,
|
||||||
authorName: author?.displayName,
|
authorName: authorName,
|
||||||
code: `${THEME_WEB_STORE_URL}/themes/${themeName.toLowerCase()}/theme.css`,
|
code: `${THEME_WEB_STORE_URL}/themes/${themeName.toLowerCase()}/theme.css`,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
|
|
|
@ -6,19 +6,22 @@ import { ImportThemeModal } from "./modals/import-theme-modal";
|
||||||
|
|
||||||
interface SettingsAppearanceProps {
|
interface SettingsAppearanceProps {
|
||||||
appearanceTheme: string | null;
|
appearanceTheme: string | null;
|
||||||
appearanceAuthor: string | null;
|
appearanceAuthorId: string | null;
|
||||||
|
appearanceAuthorName: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SettingsAppearance = ({
|
export const SettingsAppearance = ({
|
||||||
appearanceTheme,
|
appearanceTheme,
|
||||||
appearanceAuthor,
|
appearanceAuthorId,
|
||||||
|
appearanceAuthorName,
|
||||||
}: SettingsAppearanceProps) => {
|
}: SettingsAppearanceProps) => {
|
||||||
const [themes, setThemes] = useState<Theme[]>([]);
|
const [themes, setThemes] = useState<Theme[]>([]);
|
||||||
const [isImportThemeModalVisible, setIsImportThemeModalVisible] =
|
const [isImportThemeModalVisible, setIsImportThemeModalVisible] =
|
||||||
useState(false);
|
useState(false);
|
||||||
const [importTheme, setImportTheme] = useState<{
|
const [importTheme, setImportTheme] = useState<{
|
||||||
theme: string;
|
theme: string;
|
||||||
author: string;
|
authorId: string;
|
||||||
|
authorName: string;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
|
||||||
const loadThemes = async () => {
|
const loadThemes = async () => {
|
||||||
|
@ -39,14 +42,15 @@ export const SettingsAppearance = ({
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (appearanceTheme && appearanceAuthor) {
|
if (appearanceTheme && appearanceAuthorId && appearanceAuthorName) {
|
||||||
setIsImportThemeModalVisible(true);
|
setIsImportThemeModalVisible(true);
|
||||||
setImportTheme({
|
setImportTheme({
|
||||||
theme: appearanceTheme,
|
theme: appearanceTheme,
|
||||||
author: appearanceAuthor,
|
authorId: appearanceAuthorId,
|
||||||
|
authorName: appearanceAuthorName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [appearanceTheme, appearanceAuthor]);
|
}, [appearanceTheme, appearanceAuthorId, appearanceAuthorName]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-appearance">
|
<div className="settings-appearance">
|
||||||
|
@ -81,7 +85,8 @@ export const SettingsAppearance = ({
|
||||||
loadThemes();
|
loadThemes();
|
||||||
}}
|
}}
|
||||||
themeName={importTheme.theme}
|
themeName={importTheme.theme}
|
||||||
authorCode={importTheme.author}
|
authorId={importTheme.authorId}
|
||||||
|
authorName={importTheme.authorName}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -39,7 +39,11 @@ export default function Settings() {
|
||||||
{
|
{
|
||||||
tabLabel: (
|
tabLabel: (
|
||||||
<>
|
<>
|
||||||
<img src={torBoxLogo} alt="TorBox" style={{ width: 13 }} />
|
<img
|
||||||
|
src={torBoxLogo}
|
||||||
|
alt="TorBox"
|
||||||
|
style={{ width: 13, height: 13 }}
|
||||||
|
/>{" "}
|
||||||
Torbox
|
Torbox
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
|
@ -65,7 +69,8 @@ export default function Settings() {
|
||||||
currentCategoryIndex,
|
currentCategoryIndex,
|
||||||
setCurrentCategoryIndex,
|
setCurrentCategoryIndex,
|
||||||
appearanceTheme,
|
appearanceTheme,
|
||||||
appearanceAuthor,
|
appearanceAuthorId,
|
||||||
|
appearanceAuthorName,
|
||||||
}) => {
|
}) => {
|
||||||
const renderCategory = () => {
|
const renderCategory = () => {
|
||||||
if (currentCategoryIndex === 0) {
|
if (currentCategoryIndex === 0) {
|
||||||
|
@ -84,7 +89,8 @@ export default function Settings() {
|
||||||
return (
|
return (
|
||||||
<SettingsAppearance
|
<SettingsAppearance
|
||||||
appearanceTheme={appearanceTheme}
|
appearanceTheme={appearanceTheme}
|
||||||
appearanceAuthor={appearanceAuthor}
|
appearanceAuthorId={appearanceAuthorId}
|
||||||
|
appearanceAuthorName={appearanceAuthorName}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue