From d05c5ff8f22b2c46c6b507a0cac3e15bdd910d50 Mon Sep 17 00:00:00 2001 From: zerosoul Date: Thu, 10 Feb 2022 23:35:25 +0800 Subject: [PATCH] feat: lots of updates --- package.json | 193 +++++++++--------- src/app/config.js | 7 +- src/app/services/auth.js | 2 +- src/app/services/base.query.js | 49 ++++- src/app/services/channel.js | 11 +- src/app/services/contact.js | 12 +- src/app/slices/auth.data.js | 12 +- src/app/slices/channels.js | 18 +- src/app/slices/message.channel.js | 4 +- src/app/slices/message.user.js | 3 +- src/app/store.js | 54 ++--- src/app/store.with.persist.js | 56 +++++ src/common/component/ChannelModal/index.js | 6 +- src/common/component/Message.js | 52 ++++- src/common/component/NotificationHub.js | 30 ++- .../component/Send/UploadModal/index.js | 119 +++++++++++ .../component/Send/UploadModal/styled.js | 96 +++++++++ src/common/component/Send/index.js | 104 +++++++--- src/common/component/Send/styled.js | 10 + src/index.js | 53 +---- src/routes/chat/ChannelChat/index.js | 29 ++- src/routes/chat/ChannelList.js | 52 +++++ src/routes/chat/DMChat/index.js | 21 +- src/routes/chat/DMList.js | 60 ++++++ src/routes/chat/Layout.js | 86 +++++++- src/routes/chat/index.js | 78 +++---- src/routes/chat/styled.js | 5 + src/routes/home/CurrentUser.js | 1 - src/routes/home/index.js | 29 ++- src/routes/home/usePreload.js | 24 ++- src/routes/index.js | 70 +++++++ src/routes/login/index.js | 8 +- yarn.lock | 49 ++++- 33 files changed, 1072 insertions(+), 331 deletions(-) create mode 100644 src/app/store.with.persist.js create mode 100644 src/common/component/Send/UploadModal/index.js create mode 100644 src/common/component/Send/UploadModal/styled.js create mode 100644 src/routes/chat/ChannelList.js create mode 100644 src/routes/chat/DMList.js create mode 100644 src/routes/index.js diff --git a/package.json b/package.json index 809ab053..c1625404 100644 --- a/package.json +++ b/package.json @@ -1,97 +1,100 @@ { - "name": "rustchat-web", - "version": "0.1.0", - "private": true, - "homepage": "http://privoce.rustchat.com", - "dependencies": { - "@babel/core": "^7.17.0", - "@pmmmwh/react-refresh-webpack-plugin": "^0.5.4", - "@reduxjs/toolkit": "^1.7.2", - "@rtk-query/codegen-openapi": "^1.0.0-alpha.1", - "@svgr/webpack": "^6.2.1", - "axios": "^0.25.0", - "babel-loader": "^8.2.3", - "babel-plugin-named-asset-import": "^0.3.8", - "babel-preset-react-app": "^10.0.1", - "bfj": "^7.0.2", - "browserslist": "^4.19.1", - "camelcase": "^6.3.0", - "case-sensitive-paths-webpack-plugin": "^2.4.0", - "css-loader": "^6.6.0", - "css-minimizer-webpack-plugin": "^3.4.1", - "dayjs": "^1.10.7", - "dotenv": "^16.0.0", - "dotenv-expand": "^8.0.1", - "emoji-mart": "^3.0.1", - "eslint": "^8.8.0", - "event-source-polyfill": "^1.0.25", - "file-loader": "^6.2.0", - "fs-extra": "^10.0.0", - "html-webpack-plugin": "^5.5.0", - "mini-css-extract-plugin": "^2.5.3", - "react": "^17.0.2", - "react-dev-utils": "^12.0.0", - "react-dom": "^17.0.2", - "react-hot-toast": "^2.2.0", - "react-icons": "^4.3.1", - "react-redux": "^7.2.6", - "react-refresh": "^0.11.0", - "react-router-dom": "6", - "react-textarea-autosize": "^8.3.3", - "redux-persist": "^6.0.0", - "resolve": "^1.22.0", - "rooks": "^5.10.0", - "semver": "^7.3.5", - "source-map-loader": "^3.0.1", - "style-loader": "^3.3.1", - "styled-components": "^5.3.3", - "styled-reset": "^4.3.4", - "terser-webpack-plugin": "^5.3.1", - "webpack": "^5.68.0", - "webpack-dev-server": "^4.7.4", - "webpack-manifest-plugin": "^4.1.1", - "workbox-webpack-plugin": "^6.4.2" - }, - "scripts": { - "start": "node scripts/start.js", - "build": "node scripts/build.js", - "deploy": "yarn build && gh-pages -d build", - "lint": "lint-staged", - "prepare": "husky install" - }, - "browserslist": { - "production": [ - "last 5 chrome version", - "last 5 firefox version", - "last 5 safari version" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - }, - "babel": { - "presets": [ - "react-app" - ] - }, - "devDependencies": { - "@commitlint/cli": "^16.1.0", - "@commitlint/config-conventional": "^16.0.0", - "autoprefixer": "^10.4.2", - "eslint-config-prettier": "^8.3.0", - "eslint-plugin-import": "^2.25.4", - "eslint-plugin-prettier": "^4.0.0", - "eslint-plugin-react": "^7.28.0", - "eslint-plugin-react-hooks": "^4.3.0", - "gh-pages": "^3.2.3", - "husky": "^7.0.4", - "lint-staged": "^12.3.3", - "postcss": "^8.4.6", - "postcss-flexbugs-fixes": "^5.0.2", - "postcss-loader": "^6.2.1", - "postcss-preset-env": "^7.3.1", - "prettier": "^2.5.1" - } + "name": "rustchat-web", + "version": "0.1.0", + "private": true, + "homepage": "http://privoce.rustchat.com", + "dependencies": { + "@babel/core": "^7.17.0", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.4", + "@reduxjs/toolkit": "^1.7.2", + "@rtk-query/codegen-openapi": "^1.0.0-alpha.1", + "@svgr/webpack": "^6.2.1", + "animate.css": "^4.1.1", + "axios": "^0.25.0", + "babel-loader": "^8.2.3", + "babel-plugin-named-asset-import": "^0.3.8", + "babel-preset-react-app": "^10.0.1", + "bfj": "^7.0.2", + "browserslist": "^4.19.1", + "camelcase": "^6.3.0", + "case-sensitive-paths-webpack-plugin": "^2.4.0", + "css-loader": "^6.6.0", + "css-minimizer-webpack-plugin": "^3.4.1", + "dayjs": "^1.10.7", + "dotenv": "^16.0.0", + "dotenv-expand": "^8.0.1", + "emoji-mart": "^3.0.1", + "eslint": "^8.8.0", + "event-source-polyfill": "^1.0.25", + "file-loader": "^6.2.0", + "fs-extra": "^10.0.0", + "html-webpack-plugin": "^5.5.0", + "mini-css-extract-plugin": "^2.5.3", + "react": "^17.0.2", + "react-dev-utils": "^12.0.0", + "react-dnd": "^15.1.1", + "react-dnd-html5-backend": "^15.1.2", + "react-dom": "^17.0.2", + "react-hot-toast": "^2.2.0", + "react-icons": "^4.3.1", + "react-redux": "^7.2.6", + "react-refresh": "^0.11.0", + "react-router-dom": "6", + "react-textarea-autosize": "^8.3.3", + "redux-persist": "^6.0.0", + "resolve": "^1.22.0", + "rooks": "^5.10.0", + "semver": "^7.3.5", + "source-map-loader": "^3.0.1", + "style-loader": "^3.3.1", + "styled-components": "^5.3.3", + "styled-reset": "^4.3.4", + "terser-webpack-plugin": "^5.3.1", + "webpack": "^5.68.0", + "webpack-dev-server": "^4.7.4", + "webpack-manifest-plugin": "^4.1.1", + "workbox-webpack-plugin": "^6.4.2" + }, + "scripts": { + "start": "node scripts/start.js", + "build": "node scripts/build.js", + "deploy": "yarn build && gh-pages -d build", + "lint": "lint-staged", + "prepare": "husky install" + }, + "browserslist": { + "production": [ + "last 5 chrome version", + "last 5 firefox version", + "last 5 safari version" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "babel": { + "presets": [ + "react-app" + ] + }, + "devDependencies": { + "@commitlint/cli": "^16.1.0", + "@commitlint/config-conventional": "^16.0.0", + "autoprefixer": "^10.4.2", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-import": "^2.25.4", + "eslint-plugin-prettier": "^4.0.0", + "eslint-plugin-react": "^7.28.0", + "eslint-plugin-react-hooks": "^4.3.0", + "gh-pages": "^3.2.3", + "husky": "^7.0.4", + "lint-staged": "^12.3.3", + "postcss": "^8.4.6", + "postcss-flexbugs-fixes": "^5.0.2", + "postcss-loader": "^6.2.1", + "postcss-preset-env": "^7.3.1", + "prettier": "^2.5.1" + } } diff --git a/src/app/config.js b/src/app/config.js index 50bc65f7..907d1240 100644 --- a/src/app/config.js +++ b/src/app/config.js @@ -1,5 +1,10 @@ +// const BASE_URL = `${location.origin}/api`; const BASE_URL = `http://rustchat.com:3000/api`; - +export const ContentTypes = { + text: "text/plain", + image: "image/png", + json: "application/json", +}; export const tokenHeader = "X-API-Key"; export default BASE_URL; diff --git a/src/app/services/auth.js b/src/app/services/auth.js index d9a80e7d..2255aee5 100644 --- a/src/app/services/auth.js +++ b/src/app/services/auth.js @@ -8,7 +8,7 @@ export const authApi = createApi({ query: (credentials) => ({ url: "token/login", method: "POST", - body: credentials, + body: { credential: credentials, device: "web", device_token: "test" }, }), // transformResponse: (resp) => { // console.log("resp", resp); diff --git a/src/app/services/base.query.js b/src/app/services/base.query.js index 5ccf7d69..7f07dcaf 100644 --- a/src/app/services/base.query.js +++ b/src/app/services/base.query.js @@ -1,4 +1,5 @@ import { fetchBaseQuery } from "@reduxjs/toolkit/query"; +import toast from "react-hot-toast"; import BASE_URL, { tokenHeader } from "../config"; const whiteList = ["login"]; const baseQuery = fetchBaseQuery({ @@ -9,22 +10,48 @@ const baseQuery = fetchBaseQuery({ if (token && !whiteList.includes(endpoint)) { headers.set(tokenHeader, token); } + // 发送channel msg (临时举措) + // if (endpoint == "sendChannelMsg") { + // headers.set("Content-Type", "text/plain"); + // } return headers; }, }); const baseQueryWithReauth = async (args, api, extraOptions) => { let result = await baseQuery(args, api, extraOptions); - if (result.error && result.error.status === 401) { - // try to get a new token with refreshToken - // const refreshResult = await baseQuery('/refreshToken', api, extraOptions); - // if (refreshResult.data) { - // // store the new token - // api.dispatch(tokenReceived(refreshResult.data)); - // // retry the initial query - // result = await baseQuery(args, api, extraOptions); - // } else { - // api.dispatch(loggedOut()); - // } + if (result.error) { + console.log("api error", result.error); + switch (result.error.originalStatus) { + case 404: + { + toast.error("Request Not Found"); + } + break; + case 500: + { + toast.error(result.error.data || "server error"); + } + break; + case 401: + { + toast.error("token expired, please login again"); + // try to get a new token with refreshToken + // const refreshResult = await baseQuery('/refreshToken', api, extraOptions); + // if (refreshResult.data) { + // // store the new token + // api.dispatch(tokenReceived(refreshResult.data)); + // // retry the initial query + // result = await baseQuery(args, api, extraOptions); + // } else { + // api.dispatch(loggedOut()); + // } + } + + break; + + default: + break; + } } return result; }; diff --git a/src/app/services/channel.js b/src/app/services/channel.js index 266936f3..9c6bf969 100644 --- a/src/app/services/channel.js +++ b/src/app/services/channel.js @@ -1,6 +1,8 @@ import { createApi } from "@reduxjs/toolkit/query/react"; import baseQuery from "./base.query"; import { REHYDRATE } from "redux-persist"; +import { ContentTypes } from "../config"; + export const channelApi = createApi({ reducerPath: "channel", baseQuery, @@ -22,10 +24,13 @@ export const channelApi = createApi({ }), }), sendChannelMsg: builder.mutation({ - query: ({ gid, message }) => ({ - url: `group/${gid}/send`, + query: ({ id, content, type = "text" }) => ({ + headers: { + "content-type": ContentTypes[type], + }, + url: `group/${id}/send`, method: "POST", - body: message, + body: content, }), }), }), diff --git a/src/app/services/contact.js b/src/app/services/contact.js index afb93355..102b7359 100644 --- a/src/app/services/contact.js +++ b/src/app/services/contact.js @@ -1,7 +1,8 @@ import { createApi } from "@reduxjs/toolkit/query/react"; import baseQuery from "./base.query"; -import BASE_URL from "../config"; +import BASE_URL, { ContentTypes } from "../config"; import { REHYDRATE } from "redux-persist"; + export const contactApi = createApi({ reducerPath: "contact", baseQuery, @@ -22,10 +23,13 @@ export const contactApi = createApi({ }, }), sendMsg: builder.mutation({ - query: ({ uid, message }) => ({ - url: `user/${uid}/send`, + query: ({ id, content, type = "text" }) => ({ + headers: { + "content-type": ContentTypes[type], + }, + url: `user/${id}/send`, method: "POST", - body: message, + body: content, }), }), }), diff --git a/src/app/slices/auth.data.js b/src/app/slices/auth.data.js index b293100f..c80e20a8 100644 --- a/src/app/slices/auth.data.js +++ b/src/app/slices/auth.data.js @@ -2,7 +2,8 @@ import { createSlice } from "@reduxjs/toolkit"; const initialState = { user: null, - usersVersion: 0, + usersVersion: null, + afterMid: null, token: null, refreshToken: null, }; @@ -21,16 +22,25 @@ const authDataSlice = createSlice({ state.user = null; state.token = null; state.refreshToken = null; + // 清掉本地缓存auth data + // localStorage.removeItem("AUTH_DATA"); + // state.afterMid = null; + // state.usersVersion = null; }, setUsersVersion(state, action) { const { version } = action.payload; state.usersVersion = version; }, + setAfterMid(state, action) { + const { mid } = action.payload; + state.afterMid = mid; + }, }, }); export const { setAuthData, clearAuthData, setUsersVersion, + setAfterMid, } = authDataSlice.actions; export default authDataSlice.reducer; diff --git a/src/app/slices/channels.js b/src/app/slices/channels.js index 671955b8..80a49c10 100644 --- a/src/app/slices/channels.js +++ b/src/app/slices/channels.js @@ -1,18 +1,18 @@ import { createSlice } from "@reduxjs/toolkit"; - const initialState = {}; const channelsSlice = createSlice({ - name: "channels", + name: `channels`, initialState, reducers: { setChannels(state, action) { - // console.log("set channels store", action); - const chs = action.payload; - chs.forEach((c) => { - const { gid, ...rest } = c; - console.log("wtf", gid, rest); - state[gid] = rest; - }); + console.log("set channels store", state); + const chs = action.payload || []; + return Object.fromEntries( + chs.map((c) => { + const { gid, ...rest } = c; + return [gid, rest]; + }) + ); }, addChannel(state, action) { // console.log("set channels store", action); diff --git a/src/app/slices/message.channel.js b/src/app/slices/message.channel.js index 1958b5db..c2186287 100644 --- a/src/app/slices/message.channel.js +++ b/src/app/slices/message.channel.js @@ -5,6 +5,7 @@ const initialState = { // accessLogs: {}, pendingMsgs: [], }; + const channelMsgSlice = createSlice({ name: "channelMessage", initialState, @@ -16,9 +17,10 @@ const channelMsgSlice = createSlice({ created_at, mid, from_uid, + content_type, unread = true, } = action.payload; - const newMsg = { content, created_at, from_uid, unread }; + const newMsg = { content, content_type, created_at, from_uid, unread }; if (state[id]) { let replaceMsg = state[id][mid]; // 如果存在,并且新消息和缓存消息不一样,则替换掉,并且改为已读(可能有问题) diff --git a/src/app/slices/message.user.js b/src/app/slices/message.user.js index f57a92bd..60f3ddbf 100644 --- a/src/app/slices/message.user.js +++ b/src/app/slices/message.user.js @@ -10,12 +10,13 @@ const userMsgSlice = createSlice({ const { id, content, + content_type, created_at, mid, from_uid, unread = true, } = action.payload; - const newMsg = { content, created_at, from_uid, unread }; + const newMsg = { content, content_type, created_at, from_uid, unread }; if (state[id]) { let replaceMsg = state[id][mid]; // 如果存在,并且新消息和缓存消息不一样,则替换掉 diff --git a/src/app/store.js b/src/app/store.js index 6ee227f1..bf37844c 100644 --- a/src/app/store.js +++ b/src/app/store.js @@ -1,15 +1,5 @@ import { configureStore, combineReducers } from "@reduxjs/toolkit"; import { setupListeners } from "@reduxjs/toolkit/query"; -import storage from "redux-persist/lib/storage"; -import { - persistReducer, - FLUSH, - REHYDRATE, - PAUSE, - PERSIST, - PURGE, - REGISTER, -} from "redux-persist"; import authDataReducer from "./slices/auth.data"; import uiReducer from "./slices/ui"; import channelsReducer from "./slices/channels"; @@ -19,14 +9,9 @@ import { authApi } from "./services/auth"; import { contactApi } from "./services/contact"; import { channelApi } from "./services/channel"; import { serverApi } from "./services/server"; -const persistConfig = { - key: "root", - version: 1, - storage, -}; -const persistedReducer = persistReducer( - persistConfig, - combineReducers({ + +const getStore = () => { + const reducer = combineReducers({ ui: uiReducer, channels: channelsReducer, userMsg: userMsgReducer, @@ -36,21 +21,18 @@ const persistedReducer = persistReducer( [contactApi.reducerPath]: contactApi.reducer, [channelApi.reducerPath]: channelApi.reducer, [serverApi.reducerPath]: serverApi.reducer, - }) -); -const store = configureStore({ - reducer: persistedReducer, - middleware: (getDefaultMiddleware) => - getDefaultMiddleware({ - serializableCheck: { - ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], - }, - }).concat( - authApi.middleware, - contactApi.middleware, - channelApi.middleware, - serverApi.middleware - ), -}); -setupListeners(store.dispatch); -export default store; + }); + const store = configureStore({ + reducer, + middleware: (getDefaultMiddleware) => + getDefaultMiddleware().concat( + authApi.middleware, + contactApi.middleware, + channelApi.middleware, + serverApi.middleware + ), + }); + setupListeners(store.dispatch); + return store; +}; +export default getStore; diff --git a/src/app/store.with.persist.js b/src/app/store.with.persist.js new file mode 100644 index 00000000..6ee227f1 --- /dev/null +++ b/src/app/store.with.persist.js @@ -0,0 +1,56 @@ +import { configureStore, combineReducers } from "@reduxjs/toolkit"; +import { setupListeners } from "@reduxjs/toolkit/query"; +import storage from "redux-persist/lib/storage"; +import { + persistReducer, + FLUSH, + REHYDRATE, + PAUSE, + PERSIST, + PURGE, + REGISTER, +} from "redux-persist"; +import authDataReducer from "./slices/auth.data"; +import uiReducer from "./slices/ui"; +import channelsReducer from "./slices/channels"; +import channelMsgReducer from "./slices/message.channel"; +import userMsgReducer from "./slices/message.user"; +import { authApi } from "./services/auth"; +import { contactApi } from "./services/contact"; +import { channelApi } from "./services/channel"; +import { serverApi } from "./services/server"; +const persistConfig = { + key: "root", + version: 1, + storage, +}; +const persistedReducer = persistReducer( + persistConfig, + combineReducers({ + ui: uiReducer, + channels: channelsReducer, + userMsg: userMsgReducer, + channelMsg: channelMsgReducer, + authData: authDataReducer, + [authApi.reducerPath]: authApi.reducer, + [contactApi.reducerPath]: contactApi.reducer, + [channelApi.reducerPath]: channelApi.reducer, + [serverApi.reducerPath]: serverApi.reducer, + }) +); +const store = configureStore({ + reducer: persistedReducer, + middleware: (getDefaultMiddleware) => + getDefaultMiddleware({ + serializableCheck: { + ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], + }, + }).concat( + authApi.middleware, + contactApi.middleware, + channelApi.middleware, + serverApi.middleware + ), +}); +setupListeners(store.dispatch); +export default store; diff --git a/src/common/component/ChannelModal/index.js b/src/common/component/ChannelModal/index.js index 0496a90b..f2a675f1 100644 --- a/src/common/component/ChannelModal/index.js +++ b/src/common/component/ChannelModal/index.js @@ -1,16 +1,19 @@ import { useState, useEffect } from "react"; import toast from "react-hot-toast"; import { useNavigate } from "react-router-dom"; -import { useSelector } from "react-redux"; +import { useSelector, useDispatch } from "react-redux"; import Modal from "../Modal"; import ChannelIcon from "../ChannelIcon"; import Contact from "../Contact"; import StyledWrapper from "./styled"; import useFilteredUsers from "../../hook/useFilteredUsers"; +import { addChannel } from "../../../app/slices/channels"; + import { useCreateChannelMutation } from "../../../app/services/channel"; export default function ChannelModal({ personal = false, closeModal }) { const navigateTo = useNavigate(); + const dispatch = useDispatch(); const [data, setData] = useState({ name: "", dsecription: "", @@ -47,6 +50,7 @@ export default function ChannelModal({ personal = false, closeModal }) { if (isSuccess) { toast.success("create new channel success"); closeModal(); + dispatch(addChannel(newChannel)); navigateTo(`/chat/channel/${newChannel.gid}`); } }, [isSuccess, newChannel]); diff --git a/src/common/component/Message.js b/src/common/component/Message.js index b6a69dbc..f6b5f495 100644 --- a/src/common/component/Message.js +++ b/src/common/component/Message.js @@ -1,9 +1,10 @@ -import { useEffect } from "react"; +import { useEffect, useRef } from "react"; import styled from "styled-components"; import dayjs from "dayjs"; import { useDispatch } from "react-redux"; import { useInViewRef } from "rooks"; import Avatar from "./Avatar"; +import BASE_URL from "../../app/config"; import { useGetContactsQuery } from "../../app/services/contact"; import { setChannelMsgRead } from "../../app/slices/message.channel"; import { setUserMsgRead } from "../../app/slices/message.user"; @@ -50,9 +51,32 @@ const StyledMsg = styled.div` &.pending { opacity: 0.5; } + .img { + max-width: 400px; + } } } `; +const renderContent = (type, content) => { + let ctn = null; + switch (type) { + case "text/plain": + ctn = content; + break; + case "image/jpeg": + ctn = ( + + ); + break; + + default: + break; + } + return ctn; +}; export default function Message({ gid = "", mid = "", @@ -60,28 +84,34 @@ export default function Message({ fromUid, time, content, + content_type = "text/plain", unread = false, pending, }) { const [myRef, inView] = useInViewRef(); const disptach = useDispatch(); - // const wrapperRef = useRef(null); + const avatarRef = useRef(null); const { data: contacts } = useGetContactsQuery(); useEffect(() => { - // if (wrapperRef) { - // wrapperRef.current.scrollIntoView(); - if (inView && unread) { - const setMsgRead = gid ? setChannelMsgRead : setUserMsgRead; - disptach(setMsgRead({ id: gid || uid, mid })); + if (!unread) { + avatarRef.current?.scrollIntoView(false); + } + }, [unread]); + + useEffect(() => { + if (inView) { + if (unread) { + const setMsgRead = gid ? setChannelMsgRead : setUserMsgRead; + disptach(setMsgRead({ id: gid || uid, mid })); + } } - // } }, [gid, mid, uid, unread, inView]); if (!contacts) return null; const currUser = contacts.find((c) => c.uid == fromUid) || {}; return ( -
+
@@ -89,7 +119,9 @@ export default function Message({ {currUser.name} {dayjs(time).format("YYYY-MM-DD h:mm:ss A")}
-
{content}
+
+ {renderContent(content_type, content)} +
); diff --git a/src/common/component/NotificationHub.js b/src/common/component/NotificationHub.js index b60fc1e0..2b9bea4a 100644 --- a/src/common/component/NotificationHub.js +++ b/src/common/component/NotificationHub.js @@ -10,19 +10,35 @@ import { addChannel, deleteChannel, } from "../../app/slices/channels"; -import { clearAuthData, setUsersVersion } from "../../app/slices/auth.data"; +import { + clearAuthData, + setUsersVersion, + setAfterMid, +} from "../../app/slices/auth.data"; import { addChannelMsg } from "../../app/slices/message.channel"; import { addUserMsg } from "../../app/slices/message.user"; - -const NotificationHub = ({ token, usersVersion = 0 }) => { +const getQueryString = (params = {}) => { + const sp = new URLSearchParams(); + Object.entries(params).forEach(([key, val]) => { + if (val) { + sp.append(key, val); + } + }); + return sp.toString(); +}; +const NotificationHub = ({ token, usersVersion = 0, afterMid = 0 }) => { const dispatch = useDispatch(); const navigate = useNavigate(); useEffect(() => { let sse = null; if (token) { sse = new EventSource( - `${BASE_URL}/user/events?api-key=${token}&users_version=${usersVersion}` + `${BASE_URL}/user/events?${getQueryString({ + "api-key": token, + users_version: usersVersion, + after_mid: afterMid, + })}` ); sse.onopen = () => { console.info("sse opened"); @@ -40,7 +56,7 @@ const NotificationHub = ({ token, usersVersion = 0 }) => { sse.close(); } }; - }, [token]); + }, [token, usersVersion, afterMid]); const handleSSEMessage = (data) => { const { type } = data; @@ -89,11 +105,15 @@ const NotificationHub = ({ token, usersVersion = 0 }) => { case "chat": // console.log("chat data", data); if (data.gid) { + // channel msg const { gid, ...rest } = data; dispatch(addChannelMsg({ id: gid, ...rest })); } else { + // user msg dispatch(addUserMsg({ id: data.from_uid, ...data })); } + // 更新after_mid + dispatch(setAfterMid({ mid: data.mid })); break; default: diff --git a/src/common/component/Send/UploadModal/index.js b/src/common/component/Send/UploadModal/index.js new file mode 100644 index 00000000..639d58e4 --- /dev/null +++ b/src/common/component/Send/UploadModal/index.js @@ -0,0 +1,119 @@ +import { useState, useEffect } from "react"; +import { useDispatch } from "react-redux"; +// import toast from "react-hot-toast"; +// import { useNavigate } from "react-router-dom"; +// import { useSelector, useDispatch } from "react-redux"; +import { useSendChannelMsgMutation } from "../../../../app/services/channel"; +import { useSendMsgMutation } from "../../../../app/services/contact"; +import { addChannelMsg } from "../../../../app/slices/message.channel"; +import { addUserMsg } from "../../../../app/slices/message.user"; +import Modal from "../../Modal"; +import StyledWrapper from "./styled"; + +export default function UploadModal({ + type = "user", + sendTo = 0, + files = [], + closeModal, +}) { + const dispatch = useDispatch(); + const [ + sendChannelMsg, + { + error: sendChannelError, + isLoading: channelSending, + isSuccess: sendChannelSuccess, + data: sendChannelData, + }, + ] = useSendChannelMsgMutation(); + const [ + sendUserMsg, + { + error: sendUserError, + isLoading: userSending, + isSuccess: sendUserSuccess, + data: sendUserData, + }, + ] = useSendMsgMutation(); + const [blobs, setBlobs] = useState([]); + useEffect(() => { + files.forEach((file) => { + var fileReader = new FileReader(); + fileReader.onloadend = (e) => { + const { name, size, type } = file; + const obj = { name, size, type }; + let dataUrl = e.target.result; + console.log({ dataUrl }, e.target); + // let blob = new Blob([arrayBuffer]); + obj.data = dataUrl; + setBlobs((prevs) => { + return [...prevs, obj]; + }); + }; + fileReader.readAsDataURL(file); + }); + }, [files]); + const handleUpload = () => { + const uploadFn = type == "user" ? sendUserMsg : sendChannelMsg; + uploadFn({ id: sendTo, content: files[0], type: "image" }); + }; + useEffect(() => { + if (sendUserSuccess) { + dispatch(addUserMsg({ id: sendTo, ...sendUserData, unread: false })); + closeModal(); + } + }, [sendUserSuccess, sendUserData]); + + useEffect(() => { + if (sendChannelSuccess) { + const { gid, ...rest } = sendChannelData; + dispatch(addChannelMsg({ id: gid, ...rest, unread: false })); + closeModal(); + } + }, [sendChannelSuccess, sendChannelData]); + if (!sendTo) return null; + return ( + + +

Upload a file

+

Photos accept jpg, png, max size limit to 10M.

+
    + {blobs.map((b, idx) => { + console.log({ b }); + return ( +
  • + thumb +
    +
    + + {b.name} + + (click title to change name) +
    + {`${Math.floor(b.size / 1000)}KB`} +
    +
  • + ); + })} +
+
+ + +
+
+
+ ); +} diff --git a/src/common/component/Send/UploadModal/styled.js b/src/common/component/Send/UploadModal/styled.js new file mode 100644 index 00000000..0ad675b0 --- /dev/null +++ b/src/common/component/Send/UploadModal/styled.js @@ -0,0 +1,96 @@ +import styled from "styled-components"; + +const StyledWrapper = styled.div` + display: flex; + flex-direction: column; + align-items: center; + padding: 16px; + background-color: #fff; + filter: drop-shadow(0px 25px 50px rgba(31, 41, 55, 0.25)); + border-radius: 8px; + .head { + font-style: normal; + font-weight: 600; + font-size: 20px; + line-height: 30px; + margin-bottom: 8px; + } + .intro { + font-style: normal; + font-weight: normal; + font-size: 14px; + line-height: 20px; + color: #6b7280; + } + .list { + padding-top: 32px; + display: flex; + flex-direction: column; + gap: 8px; + .item { + padding: 16px; + border: 1px solid rgba(116, 127, 141, 0.2); + border-radius: 6px; + display: flex; + align-items: center; + gap: 16px; + min-width: 473px; + .thumb { + object-fit: cover; + width: 64px; + height: 64px; + border-radius: 6px; + } + .right { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 4px; + .name { + font-style: normal; + .input { + user-select: text; + line-height: 20px; + font-size: 14px; + font-weight: 600; + margin-right: 8px; + } + .tip { + line-height: 18px; + font-size: 12px; + color: #78787c; + } + } + .size { + font-size: 14px; + line-height: 20px; + color: #616161; + } + } + } + } + .btns { + padding-top: 32px; + padding-bottom: 16px; + display: flex; + align-items: center; + justify-content: flex-end; + gap: 16px; + width: 100%; + .btn { + color: #fff; + padding: 8px 16px; + background: #1fe1f9; + /* shadow-base */ + + box-shadow: 0px 1px 2px rgba(31, 41, 55, 0.08); + border-radius: 4px; + &.cancel { + color: #333; + border: 1px solid #e5e7eb; + background-color: #fff; + } + } + } +`; +export default StyledWrapper; diff --git a/src/common/component/Send/index.js b/src/common/component/Send/index.js index 1b25e878..a5ae7cd0 100644 --- a/src/common/component/Send/index.js +++ b/src/common/component/Send/index.js @@ -10,18 +10,32 @@ import { useSendMsgMutation } from "../../../app/services/contact"; import { addChannelMsg } from "../../../app/slices/message.channel"; import { addUserMsg } from "../../../app/slices/message.user"; import StyledSend from "./styled"; +import UploadModal from "./UploadModal"; const Types = { channel: "#", user: "@", }; -export default function Send({ name, type = "channel", id = "" }) { +export default function Send({ + name, + type = "channel", + id = "", + dragFiles = [], +}) { + const [files, setFiles] = useState([]); const inputRef = useRef(); const [emojiPicker, setEmojiPicker] = useState(false); const [shift, setShift] = useState(false); const [enter, setEnter] = useState(false); const [msg, setMsg] = useState(""); const dispatch = useDispatch(); + console.log("send drag files", dragFiles); + useEffect(() => { + if (dragFiles.length) { + setFiles((prev) => [...prev, ...dragFiles]); + } + }, [dragFiles]); + const toggleEmojiPicker = () => { setEmojiPicker((prev) => !prev); }; @@ -79,15 +93,20 @@ export default function Send({ name, type = "channel", id = "" }) { useEffect(() => { inputRef.current.focus(); }, [msg]); - + const handleUpload = (evt) => { + setFiles([...evt.target.files]); + }; + const resetFiles = () => { + setFiles([]); + }; const handleSendMessage = () => { if (!msg || !type || !id) return; switch (type) { case "channel": - sendChannelMsg({ gid: id, message: msg }); + sendChannelMsg({ id, content: msg }); break; case "user": - sendMsg({ uid: id, message: msg }); + sendMsg({ id, content: msg }); break; default: @@ -95,35 +114,54 @@ export default function Send({ name, type = "channel", id = "" }) { } }; return ( - - -
- + +
+ + +
+
+ +
+
+ + {emojiPicker && ( +
+ +
+ )} +
+
+ {files.length !== 0 && ( + -
-
- - {emojiPicker && ( -
- -
- )} -
-
+ )} + ); } diff --git a/src/common/component/Send/styled.js b/src/common/component/Send/styled.js index 401c150e..6b814b68 100644 --- a/src/common/component/Send/styled.js +++ b/src/common/component/Send/styled.js @@ -16,6 +16,16 @@ const StyledSend = styled.div` /* margin: 0 16px; */ .addon { cursor: pointer; + position: relative; + input { + opacity: 0; + cursor: pointer; + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + } } .input { width: 100%; diff --git a/src/index.js b/src/index.js index f034d6d4..a5ddd868 100644 --- a/src/index.js +++ b/src/index.js @@ -1,56 +1,21 @@ // import React from 'react'; import ReactDOM from "react-dom"; import { Toaster } from "react-hot-toast"; -import { Route, Routes, HashRouter } from "react-router-dom"; + import { Reset } from "styled-reset"; -import { persistStore } from "redux-persist"; -import { PersistGate } from "redux-persist/integration/react"; -import { Provider } from "react-redux"; -import store from "./app/store"; -import RequireAuth from "./common/component/RequireAuth"; +import { DndProvider } from "react-dnd"; +import { HTML5Backend } from "react-dnd-html5-backend"; +import "animate.css"; +import ReduxRoutes from "./routes"; -// import Welcome from './routes/Welcome' -import NotFoundPage from "./routes/404"; -import LoginPage from "./routes/login"; -import HomePage from "./routes/home"; -import ChatPage from "./routes/chat"; -import ContactsPage from "./routes/contacts"; - -let persistor = persistStore(store); ReactDOM.render( <> - - - - - - } /> - - - - } - > - } /> - - } /> - } /> - } /> - - - } /> - } /> - - - } /> - - - - + + + + , document.getElementById("root") ); diff --git a/src/routes/chat/ChannelChat/index.js b/src/routes/chat/ChannelChat/index.js index ee0d2a07..d63cf5b2 100644 --- a/src/routes/chat/ChannelChat/index.js +++ b/src/routes/chat/ChannelChat/index.js @@ -1,4 +1,4 @@ -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { useSelector, useDispatch } from "react-redux"; import dayjs from "dayjs"; @@ -20,7 +20,14 @@ import { StyledHeader, } from "./styled"; -export default function ChannelChat({ cid = "", unreads = 0, data = {} }) { +export default function ChannelChat({ + cid = "", + unreads = 0, + data = {}, + dropFiles = [], +}) { + // const containerRef = useRef(null); + const [dragFiles, setDragFiles] = useState([]); const dispatch = useDispatch(); const msgs = useSelector((store) => { return store.channelMsg[cid] || {}; @@ -29,6 +36,11 @@ export default function ChannelChat({ cid = "", unreads = 0, data = {} }) { const handleClearUnreads = () => { dispatch(clearChannelMsgUnread(cid)); }; + useEffect(() => { + if (dropFiles.length) { + setDragFiles(dropFiles); + } + }, [dropFiles]); useEffect(() => { console.log({ cid }); return () => { @@ -45,6 +57,8 @@ export default function ChannelChat({ cid = "", unreads = 0, data = {} }) { console.log("channel message list", msgs); return (
@@ -73,9 +87,16 @@ export default function ChannelChat({ cid = "", unreads = 0, data = {} }) {
{Object.entries(msgs).map(([mid, msg]) => { if (!msg) return null; - const { from_uid, content, created_at, unread } = msg; + const { + from_uid, + content, + content_type, + created_at, + unread, + } = msg; return (
- +
{unreads != 0 && ( diff --git a/src/routes/chat/ChannelList.js b/src/routes/chat/ChannelList.js new file mode 100644 index 00000000..90ce9560 --- /dev/null +++ b/src/routes/chat/ChannelList.js @@ -0,0 +1,52 @@ +// import React from 'react' +import { NavLink, useNavigate } from "react-router-dom"; +import { useDrop } from "react-dnd"; +import { NativeTypes } from "react-dnd-html5-backend"; +import ChannelIcon from "../../common/component/ChannelIcon"; +const NavItem = ({ data, setFiles }) => { + const navigate = useNavigate(); + const [{ isActive }, drop] = useDrop(() => ({ + accept: [NativeTypes.FILE], + drop({ dataTransfer }) { + if (dataTransfer.files.length) { + // console.log(files, rest); + setFiles([...dataTransfer.files]); + navigate(`/chat/channel/${data.id}`); + // 重置 + setTimeout(() => { + setFiles([]); + }, 300); + } + }, + collect: (monitor) => ({ + isActive: monitor.canDrop() && monitor.isOver(), + }), + })); + const { id, is_public, name, description, unreads } = data; + return ( + + + + {name} + + {unreads > 0 && {unreads}} + + ); +}; +export default function ChannelList({ channels, setDropFiles }) { + return channels.map(({ id, is_public, name, description, unreads }) => { + return ( + + ); + }); +} diff --git a/src/routes/chat/DMChat/index.js b/src/routes/chat/DMChat/index.js index b153e19a..1a080187 100644 --- a/src/routes/chat/DMChat/index.js +++ b/src/routes/chat/DMChat/index.js @@ -8,7 +8,9 @@ import Layout from "../Layout"; import { StyledHeader, StyledDMChat } from "./styled"; -export default function DMChat({ uid = "" }) { +export default function DMChat({ uid = "", dropFiles = [] }) { + console.log("dm files", dropFiles); + const [dragFiles, setDragFiles] = useState([]); const msgs = useSelector((store) => { return store.userMsg[uid] || {}; }); @@ -20,10 +22,17 @@ export default function DMChat({ uid = "" }) { setCurrUser(contacts.find((c) => c.uid == uid)); } }, [uid, contacts]); + useEffect(() => { + if (dropFiles.length) { + setDragFiles(dropFiles); + } + }, [dropFiles]); + if (!currUser) return null; console.log("user msgs", msgs); return ( @@ -35,9 +44,10 @@ export default function DMChat({ uid = "" }) { {Object.entries(msgs).map(([mid, msg]) => { if (!msg) return null; console.log("user msg", msg); - const { from_uid, content, created_at, unread } = msg; + const { from_uid, content, content_type, created_at, unread } = msg; return (
- +
); } diff --git a/src/routes/chat/DMList.js b/src/routes/chat/DMList.js new file mode 100644 index 00000000..85c79bf3 --- /dev/null +++ b/src/routes/chat/DMList.js @@ -0,0 +1,60 @@ +// import React from "react"; +import { NavLink, useNavigate } from "react-router-dom"; +import dayjs from "dayjs"; +import { useDrop } from "react-dnd"; +import { NativeTypes } from "react-dnd-html5-backend"; +import Avatar from "../../common/component/Avatar"; +const NavItem = ({ data, setFiles }) => { + const navigate = useNavigate(); + const [{ isActive }, drop] = useDrop(() => ({ + accept: [NativeTypes.FILE], + drop({ dataTransfer }) { + if (dataTransfer.files.length) { + // console.log(files, rest); + setFiles([...dataTransfer.files]); + navigate(`/chat/dm/${data.uid}`); + // 重置 + setTimeout(() => { + setFiles([]); + }, 300); + } + }, + collect: (monitor) => ({ + isActive: monitor.canDrop() && monitor.isOver(), + }), + })); + const { uid, user, lastMsg, unreads } = data; + return ( + + +
+
+ {user.name} + +
+ +
+
{lastMsg.content}
+ {unreads > 0 && {unreads}} +
+
+
+ ); +}; +export default function DMList({ sessions, setDropFiles }) { + return sessions.map(({ uid, user, lastMsg, unreads } = {}) => { + if (!user) return null; + return ( + + ); + }); +} diff --git a/src/routes/chat/Layout.js b/src/routes/chat/Layout.js index 611df79a..d1766a08 100644 --- a/src/routes/chat/Layout.js +++ b/src/routes/chat/Layout.js @@ -1,11 +1,13 @@ -import React from "react"; +// import { useState } from "react"; +import { useDrop } from "react-dnd"; +import { NativeTypes } from "react-dnd-html5-backend"; import styled from "styled-components"; const StyledWrapper = styled.article` position: relative; width: 100%; background: #fff; height: 100vh; - .head { + > .head { height: 56px; padding: 0 20px; box-shadow: 0px 1px 0px rgba(0, 0, 0, 0.1); @@ -21,16 +23,92 @@ const StyledWrapper = styled.article` border-top: 1px solid transparent; } } + .drag_tip { + display: flex; + justify-content: center; + align-items: center; + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + visibility: hidden; + /* pointer-events: none; */ + &.visible { + visibility: visible; + } + .box { + padding: 16px; + filter: drop-shadow(0px 25px 50px rgba(31, 41, 55, 0.25)); + border-radius: 8px; + background: #52edff; + .inner { + padding: 16px; + padding-top: 64px; + border: 2px dashed #a5f3fc; + border-radius: 6px; + display: flex; + flex-direction: column; + align-items: center; + color: #fff; + .head { + font-weight: 600; + font-size: 20px; + line-height: 30px; + } + .intro { + font-weight: normal; + font-size: 14px; + line-height: 20px; + } + } + } + } `; -export default function Layout({ children, header, contacts = null }) { +export default function Layout({ + children, + header, + contacts = null, + setDragFiles, +}) { + const [{ isActive }, drop] = useDrop(() => ({ + accept: [NativeTypes.FILE], + drop({ files }) { + if (files.length) { + setDragFiles([...files]); + } + }, + collect: (monitor) => ({ + isActive: monitor.canDrop() && monitor.isOver(), + }), + })); return ( - +
{header}
{children} {contacts &&
{contacts}
}
+
+
+
+

Upload to #Channel

+ + Photos accept jpg, png, max size limit to 10M. + +
+
+
); } diff --git a/src/routes/chat/index.js b/src/routes/chat/index.js index ebcda08c..a4d23cee 100644 --- a/src/routes/chat/index.js +++ b/src/routes/chat/index.js @@ -2,7 +2,7 @@ import { useState } from "react"; import { NavLink, useParams } from "react-router-dom"; import { useSelector } from "react-redux"; -import dayjs from "dayjs"; + import { MdAdd } from "react-icons/md"; import { AiOutlineCaretDown } from "react-icons/ai"; @@ -10,13 +10,16 @@ import { useGetContactsQuery } from "../../app/services/contact"; import StyledWrapper from "./styled"; import Search from "../../common/component/Search"; import Avatar from "../../common/component/Avatar"; -import ChannelIcon from "../../common/component/ChannelIcon"; import ChannelChat from "./ChannelChat"; import DMChat from "./DMChat"; +import ChannelList from "./ChannelList"; import ContactsModal from "../../common/component/ContactsModal"; import ChannelModal from "../../common/component/ChannelModal"; +import DMList from "./DMList"; export default function ChatPage() { + const [channelDropFiles, setChannelDropFiles] = useState([]); + const [userDropFiles, setUserDropFiles] = useState([]); const { channels, UserMsgData, ChannelMsgData } = useSelector((store) => { return { channels: store.channels, @@ -38,11 +41,23 @@ export default function ChatPage() { return Object.values(ChannelMsgData[gid] || {}).filter((m) => m.unread) .length; }; + if (!contacts) return null; - const Sessions = Object.keys(UserMsgData); const tmpSessionUser = contacts.find((c) => c.uid == user_id); const transformedChannels = Object.entries(channels).map(([key, obj]) => { - return { id: key, ...obj }; + const unreads = Object.values(ChannelMsgData[key] || {}).filter( + (m) => m.unread + ).length; + return { id: key, ...obj, unreads }; + }); + const sessions = Object.keys(UserMsgData).map((uid) => { + let currUser = contacts.find((c) => c.uid == uid); + if (!currUser) return undefined; + let lastMid = Object.keys(UserMsgData[uid]).sort().pop(); + let unreads = Object.values(UserMsgData[uid] || {}).filter((m) => m.unread) + .length; + let lastMsg = UserMsgData[uid][lastMid]; + return { user: currUser, unreads, uid, lastMsg }; }); return ( <> @@ -68,25 +83,10 @@ export default function ChatPage() { />
@@ -102,33 +102,8 @@ export default function ChatPage() { />
diff --git a/src/routes/chat/styled.js b/src/routes/chat/styled.js index 4b603051..f1a5db81 100644 --- a/src/routes/chat/styled.js +++ b/src/routes/chat/styled.js @@ -83,6 +83,7 @@ const StyledWrapper = styled.div` &.active { background: rgba(116, 127, 141, 0.1); } + .avatar { width: 32px; height: 32px; @@ -145,6 +146,10 @@ const StyledWrapper = styled.div` } } } + /* drop files effect */ + .drop_over { + outline: 2px solid #52edff; + } } } } diff --git a/src/routes/home/CurrentUser.js b/src/routes/home/CurrentUser.js index f7430412..b39a6b30 100644 --- a/src/routes/home/CurrentUser.js +++ b/src/routes/home/CurrentUser.js @@ -6,7 +6,6 @@ import { useSelector, useDispatch } from "react-redux"; import { useNavigate } from "react-router-dom"; import { clearAuthData } from "../../app/slices/auth.data"; - import { useLazyLogoutQuery } from "../../app/services/auth"; const StyledWrapper = styled.div` diff --git a/src/routes/home/index.js b/src/routes/home/index.js index 04ef4221..bcd347f5 100644 --- a/src/routes/home/index.js +++ b/src/routes/home/index.js @@ -1,8 +1,9 @@ // import React from 'react'; -// import { useState } from "react"; +// import { useEffect } from "react"; import { Outlet, NavLink } from "react-router-dom"; import { useSelector, useDispatch } from "react-redux"; import { toggleMenuExpand } from "../../app/slices/ui"; +// import { setAuthData } from "../../app/slices/auth.data"; import StyledWrapper from "./styled"; import ServerDropList from "./ServerDropList"; import Tools from "./Tools"; @@ -16,21 +17,37 @@ import NotificationHub from "../../common/component/NotificationHub"; export default function HomePage() { const dispatch = useDispatch(); - const { menuExpand, token, usersVersion } = useSelector((store) => { + const { + menuExpand, + authData: { token, usersVersion, afterMid }, + } = useSelector((store) => { return { - token: store.authData.token, - usersVersion: store.authData.usersVersion, + authData: store.authData, menuExpand: store.ui.menuExpand, }; }); - const { data, error, success } = usePreload(); + const { data, loading, error, success } = usePreload(); + + // useEffect(() => { + // if (authData) { + // dispatch(setAuthData(data)); + // } + // }, [authData]); + const toggleExpand = () => { dispatch(toggleMenuExpand()); }; console.log({ data, error, success }); + if (loading) { + return "loading"; + } return ( <> - +
{ + return store.authData.user; + }); + const dispatch = useDispatch(); + const navigate = useNavigate(); const { isLoading: contactsLoading, isSuccess: contactsSuccess, @@ -25,9 +35,19 @@ export default function usePreload() { // isError: groupsError, // data: groups, // } = useGetChannelsQuery(undefined, querySetting); + useEffect(() => { + if (contacts) { + const matchedUser = contacts.find((c) => c.uid == loginedUser.uid); + if (!matchedUser) { + dispatch(clearAuthData()); + navigate("/login"); + } + setChecked(true); + } + }, [contacts]); return { - loading: contactsLoading && serverLoading, + loading: contactsLoading || serverLoading || !checked, error: contactsError && serverError, success: contactsSuccess && serverSuccess, data: { diff --git a/src/routes/index.js b/src/routes/index.js new file mode 100644 index 00000000..407e76ef --- /dev/null +++ b/src/routes/index.js @@ -0,0 +1,70 @@ +// import { useState } from "react"; +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 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 store from "../app/store.with.persist"; +// import getStore from "../app/store"; + +const PageRoutes = () => { + 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 ? ( + + + + ) : ( + + )} */} + + ); +} diff --git a/src/routes/login/index.js b/src/routes/login/index.js index b0885360..7fbcd8c5 100644 --- a/src/routes/login/index.js +++ b/src/routes/login/index.js @@ -8,6 +8,7 @@ import { useLoginMutation } from "../../app/services/auth"; import { setAuthData } from "../../app/slices/auth.data"; export default function LoginPage() { + // const { token } = useSelector((store) => store.authData); const navigateTo = useNavigate(); const dispatch = useDispatch(); const [login] = useLoginMutation(); @@ -21,8 +22,7 @@ export default function LoginPage() { console.log("wtf", input); const { data, error } = await login({ ...input, - device: "web", - device_token: "test", + type: "password", }); if (error) { console.log(error); @@ -37,9 +37,11 @@ export default function LoginPage() { return; } if (data) { - dispatch(setAuthData(data)); + // 更新本地认证信息 toast.success("login success"); + dispatch(setAuthData(data)); navigateTo("/"); + // location.reload(true); } }; const handleInput = (evt) => { diff --git a/yarn.lock b/yarn.lock index a7f7c03e..71a563e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1437,6 +1437,21 @@ schema-utils "^3.0.0" source-map "^0.7.3" +"@react-dnd/asap@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@react-dnd/asap/-/asap-4.0.0.tgz#b300eeed83e9801f51bd66b0337c9a6f04548651" + integrity sha512-0XhqJSc6pPoNnf8DhdsPHtUhRzZALVzYMTzRwV4VI6DJNJ/5xxfL9OQUwb8IH5/2x7lSf7nAZrnzUD+16VyOVQ== + +"@react-dnd/invariant@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@react-dnd/invariant/-/invariant-3.0.0.tgz#ea55db612b8be3284e87b67f1a1567595cd4c386" + integrity sha512-keberJRIqPX15IK3SWS/iO1t/kGETiL1oczKrDitAaMnQ+kpHf81l3MrRmFjvfqcnApE+izEvwM6GsyoIcpsVA== + +"@react-dnd/shallowequal@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@react-dnd/shallowequal/-/shallowequal-3.0.0.tgz#ccfe1d6eead7591b4cebf6d7fec7150eb4de1962" + integrity sha512-1ELWQdJB2UrCXTKK5cCD9uGLLIwECLIEdttKA255owdpchtXohIjZBTlFJszwYi2ZKe2Do+QvUzsGyGCMNwbdw== + "@reduxjs/toolkit@^1.7.2": version "1.7.2" resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.7.2.tgz#b428aaef92582379464f9de698dbb71957eafb02" @@ -2075,6 +2090,11 @@ ajv@^8.0.0, ajv@^8.6.0, ajv@^8.8.0: require-from-string "^2.0.2" uri-js "^4.2.2" +animate.css@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/animate.css/-/animate.css-4.1.1.tgz#614ec5a81131d7e4dc362a58143f7406abd68075" + integrity sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ== + ansi-escapes@^4.3.0: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" @@ -3140,6 +3160,15 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +dnd-core@15.1.1: + version "15.1.1" + resolved "https://registry.yarnpkg.com/dnd-core/-/dnd-core-15.1.1.tgz#b4dce2d892be2a7c9ca32ffdd545350be8d52f4f" + integrity sha512-Mtj/Sltcx7stVXzeDg4g7roTe/AmzRuIf/FYOxX6F8gULbY54w066BlErBOzQfn9RIJ3gAYLGX7wvVvoBSq7ig== + dependencies: + "@react-dnd/asap" "4.0.0" + "@react-dnd/invariant" "3.0.0" + redux "^4.1.1" + dns-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" @@ -6223,6 +6252,24 @@ react-dev-utils@^12.0.0: strip-ansi "^6.0.1" text-table "^0.2.0" +react-dnd-html5-backend@^15.1.2: + version "15.1.2" + resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-15.1.2.tgz#85e2c5ad57e87190495756f68f44fd89299062fb" + integrity sha512-mem9QbutUF+aA2YC1y47G3ECjnYV/sCYKSnu5Jd7cbg3fLMPAwbnTf/JayYdnCH5l3eg9akD9dQt+cD0UdF8QQ== + dependencies: + dnd-core "15.1.1" + +react-dnd@^15.1.1: + version "15.1.1" + resolved "https://registry.yarnpkg.com/react-dnd/-/react-dnd-15.1.1.tgz#feb233200cb372ea6382b511baac6cc52a0068dc" + integrity sha512-QLrHtPU08U4c5zop0ANeqrHXaQw2EWLMn8DQoN6/e4eSN/UbB84P49/80Qg0MEF29VLB5vikSoiFh9N8ASNmpQ== + dependencies: + "@react-dnd/invariant" "3.0.0" + "@react-dnd/shallowequal" "3.0.0" + dnd-core "15.1.1" + fast-deep-equal "^3.1.3" + hoist-non-react-statics "^3.3.2" + react-dom@^17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" @@ -6381,7 +6428,7 @@ redux-thunk@^2.4.1: resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.1.tgz#0dd8042cf47868f4b29699941de03c9301a75714" integrity sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q== -redux@^4.0.0, redux@^4.1.2: +redux@^4.0.0, redux@^4.1.1, redux@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.2.tgz#140f35426d99bb4729af760afcf79eaaac407104" integrity sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==