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_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",
|
||||
|
|
|
@ -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}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue