refactor: rework hooks usage

This commit is contained in:
Stavros
2025-09-01 16:41:12 +03:00
parent 3873bb279c
commit 4ef5eef167
8 changed files with 100 additions and 80 deletions

View File

@@ -1,7 +1,7 @@
import { useAppContext } from "@/context/app-context"; import { useAppContext } from "@/context/app-context";
import { LanguageSelector } from "../language/language"; import { LanguageSelector } from "../language/language";
import { Outlet } from "react-router"; import { Outlet } from "react-router";
import { useState } from "react"; import { useCallback, useState } from "react";
import { DomainWarning } from "../domain-warning/domain-warning"; import { DomainWarning } from "../domain-warning/domain-warning";
const BaseLayout = ({ children }: { children: React.ReactNode }) => { const BaseLayout = ({ children }: { children: React.ReactNode }) => {
@@ -24,20 +24,17 @@ const BaseLayout = ({ children }: { children: React.ReactNode }) => {
export const Layout = () => { export const Layout = () => {
const { appUrl } = useAppContext(); const { appUrl } = useAppContext();
const [ignoreDomainWarning, setIgnoreDomainWarning] = useState(false); const [ignoreDomainWarning, setIgnoreDomainWarning] = useState(() => {
return window.sessionStorage.getItem("ignoreDomainWarning") === "true";
});
const currentUrl = window.location.origin; const currentUrl = window.location.origin;
const sessionIgnore = window.sessionStorage.getItem("ignoreDomainWarning");
const handleIgnore = () => { const handleIgnore = useCallback(() => {
window.sessionStorage.setItem("ignoreDomainWarning", "true"); window.sessionStorage.setItem("ignoreDomainWarning", "true");
setIgnoreDomainWarning(true); setIgnoreDomainWarning(true);
}; }, [setIgnoreDomainWarning]);
if ( if (!ignoreDomainWarning && appUrl !== currentUrl) {
!ignoreDomainWarning &&
appUrl !== currentUrl &&
sessionIgnore !== "true"
) {
return ( return (
<BaseLayout> <BaseLayout>
<DomainWarning <DomainWarning

View File

@@ -14,6 +14,7 @@
"loginOauthFailSubtitle": "Failed to get OAuth URL", "loginOauthFailSubtitle": "Failed to get OAuth URL",
"loginOauthSuccessTitle": "Redirecting", "loginOauthSuccessTitle": "Redirecting",
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider", "loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
"continueTitle": "Continue",
"continueRedirectingTitle": "Redirecting...", "continueRedirectingTitle": "Redirecting...",
"continueRedirectingSubtitle": "You should be redirected to the app soon", "continueRedirectingSubtitle": "You should be redirected to the app soon",
"continueRedirectManually": "Redirect me manually", "continueRedirectManually": "Redirect me manually",

View File

@@ -14,6 +14,7 @@
"loginOauthFailSubtitle": "Failed to get OAuth URL", "loginOauthFailSubtitle": "Failed to get OAuth URL",
"loginOauthSuccessTitle": "Redirecting", "loginOauthSuccessTitle": "Redirecting",
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider", "loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
"continueTitle": "Continue",
"continueRedirectingTitle": "Redirecting...", "continueRedirectingTitle": "Redirecting...",
"continueRedirectingSubtitle": "You should be redirected to the app soon", "continueRedirectingSubtitle": "You should be redirected to the app soon",
"continueRedirectManually": "Redirect me manually", "continueRedirectManually": "Redirect me manually",

View File

@@ -15,42 +15,68 @@ import DOMPurify from "dompurify";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
export const ContinuePage = () => { export const ContinuePage = () => {
const { rootDomain } = useAppContext();
const { isLoggedIn } = useUserContext(); const { isLoggedIn } = useUserContext();
const { search } = useLocation();
const { t } = useTranslation();
const navigate = useNavigate();
const [loading, setLoading] = useState(false);
const [showRedirectButton, setShowRedirectButton] = useState(false);
const searchParams = new URLSearchParams(search);
const redirectUri = searchParams.get("redirect_uri");
const isValidRedirectUri =
redirectUri !== null ? isValidUrl(DOMPurify.sanitize(redirectUri)) : false;
const redirectUriObj = isValidRedirectUri
? new URL(redirectUri as string)
: null;
const isTrustedRedirectUri =
redirectUriObj !== null
? redirectUriObj.hostname === rootDomain ||
redirectUriObj.hostname.endsWith(`.${rootDomain}`)
: false;
const isHttpsDowngrade =
redirectUriObj !== null
? redirectUriObj.protocol === "http:" &&
window.location.protocol === "https:"
: false;
const handleRedirect = () => {
setLoading(true);
window.location.replace(DOMPurify.sanitize(redirectUriObj!.toString()));
};
useEffect(() => {
if (
!isLoggedIn ||
!isValidRedirectUri ||
!isTrustedRedirectUri ||
isHttpsDowngrade
) {
return;
}
setTimeout(() => {
handleRedirect();
}, 100);
setTimeout(() => {
setLoading(false);
setShowRedirectButton(true);
}, 1000);
}, []);
if (!isLoggedIn) { if (!isLoggedIn) {
return <Navigate to="/login" />; return <Navigate to="/login" />;
} }
const { rootDomain } = useAppContext(); if (!isValidRedirectUri) {
const { search } = useLocation();
const [loading, setLoading] = useState(false);
const [showRedirectButton, setShowRedirectButton] = useState(false);
const searchParams = new URLSearchParams(search);
const redirectURI = searchParams.get("redirect_uri");
if (!redirectURI) {
return <Navigate to="/logout" />; return <Navigate to="/logout" />;
} }
if (!isValidUrl(DOMPurify.sanitize(redirectURI))) { if (!isTrustedRedirectUri) {
return <Navigate to="/logout" />;
}
const { t } = useTranslation();
const navigate = useNavigate();
const handleRedirect = () => {
setLoading(true);
window.location.href = DOMPurify.sanitize(redirectURI);
};
const redirectURLObj = new URL(redirectURI);
if (
!(redirectURLObj.hostname == rootDomain) &&
!redirectURLObj.hostname.endsWith(`.${rootDomain}`)
) {
return ( return (
<Card className="min-w-xs sm:min-w-sm"> <Card className="min-w-xs sm:min-w-sm">
<CardHeader> <CardHeader>
@@ -88,10 +114,7 @@ export const ContinuePage = () => {
); );
} }
if ( if (isHttpsDowngrade) {
redirectURLObj.protocol === "http:" &&
window.location.protocol === "https:"
) {
return ( return (
<Card className="min-w-xs sm:min-w-sm"> <Card className="min-w-xs sm:min-w-sm">
<CardHeader> <CardHeader>
@@ -124,16 +147,6 @@ export const ContinuePage = () => {
); );
} }
useEffect(() => {
setTimeout(() => {
handleRedirect();
}, 100);
setTimeout(() => {
setLoading(false);
setShowRedirectButton(true);
}, 1000);
}, []);
return ( return (
<Card className="min-w-xs sm:min-w-sm"> <Card className="min-w-xs sm:min-w-sm">
<CardHeader> <CardHeader>

View File

@@ -24,12 +24,8 @@ import { toast } from "sonner";
export const LoginPage = () => { export const LoginPage = () => {
const { isLoggedIn } = useUserContext(); const { isLoggedIn } = useUserContext();
const { configuredProviders, title, oauthAutoRedirect, genericName } =
if (isLoggedIn) { useAppContext();
return <Navigate to="/logout" />;
}
const { configuredProviders, title, oauthAutoRedirect, genericName } = useAppContext();
const { search } = useLocation(); const { search } = useLocation();
const { t } = useTranslation(); const { t } = useTranslation();
const isMounted = useIsMounted(); const isMounted = useIsMounted();
@@ -54,7 +50,7 @@ export const LoginPage = () => {
}); });
setTimeout(() => { setTimeout(() => {
window.location.href = data.data.url; window.location.replace(data.data.url);
}, 500); }, 500);
}, },
onError: () => { onError: () => {
@@ -100,6 +96,7 @@ export const LoginPage = () => {
if ( if (
oauthConfigured && oauthConfigured &&
configuredProviders.includes(oauthAutoRedirect) && configuredProviders.includes(oauthAutoRedirect) &&
!isLoggedIn &&
redirectUri redirectUri
) { ) {
oauthMutation.mutate(oauthAutoRedirect); oauthMutation.mutate(oauthAutoRedirect);
@@ -107,6 +104,10 @@ export const LoginPage = () => {
} }
}, []); }, []);
if (isLoggedIn) {
return <Navigate to="/logout" />;
}
return ( return (
<Card className="min-w-xs sm:min-w-sm"> <Card className="min-w-xs sm:min-w-sm">
<CardHeader> <CardHeader>
@@ -126,7 +127,10 @@ export const LoginPage = () => {
icon={<GoogleIcon />} icon={<GoogleIcon />}
className="w-full" className="w-full"
onClick={() => oauthMutation.mutate("google")} onClick={() => oauthMutation.mutate("google")}
loading={oauthMutation.isPending && oauthMutation.variables === "google"} loading={
oauthMutation.isPending &&
oauthMutation.variables === "google"
}
disabled={oauthMutation.isPending || loginMutation.isPending} disabled={oauthMutation.isPending || loginMutation.isPending}
/> />
)} )}
@@ -136,7 +140,10 @@ export const LoginPage = () => {
icon={<GithubIcon />} icon={<GithubIcon />}
className="w-full" className="w-full"
onClick={() => oauthMutation.mutate("github")} onClick={() => oauthMutation.mutate("github")}
loading={oauthMutation.isPending && oauthMutation.variables === "github"} loading={
oauthMutation.isPending &&
oauthMutation.variables === "github"
}
disabled={oauthMutation.isPending || loginMutation.isPending} disabled={oauthMutation.isPending || loginMutation.isPending}
/> />
)} )}
@@ -146,7 +153,10 @@ export const LoginPage = () => {
icon={<GenericIcon />} icon={<GenericIcon />}
className="w-full" className="w-full"
onClick={() => oauthMutation.mutate("generic")} onClick={() => oauthMutation.mutate("generic")}
loading={oauthMutation.isPending && oauthMutation.variables === "generic"} loading={
oauthMutation.isPending &&
oauthMutation.variables === "generic"
}
disabled={oauthMutation.isPending || loginMutation.isPending} disabled={oauthMutation.isPending || loginMutation.isPending}
/> />
)} )}

View File

@@ -17,11 +17,6 @@ import { toast } from "sonner";
export const LogoutPage = () => { export const LogoutPage = () => {
const { provider, username, isLoggedIn, email } = useUserContext(); const { provider, username, isLoggedIn, email } = useUserContext();
if (!isLoggedIn) {
return <Navigate to="/login" />;
}
const { genericName } = useAppContext(); const { genericName } = useAppContext();
const { t } = useTranslation(); const { t } = useTranslation();
@@ -33,7 +28,7 @@ export const LogoutPage = () => {
description: t("logoutSuccessSubtitle"), description: t("logoutSuccessSubtitle"),
}); });
setTimeout(async () => { setTimeout(() => {
window.location.replace("/login"); window.location.replace("/login");
}, 500); }, 500);
}, },
@@ -44,6 +39,10 @@ export const LogoutPage = () => {
}, },
}); });
if (!isLoggedIn) {
return <Navigate to="/login" />;
}
return ( return (
<Card className="min-w-xs sm:min-w-sm"> <Card className="min-w-xs sm:min-w-sm">
<CardHeader> <CardHeader>

View File

@@ -19,11 +19,6 @@ import { toast } from "sonner";
export const TotpPage = () => { export const TotpPage = () => {
const { totpPending } = useUserContext(); const { totpPending } = useUserContext();
if (!totpPending) {
return <Navigate to="/" />;
}
const { t } = useTranslation(); const { t } = useTranslation();
const { search } = useLocation(); const { search } = useLocation();
const formId = useId(); const formId = useId();
@@ -52,6 +47,10 @@ export const TotpPage = () => {
}, },
}); });
if (!totpPending) {
return <Navigate to="/" />;
}
return ( return (
<Card className="min-w-xs sm:min-w-sm"> <Card className="min-w-xs sm:min-w-sm">
<CardHeader> <CardHeader>

View File

@@ -12,6 +12,10 @@ import { Navigate, useLocation, useNavigate } from "react-router";
export const UnauthorizedPage = () => { export const UnauthorizedPage = () => {
const { search } = useLocation(); const { search } = useLocation();
const { t } = useTranslation();
const navigate = useNavigate();
const [loading, setLoading] = useState(false);
const searchParams = new URLSearchParams(search); const searchParams = new URLSearchParams(search);
const username = searchParams.get("username"); const username = searchParams.get("username");
@@ -19,19 +23,15 @@ export const UnauthorizedPage = () => {
const groupErr = searchParams.get("groupErr"); const groupErr = searchParams.get("groupErr");
const ip = searchParams.get("ip"); const ip = searchParams.get("ip");
if (!username && !ip) {
return <Navigate to="/" />;
}
const { t } = useTranslation();
const navigate = useNavigate();
const [loading, setLoading] = useState(false);
const handleRedirect = () => { const handleRedirect = () => {
setLoading(true); setLoading(true);
navigate("/login"); navigate("/login");
}; };
if (!username && !ip) {
return <Navigate to="/" />;
}
let i18nKey = "unauthorizedLoginSubtitle"; let i18nKey = "unauthorizedLoginSubtitle";
if (resource) { if (resource) {