feat: magic link login/reg
This commit is contained in:
@@ -90,6 +90,13 @@ export const authApi = createApi({
|
||||
body: { old_password, new_password },
|
||||
}),
|
||||
}),
|
||||
sendMagicLink: builder.mutation({
|
||||
query: (email) => ({
|
||||
url: "token/send_magic_link",
|
||||
method: "POST",
|
||||
body: { email },
|
||||
}),
|
||||
}),
|
||||
getMetamaskNonce: builder.query({
|
||||
query: (address) => ({
|
||||
url: `/token/metamask/nonce?public_address=${address}`,
|
||||
@@ -102,11 +109,20 @@ export const authApi = createApi({
|
||||
}),
|
||||
logout: builder.query({
|
||||
query: () => ({ url: `token/logout` }),
|
||||
async onQueryStarted(params, { dispatch, queryFulfilled }) {
|
||||
try {
|
||||
await queryFulfilled;
|
||||
dispatch(resetAuthData());
|
||||
} catch {
|
||||
console.log("logout error");
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const {
|
||||
useSendMagicLinkMutation,
|
||||
useGetCredentialsQuery,
|
||||
useUpdateDeviceTokenMutation,
|
||||
useGetOpenidMutation,
|
||||
|
||||
@@ -6,6 +6,7 @@ import BASE_URL, { tokenHeader } from "../config";
|
||||
const whiteList = [
|
||||
"login",
|
||||
"register",
|
||||
"sendMagicLink",
|
||||
"checkInviteTokenValid",
|
||||
"getLoginConfig",
|
||||
"getServer",
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { useEffect } from "react";
|
||||
import { useDispatch, batch } from "react-redux";
|
||||
import { resetAuthData } from "../../app/slices/auth.data";
|
||||
import { resetFootprint } from "../../app/slices/footprint";
|
||||
import { resetChannels } from "../../app/slices/channels";
|
||||
import { resetContacts } from "../../app/slices/contacts";
|
||||
@@ -26,11 +24,5 @@ export default function useLogout() {
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
dispatch(resetAuthData());
|
||||
}
|
||||
}, [isSuccess]);
|
||||
|
||||
return { clearLocalData, logout, exited: isSuccess, exiting: isLoading };
|
||||
}
|
||||
|
||||
@@ -12,7 +12,9 @@ import useStreaming from "../../common/hook/useStreaming";
|
||||
// let request = null;
|
||||
export default function usePreload() {
|
||||
const { rehydrate, rehydrated } = useRehydrate();
|
||||
const loginUid = useSelector((store) => store.authData.uid);
|
||||
const { loginUid, token } = useSelector((store) => {
|
||||
return { loginUid: store.authData.uid, token: store.authData.token };
|
||||
});
|
||||
const { setStreamingReady } = useStreaming();
|
||||
const [
|
||||
getFavorites,
|
||||
@@ -56,11 +58,9 @@ export default function usePreload() {
|
||||
getFavorites();
|
||||
}
|
||||
}, [rehydrated]);
|
||||
const canStreaming = loginUid && rehydrated;
|
||||
const canStreaming = loginUid && rehydrated && !!token;
|
||||
useEffect(() => {
|
||||
if (canStreaming) {
|
||||
setStreamingReady(true);
|
||||
}
|
||||
setStreamingReady(canStreaming);
|
||||
}, [canStreaming]);
|
||||
|
||||
return {
|
||||
|
||||
+128
-91
@@ -1,100 +1,137 @@
|
||||
import { useEffect } from 'react';
|
||||
import { Route, Routes, HashRouter } from 'react-router-dom';
|
||||
import { Provider, useSelector } from 'react-redux';
|
||||
import { useEffect } from "react";
|
||||
import { Route, Routes, HashRouter } from "react-router-dom";
|
||||
import { Provider, useSelector } from "react-redux";
|
||||
// import Welcome from './Welcome'
|
||||
import NotFoundPage from './404';
|
||||
import OAuthPage from './oauth';
|
||||
import LoginPage from './login';
|
||||
import HomePage from './home';
|
||||
import ChatPage from './chat';
|
||||
import FavoritesPage from './favs';
|
||||
import ContactsPage from './contacts';
|
||||
import RequireAuth from '../common/component/RequireAuth';
|
||||
import RequireNoAuth from '../common/component/RequireNoAuth';
|
||||
import Meta from '../common/component/Meta';
|
||||
import NotFoundPage from "./404";
|
||||
import OAuthPage from "./oauth";
|
||||
import LoginPage from "./login";
|
||||
import SendMagicLinkPage from "./sendMagicLink";
|
||||
import RegBasePage from "./reg";
|
||||
import RegPage from "./reg/Register";
|
||||
import RegWithUsernamePage from "./reg/RegWithUsername";
|
||||
import HomePage from "./home";
|
||||
import ChatPage from "./chat";
|
||||
import FavoritesPage from "./favs";
|
||||
import ContactsPage from "./contacts";
|
||||
import RequireAuth from "../common/component/RequireAuth";
|
||||
import RequireNoAuth from "../common/component/RequireNoAuth";
|
||||
import Meta from "../common/component/Meta";
|
||||
|
||||
import store from '../app/store';
|
||||
import InvitePage from './invite';
|
||||
import SettingPage from './setting';
|
||||
import SettingChannelPage from './settingChannel';
|
||||
import toast from 'react-hot-toast';
|
||||
import ResourceManagement from './resources';
|
||||
import store from "../app/store";
|
||||
import InvitePage from "./invite";
|
||||
import SettingPage from "./setting";
|
||||
import SettingChannelPage from "./settingChannel";
|
||||
import toast from "react-hot-toast";
|
||||
import ResourceManagement from "./resources";
|
||||
|
||||
const PageRoutes = () => {
|
||||
const {
|
||||
ui: { online },
|
||||
fileMessages
|
||||
} = useSelector((store) => {
|
||||
return { ui: store.ui, fileMessages: store.fileMessage };
|
||||
});
|
||||
// 掉线检测
|
||||
useEffect(() => {
|
||||
let toastId = 0;
|
||||
if (!online) {
|
||||
toast.error('Network Offline!', { duration: Infinity });
|
||||
} else {
|
||||
toast.dismiss(toastId);
|
||||
}
|
||||
}, [online]);
|
||||
const {
|
||||
ui: { online },
|
||||
fileMessages,
|
||||
} = useSelector((store) => {
|
||||
return { ui: store.ui, fileMessages: store.fileMessage };
|
||||
});
|
||||
// 掉线检测
|
||||
useEffect(() => {
|
||||
let toastId = 0;
|
||||
if (!online) {
|
||||
toast.error("Network Offline!", { duration: Infinity });
|
||||
} else {
|
||||
toast.dismiss(toastId);
|
||||
}
|
||||
}, [online]);
|
||||
|
||||
return (
|
||||
<HashRouter>
|
||||
<Routes>
|
||||
<Route path="/oauth/:token" element={<OAuthPage />} />
|
||||
<Route
|
||||
path="/login"
|
||||
element={
|
||||
<RequireNoAuth>
|
||||
<LoginPage />
|
||||
</RequireNoAuth>
|
||||
}
|
||||
/>
|
||||
<Route path="/invite" element={<InvitePage />} />
|
||||
<Route
|
||||
path="/"
|
||||
element={
|
||||
<RequireAuth>
|
||||
<HomePage />
|
||||
</RequireAuth>
|
||||
}
|
||||
>
|
||||
<Route path="setting">
|
||||
<Route index element={<SettingPage />} />
|
||||
<Route path="channel/:cid" element={<SettingChannelPage />} />
|
||||
</Route>
|
||||
<Route index element={<ChatPage />} />
|
||||
<Route path="chat">
|
||||
<Route index element={<ChatPage />} />
|
||||
<Route path="channel/:channel_id" element={<ChatPage />} />
|
||||
<Route path="dm/:user_id" element={<ChatPage />} />
|
||||
</Route>
|
||||
<Route path="contacts">
|
||||
<Route index element={<ContactsPage />} />
|
||||
<Route path=":user_id" element={<ContactsPage />} />
|
||||
</Route>
|
||||
<Route path="favs" element={<FavoritesPage />}></Route>
|
||||
<Route path="files" element={<ResourceManagement fileMessages={fileMessages} />}></Route>
|
||||
</Route>
|
||||
<Route path="*" element={<NotFoundPage />} />
|
||||
</Routes>
|
||||
</HashRouter>
|
||||
);
|
||||
return (
|
||||
<HashRouter>
|
||||
<Routes>
|
||||
<Route path="/oauth/:token" element={<OAuthPage />} />
|
||||
<Route
|
||||
path="/login"
|
||||
element={
|
||||
<RequireNoAuth>
|
||||
<LoginPage />
|
||||
</RequireNoAuth>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/send_magic_link"
|
||||
element={
|
||||
<RequireNoAuth>
|
||||
<SendMagicLinkPage />
|
||||
</RequireNoAuth>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/reg"
|
||||
element={
|
||||
<RequireNoAuth>
|
||||
<RegBasePage />
|
||||
</RequireNoAuth>
|
||||
}
|
||||
>
|
||||
<Route index element={<RegPage />} />
|
||||
<Route path="magiclink">
|
||||
<Route index element={<RegWithUsernamePage />} />
|
||||
<Route path=":token" element={<RegWithUsernamePage />} />
|
||||
</Route>
|
||||
</Route>
|
||||
<Route
|
||||
path="/email_login"
|
||||
element={
|
||||
<RequireNoAuth>
|
||||
<SendMagicLinkPage />
|
||||
</RequireNoAuth>
|
||||
}
|
||||
/>
|
||||
<Route path="/invite" element={<InvitePage />} />
|
||||
<Route
|
||||
path="/"
|
||||
element={
|
||||
<RequireAuth>
|
||||
<HomePage />
|
||||
</RequireAuth>
|
||||
}
|
||||
>
|
||||
<Route path="setting">
|
||||
<Route index element={<SettingPage />} />
|
||||
<Route path="channel/:cid" element={<SettingChannelPage />} />
|
||||
</Route>
|
||||
<Route index element={<ChatPage />} />
|
||||
<Route path="chat">
|
||||
<Route index element={<ChatPage />} />
|
||||
<Route path="channel/:channel_id" element={<ChatPage />} />
|
||||
<Route path="dm/:user_id" element={<ChatPage />} />
|
||||
</Route>
|
||||
<Route path="contacts">
|
||||
<Route index element={<ContactsPage />} />
|
||||
<Route path=":user_id" element={<ContactsPage />} />
|
||||
</Route>
|
||||
<Route path="favs" element={<FavoritesPage />}></Route>
|
||||
<Route
|
||||
path="files"
|
||||
element={<ResourceManagement fileMessages={fileMessages} />}
|
||||
></Route>
|
||||
</Route>
|
||||
<Route path="*" element={<NotFoundPage />} />
|
||||
</Routes>
|
||||
</HashRouter>
|
||||
);
|
||||
};
|
||||
// const local_key = "AUTH_DATA";
|
||||
export default function ReduxRoutes() {
|
||||
// const [authData, setAuthData] = useState(
|
||||
// JSON.parse(localStorage.getItem(local_key))
|
||||
// );
|
||||
// const updateAuthData = (data) => {
|
||||
// localStorage.setItem(local_key, JSON.stringify(data));
|
||||
// setAuthData(data);
|
||||
// };
|
||||
return (
|
||||
<Provider store={store}>
|
||||
{/* <PersistGate loading={null} persistor={persistStore(store)}> */}
|
||||
<Meta />
|
||||
<PageRoutes />
|
||||
{/* </PersistGate> */}
|
||||
</Provider>
|
||||
);
|
||||
// const [authData, setAuthData] = useState(
|
||||
// JSON.parse(localStorage.getItem(local_key))
|
||||
// );
|
||||
// const updateAuthData = (data) => {
|
||||
// localStorage.setItem(local_key, JSON.stringify(data));
|
||||
// setAuthData(data);
|
||||
// };
|
||||
return (
|
||||
<Provider store={store}>
|
||||
{/* <PersistGate loading={null} persistor={persistStore(store)}> */}
|
||||
<Meta />
|
||||
<PageRoutes />
|
||||
{/* </PersistGate> */}
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// import { useState, useEffect } from "react";
|
||||
import Button from "../../common/component/styled/Button";
|
||||
import styled from "styled-components";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
const StyledMagicButton = styled(Button)`
|
||||
width: 100%;
|
||||
margin-bottom: 16px;
|
||||
`;
|
||||
export default function MagicLinkLogin() {
|
||||
const navigate = useNavigate();
|
||||
const handleGoogleLogin = () => {
|
||||
navigate("/send_magic_link");
|
||||
// signIn();
|
||||
};
|
||||
return (
|
||||
<StyledMagicButton onClick={handleGoogleLogin} href="#">
|
||||
Sign in with Magic Link
|
||||
</StyledMagicButton>
|
||||
);
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import SolidLoginButton from "./SolidLoginButton";
|
||||
import Input from "../../common/component/styled/Input";
|
||||
import Button from "../../common/component/styled/Button";
|
||||
import GoogleLoginButton from "./GoogleLoginButton";
|
||||
import MagicLinkLogin from "./MagicLinkLogin";
|
||||
import { useLoginMutation } from "../../app/services/auth";
|
||||
import { useGetLoginConfigQuery } from "../../app/services/server";
|
||||
import { setAuthData } from "../../app/slices/auth.data";
|
||||
@@ -31,6 +32,9 @@ export default function LoginPage() {
|
||||
const query = new URLSearchParams(location.search);
|
||||
const code = query.get("code");
|
||||
const state = query.get("state");
|
||||
const token = query.get("token");
|
||||
const exists = query.get("exists");
|
||||
// oidc login
|
||||
if (code && state) {
|
||||
login({
|
||||
code,
|
||||
@@ -38,6 +42,21 @@ export default function LoginPage() {
|
||||
type: "oidc",
|
||||
});
|
||||
}
|
||||
// magic link
|
||||
if (token && typeof exists !== "undefined") {
|
||||
console.log("tokken", token, exists);
|
||||
const isLogin = exists == "true";
|
||||
if (isLogin) {
|
||||
// login
|
||||
login({
|
||||
token,
|
||||
type: "magiclink",
|
||||
});
|
||||
} else {
|
||||
// reg
|
||||
location.href = `/#/reg/magiclink/${token}`;
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -134,6 +153,7 @@ export default function LoginPage() {
|
||||
{(enableGoogleLogin || enableMetamaskLogin || oidc.length > 0) && (
|
||||
<hr className="or" />
|
||||
)}
|
||||
<MagicLinkLogin />
|
||||
{enableGoogleLogin && <GoogleLoginButton login={login} />}
|
||||
{enableMetamaskLogin && <MetamaskLoginButton login={login} />}
|
||||
{oidc.length > 0 && <SolidLoginButton issuers={oidc} />}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/* eslint-disable no-undef */
|
||||
import { useState, useEffect } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
// import { useNavigate } from "react-router-dom";
|
||||
// import toast from "react-hot-toast";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { setAuthData } from "../../app/slices/auth.data";
|
||||
|
||||
import Input from "../../common/component/styled/Input";
|
||||
import Button from "../../common/component/styled/Button";
|
||||
import { useLoginMutation } from "../../app/services/auth";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
export default function RegWithUsername() {
|
||||
const { token } = useParams();
|
||||
const [login, { isLoading, error, isSuccess, data }] = useLoginMutation();
|
||||
// const navigateTo = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
const [username, setUsername] = useState("");
|
||||
useEffect(() => {
|
||||
console.log("errr", error);
|
||||
switch (error?.status) {
|
||||
case 401:
|
||||
toast.error("Invalided Token");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}, [error]);
|
||||
useEffect(() => {
|
||||
if (isSuccess && data) {
|
||||
// 更新本地认证信息
|
||||
console.log("login data", data);
|
||||
toast.success("login success");
|
||||
dispatch(setAuthData(data));
|
||||
location.href = `/#/`;
|
||||
}
|
||||
}, [isSuccess, data]);
|
||||
|
||||
const handleLogin = (evt) => {
|
||||
evt.preventDefault();
|
||||
login({
|
||||
token,
|
||||
username,
|
||||
type: "magiclink",
|
||||
});
|
||||
// sendMagicLink(email);
|
||||
};
|
||||
|
||||
const handleInput = (evt) => {
|
||||
const { value } = evt.target;
|
||||
setUsername(value);
|
||||
};
|
||||
if (!token) return "no token";
|
||||
return (
|
||||
<>
|
||||
<div className="tips">
|
||||
<h2 className="title">What’s your name</h2>
|
||||
<span className="desc">
|
||||
Enter a name or handle so people know how you’d like to be called.
|
||||
Your name will only be visible to others in spaces you joined.
|
||||
</span>
|
||||
</div>
|
||||
<form onSubmit={handleLogin}>
|
||||
<Input
|
||||
className="large"
|
||||
name="username"
|
||||
value={username}
|
||||
required
|
||||
placeholder="Type a name"
|
||||
onChange={handleInput}
|
||||
/>
|
||||
<Button type="submit" disabled={isLoading || !username || isSuccess}>
|
||||
{isLoading ? "Logining" : `Continue`}
|
||||
</Button>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/* eslint-disable no-undef */
|
||||
import { useState, useEffect } from "react";
|
||||
// import { useDispatch } from "react-redux";
|
||||
// import { useNavigate } from "react-router-dom";
|
||||
import toast from "react-hot-toast";
|
||||
import Input from "../../common/component/styled/Input";
|
||||
import Button from "../../common/component/styled/Button";
|
||||
// import { useSendMagicLinkMutation } from "../../app/services/auth";
|
||||
|
||||
export default function Reg() {
|
||||
// const [
|
||||
// sendMagicLink,
|
||||
// { data, isSuccess, isLoading, error },
|
||||
// ] = useSendMagicLinkMutation();
|
||||
// const navigateTo = useNavigate();
|
||||
// const dispatch = useDispatch();
|
||||
const [username, setUsername] = useState("");
|
||||
|
||||
const handleLogin = (evt) => {
|
||||
evt.preventDefault();
|
||||
// sendMagicLink(email);
|
||||
};
|
||||
|
||||
const handleInput = (evt) => {
|
||||
const { value } = evt.target;
|
||||
console.log(value);
|
||||
setUsername(value);
|
||||
};
|
||||
return (
|
||||
<form onSubmit={handleLogin}>
|
||||
<Input
|
||||
className="large"
|
||||
name="username"
|
||||
value={username}
|
||||
required
|
||||
placeholder="Enter your Name22"
|
||||
onChange={handleInput}
|
||||
/>
|
||||
<Button type="submit">{isLoading ? "Sending" : `Register`}</Button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/* eslint-disable no-undef */
|
||||
import { Outlet } from "react-router-dom";
|
||||
import StyledWrapper from "./styled";
|
||||
|
||||
export default function Reg() {
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<div className="form">
|
||||
<Outlet />
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import styled from "styled-components";
|
||||
const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
.form {
|
||||
max-width: 440px;
|
||||
padding: 36px 40px 32px 40px;
|
||||
/* border: 1px solid #eee; */
|
||||
box-shadow: 0px 4px 8px -2px rgba(16, 24, 40, 0.1),
|
||||
0px 2px 4px -2px rgba(16, 24, 40, 0.06);
|
||||
border-radius: 12px;
|
||||
.tips {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-bottom: 24px;
|
||||
.logo {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
margin-bottom: 28px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.title {
|
||||
font-weight: 600;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
color: #101828;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.desc {
|
||||
text-align: center;
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
color: #667085;
|
||||
}
|
||||
}
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
width: 360px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
@@ -0,0 +1,86 @@
|
||||
/* eslint-disable no-undef */
|
||||
import { useState, useEffect } from "react";
|
||||
// import { useDispatch } from "react-redux";
|
||||
// import { useNavigate } from "react-router-dom";
|
||||
import toast from "react-hot-toast";
|
||||
import BASE_URL from "../../app/config";
|
||||
import StyledWrapper from "./styled";
|
||||
import Input from "../../common/component/styled/Input";
|
||||
import Button from "../../common/component/styled/Button";
|
||||
import { useSendMagicLinkMutation } from "../../app/services/auth";
|
||||
|
||||
export default function SendMagicLinkPage() {
|
||||
const [
|
||||
sendMagicLink,
|
||||
{ isSuccess, isLoading, error },
|
||||
] = useSendMagicLinkMutation();
|
||||
// const navigateTo = useNavigate();
|
||||
// const dispatch = useDispatch();
|
||||
const [email, setEmail] = useState("");
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
toast.success("Send Email Successfully!");
|
||||
}
|
||||
}, [isSuccess]);
|
||||
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
console.log(error);
|
||||
switch (error.status) {
|
||||
case "PARSING_ERROR":
|
||||
toast.error(error.data);
|
||||
break;
|
||||
case 401:
|
||||
toast.error("username or password incorrect");
|
||||
break;
|
||||
case 404:
|
||||
toast.error("account not exsit");
|
||||
break;
|
||||
default:
|
||||
toast.error("something error");
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}, [error]);
|
||||
|
||||
const handleLogin = (evt) => {
|
||||
evt.preventDefault();
|
||||
sendMagicLink(email);
|
||||
};
|
||||
|
||||
const handleInput = (evt) => {
|
||||
const { value } = evt.target;
|
||||
console.log(value);
|
||||
setEmail(value);
|
||||
};
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<div className="form">
|
||||
<div className="tips">
|
||||
<img
|
||||
src={`${BASE_URL}/resource/organization/logo`}
|
||||
alt="logo"
|
||||
className="logo"
|
||||
/>
|
||||
<h2 className="title">Login to Rustchat</h2>
|
||||
<span className="desc">Please enter your Email</span>
|
||||
</div>
|
||||
<form onSubmit={handleLogin}>
|
||||
<Input
|
||||
type="email"
|
||||
className="large"
|
||||
name="email"
|
||||
value={email}
|
||||
required
|
||||
placeholder="Enter your email"
|
||||
onChange={handleInput}
|
||||
/>
|
||||
<Button type="submit" disabled={isLoading || !email}>
|
||||
{isLoading ? "Sending" : `Continue with Email`}
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import styled from "styled-components";
|
||||
const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
.form {
|
||||
padding: 36px 40px 32px 40px;
|
||||
/* border: 1px solid #eee; */
|
||||
box-shadow: 0px 4px 8px -2px rgba(16, 24, 40, 0.1),
|
||||
0px 2px 4px -2px rgba(16, 24, 40, 0.06);
|
||||
border-radius: 12px;
|
||||
.tips {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-bottom: 24px;
|
||||
.logo {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
margin-bottom: 28px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.title {
|
||||
font-weight: 600;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
color: #101828;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
.desc {
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
color: #667085;
|
||||
}
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
width: 360px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
@@ -1,7 +1,7 @@
|
||||
// import React from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
// import { useNavigate } from "react-router-dom";
|
||||
|
||||
import StyledModal from "../../common/component/styled/Modal";
|
||||
import Button from "../../common/component/styled/Button";
|
||||
@@ -27,8 +27,8 @@ const StyledConfirm = styled(StyledModal)`
|
||||
}
|
||||
`;
|
||||
import Modal from "../../common/component/Modal";
|
||||
import toast from "react-hot-toast";
|
||||
export default function LogoutConfirmModal({ closeModal }) {
|
||||
const navigate = useNavigate();
|
||||
const [clearLocal, setClearLocal] = useState(false);
|
||||
const { logout, exited, exiting, clearLocalData } = useLogout();
|
||||
const handleLogout = () => {
|
||||
@@ -43,7 +43,11 @@ export default function LogoutConfirmModal({ closeModal }) {
|
||||
console.log("clear all store");
|
||||
clearLocalData();
|
||||
}
|
||||
navigate("/");
|
||||
toast.success("Logout Successfully");
|
||||
setTimeout(() => {
|
||||
location.href = `${location.origin}#/login`;
|
||||
}, 500);
|
||||
// location.reload();
|
||||
}
|
||||
}, [exited, clearLocal]);
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user