Merge branch 'feature/custom-themes' of github.com:hydralauncher/hydra into feature/custom-themes

This commit is contained in:
Chubby Granny Chaser 2025-02-16 21:43:20 +00:00
commit 777f8573d2
No known key found for this signature in database
6 changed files with 63 additions and 32 deletions

View file

@ -328,7 +328,11 @@
"torbox_description": "TorBox is your premium seedbox service rivaling even the best servers on the market.",
"torbox_account_linked": "TorBox 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": {
"download_complete": "Download complete",

View file

@ -101,11 +101,12 @@ const handleDeepLinkPath = (uri?: string) => {
if (url.host === "install-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(
`settings?theme=${themeName}&author=${authorCode}`
`settings?theme=${themeName}&authorId=${authorId}&authorName=${authorName}`
);
}
}

View file

@ -14,7 +14,8 @@ export interface SettingsContext {
blockedUsers: UserBlocks["blocks"];
fetchBlockedUsers: () => Promise<void>;
appearanceTheme: string | null;
appearanceAuthor: string | null;
appearanceAuthorId: string | null;
appearanceAuthorName: string | null;
}
export const settingsContext = createContext<SettingsContext>({
@ -26,7 +27,8 @@ export const settingsContext = createContext<SettingsContext>({
blockedUsers: [],
fetchBlockedUsers: async () => {},
appearanceTheme: null,
appearanceAuthor: null,
appearanceAuthorId: null,
appearanceAuthorName: null,
});
const { Provider } = settingsContext;
@ -42,7 +44,12 @@ export function SettingsContextProvider({
const dispatch = useAppDispatch();
const [sourceUrl, setSourceUrl] = 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 [blockedUsers, setBlockedUsers] = useState<UserBlocks["blocks"]>([]);
@ -50,7 +57,8 @@ export function SettingsContextProvider({
const [searchParams] = useSearchParams();
const defaultSourceUrl = searchParams.get("urls");
const defaultAppearanceTheme = searchParams.get("theme");
const defaultAppearanceAuthor = searchParams.get("author");
const defaultAppearanceAuthorId = searchParams.get("authorId");
const defaultAppearanceAuthorName = searchParams.get("authorName");
useEffect(() => {
if (sourceUrl) setCurrentCategoryIndex(2);
@ -67,11 +75,20 @@ export function SettingsContextProvider({
}, [appearanceTheme]);
useEffect(() => {
if (defaultAppearanceTheme && defaultAppearanceAuthor) {
if (
defaultAppearanceTheme &&
defaultAppearanceAuthorId &&
defaultAppearanceAuthorName
) {
setAppearanceTheme(defaultAppearanceTheme);
setAppearanceAuthor(defaultAppearanceAuthor);
setAppearanceAuthorId(defaultAppearanceAuthorId);
setAppearanceAuthorName(defaultAppearanceAuthorName);
}
}, [defaultAppearanceTheme, defaultAppearanceAuthor]);
}, [
defaultAppearanceTheme,
defaultAppearanceAuthorId,
defaultAppearanceAuthorName,
]);
const fetchBlockedUsers = useCallback(async () => {
const blockedUsers = await window.electron.getBlockedUsers(12, 0);
@ -102,7 +119,8 @@ export function SettingsContextProvider({
sourceUrl,
blockedUsers,
appearanceTheme,
appearanceAuthor,
appearanceAuthorId,
appearanceAuthorName,
}}
>
{children}

View file

@ -2,7 +2,7 @@ import { Button } from "@renderer/components/button/button";
import { Modal } from "@renderer/components/modal/modal";
import { useTranslation } from "react-i18next";
import "./modals.scss";
import { Theme, UserProfile } from "@types";
import { Theme } from "@types";
import { injectCustomCss, removeCustomCss } from "@renderer/helpers";
import { useToast } from "@renderer/hooks";
import { THEME_WEB_STORE_URL } from "@renderer/constants";
@ -13,7 +13,8 @@ interface ImportThemeModalProps {
onClose: () => void;
onThemeImported: () => void;
themeName: string;
authorCode: string;
authorId: string;
authorName: string;
}
export const ImportThemeModal = ({
@ -21,23 +22,19 @@ export const ImportThemeModal = ({
onClose,
onThemeImported,
themeName,
authorCode,
authorId,
authorName,
}: ImportThemeModalProps) => {
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: author?.id,
authorName: author?.displayName,
author: authorId,
authorName: authorName,
code: `${THEME_WEB_STORE_URL}/themes/${themeName.toLowerCase()}/theme.css`,
createdAt: new Date(),
updatedAt: new Date(),

View file

@ -6,19 +6,22 @@ import { ImportThemeModal } from "./modals/import-theme-modal";
interface SettingsAppearanceProps {
appearanceTheme: string | null;
appearanceAuthor: string | null;
appearanceAuthorId: string | null;
appearanceAuthorName: string | null;
}
export const SettingsAppearance = ({
appearanceTheme,
appearanceAuthor,
appearanceAuthorId,
appearanceAuthorName,
}: SettingsAppearanceProps) => {
const [themes, setThemes] = useState<Theme[]>([]);
const [isImportThemeModalVisible, setIsImportThemeModalVisible] =
useState(false);
const [importTheme, setImportTheme] = useState<{
theme: string;
author: string;
authorId: string;
authorName: string;
} | null>(null);
const loadThemes = async () => {
@ -39,14 +42,15 @@ export const SettingsAppearance = ({
}, []);
useEffect(() => {
if (appearanceTheme && appearanceAuthor) {
if (appearanceTheme && appearanceAuthorId && appearanceAuthorName) {
setIsImportThemeModalVisible(true);
setImportTheme({
theme: appearanceTheme,
author: appearanceAuthor,
authorId: appearanceAuthorId,
authorName: appearanceAuthorName,
});
}
}, [appearanceTheme, appearanceAuthor]);
}, [appearanceTheme, appearanceAuthorId, appearanceAuthorName]);
return (
<div className="settings-appearance">
@ -81,7 +85,8 @@ export const SettingsAppearance = ({
loadThemes();
}}
themeName={importTheme.theme}
authorCode={importTheme.author}
authorId={importTheme.authorId}
authorName={importTheme.authorName}
/>
)}
</div>

View file

@ -39,7 +39,11 @@ export default function Settings() {
{
tabLabel: (
<>
<img src={torBoxLogo} alt="TorBox" style={{ width: 13 }} />
<img
src={torBoxLogo}
alt="TorBox"
style={{ width: 13, height: 13 }}
/>{" "}
Torbox
</>
),
@ -65,7 +69,8 @@ export default function Settings() {
currentCategoryIndex,
setCurrentCategoryIndex,
appearanceTheme,
appearanceAuthor,
appearanceAuthorId,
appearanceAuthorName,
}) => {
const renderCategory = () => {
if (currentCategoryIndex === 0) {
@ -84,7 +89,8 @@ export default function Settings() {
return (
<SettingsAppearance
appearanceTheme={appearanceTheme}
appearanceAuthor={appearanceAuthor}
appearanceAuthorId={appearanceAuthorId}
appearanceAuthorName={appearanceAuthorName}
/>
);
}