mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-09-03 12:41:45 +02:00
feat: improve signin/signup dialogs ux
This commit is contained in:
@@ -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()} />);
|
||||
};
|
||||
|
||||
@@ -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()} />);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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" };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user