This repository has been archived on 2022-08-14. You can view files and clone it, but cannot push or open issues or pull requests.
outline/app/scenes/Login/index.js

232 lines
5.5 KiB
JavaScript

// @flow
import * as React from "react";
import styled from "styled-components";
import { BackIcon, EmailIcon } from "outline-icons";
import { observer, inject } from "mobx-react";
import { Redirect } from "react-router-dom";
import { find } from "lodash";
import Flex from "components/Flex";
import TeamLogo from "components/TeamLogo";
import OutlineLogo from "components/OutlineLogo";
import Heading from "components/Heading";
import PageTitle from "components/PageTitle";
import ButtonLarge from "components/ButtonLarge";
import HelpText from "components/HelpText";
import Fade from "components/Fade";
import Service from "./Service";
import Notices from "./Notices";
import AuthStore from "stores/AuthStore";
import getQueryVariable from "shared/utils/getQueryVariable";
import env from "env";
type Props = {
auth: AuthStore,
location: Object,
};
type State = {
emailLinkSentTo: string,
};
@observer
class Login extends React.Component<Props, State> {
state = {
emailLinkSentTo: "",
};
handleReset = () => {
this.setState({ emailLinkSentTo: "" });
};
handleEmailSuccess = email => {
this.setState({ emailLinkSentTo: email });
};
render() {
const { auth, location } = this.props;
const { config } = auth;
const isCreate = location.pathname === "/create";
if (auth.authenticated) {
return <Redirect to="/home" />;
}
// we're counting on the config request being fast
if (!config) {
return null;
}
const hasMultipleServices = config.services.length > 1;
const defaultService = find(
config.services,
service => service.id === auth.lastSignedIn
);
const header =
env.DEPLOYMENT === "hosted" &&
(config.hostname ? (
<Back href={process.env.URL}>
<BackIcon color="currentColor" /> Back to home
</Back>
) : (
<Back href="https://www.getoutline.com">
<BackIcon color="currentColor" /> Back to website
</Back>
));
if (this.state.emailLinkSentTo) {
return (
<Background>
{header}
<Centered align="center" justify="center" column auto>
<PageTitle title="Check your email" />
<CheckEmailIcon size={38} color="currentColor" />
<Heading>Check your email</Heading>
<Note>
A magic sign-in link has been sent to the email{" "}
<em>{this.state.emailLinkSentTo}</em>, no password needed.
</Note>
<br />
<ButtonLarge onClick={this.handleReset} fullwidth neutral>
Back to login
</ButtonLarge>
</Centered>
</Background>
);
}
return (
<Background>
{header}
<Centered align="center" justify="center" column auto>
<PageTitle title="Login" />
<Logo>
{env.TEAM_LOGO && env.DEPLOYMENT !== "hosted" ? (
<TeamLogo src={env.TEAM_LOGO} />
) : (
<OutlineLogo size={38} fill="currentColor" />
)}
</Logo>
{isCreate ? (
<Heading>Create an account</Heading>
) : (
<Heading>Login to {config.name || "Outline"}</Heading>
)}
<Notices notice={getQueryVariable("notice")} />
{defaultService && (
<React.Fragment key={defaultService.id}>
<Service
isCreate={isCreate}
onEmailSuccess={this.handleEmailSuccess}
{...defaultService}
/>
{hasMultipleServices && (
<React.Fragment>
<Note>
You signed in with {defaultService.name} last time.
</Note>
<Or />
</React.Fragment>
)}
</React.Fragment>
)}
{config.services.map(service => {
if (service.id === auth.lastSignedIn) {
return null;
}
return (
<Service
key={service.id}
isCreate={isCreate}
onEmailSuccess={this.handleEmailSuccess}
{...service}
/>
);
})}
</Centered>
</Background>
);
}
}
const CheckEmailIcon = styled(EmailIcon)`
margin-bottom: -1.5em;
`;
const Background = styled(Fade)`
width: 100vw;
height: 100vh;
background: ${props => props.theme.background};
display: flex;
`;
const Logo = styled.div`
margin-bottom: -1.5em;
height: 38px;
`;
const Note = styled(HelpText)`
text-align: center;
font-size: 14px;
em {
font-style: normal;
font-weight: 500;
}
`;
const Back = styled.a`
display: flex;
align-items: center;
color: inherit;
padding: 32px;
font-weight: 500;
position: absolute;
svg {
transition: transform 100ms ease-in-out;
}
&:hover {
svg {
transform: translateX(-4px);
}
}
`;
const Or = styled.hr`
margin: 1em 0;
position: relative;
width: 100%;
&:after {
content: "Or";
display: block;
position: absolute;
left: 50%;
transform: translate3d(-50%, -50%, 0);
text-transform: uppercase;
font-size: 11px;
color: ${props => props.theme.textSecondary};
background: ${props => props.theme.background};
border-radius: 2px;
padding: 0 4px;
}
`;
const Centered = styled(Flex)`
user-select: none;
width: 90vw;
height: 100%;
max-width: 320px;
margin: 0 auto;
`;
export default inject("auth")(Login);