Merge pull request #1184 from ONLYOFFICE/feature/login-error-page

Feature/login error page
This commit is contained in:
Alexey Safronov 2023-02-08 16:48:34 +03:00 committed by GitHub
commit ecd1e8537e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 151 additions and 46 deletions

View File

@ -3,27 +3,27 @@ import StyledButton from "@docspace/components/button/styled-button";
import Base from "@docspace/components/themes/base";
const activeCss = css`
border-color: ${(props) => props.$currentColorScheme.main.buttons};
border-color: ${(props) => props.$currentColorScheme.main?.buttons};
background: ${(props) =>
props.primary && props.$currentColorScheme.main.buttons};
props.primary && props.$currentColorScheme.main?.buttons};
opacity: ${(props) => !props.isDisabled && "1"};
filter: ${(props) =>
props.primary &&
(props.theme.isBase ? "brightness(90%)" : "brightness(82%)")};
color: ${(props) => props.$currentColorScheme.text.buttons};
color: ${(props) => props.$currentColorScheme.text?.buttons};
`;
const hoverCss = css`
border-color: ${(props) => props.$currentColorScheme.main.buttons};
border-color: ${(props) => props.$currentColorScheme.main?.buttons};
background: ${(props) =>
props.primary && props.$currentColorScheme.main.buttons};
props.primary && props.$currentColorScheme.main?.buttons};
opacity: ${(props) => props.primary && !props.isDisabled && "0.85"};
color: ${(props) => props.primary && props.$currentColorScheme.text.buttons};
color: ${(props) => props.primary && props.$currentColorScheme.text?.buttons};
`;
const getDefaultStyles = ({
@ -41,16 +41,16 @@ const getDefaultStyles = ({
css`
${primary &&
css`
background: ${$currentColorScheme.main.buttons};
background: ${$currentColorScheme.main?.buttons};
opacity: ${isDisabled && "0.6"};
border: ${`1px solid`} ${$currentColorScheme.main.buttons};
color: ${$currentColorScheme.text.buttons};
border: ${`1px solid`} ${$currentColorScheme.main?.buttons};
color: ${$currentColorScheme.text?.buttons};
.loader {
svg {
color: ${$currentColorScheme.text.buttons};
color: ${$currentColorScheme.text?.buttons};
}
background-color: ${$currentColorScheme.main.buttons};
background-color: ${$currentColorScheme.main?.buttons};
}
`}

View File

@ -5,7 +5,7 @@ const getDefaultStyles = ({ $currentColorScheme }) =>
$currentColorScheme &&
css`
.login-link {
color: ${$currentColorScheme.main.accent};
color: ${$currentColorScheme?.main?.accent};
}
`;

View File

@ -7,8 +7,8 @@ const getDefaultStyles = ({ $currentColorScheme, hasError, theme }) =>
css`
:focus-within {
border-color: ${hasError
? theme.textArea.focusErrorBorderColor
: $currentColorScheme.main.accent};
? theme?.textArea.focusErrorBorderColor
: $currentColorScheme.main?.accent};
}
`;

View File

@ -334,13 +334,22 @@ const ErrorContainer = (props) => {
</svg>
</div>
{headerText && (
<Headline id="header" type="header">
<Headline id="header" type="header" theme={rest?.theme}>
{headerText}
</Headline>
)}
{bodyText && <Text id="text">{bodyText}</Text>}
{bodyText && (
<Text id="text" theme={rest?.theme}>
{bodyText}
</Text>
)}
{customizedBodyText && (
<Text id="customized-text" fontWeight={600} fontSize="13px">
<Text
id="customized-text"
fontWeight={600}
theme={rest?.theme}
fontSize="13px"
>
{customizedBodyText}
</Text>
)}
@ -348,6 +357,7 @@ const ErrorContainer = (props) => {
{buttonText && buttonUrl && (
<div id="button-container">
<Button
theme={rest?.theme}
id="button"
size="normal"
scale

View File

@ -95,7 +95,7 @@ const MoreLoginModal: React.FC<IMoreLoginNodalProps> = (props) => {
/>
</ProviderRow>
)}
{providers.map((item, index) => {
{providers?.map((item, index) => {
if (!providersData[item.provider]) return;
const { icon, label } = providersData[item.provider];

View File

@ -375,3 +375,5 @@ export const PortalFeaturesLimitations = Object.freeze({
});
export const EDITOR_ID = "docspace_editor";
export const wrongPortalNameUrl = `https://www.onlyoffice.com/wrongportalname.aspx`;

View File

@ -12,6 +12,7 @@ import { version } from "../package.json";
import SocketIOHelper from "../utils/socket";
import { Dark, Base } from "@docspace/components/themes";
import { initPluginStore } from "../../client/src/helpers/plugins";
import { wrongPortalNameUrl } from "@docspace/common/constants";
const themes = {
Dark: Dark,
@ -241,7 +242,7 @@ class SettingsStore {
if (err?.response?.status === 404) {
// portal not found
return window.location.replace(
`https://www.onlyoffice.com/wrongportalname.aspx?url=${window.location.hostname}`
`${wrongPortalNameUrl}?url=${window.location.hostname}`
);
}
});

View File

@ -466,7 +466,7 @@ export const getConvertedSize = (t, size) => {
);
};
export const getBgPattern = (colorSchemeId: number) => {
export const getBgPattern = (colorSchemeId: number | undefined) => {
switch (colorSchemeId) {
case 1:
return `url('${BackgroundPatternReactSvgUrl}')`;

View File

@ -56,6 +56,7 @@ declare global {
utcHoursOffset: number;
utcOffset: string;
version: string;
standalone: boolean;
}
interface IBuildInfo {
@ -89,15 +90,23 @@ declare global {
selected: number;
themes: ITheme[];
}
interface IError {
status: number;
standalone: boolean;
message: string | undefined;
}
interface IInitialState {
portalSettings: IPortalSettings;
buildInfo: IBuildInfo;
providers: ProvidersType;
capabilities: ICapabilities;
match: MatchType;
currentColorScheme: ITheme;
isAuth: boolean;
logoUrls: any;
portalSettings?: IPortalSettings;
buildInfo?: IBuildInfo;
providers?: ProvidersType;
capabilities?: ICapabilities;
match?: MatchType;
currentColorScheme?: ITheme;
isAuth?: boolean;
logoUrls?: any;
error?: IError;
}
interface DevRequest {

View File

@ -6,12 +6,28 @@ import CodeLogin from "./components/CodeLogin";
import initLoginStore from "../store";
import { Provider as MobxProvider } from "mobx-react";
import SimpleNav from "../client/components/sub-components/SimpleNav";
import { wrongPortalNameUrl } from "@docspace/common/constants";
interface ILoginProps extends IInitialState {
isDesktopEditor?: boolean;
}
const App: React.FC<ILoginProps> = (props) => {
const loginStore = initLoginStore(props.currentColorScheme);
const loginStore = initLoginStore(props?.currentColorScheme || {});
React.useEffect(() => {
if (window && props.error) {
const { status, standalone, message } = props.error;
if (status === 404 && !standalone) {
window.location.replace(
`${wrongPortalNameUrl}?url=${window.location.hostname}`
);
}
throw new Error(message);
}
}, []);
return (
<MobxProvider {...loginStore}>
<SimpleNav {...props} />

View File

@ -1,15 +1,18 @@
import React from "react";
import ErrorContainer from "@docspace/common/components/ErrorContainer";
import { useTranslation } from "react-i18next";
import { Dark, Base } from "@docspace/components/themes";
interface IError520Props {
match?: {
params: MatchType;
};
theme?: any;
}
interface IErrorBoundaryProps extends IError520Props {
onError?: (error: any, errorInfo: any) => void;
theme?: any;
children?: React.ReactNode;
}
@ -21,8 +24,21 @@ const Error520: React.FC<IError520Props> = ({ match }) => {
const { t } = useTranslation(["Common"]);
const { error } = (match && match.params) || {};
const theme =
typeof window !== "undefined" &&
window.matchMedia &&
window.matchMedia("(prefers-color-scheme: dark)").matches
? Dark
: Base;
const themeProps = theme ? { theme } : {};
return (
<ErrorContainer headerText={t("SomethingWentWrong")} bodyText={error} />
<ErrorContainer
headerText={t("SomethingWentWrong")}
bodyText={error}
{...themeProps}
/>
);
};

View File

@ -43,7 +43,7 @@ const Login: React.FC<ILoginProps> = ({
logoUrls,
}) => {
const isRestoringPortal =
portalSettings.tenantStatus === TenantStatus.PortalRestore;
portalSettings?.tenantStatus === TenantStatus.PortalRestore;
useEffect(() => {
isRestoringPortal && window.location.replace("/preparation-portal");
@ -52,7 +52,11 @@ const Login: React.FC<ILoginProps> = ({
const [moreAuthVisible, setMoreAuthVisible] = useState(false);
const [recoverDialogVisible, setRecoverDialogVisible] = useState(false);
const { enabledJoin, greetingSettings, enableAdmMess } = portalSettings;
const { enabledJoin, greetingSettings, enableAdmMess } = portalSettings || {
enabledJoin: false,
greetingSettings: false,
enableAdmMess: false,
};
const ssoLabel = capabilities?.ssoLabel;
const ssoUrl = capabilities?.ssoUrl;
@ -96,7 +100,7 @@ const Login: React.FC<ILoginProps> = ({
const oauthDataExists = () => {
let existProviders = 0;
providers && providers.length > 0;
providers.map((item) => {
providers?.map((item) => {
if (!providersData[item.provider]) return;
existProviders++;
});
@ -187,10 +191,12 @@ const Login: React.FC<ILoginProps> = ({
setRecoverDialogVisible(!recoverDialogVisible);
};
const bgPattern = getBgPattern(currentColorScheme.id);
const bgPattern = getBgPattern(currentColorScheme?.id);
const logo = Object.values(logoUrls)[1];
const logoUrl = !theme.isBase
const logo = logoUrls && Object.values(logoUrls)[1];
const logoUrl = !logo
? undefined
: !theme?.isBase
? getLogoFromPath(logo.path.dark)
: getLogoFromPath(logo.path.light);
@ -242,7 +248,7 @@ const Login: React.FC<ILoginProps> = ({
<LoginForm
isDesktop={!!isDesktopEditor}
isLoading={isLoading}
hashSettings={portalSettings.passwordHash}
hashSettings={portalSettings?.passwordHash}
setIsLoading={setIsLoading}
onRecoverDialogVisible={onRecoverDialogVisible}
match={match}

View File

@ -59,7 +59,10 @@ const LoginForm: React.FC<ILoginFormProps> = ({
const { t } = useTranslation(["Login", "Common"]);
const { message, confirmedEmail } = match;
const { message, confirmedEmail } = match || {
message: "",
confirmedEmail: "",
};
const authCallback = (profile: string) => {
localStorage.removeItem("profile");

View File

@ -10,11 +10,11 @@ const StyledNav = styled.div`
height: 48px;
align-items: center;
justify-content: center;
background-color: ${(props) => props.theme.login.navBackground};
background-color: ${(props) => props.theme?.login?.navBackground};
svg {
path:last-child {
fill: ${(props) => props.theme.client.home.logoColor};
fill: ${(props) => props.theme.client?.home?.logoColor};
}
}
@media ${hugeMobile} {
@ -23,9 +23,11 @@ const StyledNav = styled.div`
`;
const SimpleNav = ({ theme, logoUrls }) => {
const logo = Object.values(logoUrls)[0];
const logo = logoUrls && Object.values(logoUrls)[0];
const logoUrl = !theme.isBase
const logoUrl = !logo
? undefined
: !theme?.isBase
? getLogoFromPath(logo.path.dark)
: getLogoFromPath(logo.path.light);

View File

@ -1,3 +1,4 @@
import { getAppearanceTheme } from "@docspace/common/api/settings";
import express, { Response } from "express";
import template from "./lib/template";
import path from "path";
@ -44,8 +45,9 @@ app.use(logger("dev", { stream: stream }));
app.get("*", async (req: ILoginRequest, res: Response, next) => {
const { i18n, cookies, headers, query, t, url } = req;
let initialState: IInitialState;
let initialState: IInitialState = {};
let assets: assetsType;
let standalone = false;
initSSR(headers);
@ -57,7 +59,8 @@ app.get("*", async (req: ILoginRequest, res: Response, next) => {
next();
}
let currentLanguage: string = initialState.portalSettings.culture;
let currentLanguage: string = initialState?.portalSettings?.culture || "en";
standalone = initialState?.portalSettings?.standalone ? true : false;
if (cookies && cookies[LANGUAGE]) {
currentLanguage = cookies[LANGUAGE];
@ -103,7 +106,42 @@ app.get("*", async (req: ILoginRequest, res: Response, next) => {
if (e instanceof Error) {
message = e.message;
}
const status = e?.response?.status === 404 ? 404 : 520;
if (status !== 404 && !initialState.currentColorScheme) {
const availableThemes: IThemes = await getAppearanceTheme();
const currentColorScheme = availableThemes.themes.find((theme) => {
return availableThemes.selected === theme.id;
});
initialState.currentColorScheme = currentColorScheme;
}
initialState.error = {
status,
standalone,
message,
};
const { component, styleTags } = renderApp(i18n, initialState, url);
assets = await getAssets();
const htmlString = template(
initialState,
component,
styleTags,
{},
"en",
assets,
t
);
winston.error(message);
res.send(htmlString);
}
});

View File

@ -35,7 +35,9 @@ const template: Template = (
? `${t("Authorization")} ${organizationName}`
: title;
const favicon = getLogoFromPath(initLoginState.logoUrls[2]?.path?.light);
const favicon = getLogoFromPath(
initLoginState?.logoUrls && initLoginState?.logoUrls[2]?.path?.light
);
let clientScripts =
assets && assets.hasOwnProperty("client.js")

View File

@ -15,7 +15,7 @@ class LoginStore {
setTheme = (theme) => {
this.theme = theme;
}
};
setCurrentColorScheme = (currentColorScheme: ITheme) => {
this.currentColorScheme = currentColorScheme;