Login: move thirdparty requests for server and fix image blinking
This commit is contained in:
parent
28c4beb89f
commit
9e040104ec
@ -144,7 +144,7 @@ const SocialNetworks = (props) => {
|
||||
return (
|
||||
<div key={`${item.provider}ProviderItem`}>
|
||||
<SocialButton
|
||||
iconName={icon}
|
||||
IconComponent={icon}
|
||||
label={getProviderTranslation(label, t, item.linked)}
|
||||
$iconOptions={iconOptions}
|
||||
onClick={onClick}
|
||||
|
@ -24,11 +24,8 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { cookies } from "next/headers";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
import { SYSTEM_THEME_KEY } from "@docspace/shared/constants";
|
||||
import { ThemeKeys, WhiteLabelLogoType } from "@docspace/shared/enums";
|
||||
import { getBgPattern, getLogoUrl } from "@docspace/shared/utils/common";
|
||||
import { Scrollbar } from "@docspace/shared/components/scrollbar";
|
||||
import { ColorTheme, ThemeId } from "@docspace/shared/components/color-theme";
|
||||
@ -56,10 +53,6 @@ export default async function Layout({
|
||||
getColorTheme(),
|
||||
]);
|
||||
|
||||
const cookieStore = cookies();
|
||||
|
||||
const systemTheme = cookieStore.get(SYSTEM_THEME_KEY)?.value as ThemeKeys;
|
||||
|
||||
const bgPattern = getBgPattern(colorTheme?.selected);
|
||||
|
||||
const objectSettings = typeof settings === "string" ? undefined : settings;
|
||||
@ -68,8 +61,7 @@ export default async function Layout({
|
||||
|
||||
return (
|
||||
<div style={{ width: "100%", height: "100%" }}>
|
||||
<SimpleNav systemTheme={systemTheme} />
|
||||
|
||||
<SimpleNav />
|
||||
<LoginFormWrapper id="login-page" bgPattern={bgPattern}>
|
||||
<div className="bg-cover" />
|
||||
<Scrollbar id="customScrollBar">
|
||||
|
@ -24,7 +24,14 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { getSettings } from "@/utils/actions";
|
||||
import { PROVIDERS_DATA } from "@docspace/shared/constants";
|
||||
|
||||
import {
|
||||
getCapabilities,
|
||||
getSettings,
|
||||
getSSO,
|
||||
getThirdPartyProviders,
|
||||
} from "@/utils/actions";
|
||||
import Login from "@/components/Login";
|
||||
import LoginForm from "@/components/LoginForm";
|
||||
import ThirdParty from "@/components/ThirdParty";
|
||||
@ -32,7 +39,26 @@ import RecoverAccess from "@/components/RecoverAccess";
|
||||
import Register from "@/components/Register";
|
||||
|
||||
async function Page() {
|
||||
const settings = await getSettings();
|
||||
const [settings, thirdParty, capabilities, ssoSettings] = await Promise.all([
|
||||
getSettings(),
|
||||
getThirdPartyProviders(),
|
||||
getCapabilities(),
|
||||
getSSO(),
|
||||
]);
|
||||
|
||||
const ssoUrl = capabilities ? capabilities.ssoUrl : "";
|
||||
const hideAuthPage = ssoSettings ? ssoSettings.hideAuthPage : false;
|
||||
const ssoExists = !!ssoUrl;
|
||||
const oauthDataExists =
|
||||
!capabilities?.oauthEnabled || !thirdParty || thirdParty.length === 0
|
||||
? false
|
||||
: thirdParty
|
||||
.map((item) => {
|
||||
if (!(item.provider in PROVIDERS_DATA)) return undefined;
|
||||
|
||||
return item;
|
||||
})
|
||||
.some((item) => !!item);
|
||||
|
||||
return (
|
||||
<Login>
|
||||
@ -43,8 +69,16 @@ async function Page() {
|
||||
cookieSettingsEnabled={settings?.cookieSettingsEnabled}
|
||||
reCaptchaPublicKey={settings?.recaptchaPublicKey}
|
||||
reCaptchaType={settings?.recaptchaType}
|
||||
ldapDomain={capabilities?.ldapDomain}
|
||||
/>
|
||||
<ThirdParty
|
||||
thirdParty={thirdParty}
|
||||
capabilities={capabilities}
|
||||
ssoExists={ssoExists}
|
||||
ssoUrl={ssoUrl}
|
||||
hideAuthPage={hideAuthPage}
|
||||
oauthDataExists={oauthDataExists}
|
||||
/>
|
||||
<ThirdParty />
|
||||
{settings.enableAdmMess && <RecoverAccess />}
|
||||
{settings.enabledJoin && (
|
||||
<Register
|
||||
|
@ -36,11 +36,11 @@ import type {
|
||||
TFirebaseSettings,
|
||||
TSettings,
|
||||
} from "@docspace/shared/api/settings/types";
|
||||
import FirebaseHelper from "@docspace/shared/utils/firebase";
|
||||
|
||||
import useTheme from "@/hooks/useTheme";
|
||||
import useDeviceType from "@/hooks/useDeviceType";
|
||||
import useI18N from "@/hooks/useI18N";
|
||||
import FirebaseHelper from "@docspace/shared/utils/firebase";
|
||||
|
||||
import pkg from "../../package.json";
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
import { cookies, headers } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { Toast } from "@docspace/shared/components/toast";
|
||||
import { getBaseUrl } from "@docspace/shared/utils/next-ssr-helper";
|
||||
import { TenantStatus, ThemeKeys } from "@docspace/shared/enums";
|
||||
@ -58,18 +59,11 @@ export default async function RootLayout({
|
||||
|
||||
let redirectUrl = "";
|
||||
|
||||
const timers = { otherOperations: 0 };
|
||||
|
||||
const startOtherOperationsDate = new Date();
|
||||
|
||||
const [settings, colorTheme] = await Promise.all([
|
||||
getSettings(),
|
||||
getColorTheme(),
|
||||
]);
|
||||
|
||||
timers.otherOperations =
|
||||
new Date().getTime() - startOtherOperationsDate.getTime();
|
||||
|
||||
if (settings === "access-restricted") redirectUrl = `/${settings}`;
|
||||
|
||||
if (settings === "portal-not-found") {
|
||||
@ -132,7 +126,6 @@ export default async function RootLayout({
|
||||
systemTheme: systemTheme?.value as ThemeKeys,
|
||||
}}
|
||||
redirectURL={redirectUrl}
|
||||
timers={timers}
|
||||
>
|
||||
<Toast isSSR />
|
||||
{children}
|
||||
|
@ -33,7 +33,6 @@ import { useSearchParams } from "next/navigation";
|
||||
import { useTheme } from "styled-components";
|
||||
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
|
||||
import { WhiteLabelLogoType } from "@docspace/shared/enums";
|
||||
import { getLogoUrl } from "@docspace/shared/utils/common";
|
||||
|
||||
|
@ -38,6 +38,7 @@ import { useTranslation } from "react-i18next";
|
||||
import ReCAPTCHA from "react-google-recaptcha";
|
||||
import HCaptcha from "@hcaptcha/react-hcaptcha";
|
||||
import { useTheme } from "styled-components";
|
||||
import { Id } from "react-toastify";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
@ -52,6 +53,7 @@ import { toastr } from "@docspace/shared/components/toast";
|
||||
import { thirdPartyLogin, checkConfirmLink } from "@docspace/shared/api/user";
|
||||
import { setWithCredentialsStatus } from "@docspace/shared/api/client";
|
||||
import { TValidate } from "@docspace/shared/components/email-input/EmailInput.types";
|
||||
import { RecaptchaType } from "@docspace/shared/enums";
|
||||
|
||||
import { LoginFormProps } from "@/types";
|
||||
import { getEmailFromInvitation, getConfirmDataFromInvitation } from "@/utils";
|
||||
@ -59,11 +61,11 @@ import { getEmailFromInvitation, getConfirmDataFromInvitation } from "@/utils";
|
||||
import EmailContainer from "./sub-components/EmailContainer";
|
||||
import PasswordContainer from "./sub-components/PasswordContainer";
|
||||
import ForgotContainer from "./sub-components/ForgotContainer";
|
||||
import LDAPContainer from "./sub-components/LDAPContainer";
|
||||
|
||||
import { LoginDispatchContext, LoginValueContext } from "../Login";
|
||||
|
||||
import { StyledCaptcha } from "./LoginForm.styled";
|
||||
import { LoginDispatchContext, LoginValueContext } from "../Login";
|
||||
import LDAPContainer from "./sub-components/LDAPContainer";
|
||||
import { RecaptchaType } from "@docspace/shared/enums";
|
||||
|
||||
let showToastr = true;
|
||||
|
||||
@ -72,10 +74,11 @@ const LoginForm = ({
|
||||
cookieSettingsEnabled,
|
||||
reCaptchaPublicKey,
|
||||
reCaptchaType,
|
||||
ldapDomain,
|
||||
}: LoginFormProps) => {
|
||||
const { isLoading, isModalOpen, ldapDomain } = useContext(LoginValueContext);
|
||||
const { isLoading, isModalOpen } = useContext(LoginValueContext);
|
||||
const { setIsLoading } = useContext(LoginDispatchContext);
|
||||
const toastId = useRef(null);
|
||||
const toastId = useRef<Id>();
|
||||
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
|
@ -54,7 +54,7 @@ interface IEmailContainer {
|
||||
onBlurEmail: () => void;
|
||||
onValidateEmail: (res: TValidate) => undefined;
|
||||
isLdapLogin: boolean;
|
||||
ldapDomain: string;
|
||||
ldapDomain?: string;
|
||||
}
|
||||
|
||||
const EmailContainer = ({
|
||||
|
@ -49,7 +49,7 @@ const ForgotPasswordModalDialog = ({
|
||||
userEmail,
|
||||
onDialogClose,
|
||||
}: ForgotPasswordModalDialogProps) => {
|
||||
const [email, setEmail] = useState(userEmail);
|
||||
const [email, setEmail] = useState(userEmail ?? "");
|
||||
const [emailError, setEmailError] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [errorText, setErrorText] = useState("");
|
||||
|
@ -34,6 +34,7 @@ import { mobile } from "@docspace/shared/utils/device";
|
||||
import { getLogoUrl } from "@docspace/shared/utils/common";
|
||||
import { Base, Dark } from "@docspace/shared/themes";
|
||||
import { ThemeKeys, WhiteLabelLogoType } from "@docspace/shared/enums";
|
||||
|
||||
import LanguageComboboxWrapper from "./LanguageCombobox";
|
||||
|
||||
const StyledSimpleNav = styled.div`
|
||||
@ -60,19 +61,19 @@ const StyledSimpleNav = styled.div`
|
||||
|
||||
StyledSimpleNav.defaultProps = { theme: Base };
|
||||
|
||||
interface SimpleNavProps {
|
||||
systemTheme: ThemeKeys;
|
||||
}
|
||||
interface SimpleNavProps {}
|
||||
|
||||
const SimpleNav = ({ systemTheme }: SimpleNavProps) => {
|
||||
const SimpleNav = ({}: SimpleNavProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const isDark = !theme.isBase;
|
||||
|
||||
const logoUrl = getLogoUrl(WhiteLabelLogoType.LightSmall, isDark);
|
||||
|
||||
return (
|
||||
<StyledSimpleNav id="login-header">
|
||||
<img className="logo" src={logoUrl} alt="logo-url" />
|
||||
<LanguageComboboxWrapper />
|
||||
{/* <LanguageComboboxWrapper /> */}
|
||||
</StyledSimpleNav>
|
||||
);
|
||||
};
|
||||
|
@ -33,22 +33,13 @@ import styled from "styled-components";
|
||||
|
||||
import { SocialButtonsGroup } from "@docspace/shared/components/social-buttons-group";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { PROVIDERS_DATA } from "@docspace/shared/constants";
|
||||
import { getOAuthToken, getLoginLink } from "@docspace/shared/utils/common";
|
||||
import {
|
||||
TCapabilities,
|
||||
TGetSsoSettings,
|
||||
TThirdPartyProvider,
|
||||
} from "@docspace/shared/api/settings/types";
|
||||
import { Nullable } from "@docspace/shared/types";
|
||||
|
||||
import SSOIcon from "PUBLIC_DIR/images/sso.react.svg?url";
|
||||
|
||||
import {
|
||||
getCapabilities,
|
||||
getSSO,
|
||||
getThirdPartyProviders,
|
||||
} from "@/utils/actions";
|
||||
import SSOIcon from "PUBLIC_DIR/images/sso.react.svg";
|
||||
|
||||
import { LoginDispatchContext, LoginValueContext } from "./Login";
|
||||
|
||||
@ -57,7 +48,23 @@ const StyledThirdParty = styled.div<{ isVisible: boolean }>`
|
||||
height: auto;
|
||||
`;
|
||||
|
||||
const ThirdParty = () => {
|
||||
type ThirdPartyProps = {
|
||||
thirdParty?: TThirdPartyProvider[];
|
||||
capabilities?: TCapabilities;
|
||||
ssoUrl?: string;
|
||||
ssoExists?: boolean;
|
||||
oauthDataExists?: boolean;
|
||||
hideAuthPage?: boolean;
|
||||
};
|
||||
|
||||
const ThirdParty = ({
|
||||
thirdParty,
|
||||
capabilities,
|
||||
ssoUrl,
|
||||
ssoExists,
|
||||
oauthDataExists,
|
||||
hideAuthPage,
|
||||
}: ThirdPartyProps) => {
|
||||
const { isLoading } = useContext(LoginValueContext);
|
||||
const { setIsModalOpen, setLdapDomain } = useContext(LoginDispatchContext);
|
||||
|
||||
@ -65,33 +72,7 @@ const ThirdParty = () => {
|
||||
|
||||
const { t } = useTranslation(["Login", "Common"]);
|
||||
|
||||
const [capabilities, setCapabilities] =
|
||||
React.useState<Nullable<TCapabilities>>(null);
|
||||
const [thirdPartyProvider, setThirdPartyProvider] =
|
||||
React.useState<Nullable<TThirdPartyProvider[]>>(null);
|
||||
const [ssoSettings, setSsoSettings] =
|
||||
React.useState<Nullable<TGetSsoSettings>>(null);
|
||||
|
||||
const getData = useCallback(async () => {
|
||||
const [thirdParty, capabilities, ssoSettings] = await Promise.all([
|
||||
getThirdPartyProviders(),
|
||||
getCapabilities(),
|
||||
getSSO(),
|
||||
]);
|
||||
|
||||
if (thirdParty) setThirdPartyProvider(thirdParty);
|
||||
if (capabilities) setCapabilities(capabilities);
|
||||
if (ssoSettings) setSsoSettings(ssoSettings);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
getData();
|
||||
}, [getData]);
|
||||
|
||||
useEffect(() => {
|
||||
const ssoUrl = capabilities ? capabilities.ssoUrl : "";
|
||||
const hideAuthPage = ssoSettings ? ssoSettings.hideAuthPage : false;
|
||||
|
||||
if (capabilities?.ldapEnabled && capabilities.ldapDomain)
|
||||
setLdapDomain(capabilities.ldapDomain);
|
||||
|
||||
@ -102,25 +83,7 @@ const ThirdParty = () => {
|
||||
) {
|
||||
window.location.replace(ssoUrl);
|
||||
}
|
||||
}, [capabilities, searchParams, ssoSettings, setLdapDomain]);
|
||||
|
||||
const ssoExists = () => {
|
||||
if (capabilities?.ssoUrl) return true;
|
||||
else return false;
|
||||
};
|
||||
|
||||
const oauthDataExists = () => {
|
||||
if (!capabilities?.oauthEnabled) return false;
|
||||
|
||||
let existProviders = 0;
|
||||
if (thirdPartyProvider && thirdPartyProvider.length > 0)
|
||||
thirdPartyProvider?.map((item) => {
|
||||
if (!(item.provider in PROVIDERS_DATA)) return;
|
||||
existProviders++;
|
||||
});
|
||||
|
||||
return !!existProviders;
|
||||
};
|
||||
}, [capabilities, searchParams, setLdapDomain, ssoUrl, hideAuthPage]);
|
||||
|
||||
const onSocialButtonClick = useCallback(
|
||||
(e: React.MouseEvent<Element, MouseEvent>) => {
|
||||
@ -171,7 +134,7 @@ const ThirdParty = () => {
|
||||
[],
|
||||
);
|
||||
|
||||
const ssoProps = ssoExists()
|
||||
const ssoProps = ssoExists
|
||||
? {
|
||||
ssoUrl: capabilities?.ssoUrl,
|
||||
ssoLabel: capabilities?.ssoLabel,
|
||||
@ -179,7 +142,7 @@ const ThirdParty = () => {
|
||||
}
|
||||
: {};
|
||||
|
||||
const isVisible = oauthDataExists() || ssoExists();
|
||||
const isVisible = oauthDataExists || ssoExists;
|
||||
|
||||
return (
|
||||
isVisible && (
|
||||
@ -188,7 +151,7 @@ const ThirdParty = () => {
|
||||
<Text className="or-label">{t("Common:orContinueWith")}</Text>
|
||||
</div>
|
||||
<SocialButtonsGroup
|
||||
providers={thirdPartyProvider ?? undefined}
|
||||
providers={thirdParty ?? undefined}
|
||||
onClick={onSocialButtonClick}
|
||||
onMoreAuthToggle={setIsModalOpen}
|
||||
t={t}
|
||||
|
@ -25,14 +25,12 @@
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import React from "react";
|
||||
import { i18n } from "i18next";
|
||||
|
||||
import { getCookie, getLanguage } from "@docspace/shared/utils";
|
||||
import { getCookie } from "@docspace/shared/utils";
|
||||
import { LANGUAGE } from "@docspace/shared/constants";
|
||||
import { TSettings } from "@docspace/shared/api/settings/types";
|
||||
|
||||
import { getI18NInstance } from "@/utils/i18n";
|
||||
import { setCookie } from "@docspace/shared/utils/cookie";
|
||||
|
||||
interface UseI18NProps {
|
||||
settings?: TSettings;
|
||||
|
@ -24,8 +24,10 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import useDeviceType from "@/hooks/useDeviceType";
|
||||
import ErrorBoundary from "@docspace/shared/components/error-boundary/ErrorBoundary";
|
||||
|
||||
import useDeviceType from "@/hooks/useDeviceType";
|
||||
|
||||
import { ErrorBoundaryProps } from "./ErrorBoundary.types";
|
||||
|
||||
const ErrorBoundaryWrapper = (props: ErrorBoundaryProps) => {
|
||||
|
@ -33,20 +33,18 @@ import { ThemeProvider } from "@docspace/shared/components/theme-provider";
|
||||
import { TFirebaseSettings } from "@docspace/shared/api/settings/types";
|
||||
import FirebaseHelper from "@docspace/shared/utils/firebase";
|
||||
import { TUser } from "@docspace/shared/api/people/types";
|
||||
import { ThemeKeys } from "@docspace/shared/enums";
|
||||
import { Base, Dark } from "@docspace/shared/themes";
|
||||
|
||||
import { TDataContext } from "@/types";
|
||||
import useI18N from "@/hooks/useI18N";
|
||||
import useTheme from "@/hooks/useTheme";
|
||||
|
||||
import pkgFile from "../../package.json";
|
||||
|
||||
import ErrorBoundaryWrapper from "./ErrorBoundary";
|
||||
|
||||
export const Providers = ({
|
||||
children,
|
||||
value,
|
||||
timers,
|
||||
redirectURL,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
@ -61,10 +59,6 @@ export const Providers = ({
|
||||
if (redirectURL) window.location.replace(redirectURL);
|
||||
}, [redirectURL]);
|
||||
|
||||
React.useEffect(() => {
|
||||
console.log("Timers:", { ...timers });
|
||||
}, [timers]);
|
||||
|
||||
const { i18n } = useI18N({
|
||||
settings: value.settings,
|
||||
});
|
||||
|
@ -86,6 +86,7 @@ export type LoginFormProps = {
|
||||
reCaptchaPublicKey?: string;
|
||||
reCaptchaType?: RecaptchaType;
|
||||
cookieSettingsEnabled: boolean;
|
||||
ldapDomain?: string;
|
||||
};
|
||||
|
||||
export type ForgotPasswordModalDialogProps = {
|
||||
|
Loading…
Reference in New Issue
Block a user