feat: improve signin/signup dialogs ux

This commit is contained in:
thecodrr
2020-11-22 15:11:26 +05:00
parent a853ae119a
commit 286376cf9c
9 changed files with 94 additions and 157 deletions

View File

@@ -1,14 +1,12 @@
import React, { useState } from "react";
import { Flex, Button, Text } from "rebass";
import Input from "../inputs";
import * as Icon from "../icons";
import Dialog, { showDialog } from "./dialog";
import { showSignUpDialog } from "./signupdialog";
import { useStore } from "../../stores/user-store";
import PasswordInput from "../inputs/password";
import Dropper from "../dropper";
import Field from "../field";
const form = { error: true };
const requiredValues = ["username", "password"];
function LoginDialog(props) {
const { onClose } = props;
const [error, setError] = useState();
@@ -24,15 +22,21 @@ function LoginDialog(props) {
onClose={onClose}
negativeButton={{ text: "Cancel", onClick: onClose }}
positiveButton={{
text: "Sign me in",
props: {
form: "loginForm",
type: "submit",
},
text: "Sign in",
loading: isLoggingIn,
disabled: isLoggingIn,
onClick: () => submit(setError, form, login, onClose),
// onClick: () => {
// console.log(document.getElementById("loginForm"));
// }, //submit(setError, form, login, onClose),
}}
buttonsAlignment="center"
footer={
<>
<Text textAlign="center" color="gray" mt={3}>
<Text fontSize="title" textAlign="center" color="gray" mt={3}>
Don't have an account?
</Text>
<Button
@@ -49,33 +53,46 @@ function LoginDialog(props) {
}
>
<Flex
variant="columnFill"
onKeyDown={(e) => {
if (e.key === "Enter") submit(setError, form, login, onClose);
id="loginForm"
as="form"
onSubmit={(e) => {
e.preventDefault();
const data = requiredValues.reduce((prev, curr) => {
prev[curr] = e.target[curr].value;
return prev;
}, {});
setError();
login(data)
.then(onClose)
.catch((e) => setError(e.message));
}}
variant="columnFill"
>
<Dropper mt={2} form={form}>
<Input autoFocus name="username" title="Username" />
<PasswordInput />
</Dropper>
<Field
autoFocus
required
id="username"
label="Username"
name="username"
autoComplete="username"
/>
<Field
required
id="password"
label="Password"
name="password"
type="password"
autoComplete="current-password"
sx={{ mt: 1 }}
/>
{error && <Text variant="error">{error}</Text>}
{/* <Button variant="anchor" onClick={showSignUpDialog}>
I don't have an account
</Button> */}
</Flex>
</Dialog>
);
}
function submit(setError, form, login, onClose) {
setError();
if (form.error) return;
login(form)
.then(onClose)
.catch((e) => setError(e.message));
}
export const showLogInDialog = () => {
return showDialog((perform) => <LoginDialog onClose={() => perform()} />);
};

View File

@@ -1,16 +1,12 @@
import React, { useState } from "react";
import { Text, Box, Button } from "rebass";
import Input from "../inputs";
import * as Icon from "../icons";
import Dialog, { showDialog } from "./dialog";
//import { db } from "../../common";
import EmailInput from "../inputs/email";
import PasswordInput from "../inputs/password";
import Dropper from "../dropper";
import { useStore } from "../../stores/user-store";
import { showLogInDialog } from "./logindialog";
import Field from "../field";
const form = { error: true };
const requiredValues = ["email", "username", "password"];
function SignUpDialog(props) {
const { onClose } = props;
const [error, setError] = useState();
@@ -27,14 +23,17 @@ function SignUpDialog(props) {
negativeButton={{ text: "Cancel", onClick: onClose }}
buttonsAlignment="center"
positiveButton={{
text: "Create my account",
props: {
form: "signupForm",
type: "submit",
},
text: "Create account",
loading: isSigningIn,
disabled: isSigningIn,
onClick: () => submit(setError, form, signup, onClose),
}}
footer={
<>
<Text textAlign="center" color="gray" mt={3}>
<Text fontSize="title" textAlign="center" color="gray" mt={3}>
Already have an account?
</Text>
<Button
@@ -49,37 +48,52 @@ function SignUpDialog(props) {
}
>
<Box
onKeyDown={(e) => {
if (e.key === "Enter") submit(setError, form, signup, onClose);
id="signupForm"
as="form"
onSubmit={(e) => {
e.preventDefault();
const data = requiredValues.reduce((prev, curr) => {
prev[curr] = e.target[curr].value;
return prev;
}, {});
setError();
signup(data)
.then(onClose)
.catch((e) => setError(e.message));
}}
>
<Dropper mt={2} form={form}>
<Input autoFocus title="Username" name="username" />
<EmailInput />
<PasswordInput confirm />
</Dropper>
<Field
autoFocus
required
id="email"
type="email"
label="Email"
name="email"
/>
<Field
required
id="username"
label="Username"
name="username"
sx={{ mt: 1 }}
/>
<Field
required
id="password"
label="Password"
name="password"
type="password"
autoComplete="new-password"
sx={{ mt: 1 }}
/>
{error && <Text variant="error">{error}</Text>}
</Box>
</Dialog>
);
}
function submit(setError, form, signup, onClose) {
setError();
if (form.password !== form.confirm) {
form.error = true;
setError("Passwords do not match.");
}
if (form.error) return;
signup(form)
.then(onClose)
.catch((e) => setError(e.message));
}
export function showSignUpDialog() {
return showDialog((perform) => <SignUpDialog onClose={() => perform()} />);
}
/**
*/

View File

@@ -1,12 +0,0 @@
import React from "react";
function Dropper(props) {
const { children } = props;
const fillChildren = React.Children.map(children, (child, index) => {
if (!child) return;
const childProps = { ...props, children: child.props.children };
return React.cloneElement(child, childProps);
});
return <React.Fragment>{fillChildren}</React.Fragment>;
}
export default Dropper;

View File

@@ -124,3 +124,5 @@ export const ChevronUp = createIcon(Icons.mdiChevronUp);
export const SortAsc = createIcon(Icons.mdiSortAscending);
export const SortDesc = createIcon(Icons.mdiSortDescending);
export const Password = createIcon(Icons.mdiFormTextboxPassword);

View File

@@ -1,17 +0,0 @@
import React from "react";
import Input from "./index";
import { isValidEmail } from "../../utils/validation";
function EmailInput(props) {
return (
<Input
title="Email"
name="email"
type="email"
error="Email is not valid."
validate={isValidEmail}
{...props}
/>
);
}
export default EmailInput;

View File

@@ -1,42 +0,0 @@
import React, { useState } from "react";
import { Input as RebassInput } from "@rebass/forms";
import { Box, Text } from "rebass";
import { toTitleCase } from "../../utils/string";
function Input(props) {
const { required = true, validate, variant = "input" } = props;
const { name, form, title } = props;
const [themeVariant, setThemeVariant] = useState(variant);
const [error, setError] = useState(props.error);
return (
<Box>
<RebassInput
{...props}
variant={themeVariant}
placeholder={title}
onChange={event => {
const { value } = event.target;
const isValid = !validate || validate(value, form);
if ((!value.trim() && required) || !isValid) {
setThemeVariant("error");
if (form) form.error = true;
const error =
!isValid || !name
? props.error
: `${toTitleCase(name)} is required.`;
setError(error);
} else {
setThemeVariant(variant);
if (form) {
form[name] = value;
form.error = false;
}
}
}}
/>
{themeVariant === "error" && <Text variant="error">{error}</Text>}
</Box>
);
}
export default Input;

View File

@@ -1,25 +0,0 @@
import React from "react";
import Input from "./index";
import Dropper from "../dropper";
function PasswordInput(props) {
const { confirm } = props;
return (
<Dropper {...props}>
<Input title="Password" type="password" name="password" />
{confirm && (
<Input
title="Confirm Password"
type="password"
error="Passwords do not match"
name="confirm"
validate={(password, form) =>
!form.password || form.password === password
}
/>
)}
</Dropper>
);
}
export default PasswordInput;

View File

@@ -46,7 +46,7 @@ class Primary {
class Secondary {
constructor() {
return { variant: "buttons.default", color: "text", bg: "bgSecondary" };
return { variant: "buttons.default", color: "text", bg: "border" };
}
}

View File

@@ -8953,8 +8953,8 @@ normalize-url@^3.0.0, normalize-url@^3.0.1:
integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==
"notes-core@git+ssh://git@github.com:streetwriters/notesnook-core.git":
version "2.3.5"
resolved "git+ssh://git@github.com:streetwriters/notesnook-core.git#8235c1fec8632a35965116fb9bd01a5a3f33fe8b"
version "2.4.0"
resolved "git+ssh://git@github.com:streetwriters/notesnook-core.git#a92379da34ae59069f3e96e846b410e282efe721"
dependencies:
fast-sort "^2.0.1"
fuzzysearch "^1.0.3"