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
- }
}
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
+
+
-
+
+ {this.state.error}
+
+
+
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