From 6919b7404df58cd06380e364ea518835cccd0352 Mon Sep 17 00:00:00 2001 From: zerosoul Date: Mon, 14 Feb 2022 16:39:44 +0800 Subject: [PATCH] feat: invite and reg --- src/app/services/contact.js | 73 +++++++++------- src/routes/index.js | 108 +++++++++++------------ src/routes/invite/index.js | 168 ++++++++++++++++++++++++++++++++++++ src/routes/invite/styled.js | 75 ++++++++++++++++ 4 files changed, 338 insertions(+), 86 deletions(-) create mode 100644 src/routes/invite/index.js create mode 100644 src/routes/invite/styled.js diff --git a/src/app/services/contact.js b/src/app/services/contact.js index 102b7359..0be7a14b 100644 --- a/src/app/services/contact.js +++ b/src/app/services/contact.js @@ -1,38 +1,45 @@ -import { createApi } from "@reduxjs/toolkit/query/react"; -import baseQuery from "./base.query"; -import BASE_URL, { ContentTypes } from "../config"; -import { REHYDRATE } from "redux-persist"; +import { createApi } from '@reduxjs/toolkit/query/react'; +import baseQuery from './base.query'; +import BASE_URL, { ContentTypes } from '../config'; +import { REHYDRATE } from 'redux-persist'; export const contactApi = createApi({ - reducerPath: "contact", - baseQuery, - extractRehydrationInfo(action, { reducerPath }) { - if (action.type === REHYDRATE) { - return action.payload ? action.payload[reducerPath] : undefined; - } - }, - endpoints: (builder) => ({ - getContacts: builder.query({ - query: () => ({ url: `user` }), - transformResponse: (data) => { - return data.map((user) => { - const avatar = `${BASE_URL}/resource/avatar?uid=${user.uid}`; - user.avatar = avatar; - return user; - }); - }, - }), - sendMsg: builder.mutation({ - query: ({ id, content, type = "text" }) => ({ - headers: { - "content-type": ContentTypes[type], - }, - url: `user/${id}/send`, - method: "POST", - body: content, - }), - }), + reducerPath: 'contact', + baseQuery, + extractRehydrationInfo(action, { reducerPath }) { + if (action.type === REHYDRATE) { + return action.payload ? action.payload[reducerPath] : undefined; + } + }, + endpoints: (builder) => ({ + getContacts: builder.query({ + query: () => ({ url: `user` }), + transformResponse: (data) => { + return data.map((user) => { + const avatar = `${BASE_URL}/resource/avatar?uid=${user.uid}`; + user.avatar = avatar; + return user; + }); + } }), + register: builder.mutation({ + query: (data) => ({ + url: `user/register`, + method: 'POST', + body: data + }) + }), + sendMsg: builder.mutation({ + query: ({ id, content, type = 'text' }) => ({ + headers: { + 'content-type': ContentTypes[type] + }, + url: `user/${id}/send`, + method: 'POST', + body: content + }) + }) + }) }); -export const { useGetContactsQuery, useSendMsgMutation } = contactApi; +export const { useGetContactsQuery, useSendMsgMutation, useRegisterMutation } = contactApi; diff --git a/src/routes/index.js b/src/routes/index.js index 407e76ef..90b4731e 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -1,70 +1,72 @@ // import { useState } from "react"; -import { Route, Routes, HashRouter } from "react-router-dom"; -import { Provider } from "react-redux"; +import { Route, Routes, HashRouter } from 'react-router-dom'; +import { Provider } from 'react-redux'; -import { persistStore } from "redux-persist"; -import { PersistGate } from "redux-persist/integration/react"; +import { persistStore } from 'redux-persist'; +import { PersistGate } from 'redux-persist/integration/react'; // import Welcome from './Welcome' -import NotFoundPage from "./404"; -import LoginPage from "./login"; -import HomePage from "./home"; -import ChatPage from "./chat"; -import ContactsPage from "./contacts"; -import RequireAuth from "../common/component/RequireAuth"; +import NotFoundPage from './404'; +import LoginPage from './login'; +import HomePage from './home'; +import ChatPage from './chat'; +import ContactsPage from './contacts'; +import RequireAuth from '../common/component/RequireAuth'; -import store from "../app/store.with.persist"; +import store from '../app/store.with.persist'; +import InvitePage from './invite'; // import getStore from "../app/store"; const PageRoutes = () => { - return ( - - - } /> - - - - } - > - } /> - - } /> - } /> - } /> - - - } /> - } /> - - - } /> - - - ); + return ( + + + } /> + } /> + + + + } + > + } /> + + } /> + } /> + } /> + + + } /> + } /> + + + } /> + + + ); }; // 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 ( - - - - - {/* {authData ? ( + // const [authData, setAuthData] = useState( + // JSON.parse(localStorage.getItem(local_key)) + // ); + // const updateAuthData = (data) => { + // localStorage.setItem(local_key, JSON.stringify(data)); + // setAuthData(data); + // }; + return ( + + + + + {/* {authData ? ( ) : ( )} */} - - ); + + ); } diff --git a/src/routes/invite/index.js b/src/routes/invite/index.js new file mode 100644 index 00000000..a1ee6698 --- /dev/null +++ b/src/routes/invite/index.js @@ -0,0 +1,168 @@ +import { useState, useEffect } from 'react'; +import StyledWrapper from './styled'; +import { useNavigate } from 'react-router-dom'; +import toast from 'react-hot-toast'; + +import { useRegisterMutation } from '../../app/services/contact'; +import { useCheckInviteTokenValidMutation } from '../../app/services/auth'; + +export default function InvitePage() { + const [secondPwd, setSecondPwd] = useState(''); + const [samePwd, setSamePwd] = useState(true); + const [token, setToken] = useState(''); + const [valid, setValid] = useState(false); + // const [sp] = useSearchParams(); + const navigateTo = useNavigate(); + const [register, { data, isLoading, isSuccess, isError, error }] = useRegisterMutation(); + const [checkToken, { data: isValid, isLoading: checkLoading, isSuccess: checkSuccess }] = + useCheckInviteTokenValidMutation(); + useEffect(() => { + // console.log(search); + const query = new URLSearchParams(location.search); + setToken(query.get('token')); + }, []); + useEffect(() => { + if (token) { + checkToken(token); + } + }, [token]); + useEffect(() => { + if (checkSuccess) { + console.log({ isValid }); + setValid(isValid); + } else { + setValid(false); + } + }, [checkSuccess, isValid]); + + const [input, setInput] = useState({ + name: '', + email: '', + password: '' + }); + + const handleReg = (evt) => { + evt.preventDefault(); + if (!samePwd) { + toast.error('two passwords not same'); + return; + } + console.log('wtf', input); + register({ + ...input, + magic_token: token, + gender: 1 + }); + }; + const handleInput = (evt) => { + const { type } = evt.target.dataset; + const { value } = evt.target; + console.log(type, value); + setInput((prev) => { + prev[type] = value; + return { ...prev }; + }); + }; + const handleSecondPwdInput = (evt) => { + const { value } = evt.target; + setSecondPwd(value); + }; + const handlePwdCheck = () => { + if (secondPwd) { + setSamePwd(secondPwd == input.password); + } + }; + useEffect(() => { + if (!samePwd) { + toast.error('two passwords not same'); + } + }, [samePwd]); + + useEffect(() => { + if (isSuccess && data) { + // 去登录 + toast.success('register success, login please'); + setTimeout(() => { + navigateTo('/login'); + }, 500); + } else if (isError) { + console.log('register failed', error); + switch (error.status) { + case 412: + toast.error('register failed: invalid token or expired'); + break; + case 409: { + const tips = { + email_conflict: 'email conflict', + name_conflict: 'name conflict' + }; + toast.error(`register failed: ${tips[error.data?.reason]}`); + break; + } + default: + toast.error('register failed'); + break; + } + } + }, [data, isSuccess, isError, error]); + + const { email, password, name } = input; + if (!token) return 'token not found'; + if (checkLoading) return 'checking token valid'; + if (!valid) return 'invite token expires or invalid'; + return ( + +
+
+ logo +

Sign Up to Rustchat

+ Please enter your details. +
+
+ + + + + +
+
+
+ ); +} diff --git a/src/routes/invite/styled.js b/src/routes/invite/styled.js new file mode 100644 index 00000000..e31c9563 --- /dev/null +++ b/src/routes/invite/styled.js @@ -0,0 +1,75 @@ +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; + } + .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; + input { + width: 360px; + background: #ffffff; + border: 1px solid #d0d5dd; + box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); + border-radius: 8px; + padding: 10px 14px; + font-weight: normal; + font-size: 16px; + line-height: 24px; + color: #667085; + } + } + .btn { + display: inline-block; + text-align: center; + width: 100%; + font-weight: 500; + font-size: 16px; + line-height: 24px; + color: #ffffff; + padding: 10px; + background: #1fe1f9; + border: 1px solid #1fe1f9; + box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); + border-radius: 8px; + &.google { + color: #344054; + border-color: #d0d5dd; + background: none; + } + } + } +`; + +export default StyledWrapper;