diff --git a/packages/app/src/components/DraggableDrawer/index.jsx b/packages/app/src/components/DraggableDrawer/index.jsx index 3f3a8e01..8f4a9f09 100755 --- a/packages/app/src/components/DraggableDrawer/index.jsx +++ b/packages/app/src/components/DraggableDrawer/index.jsx @@ -1,11 +1,11 @@ // © Jack Hanford https://github.com/hanford/react-drag-drawer -import React, { Component } from "react"; -import { Motion, spring, presets } from "react-motion"; -import PropTypes from "prop-types"; -import document from "global/document"; -import Observer from "react-intersection-observer"; -import { css } from "@emotion/css"; -import { createPortal } from "react-dom"; +import React, { Component } from "react" +import { Motion, spring, presets } from "react-motion" +import PropTypes from "prop-types" +import document from "global/document" +import Observer from "react-intersection-observer" +import { css } from "@emotion/css" +import { createPortal } from "react-dom" import { isDirectionBottom, @@ -67,11 +67,16 @@ export default class Drawer extends Component { listenersAttached: false } + DESKTOP_MODE = false DRAGGER_HEIGHT_SIZE = 100 MAX_NEGATIVE_SCROLL = 5 SCROLL_TO_CLOSE = 475 ALLOW_DRAWER_TRANSFORM = true + componentDidMount() { + this.DESKTOP_MODE = !window.isMobile + } + componentDidUpdate(prevProps, nextState) { // in the process of closing the drawer if (!this.props.open && prevProps.open) { @@ -333,6 +338,17 @@ export default class Drawer extends Component { this.ALLOW_DRAWER_TRANSFORM = inView } + onClickOutside = (event) => { + if (!this.props.allowClose) { + return false + } + + // check if is clicking outside main component + if (this.drawer && !this.drawer.contains(event.target)) { + this.props.onRequestClose(this) + } + } + preventDefault = (event) => event.preventDefault() stopPropagation = (event) => event.stopPropagation() @@ -392,7 +408,7 @@ export default class Drawer extends Component {
diff --git a/packages/app/src/components/Login/index.jsx b/packages/app/src/components/Login/index.jsx index a00044ce..84999bd1 100755 --- a/packages/app/src/components/Login/index.jsx +++ b/packages/app/src/components/Login/index.jsx @@ -1,58 +1,28 @@ import React from "react" import * as antd from "antd" import { Icons } from "components/Icons" +import classnames from "classnames" import AuthModel from "models/auth" import config from "config" import "./index.less" -const LoginSteps = { - "username": (props) => { - const handleUpdate = (e) => { - props.onUpdate(e.target.value) - } +const stepsOnError = { + username: "This username or email is not exist", + password: "Password is incorrect", +} - return
- Username or Email +const stepsValidations = { + username: async (state) => { + const check = await AuthModel.usernameValidation(state.username).catch((err) => { + return { + exists: false + } + }) -
- -
-
+ return check.exists }, - "password": (props) => { - const handleUpdate = (e) => { - props.onUpdate(e.target.value) - } - - return
- Password - -
- -
-
- } } const phasesToSteps = { @@ -72,6 +42,8 @@ export default class Login extends React.Component { phase: 0, } + formRef = React.createRef() + handleFinish = async () => { const payload = { username: this.state.loginInputs.username, @@ -136,6 +108,14 @@ export default class Login extends React.Component { } onUpdateInput = (input, value) => { + // remove error from ref + this.formRef.current.setFields([ + { + name: input, + errors: [] + } + ]) + this.setState({ loginInputs: { ...this.state.loginInputs, @@ -144,19 +124,28 @@ export default class Login extends React.Component { }) } - renderCurrentInput = () => { - const { phase } = this.state + nextStep = async () => { + const phase = phasesToSteps[this.state.phase] - const step = phasesToSteps[phase] + if (typeof stepsValidations[phase] === "function") { + this.toogleLoading(true) - return React.createElement(LoginSteps[step], { - onUpdate: (...props) => this.onUpdateInput(step, ...props), - next: this.nextStep, - defaultValue: this.state.loginInputs[step], - }) - } + const result = await stepsValidations[phase](this.state.loginInputs) + + this.toogleLoading(false) + + if (!result) { + this.formRef.current.setFields([ + { + name: phase, + errors: [stepsOnError[phase]] + }, + ]) + + return false + } + } - nextStep = () => { const to = this.state.phase + 1 if (!phasesToSteps[to]) { @@ -204,36 +193,71 @@ export default class Login extends React.Component { To continue to {config.app.siteName} -
- {this.renderCurrentInput()} + + + Username or Email + this.onUpdateInput("username", e.target.value)} + onPressEnter={this.nextStep} + disabled={this.state.phase !== 0} + autoFocus + /> + -
-
+ 0 && - Back - + ["hidden"]: this.state.phase !== 1, } - - Continue - -
-
+ )} + > + Password + this.onUpdateInput("password", e.target.value)} + onPressEnter={this.nextStep} + /> + +
-
- {this.state.error} -
+
+ { + this.state.phase > 0 && + Back + + } + + Continue + +
-
- You need a account? -
+
+ {this.state.error} +
+ +
+ You need a account?
diff --git a/packages/app/src/components/Login/index.less b/packages/app/src/components/Login/index.less index 0ddb7d91..c9ff2b48 100755 --- a/packages/app/src/components/Login/index.less +++ b/packages/app/src/components/Login/index.less @@ -60,23 +60,27 @@ margin-bottom: 20px; - .component { - margin-top: 5px; - } + transition: all 150ms ease-in-out; - .component-row { - display: inline-flex; - flex-direction: row; - - justify-content: space-between; + &.hidden { + height: 0px; + opacity: 0; + margin: 0; } } + } - .field-error { - color: red; - font-size: 0.8rem; - margin: 20px 0; - } + .component-row { + display: inline-flex; + flex-direction: row; + + justify-content: space-between; + } + + .field-error { + color: red; + font-size: 0.8rem; + margin: 20px 0; } } diff --git a/packages/comty.js/src/models/auth/index.js b/packages/comty.js/src/models/auth/index.js index cc5c833a..0ea49313 100755 --- a/packages/comty.js/src/models/auth/index.js +++ b/packages/comty.js/src/models/auth/index.js @@ -1,6 +1,8 @@ import request from "../../handlers/request" import SessionModel from "../session" +const emailRegex = new RegExp(/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/) + export default class AuthModel { static login = async (payload) => { const response = await request({ @@ -50,4 +52,31 @@ export default class AuthModel { return response } + + static usernameValidation = async (username) => { + let payload = {} + + // check if usename arguemnt is an email + if (emailRegex.test(username)) { + payload.email = username + } else { + payload.username = username + } + + const response = await request({ + method: "get", + url: "/auth/login/validation", + params: payload, + }).catch((error) => { + console.error(error) + + return false + }) + + if (!response) { + throw new Error("Unable to validate user") + } + + return response.data + } } \ No newline at end of file diff --git a/packages/server/src/controllers/AuthController/endpoints/dataValidation.js b/packages/server/src/controllers/AuthController/endpoints/dataValidation.js new file mode 100644 index 00000000..e1a6275a --- /dev/null +++ b/packages/server/src/controllers/AuthController/endpoints/dataValidation.js @@ -0,0 +1,37 @@ +import { User } from "@models" + +export default { + method: "GET", + route: "/login/validation", + fn: async function (req, res) { + // just check if the provided user or/and email exists, if is return false, otherwise return true + const { username, email } = req.query + + if (!username && !email) { + return res.status(400).json({ + message: "Missing username or email", + }) + } + + const user = await User.findOne({ + $or: [ + { username: username }, + { email: email }, + ] + }).catch((error) => { + return false + }) + + if (user) { + return res.json({ + message: "User already exists", + exists: true, + }) + } else { + return res.json({ + message: "User doesn't exists", + exists: false, + }) + } + } +} \ No newline at end of file