refactor: socket and redux

pull/13/head
moonrailgun 4 years ago
parent b3b88968a6
commit b34fa903d5

@ -4,17 +4,57 @@ import _isNil from 'lodash/isNil';
let socket: Socket;
class SocketEventError extends Error {
name: 'SocketEventError';
}
type SocketEventRespones<T = unknown> =
| {
result: true;
data: T;
}
| {
result: false;
message: string;
};
/**
* Socket
*/
export class AppSocket {
constructor(private socket: Socket) {}
get connected(): boolean {
return socket.connected;
}
async request<T = unknown>(
eventName: string,
eventData: unknown = {}
): Promise<T> {
return new Promise((resolve, reject) => {
this.socket.emit(eventName, eventData, (resp: SocketEventRespones<T>) => {
if (resp.result === true) {
resolve(resp.data);
} else if (resp.result === false) {
reject(new SocketEventError(resp.message));
}
});
});
}
}
/**
* Socket
* Socket
* @param token Token
*/
export function createSocket(token: string) {
export function createSocket(token: string): Promise<AppSocket> {
if (!_isNil(socket)) {
socket.close();
}
return new Promise<void>((resolve, reject) => {
return new Promise((resolve, reject) => {
socket = io(config.serverUrl, {
transports: ['websocket'],
auth: {
@ -23,7 +63,7 @@ export function createSocket(token: string) {
});
socket.once('connect', () => {
// 连接成功
resolve();
resolve(new AppSocket(socket));
});
socket.once('error', () => {
reject();

@ -1,6 +1,7 @@
// api
export { buildStorage } from './api/buildStorage';
export { createSocket } from './api/socket';
export type { AppSocket } from './api/socket';
// components
export { FastForm } from './components/FastForm/index';
@ -25,3 +26,8 @@ export { setTokenGetter } from './manager/request';
// model
export { loginWithEmail, registerWithEmail } from './model/user';
// redux
export { setupRedux } from './redux/setup';
export { createStore } from './redux/store';
export type { AppStore, AppDispatch } from './redux/store';

@ -7,10 +7,12 @@
"license": "GPLv3",
"private": true,
"dependencies": {
"@reduxjs/toolkit": "^1.6.0",
"axios": "^0.21.1",
"formik": "^2.2.9",
"lodash": "^4.17.21",
"react-native-storage": "npm:@trpgengine/react-native-storage@^1.0.1",
"redux": "^4.1.0",
"yup": "^0.32.9"
},
"devDependencies": {

@ -0,0 +1,12 @@
import type { AppStore } from './store';
import type { AppSocket } from '../api/socket';
/**
* Redux
*/
export function setupRedux(socket: AppSocket, store: AppStore) {
socket.request('friend.getAllFriends').then((resp) => {
// TODO
console.log('好友列表', resp);
});
}

@ -0,0 +1,8 @@
import { combineReducers } from '@reduxjs/toolkit';
import { userReducer } from './user';
export const appReducer = combineReducers({
user: userReducer,
});
export type AppState = ReturnType<typeof appReducer>;

@ -0,0 +1,11 @@
import { createSlice } from '@reduxjs/toolkit';
const initialState = {};
const userSlice = createSlice({
name: 'user',
initialState,
reducers: {},
});
export const userReducer = userSlice.reducer;

@ -0,0 +1,15 @@
import { configureStore } from '@reduxjs/toolkit';
import { appReducer } from './slices';
export function createStore() {
const store = configureStore({
reducer: appReducer,
middleware: (getDefaultMiddleware) => getDefaultMiddleware(),
devTools: process.env.NODE_ENV !== 'production',
});
return store;
}
export type AppStore = ReturnType<typeof createStore>;
export type AppDispatch = AppStore['dispatch'];

@ -21,6 +21,7 @@
"pawchat-shared": "*",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-redux": "^7.2.4",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"socket.io-client": "^4.1.2",

@ -0,0 +1,28 @@
import { createStore } from 'pawchat-shared';
import React, { useMemo } from 'react';
import { useEnsureSocket } from '../hooks/useEnsureSocket';
import { LoadingSpinner } from './LoadingSpinner';
import { Provider as ReduxProvider } from 'react-redux';
import { useSetupRedux } from '../hooks/useSetupRedux';
/**
* Provider
*
*/
export const MainProvider: React.FC = React.memo((props) => {
const store = useMemo(() => createStore(), []);
const { socket, loading } = useEnsureSocket();
useSetupRedux(socket, store);
if (loading) {
return (
<div className="w-screen h-screen flex items-center justify-center bg-gray-700 text-white text-xl">
<LoadingSpinner tip="正在连接到聊天服务器..." />
</div>
);
}
return <ReduxProvider store={store}>{props.children}</ReduxProvider>;
});
MainProvider.displayName = 'MainProvider';

@ -5,13 +5,21 @@ import { getUserJWT } from '../utils/jwt-helper';
* Socket
*/
export function useEnsureSocket() {
const { loading, error } = useAsync(async () => {
const {
value: socket,
loading,
error,
} = useAsync(async () => {
const token = await getUserJWT();
if (typeof token === 'string') {
await createSocket(token);
console.log('当前socket连接成功'); // TODO
if (typeof token !== 'string') {
throw new Error('Token不合法');
}
const socket = await createSocket(token);
console.log('当前socket连接成功');
return socket;
}, []);
return { loading, error };
return { loading, error, socket };
}

@ -0,0 +1,13 @@
import { AppSocket, AppStore, setupRedux } from 'pawchat-shared';
import { useEffect } from 'react';
/**
*
*/
export function useSetupRedux(socket: AppSocket, store: AppStore) {
useEffect(() => {
if (socket !== undefined && store !== undefined) {
setupRedux(socket, store);
}
}, [socket, store]);
}

@ -2,6 +2,7 @@ import { Icon } from '@iconify/react';
import clsx, { ClassValue } from 'clsx';
import React, { useLayoutEffect } from 'react';
import { LoadingSpinner } from '../components/LoadingSpinner';
import { MainProvider } from '../components/MainProvider';
import { useEnsureSocket } from '../hooks/useEnsureSocket';
const NavbarNavItem: React.FC<{
@ -20,47 +21,39 @@ const NavbarNavItem: React.FC<{
});
export const MainRoute: React.FC = React.memo(() => {
const { loading } = useEnsureSocket();
if (loading) {
return (
<div className="w-screen h-screen flex items-center justify-center bg-gray-700 text-white text-xl">
<LoadingSpinner tip="正在连接到聊天服务器..." />
</div>
);
}
return (
<div className="flex h-full">
<div className="w-16 bg-gray-900 flex flex-col justify-start items-center pt-4 pb-4 p-1">
{/* Navbar */}
<div className="flex-1">
<NavbarNavItem />
<div className="h-px w-full bg-white mt-4 mb-4"></div>
<div className="space-y-2">
<NavbarNavItem />
<NavbarNavItem />
<MainProvider>
<div className="w-16 bg-gray-900 flex flex-col justify-start items-center pt-4 pb-4 p-1">
{/* Navbar */}
<div className="flex-1">
<NavbarNavItem />
<NavbarNavItem />
<NavbarNavItem className="bg-green-500">
<Icon className="text-3xl text-white" icon="mdi-plus" />
</NavbarNavItem>
<div className="h-px w-full bg-white mt-4 mb-4"></div>
<div className="space-y-2">
<NavbarNavItem />
<NavbarNavItem />
<NavbarNavItem />
<NavbarNavItem />
<NavbarNavItem className="bg-green-500">
<Icon className="text-3xl text-white" icon="mdi-plus" />
</NavbarNavItem>
</div>
</div>
<div>
<Icon
className="text-3xl text-white cursor-pointer"
icon="mdi-dots-horizontal"
/>
</div>
</div>
<div>
<Icon
className="text-3xl text-white cursor-pointer"
icon="mdi-dots-horizontal"
/>
</div>
</div>
<div className="w-56 bg-gray-800 p-2">
{/* Sidebar */}
<div className="w-full hover:bg-white hover:bg-opacity-20 cursor-pointer text-white rounded p-2">
<div className="w-56 bg-gray-800 p-2">
{/* Sidebar */}
<div className="w-full hover:bg-white hover:bg-opacity-20 cursor-pointer text-white rounded p-2">
</div>
</div>
</div>
<div className="flex-auto bg-gray-700">{/* Main Content */}</div>
<div className="flex-auto bg-gray-700">{/* Main Content */}</div>
</MainProvider>
</div>
);
});

@ -606,6 +606,16 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"
"@reduxjs/toolkit@^1.6.0":
version "1.6.0"
resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.6.0.tgz#0a17c6941c57341f8b31e982352b495ab69d5add"
integrity sha512-eGL50G+Vj5AG5uD0lineb6rRtbs96M8+hxbcwkHpZ8LQcmt0Bm33WyBSnj5AweLkjQ7ZP+KFRDHiLMznljRQ3A==
dependencies:
immer "^9.0.1"
redux "^4.1.0"
redux-thunk "^2.3.0"
reselect "^4.0.0"
"@sinonjs/commons@^1.7.0":
version "1.8.3"
resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d"
@ -819,6 +829,14 @@
resolved "https://registry.npmjs.org/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934"
integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==
"@types/hoist-non-react-statics@^3.3.0":
version "3.3.1"
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
dependencies:
"@types/react" "*"
hoist-non-react-statics "^3.3.0"
"@types/html-minifier-terser@^5.0.0":
version "5.1.1"
resolved "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz#3c9ee980f1a10d6021ae6632ca3e79ca2ec4fb50"
@ -929,6 +947,16 @@
dependencies:
"@types/react" "*"
"@types/react-redux@^7.1.16":
version "7.1.16"
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.16.tgz#0fbd04c2500c12105494c83d4a3e45c084e3cb21"
integrity sha512-f/FKzIrZwZk7YEO9E1yoxIuDNRiDducxkFlkw/GNMGEnK9n4K8wJzlJBghpSuOVDgEUHoDkDF7Gi9lHNQR4siw==
dependencies:
"@types/hoist-non-react-statics" "^3.3.0"
"@types/react" "*"
hoist-non-react-statics "^3.3.0"
redux "^4.0.0"
"@types/react-router-dom@^5.1.7":
version "5.1.7"
resolved "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.7.tgz#a126d9ea76079ffbbdb0d9225073eb5797ab7271"
@ -3352,7 +3380,7 @@ history@^4.9.0:
tiny-warning "^1.0.0"
value-equal "^1.0.1"
hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1:
hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2:
version "3.3.2"
resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
@ -3543,6 +3571,11 @@ image-size@~0.5.0:
resolved "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c"
integrity sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=
immer@^9.0.1:
version "9.0.3"
resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.3.tgz#146e2ba8b84d4b1b15378143c2345559915097f4"
integrity sha512-mONgeNSMuyjIe0lkQPa9YhdmTv8P19IeHV0biYhcXhbd5dhdB9HSK93zBpyKjp6wersSUgT5QyU0skmejUVP2A==
import-cwd@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz#20845547718015126ea9b3676b7592fb8bd4cf92"
@ -5581,7 +5614,7 @@ prompts@^2.0.1:
kleur "^3.0.3"
sisteransi "^1.0.5"
prop-types@^15.6.2:
prop-types@^15.6.2, prop-types@^15.7.2:
version "15.7.2"
resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@ -6051,7 +6084,7 @@ react-fast-compare@^2.0.1:
resolved "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1:
react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1:
version "16.13.1"
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@ -6069,6 +6102,18 @@ react-is@^17.0.1:
opencollective "^1.0.3"
opencollective-postinstall "^2.0.2"
react-redux@^7.2.4:
version "7.2.4"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.4.tgz#1ebb474032b72d806de2e0519cd07761e222e225"
integrity sha512-hOQ5eOSkEJEXdpIKbnRyl04LhaWabkDPV+Ix97wqQX3T3d2NQ8DUblNXXtNMavc7DpswyQM6xfaN4HQDKNY2JA==
dependencies:
"@babel/runtime" "^7.12.1"
"@types/react-redux" "^7.1.16"
hoist-non-react-statics "^3.3.2"
loose-envify "^1.4.0"
prop-types "^15.7.2"
react-is "^16.13.1"
react-router-dom@^5.2.0:
version "5.2.0"
resolved "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz#9e65a4d0c45e13289e66c7b17c7e175d0ea15662"
@ -6167,6 +6212,18 @@ reduce-css-calc@^2.1.8:
css-unit-converter "^1.1.1"
postcss-value-parser "^3.3.0"
redux-thunk@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"
integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==
redux@^4.0.0, redux@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.0.tgz#eb049679f2f523c379f1aff345c8612f294c88d4"
integrity sha512-uI2dQN43zqLWCt6B/BMGRMY6db7TTY4qeHHfGeKb3EOhmOKjU3KdWvNLJyqaHRksv/ErdNH7cFZWg9jXtewy4g==
dependencies:
"@babel/runtime" "^7.9.2"
regenerator-runtime@^0.10.0:
version "0.10.5"
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658"
@ -6244,6 +6301,11 @@ requires-port@^1.0.0:
resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
reselect@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.0.0.tgz#f2529830e5d3d0e021408b246a206ef4ea4437f7"
integrity sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA==
resize-observer-polyfill@^1.5.0, resize-observer-polyfill@^1.5.1:
version "1.5.1"
resolved "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"

Loading…
Cancel
Save