当前位置:网站首页>【山大会议】使用TypeScript为项目进行重构
【山大会议】使用TypeScript为项目进行重构
2022-06-22 14:41:00 【小栗帽今天吃什么】
文章目录
TypeScript 简介
TypeScript 是 JavaScript 的一个超集,支持 ECMAScript 6 标准。TypeScript 由微软开发的自由和开源的编程语言。其设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的 JavaScript 可以运行在任何浏览器上。
简单点说,你可以认为它是一个强类型的 JavaScript 方言,在面对一些大型项目时,拥有类型检查总能协助开发者降低一些 bug 的出现几率。随着本项目的规模不断扩大,我决定为项目引入 TypeScript ,为项目进行重构。
依赖安装
首先我们安装好 typescript
yarn add -D typescript
为了让 webpack 能够识别我们书写的 ts 代码,我们还需要引入 ts-loader:
yarn add -D ts-loader
由于 typescript 与 react 结合得很好,我们不需要再使用 babel 对代码进行转换,typescript 自己会将自己编译好。因此,我们也可以将原来的 babel 依赖从项目中移除了:
yarn remove @babel/core @babel/plugin-proposal-class-properties @babel/preset-env @babel/preset-react babel-loader
不过,由于 react 与 react-dom 本身没有实现类型定义,所以我们为了能用 ts 书写 react 代码,还需要额外引入 react 和 react-dom 的 types 依赖:
yarn add -D @types/react @types/react-dom
现在,所需要的依赖已经安装好了,我们可以来书写代码了。
tsconfig.json
我们在项目的根目录下打开终端,键入命令:
npx typescript init
我们会在项目根目录下看到一个自动生成的 tsconfig.json 文件,我们可以根据自己的实际需要进行配置,我的配置如下:
{
"compilerOptions": {
"target": "es2016",
"jsx": "react",
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"baseUrl": "./",
"resolveJsonModule": true,
"paths": {
"Components/*": ["src/Components/*"],
"Utils/*": ["src/Utils/*"],
"Views/*": ["src/Views/*"]
}
},
"include": ["src/**/*"]
}
修改 webpack.config.js
由于我们将项目重构为了 typescript 的项目,因此,我们编写的不再是 .js 、.jsx 文件,而是 .ts 和 .tsx 文件,我们的 webpack.config.js 也需要据此做出改动:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {
CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
devServer: {
static: path.join(__dirname, 'public'),
host: '127.0.0.1',
port: 9000,
},
resolve: {
extensions: ['.js', '.json', '.ts', '.tsx'],
alias: {
Components: path.join(__dirname, 'src/Components'),
Views: path.join(__dirname, 'src/Views'),
Utils: path.join(__dirname, 'src/Utils'),
},
},
entry: {
login: './src/Views/Login/index.tsx',
register: './src/Views/Register/index.tsx',
},
output: {
path: path.resolve(__dirname, './build'),
filename: '[name]/index.[chunkhash:8].js',
},
module: {
rules: [
{
test: /\.(sa|sc|c)ss$/,
use: [
'style-loader',
'css-loader',
'resolve-url-loader',
{
loader: 'sass-loader',
options: {
sourceMap: true,
},
},
],
},
{
test: /\.tsx?$/,
exclude: /(node_modules|bower_components)/,
use: [
{
loader: 'ts-loader',
},
],
},
{
test: /\.(png|jpg|gif|mp3)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 1024, //对文件的大小做限制,1kb
},
},
],
},
],
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
filename: 'login/index.html',
chunks: ['login'],
template: './public/index.html',
}),
new HtmlWebpackPlugin({
filename: 'register/index.html',
chunks: ['register'],
template: './public/index.html',
}),
],
};
修改旧的代码
index.tsx
import React from 'react';
// let rootDiv = document.getElementById('root')
// if (!rootDiv) {
// rootDiv = document.createElement('div')
// rootDiv.setAttribute('id', 'root')
// }
// createRoot(rootDiv).render(<App />)
import {
render } from 'react-dom';
// import { createRoot } from 'react-dom/client';
import App from './App';
render(<App />, document.getElementById('root'));
登录页
import {
LoadingOutlined, LockOutlined, UserOutlined } from '@ant-design/icons';
import {
Checkbox, Form, Input } from 'antd';
import {
globalMessage } from 'Components/GlobalMessage/GlobalMessage';
import {
LogoIcon, MinimizeIcon, RegisterIcon, ShutdownIcon } from 'Components/MyIcon/MyIcon';
import RippleButton from 'Components/RippleButton/RippleButton';
import {
Victor } from 'Components/Victor/Victor';
import React, {
useEffect, useRef, useState } from 'react';
import {
ajax } from 'Utils/Axios/Axios';
import {
decodeJWT } from 'Utils/Global';
import './App.scss';
export default function App() {
const [form] = Form.useForm();
const lastUserId = localStorage.getItem('userId');
const [userId, setUserId] = useState(lastUserId === 'null' ? '' : lastUserId);
const [userPassword, setUserPassword] = useState('');
const [rememberPassword, setRememberPassword] = useState(
localStorage.getItem('rememberPassword') === 'true'
);
useEffect(() => {
if (rememberPassword) {
(window as any).ipc.invoke('GET_LAST_PASSWORD').then((psw: string) => {
form.setFieldsValue({
password: psw });
});
}
}, []);
const [rotating, setRotating] = useState(false);
useEffect(() => {
if (rotating) {
const mainBody = mainBodyRef.current as HTMLDivElement;
mainBody.style.animationName = 'rotateOut';
let timeout = setTimeout(() => {
mainBody.style.animationName = 'rotateIn';
setShowRegister(!showRegister);
clearTimeout(timeout);
}, 250);
return () => {
if (timeout) {
clearTimeout(timeout);
}
};
}
}, [rotating]);
const [showRegister, setShowRegister] = useState(false);
useEffect(() => {
let timeout = setTimeout(() => {
setRotating(false);
clearTimeout(timeout)
}, 250);
return () => {
if (timeout) {
clearTimeout(timeout);
}
};
}, [showRegister]);
const mainBodyRef = useRef<HTMLDivElement>(null);
// 设置动态背景
useEffect(() => {
const victor = Victor('header', 'canvas');
const theme = ['#ff1324', '#ff3851'];
if (victor)
victor(theme).set();
}, []);
const [isLogining, setIsLogining] = useState(false);
const login = () => {
form.validateFields(['username', 'password'])
.then(async (values) => {
setIsLogining(true);
const text = values.username;
const password = values.password;
const res = await ajax.post('/login_and_register/login', {
text, password });
if (res.code === 200) {
globalMessage.success('登录成功');
localStorage.setItem('rememberPassword', `${
rememberPassword}`);
localStorage.setItem('autoLogin', `${
autoLogin}`);
(window as any).ipc.send('SAFE_PASSWORD', rememberPassword, password);
localStorage.setItem('userId', text);
(window as any).ipc.send('USER_LOGIN', res.data.token, decodeJWT(res.data.token).email);
} else {
globalMessage.error(res.message);
setIsLogining(false);
}
})
.catch((err) => {
if (err.ajax) {
globalMessage.error('服务器错误,请稍后重试');
} else {
const {
values } = err;
if (values.username === undefined) {
globalMessage.error('请输入用户名或邮箱!');
} else if (values.password === undefined) {
globalMessage.error('请输入密码!');
}
}
setIsLogining(false);
});
};
/** * INFO: 自动登录 */
const [autoLogin, setAutoLogin] = useState(localStorage.getItem('autoLogin') === 'true');
useEffect(() => {
let timeout = setTimeout(() => {
if (autoLogin) login();
clearTimeout(timeout);
}, 0);
return () => {
if (timeout) {
clearTimeout(timeout);
}
};
}, []);
return (
<>
<div id='dragBar' />
<div id='mainBody' ref={
mainBodyRef}>
<div id='header'>
<div id='titleBar'>
<LogoIcon style={
{
fontSize: '1.5rem' }} />
<span style={
{
fontFamily: 'Microsoft Yahei' }}>山大会议</span>
<button
className='titleBtn'
id='shutdown'
title='退出'
onClick={
() => {
(window as any).ipc.send('QUIT');
}}>
<ShutdownIcon />
</button>
<button
className='titleBtn'
id='minimize'
title='最小化'
onClick={
() => {
(window as any).ipc.send('MINIMIZE_LOGIN_WINDOW');
}}>
<MinimizeIcon />
</button>
<button
className='titleBtn'
id='switch'
title={
showRegister ? '返回登录' : '注册账号'}
onClick={
() => {
setRotating(true);
}}>
<RegisterIcon />
</button>
</div>
<div id='canvas' />
</div>
<div className='main'>
<div
className='form'
id='loginForm'
style={
{
display: showRegister ? 'none' : 'block' }}>
<Form form={
form}>
<Form.Item
name='username'
rules={
[
{
required: true,
message: '请输入用户名或邮箱',
},
]}
initialValue={
userId}>
<Input
placeholder='请输入用户名或邮箱'
spellCheck={
false}
prefix={
<UserOutlined />}
size={
'large'}
style={
{
width: '65%' }}
onChange={
(event) => {
setUserId(event.target.value);
}}
/>
</Form.Item>
<Form.Item
name='password'
rules={
[
{
required: true,
message: '密码不得为空',
},
]}
initialValue={
userPassword}>
<Input.Password
placeholder='请输入密码'
spellCheck={
false}
prefix={
<LockOutlined />}
size={
'large'}
style={
{
width: '65%' }}
onChange={
(event) => {
setUserPassword(event.target.value);
}}
/>
</Form.Item>
<Form.Item>
<Checkbox
style={
{
fontSize: '0.75rem' }}
checked={
rememberPassword}
onChange={
(e) => {
setRememberPassword(e.target.checked);
}}>
记住密码
</Checkbox>
<Checkbox
style={
{
fontSize: '0.75rem' }}
checked={
autoLogin}
onChange={
(e) => {
setAutoLogin(e.target.checked);
}}>
自动登录
</Checkbox>
</Form.Item>
<Form.Item>
<RippleButton
className='submit'
onClick={
login}
disabled={
isLogining}>
<>{
isLogining && <LoadingOutlined />} 登 录</>
</RippleButton>
</Form.Item>
</Form>
</div>
<div
className='form'
id='registerForm'
style={
{
display: showRegister ? 'flex' : 'none' }}>
<RippleButton
className='submit'
onClick={
() => {
const registerUrl =
process.env.NODE_ENV === 'development'
? './register/'
: '../register/index.html';
window.open(registerUrl);
}}>
注 册
</RippleButton>
</div>
</div>
</div>
</>
);
}
Victor.ts
let CAV: any = {
FRONT: 0,
BACK: 1,
DOUBLE: 2,
SVGNS: 'http://www.w3.org/2000/svg',
Array: typeof Float32Array === 'function' ? Float32Array : Array,
Utils: {
isNumber: function (a: any) {
return !isNaN(parseFloat(a)) && isFinite(a);
},
}
};
(function () {
for (
var a = 0, b = ['ms', 'moz', 'webkit', 'o'], c = 0;
c < b.length && !window.requestAnimationFrame;
++c
)
(window.requestAnimationFrame = (window as any)[b[c] + 'RequestAnimationFrame']),
(window.cancelAnimationFrame =
(window as any)[b[c] + 'CancelAnimationFrame'] ||
(window as any)[b[c] + 'CancelRequestAnimationFrame']);
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function (b) {
var c = new Date().getTime(),
f = Math.max(0, 16 - (c - a)),
g = window.setTimeout(function () {
b(c + f);
}, f);
a = c + f;
return g;
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function (a) {
clearTimeout(a);
};
})();
const randomInRange = function (a: number, b: number) {
return a + (b - a) * Math.random();
};
const clamp = function (a: number, b: number, c: number) {
a = Math.max(a, b);
return (a = Math.min(a, c));
};
CAV.Vector3 = {
create: function (a: any, b: any, c: any) {
var d = new CAV.Array(3);
this.set(d, a, b, c);
return d;
},
clone: function (a: any) {
var b = this.create();
this.copy(b, a);
return b;
},
set: function (a: any[], b: number, c: number, d: number) {
a[0] = b || 0;
a[1] = c || 0;
a[2] = d || 0;
return this;
},
setX: function (a: any[], b: number) {
a[0] = b || 0;
return this;
},
setY: function (a: any[], b: number) {
a[1] = b || 0;
return this;
},
setZ: function (a: any[], b: number) {
a[2] = b || 0;
return this;
},
copy: function (a: any[], b: any[]) {
a[0] = b[0];
a[1] = b[1];
a[2] = b[2];
return this;
},
add: function (a: any[], b: any[]) {
a[0] += b[0];
a[1] += b[1];
a[2] += b[2];
return this;
},
addVectors: function (a: any[], b: any[], c: any[]) {
a[0] = b[0] + c[0];
a[1] = b[1] + c[1];
a[2] = b[2] + c[2];
return this;
},
addScalar: function (a: any[], b: any) {
a[0] += b;
a[1] += b;
a[2] += b;
return this;
},
subtract: function (a: number[], b: number[]) {
a[0] -= b[0];
a[1] -= b[1];
a[2] -= b[2];
return this;
},
subtractVectors: function (a: number[], b: number[], c: number[]) {
a[0] = b[0] - c[0];
a[1] = b[1] - c[1];
a[2] = b[2] - c[2];
return this;
},
subtractScalar: function (a: number[], b: number) {
a[0] -= b;
a[1] -= b;
a[2] -= b;
return this;
},
multiply: function (a: number[], b: number[]) {
a[0] *= b[0];
a[1] *= b[1];
a[2] *= b[2];
return this;
},
multiplyVectors: function (a: number[], b: number[], c: number[]) {
a[0] = b[0] * c[0];
a[1] = b[1] * c[1];
a[2] = b[2] * c[2];
return this;
},
multiplyScalar: function (a: number[], b: number) {
a[0] *= b;
a[1] *= b;
a[2] *= b;
return this;
},
divide: function (a: number[], b: number[]) {
a[0] /= b[0];
a[1] /= b[1];
a[2] /= b[2];
return this;
},
divideVectors: function (a: number[], b: number[], c: number[]) {
a[0] = b[0] / c[0];
a[1] = b[1] / c[1];
a[2] = b[2] / c[2];
return this;
},
divideScalar: function (a: number[], b: number) {
b !== 0 ? ((a[0] /= b), (a[1] /= b), (a[2] /= b)) : ((a[0] = 0), (a[1] = 0), (a[2] = 0));
return this;
},
cross: function (a: number[], b: number[]) {
var c = a[0],
d = a[1],
e = a[2];
a[0] = d * b[2] - e * b[1];
a[1] = e * b[0] - c * b[2];
a[2] = c * b[1] - d * b[0];
return this;
},
crossVectors: function (a: number[], b: number[], c: number[]) {
a[0] = b[1] * c[2] - b[2] * c[1];
a[1] = b[2] * c[0] - b[0] * c[2];
a[2] = b[0] * c[1] - b[1] * c[0];
return this;
},
min: function (a: any[], b: number) {
a[0] < b && (a[0] = b);
a[1] < b && (a[1] = b);
a[2] < b && (a[2] = b);
return this;
},
max: function (a: any[], b: number) {
a[0] > b && (a[0] = b);
a[1] > b && (a[1] = b);
a[2] > b && (a[2] = b);
return this;
},
clamp: function (a: any, b: any, c: any) {
this.min(a, b);
this.max(a, c);
return this;
},
limit: function (a: any, b: number | null, c: number | null) {
var d = this.length(a);
b !== null && d < b ? this.setLength(a, b) : c !== null && d > c && this.setLength(a, c);
return this;
},
dot: function (a: number[], b: number[]) {
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
},
normalise: function (a: any) {
return this.divideScalar(a, this.length(a));
},
negate: function (a: any) {
return this.multiplyScalar(a, -1);
},
distanceSquared: function (a: number[], b: number[]) {
var c = a[0] - b[0],
d = a[1] - b[1],
e = a[2] - b[2];
return c * c + d * d + e * e;
},
distance: function (a: any, b: any) {
return Math.sqrt(this.distanceSquared(a, b));
},
lengthSquared: function (a: number[]) {
return a[0] * a[0] + a[1] * a[1] + a[2] * a[2];
},
length: function (a: any) {
return Math.sqrt(this.lengthSquared(a));
},
setLength: function (a: any, b: number) {
var c = this.length(a);
c !== 0 && b !== c && this.multiplyScalar(a, b / c);
return this;
},
};
CAV.Vector4 = {
create: function (a: any, b: any, c: any) {
var d = new CAV.Array(4);
this.set(d, a, b, c);
return d;
},
set: function (a: any[], b: number, c: number, d: number, e: number) {
a[0] = b || 0;
a[1] = c || 0;
a[2] = d || 0;
a[3] = e || 0;
return this;
},
setX: function (a: any[], b: number) {
a[0] = b || 0;
return this;
},
setY: function (a: any[], b: number) {
a[1] = b || 0;
return this;
},
setZ: function (a: any[], b: number) {
a[2] = b || 0;
return this;
},
setW: function (a: any[], b: number) {
a[3] = b || 0;
return this;
},
add: function (a: any[], b: any[]) {
a[0] += b[0];
a[1] += b[1];
a[2] += b[2];
a[3] += b[3];
return this;
},
multiplyVectors: function (a: number[], b: number[], c: number[]) {
a[0] = b[0] * c[0];
a[1] = b[1] * c[1];
a[2] = b[2] * c[2];
a[3] = b[3] * c[3];
return this;
},
multiplyScalar: function (a: number[], b: number) {
a[0] *= b;
a[1] *= b;
a[2] *= b;
a[3] *= b;
return this;
},
min: function (a: any[], b: number) {
a[0] < b && (a[0] = b);
a[1] < b && (a[1] = b);
a[2] < b && (a[2] = b);
a[3] < b && (a[3] = b);
return this;
},
max: function (a: any[], b: number) {
a[0] > b && (a[0] = b);
a[1] > b && (a[1] = b);
a[2] > b && (a[2] = b);
a[3] > b && (a[3] = b);
return this;
},
clamp: function (a: any, b: any, c: any) {
this.min(a, b);
this.max(a, c);
return this;
},
};
CAV.Color = function (a: string, b: any) {
this.rgba = CAV.Vector4.create();
this.hex = a || '#000000';
this.opacity = CAV.Utils.isNumber(b) ? b : 1;
this.set(this.hex, this.opacity);
};
CAV.Color.prototype = {
set: function (a: string, b: any) {
var a = a.replace('#', ''),
c = a.length / 3;
this.rgba[0] = parseInt(a.substring(c * 0, c * 1), 16) / 255;
this.rgba[1] = parseInt(a.substring(c * 1, c * 2), 16) / 255;
this.rgba[2] = parseInt(a.substring(c * 2, c * 3), 16) / 255;
this.rgba[3] = CAV.Utils.isNumber(b) ? b : this.rgba[3];
return this;
},
hexify: function (a: string | number) {
a = Math.ceil(Number(a) * 255).toString(16);
a.length === 1 && (a = '0' + a);
return a;
},
format: function () {
var a = this.hexify(this.rgba[0]),
b = this.hexify(this.rgba[1]),
c = this.hexify(this.rgba[2]);
return (this.hex = '#' + a + b + c);
},
};
CAV.Object = function () {
this.position = CAV.Vector3.create();
};
CAV.Object.prototype = {
setPosition: function (a: any, b: any, c: any) {
CAV.Vector3.set(this.position, a, b, c);
return this;
},
};
CAV.Light = function (a: any, b: any) {
CAV.Object.call(this);
this.ambient = new CAV.Color(a || '#FFFFFF');
this.diffuse = new CAV.Color(b || '#FFFFFF');
this.ray = CAV.Vector3.create();
};
CAV.Light.prototype = Object.create(CAV.Object.prototype);
CAV.Vertex = function (a: any, b: any, c: any) {
this.position = CAV.Vector3.create(a, b, c);
};
CAV.Vertex.prototype = {
setPosition: function (a: any, b: any, c: any) {
CAV.Vector3.set(this.position, a, b, c);
return this;
},
};
CAV.Triangle = function (a: any, b: any, c: any) {
this.a = a || new CAV.Vertex();
this.b = b || new CAV.Vertex();
this.c = c || new CAV.Vertex();
this.vertices = [this.a, this.b, this.c];
this.u = CAV.Vector3.create();
this.v = CAV.Vector3.create();
this.centroid = CAV.Vector3.create();
this.normal = CAV.Vector3.create();
this.color = new CAV.Color();
this.polygon = document.createElementNS(CAV.SVGNS, 'polygon');
this.polygon.setAttributeNS(null, 'stroke-linejoin', 'round');
this.polygon.setAttributeNS(null, 'stroke-miterlimit', '1');
this.polygon.setAttributeNS(null, 'stroke-width', '1');
this.computeCentroid();
this.computeNormal();
};
CAV.Triangle.prototype = {
computeCentroid: function () {
this.centroid[0] = this.a.position[0] + this.b.position[0] + this.c.position[0];
this.centroid[1] = this.a.position[1] + this.b.position[1] + this.c.position[1];
this.centroid[2] = this.a.position[2] + this.b.position[2] + this.c.position[2];
CAV.Vector3.divideScalar(this.centroid, 3);
return this;
},
computeNormal: function () {
CAV.Vector3.subtractVectors(this.u, this.b.position, this.a.position);
CAV.Vector3.subtractVectors(this.v, this.c.position, this.a.position);
CAV.Vector3.crossVectors(this.normal, this.u, this.v);
CAV.Vector3.normalise(this.normal);
return this;
},
};
CAV.Geometry = function () {
this.vertices = [];
this.triangles = [];
this.dirty = false;
};
CAV.Geometry.prototype = {
update: function () {
if (this.dirty) {
var a, b;
for (a = this.triangles.length - 1; a >= 0; a--)
(b = this.triangles[a]), b.computeCentroid(), b.computeNormal();
this.dirty = false;
}
return this;
},
};
CAV.Plane = function (a: number, b: number, _c: number, d: number) {
let t0, t1;
CAV.Geometry.call(this);
this.width = a || 100;
this.height = b || 100;
this.segments = _c || 4;
this.slices = d || 4;
this.segmentWidth = this.width / this.segments;
this.sliceHeight = this.height / this.slices;
var e,
f,
g,
c = [];
e = this.width * -0.5;
f = this.height * 0.5;
for (a = 0; a <= this.segments; a++) {
c.push([]);
for (b = 0; b <= this.slices; b++)
(d = new CAV.Vertex(e + a * this.segmentWidth, f - b * this.sliceHeight)),
(c[a] as Array<any>).push(d),
this.vertices.push(d);
}
for (a = 0; a < this.segments; a++)
for (b = 0; b < this.slices; b++)
(d = c[a + 0][b + 0]),
(e = c[a + 0][b + 1]),
(f = c[a + 1][b + 0]),
(g = c[a + 1][b + 1]),
(t0 = new CAV.Triangle(d, e, f)),
(t1 = new CAV.Triangle(f, e, g)),
this.triangles.push(t0, t1);
};
CAV.Plane.prototype = Object.create(CAV.Geometry.prototype);
CAV.Material = function (a: any, b: any) {
this.ambient = new CAV.Color(a || '#444444');
this.diffuse = new CAV.Color(b || '#FFFFFF');
this.slave = new CAV.Color();
};
CAV.Mesh = function (a: any, b: any) {
CAV.Object.call(this);
this.geometry = a || new CAV.Geometry();
this.material = b || new CAV.Material();
this.side = CAV.FRONT;
this.visible = true;
};
CAV.Mesh.prototype = Object.create(CAV.Object.prototype);
CAV.Mesh.prototype.update = function (a: string | any[], b: any) {
var c, d, e, f, g;
this.geometry.update();
if (b)
for (c = this.geometry.triangles.length - 1; c >= 0; c--) {
d = this.geometry.triangles[c];
CAV.Vector4.set(d.color.rgba);
for (e = a.length - 1; e >= 0; e--)
(f = a[e]),
CAV.Vector3.subtractVectors(f.ray, f.position, d.centroid),
CAV.Vector3.normalise(f.ray),
(g = CAV.Vector3.dot(d.normal, f.ray)),
this.side === CAV.FRONT
? (g = Math.max(g, 0))
: this.side === CAV.BACK
? (g = Math.abs(Math.min(g, 0)))
: this.side === CAV.DOUBLE && (g = Math.max(Math.abs(g), 0)),
CAV.Vector4.multiplyVectors(
this.material.slave.rgba,
this.material.ambient.rgba,
f.ambient.rgba
),
CAV.Vector4.add(d.color.rgba, this.material.slave.rgba),
CAV.Vector4.multiplyVectors(
this.material.slave.rgba,
this.material.diffuse.rgba,
f.diffuse.rgba
),
CAV.Vector4.multiplyScalar(this.material.slave.rgba, g),
CAV.Vector4.add(d.color.rgba, this.material.slave.rgba);
CAV.Vector4.clamp(d.color.rgba, 0, 1);
}
return this;
};
CAV.Scene = function () {
this.meshes = [];
this.lights = [];
};
CAV.Scene.prototype = {
add: function (a: any) {
a instanceof CAV.Mesh && !~this.meshes.indexOf(a)
? this.meshes.push(a)
: a instanceof CAV.Light && !~this.lights.indexOf(a) && this.lights.push(a);
return this;
},
remove: function (a: any) {
a instanceof CAV.Mesh && ~this.meshes.indexOf(a)
? this.meshes.splice(this.meshes.indexOf(a), 1)
: a instanceof CAV.Light &&
~this.lights.indexOf(a) &&
this.lights.splice(this.lights.indexOf(a), 1);
return this;
},
};
CAV.Renderer = function () {
this.halfHeight = this.halfWidth = this.height = this.width = 0;
};
CAV.Renderer.prototype = {
setSize: function (a: any, b: any) {
if (!(this.width === a && this.height === b))
return (
(this.width = a),
(this.height = b),
(this.halfWidth = this.width * 0.5),
(this.halfHeight = this.height * 0.5),
this
);
},
clear: function () {
return this;
},
render: function () {
return this;
},
};
CAV.CanvasRenderer = function () {
CAV.Renderer.call(this);
this.element = document.createElement('canvas');
this.element.style.display = 'block';
this.context = this.element.getContext('2d');
this.setSize(this.element.width, this.element.height);
};
CAV.CanvasRenderer.prototype = Object.create(CAV.Renderer.prototype);
CAV.CanvasRenderer.prototype.setSize = function (a: any, b: any) {
CAV.Renderer.prototype.setSize.call(this, a, b);
this.element.width = a;
this.element.height = b;
this.context.setTransform(1, 0, 0, -1, this.halfWidth, this.halfHeight);
return this;
};
CAV.CanvasRenderer.prototype.clear = function () {
CAV.Renderer.prototype.clear.call(this);
this.context.clearRect(-this.halfWidth, -this.halfHeight, this.width, this.height);
return this;
};
CAV.CanvasRenderer.prototype.render = function (a: {
meshes: string | any[]; lights: any; }) {
CAV.Renderer.prototype.render.call(this, a);
var b, c, d, e, f;
this.clear();
this.context.lineJoin = 'round';
this.context.lineWidth = 1;
for (b = a.meshes.length - 1; b >= 0; b--)
if (((c = a.meshes[b]), c.visible)) {
c.update(a.lights, true);
for (d = c.geometry.triangles.length - 1; d >= 0; d--)
(e = c.geometry.triangles[d]),
(f = e.color.format()),
this.context.beginPath(),
this.context.moveTo(e.a.position[0], e.a.position[1]),
this.context.lineTo(e.b.position[0], e.b.position[1]),
this.context.lineTo(e.c.position[0], e.c.position[1]),
this.context.closePath(),
(this.context.strokeStyle = f),
(this.context.fillStyle = f),
this.context.stroke(),
this.context.fill();
}
return this;
};
export function Victor(container: string, anitOut: string) {
let J, l;
if (!!document.createElement('canvas').getContext) {
var t = {
width: 1.5,
height: 1.5,
depth: 10,
segments: 12,
slices: 6,
xRange: 0.8,
yRange: 0.1,
zRange: 1,
ambient: '#525252',
diffuse: '#FFFFFF',
speed: 0.0002,
};
var G = {
count: 2,
xyScalar: 1,
zOffset: 100,
ambient: '#002c4a',
diffuse: '#005584',
speed: 0.001,
gravity: 1200,
dampening: 0.95,
minLimit: 10,
maxLimit: null,
minDistance: 20,
maxDistance: 400,
autopilot: false,
draw: false,
bounds: CAV.Vector3.create(),
step: CAV.Vector3.create(
randomInRange(0.2, 1),
randomInRange(0.2, 1),
randomInRange(0.2, 1)
),
};
var m = 'canvas';
var E = 'svg';
var x = {
renderer: m,
};
var i: number,
n = Date.now();
var L = CAV.Vector3.create();
var k = CAV.Vector3.create();
var z = document.getElementById(container || 'container');
var w = document.getElementById(anitOut || 'anitOut');
var D: {
element: any; setSize: (arg0: number, arg1: number) => void; clear: () => void; width: number; height: number; halfWidth: any; halfHeight: any; render: (arg0: any) => void; }, I: {
remove: (arg0: any) => void; add: (arg0: any) => void; lights: string | any[]; }, h: any, q: {
vertices: string | any[]; segmentWidth: number; sliceHeight: number; dirty: boolean; }, y;
var g: any;
var r;
function C() {
F();
p();
s();
B();
v();
K((z as HTMLElement).offsetWidth, (z as HTMLElement).offsetHeight);
o();
}
function F() {
g = new CAV.CanvasRenderer();
H(x.renderer);
}
function H(N: string) {
if (D) {
(w as HTMLElement).removeChild(D.element);
}
switch (N) {
case m:
D = g;
break;
}
D.setSize((z as HTMLElement).offsetWidth, (z as HTMLElement).offsetHeight);
(w as HTMLElement).appendChild(D.element);
}
function p() {
I = new CAV.Scene();
}
function s() {
I.remove(h);
D.clear();
q = new CAV.Plane(t.width * D.width, t.height * D.height, t.segments, t.slices);
y = new CAV.Material(t.ambient, t.diffuse);
h = new CAV.Mesh(q, y);
I.add(h);
var N, O;
for (N = q.vertices.length - 1; N >= 0; N--) {
O = q.vertices[N];
O.anchor = CAV.Vector3.clone(O.position);
O.step = CAV.Vector3.create(
randomInRange(0.2, 1),
randomInRange(0.2, 1),
randomInRange(0.2, 1)
);
O.time = randomInRange(0, Math.PI * 2);
}
}
function B() {
var O, N;
for (O = I.lights.length - 1; O >= 0; O--) {
N = I.lights[O];
I.remove(N);
}
D.clear();
for (O = 0; O < G.count; O++) {
N = new CAV.Light(G.ambient, G.diffuse);
N.ambientHex = N.ambient.format();
N.diffuseHex = N.diffuse.format();
I.add(N);
N.mass = randomInRange(0.5, 1);
N.velocity = CAV.Vector3.create();
N.acceleration = CAV.Vector3.create();
N.force = CAV.Vector3.create();
}
}
function K(O: number, N: number) {
D.setSize(O, N);
CAV.Vector3.set(L, D.halfWidth, D.halfHeight);
s();
}
function o() {
i = Date.now() - n;
u();
M();
requestAnimationFrame(o);
}
function u() {
var Q,
P,
O,
R,
T,
V,
U,
S = t.depth / 2;
CAV.Vector3.copy(G.bounds, L);
CAV.Vector3.multiplyScalar(G.bounds, G.xyScalar);
CAV.Vector3.setZ(k, G.zOffset);
for (R = I.lights.length - 1; R >= 0; R--) {
T = I.lights[R];
CAV.Vector3.setZ(T.position, G.zOffset);
var N = clamp(
CAV.Vector3.distanceSquared(T.position, k),
G.minDistance,
G.maxDistance
);
var W = (G.gravity * T.mass) / N;
CAV.Vector3.subtractVectors(T.force, k, T.position);
CAV.Vector3.normalise(T.force);
CAV.Vector3.multiplyScalar(T.force, W);
CAV.Vector3.set(T.acceleration);
CAV.Vector3.add(T.acceleration, T.force);
CAV.Vector3.add(T.velocity, T.acceleration);
CAV.Vector3.multiplyScalar(T.velocity, G.dampening);
CAV.Vector3.limit(T.velocity, G.minLimit, G.maxLimit);
CAV.Vector3.add(T.position, T.velocity);
}
for (V = q.vertices.length - 1; V >= 0; V--) {
U = q.vertices[V];
Q = Math.sin(U.time + U.step[0] * i * t.speed);
P = Math.cos(U.time + U.step[1] * i * t.speed);
O = Math.sin(U.time + U.step[2] * i * t.speed);
CAV.Vector3.set(
U.position,
t.xRange * q.segmentWidth * Q,
t.yRange * q.sliceHeight * P,
t.zRange * S * O - S
);
CAV.Vector3.add(U.position, U.anchor);
}
q.dirty = true;
}
function M() {
D.render(I);
}
J = (O: any) => {
var Q,
N,
S = O;
var P = function (T: any) {
for (Q = 0, l = I.lights.length; Q < l; Q++) {
N = I.lights[Q];
N.ambient.set(T);
N.ambientHex = N.ambient.format();
}
};
var R = function (T: any) {
for (Q = 0, l = I.lights.length; Q < l; Q++) {
N = I.lights[Q];
N.diffuse.set(T);
N.diffuseHex = N.diffuse.format();
}
};
return {
set: function () {
P(S[0]);
R(S[1]);
},
};
};
function v() {
window.addEventListener('resize', j);
}
function A(N: {
x: any; y: number; }) {
CAV.Vector3.set(k, N.x, D.height - N.y);
CAV.Vector3.subtract(k, L);
}
function j(N: any) {
K((z as HTMLElement).offsetWidth, (z as HTMLElement).offsetHeight);
M();
}
C();
}
return J;
}
注册页
import {
KeyOutlined,
LockFilled,
LockOutlined,
MailOutlined,
UserOutlined
} from '@ant-design/icons';
import {
Button, Form, Input, notification, Select } from 'antd';
import React, {
useEffect, useState } from 'react';
import {
ajax } from 'Utils/Axios/Axios';
import './App.scss';
export default function App() {
const [sendCaptchaTick, setSendCaptchaTick] = useState(0);
const [sendCaptchaInterval, setSendCaptchaInterval] = useState<NodeJS.Timeout | null>(null);
useEffect(() => {
return () => {
if (sendCaptchaInterval) {
clearInterval(sendCaptchaInterval);
setSendCaptchaInterval(null);
}
};
}, []);
const [chosenEmail, setChosenEmail] = useState('@mail.sdu.edu.cn');
const [form] = Form.useForm();
const [isRegistering, setIsRegistering] = useState(false);
const {
Option } = Select;
return (
<div className='register' style={
{
backgroundImage: `url(${
require('./bg.jpg').default})` }}>
<div className='container'>
<div className='title'>山大会议 注册账号</div>
<div className='inputs'>
<Form
onFinish={
(values) => {
submitForm(values, chosenEmail, setIsRegistering);
}}
autoComplete='off'
form={
form}>
<Form.Item
rules={
[
{
required: true,
message: '请输入注册用的昵称',
},
{
pattern: /^[^@]+$/,
message: '昵称中不允许出现"@"',
},
]}
name={
'username'}>
<Input placeholder='请输入昵称' prefix={
<UserOutlined />} />
</Form.Item>
<Form.Item
rules={
[
{
required: true, message: '请输入密码' },
{
min: 6,
message: '请输入长度超过6位的密码',
},
]}
name={
'password'}>
<Input.Password placeholder='请输入密码' prefix={
<LockOutlined />} />
</Form.Item>
<Form.Item
validateTrigger='onBlur'
rules={
[
{
required: true,
message: '请再次输入密码',
},
({
getFieldValue }) => ({
validator(rule, value) {
if (getFieldValue('password') === value) {
return Promise.resolve();
}
return Promise.reject('两次输入的密码不一致');
},
}),
]}
name={
'passwordCheck'}>
<Input.Password placeholder='请再次输入密码' prefix={
<LockFilled />} />
</Form.Item>
<Form.Item
rules={
[
{
required: true,
message: '请输入邮箱',
},
{
pattern: /^[^@]+$/,
message: '请不要再次输入"@"',
},
]}
name={
'email'}>
<Input
placeholder='请输入邮箱'
addonAfter={
<Select defaultValue={
chosenEmail} onSelect={
setChosenEmail}>
<Option value='@mail.sdu.edu.cn'>@mail.sdu.edu.cn</Option>
<Option value='@sdu.edu.cn'>@sdu.edu.cn</Option>
</Select>
}
prefix={
<MailOutlined />}
/>
</Form.Item>
<Form.Item
rules={
[
{
required: true,
message: '请输入验证码',
},
]}
name={
'captcha'}>
<Input placeholder='请输入邮箱验证码' prefix={
<KeyOutlined />} />
</Form.Item>
<Form.Item>
<div style={
{
display: 'flex', justifyContent: 'space-around' }}>
<Button
disabled={
sendCaptchaTick > 0}
loading={
sendCaptchaTick === -1}
onClick={
() => {
form.validateFields(['username', 'email'])
.then((values) => {
setSendCaptchaTick(-1);
const {
username, email } = values;
ajax.post('/login_and_register/code', {
username,
email: `${
email}${
chosenEmail}`,
}).then((response) => {
if (response.code === 200) {
notification.success({
message: '验证码发送成功',
description:
'验证码已发送,请前往邮箱查询验证码',
});
sendCaptcha(
setSendCaptchaTick,
setSendCaptchaInterval
);
} else {
notification.error({
message: '验证码发送失败',
description: response.message,
});
}
});
})
.catch(() => {
});
}}>
{
sendCaptchaTick > 0
? `${
sendCaptchaTick}秒后可再次发送`
: '发送验证码'}
</Button>
<Button loading={
isRegistering} type='primary' htmlType='submit'>
注册
</Button>
</div>
</Form.Item>
</Form>
</div>
</div>
</div>
);
}
async function submitForm(values: {
username: any; password: any; captcha: any; email: any; }, chosenEmail: string, setIsRegistering: {
(value: React.SetStateAction<boolean>): void; (arg0: boolean): void; }) {
setIsRegistering(true);
const {
username, password, captcha, email } = values;
const res = await ajax.post('/login_and_register/register', {
username,
password,
email: `${
email}${
chosenEmail}`,
code: captcha,
});
if (res.code === 200) {
notification.success({
message: '注册成功', description: '注册成功,请前往登录吧' });
} else {
notification.error({
message: '注册失败', description: res.message });
}
setIsRegistering(false);
}
function sendCaptcha(setSendCaptchaTick: {
(value: React.SetStateAction<number>): void; (arg0: number): void; }, setSendCaptchaInterval: {
(value: React.SetStateAction<NodeJS.Timeout | null>): void; (arg0: NodeJS.Timer | null): void; }) {
let sendCaptchaTick = 60;
setSendCaptchaTick(sendCaptchaTick);
const interval = setInterval(() => {
setSendCaptchaTick(--sendCaptchaTick);
if (sendCaptchaTick === 0) {
clearInterval(interval);
setSendCaptchaInterval(null);
}
}, 1000);
setSendCaptchaInterval(interval);
}
Redux
actions.ts
/** * action 类型 */
import {
ChatWebSocketType, DEVICE_TYPE } from "Utils/Constraints";
import {
DeviceInfo } from "Utils/Types";
const UNDEFINED_ACTION = 'UNDEFINED_ACTION'
// 选择当前聊天的对象 ID
export const SET_NOW_CHATTING_ID = 'SET_NOW_CHATTING_ID';
export function setNowChattingId(nowChattingId: number | null) {
return {
type: SET_NOW_CHATTING_ID, nowChattingId };
}
export const SET_NOW_WEBRTC_FRIEND_ID = 'SET_NOW_WEBRTC_FRIEND_ID';
export function setNowWebrtcFriendId(nowWebrtcFriendId: number | null) {
return {
type: SET_NOW_WEBRTC_FRIEND_ID, nowWebrtcFriendId };
}
// 更新可用的音视频设备
export const UPDATE_AVAILABLE_VIDEO_DEVICES = 'UPDATE_AVAILABLE_VIDEO_DEVICES';
export const UPDATE_AVAILABLE_AUDIO_DEVICES = 'UPDATE_AVAILABLE_AUDIO_DEVICES';
export function updateAvailableDevices(deviceType: string, devices: DeviceInfo[]) {
switch (deviceType) {
case DEVICE_TYPE.VIDEO_DEVICE:
return {
type: UPDATE_AVAILABLE_VIDEO_DEVICES, devices };
case DEVICE_TYPE.AUDIO_DEVICE:
return {
type: UPDATE_AVAILABLE_AUDIO_DEVICES, devices };
default:
return {
type: UNDEFINED_ACTION };
}
}
// 更换选中的音视频设备
export const EXCHANGE_VIDEO_DEVICE = 'EXCHANGE_VIDEO_DEVICE';
export const EXCHANGE_AUDIO_DEVICE = 'EXCHANGE_AUDIO_DEVICE';
export function exchangeMediaDevice(deviceType: string, deviceInfo: DeviceInfo) {
switch (deviceType) {
case DEVICE_TYPE.VIDEO_DEVICE:
return {
type: EXCHANGE_VIDEO_DEVICE, deviceInfo };
case DEVICE_TYPE.AUDIO_DEVICE:
return {
type: EXCHANGE_AUDIO_DEVICE, deviceInfo };
default:
return {
type: UNDEFINED_ACTION };
}
}
// 设置用户 Token
export const SET_AUTH_TOKEN = 'SET_AUTH_TOKEN';
export function setAuthToken(token: string) {
return {
type: SET_AUTH_TOKEN, token };
}
// 管理未读消息
export const ADD_UNREAD_MESSAGE = 'ADD_UNREAD_MESSAGE';
export const REMOVE_UNREAD_MESSAGES = 'REMOVE_UNREAD_MESSAGES';
export function setUnreadMessages(operation: string, payload: any) {
return {
type: operation, payload };
}
// 管理消息记录
export const INIT_MESSAGE_HISTORY = 'INIT_MESSAGE_HISTORY';
export const SYNC_CLOUD_MESSAGE_HISTORY = 'SYNC_CLOUD_MESSAGE_HISTORY';
export const GET_MORE_MESSAGE_HISTORY = 'GET_MORE_MESSAGE_HISTORY';
export const ADD_MESSAGE_HISTORY = 'ADD_MESSAGE_HISTORY';
export const REMOVE_MESSAGE_HISTORY = 'REMOVE_MESSAGE_HISTORY';
export function setMessageHistory(operation: string, payload: any) {
return {
type: operation, payload };
}
// 应用通话状态
export const SET_CALL_STATUS = 'SET_CALL_STATUS';
export function setCallStatus(status: ChatWebSocketType) {
return {
type: SET_CALL_STATUS, status };
}
reducers.ts
import {
combineReducers } from '@reduxjs/toolkit';
import {
CALL_STATUS_FREE, ChatWebSocketType } from 'Utils/Constraints';
import {
ChatMessage, DeviceInfo } from 'Utils/Types';
import {
ADD_MESSAGE_HISTORY,
ADD_UNREAD_MESSAGE,
EXCHANGE_AUDIO_DEVICE,
EXCHANGE_VIDEO_DEVICE,
GET_MORE_MESSAGE_HISTORY,
INIT_MESSAGE_HISTORY,
REMOVE_MESSAGE_HISTORY,
REMOVE_UNREAD_MESSAGES,
SET_AUTH_TOKEN,
SET_CALL_STATUS,
SET_NOW_CHATTING_ID,
SET_NOW_WEBRTC_FRIEND_ID,
SYNC_CLOUD_MESSAGE_HISTORY,
UPDATE_AVAILABLE_AUDIO_DEVICES,
UPDATE_AVAILABLE_VIDEO_DEVICES
} from './actions';
function setNowChattingId(state = null, action: {
type: string, nowChattingId: number | null }) {
if (action.type === SET_NOW_CHATTING_ID) {
return action.nowChattingId;
}
return state;
}
function setNowWebrtcFriendId(state = null, action: {
type: string, nowWebrtcFriendId: number | null }) {
if (action.type === SET_NOW_WEBRTC_FRIEND_ID) {
return action.nowWebrtcFriendId;
}
return state;
}
function updateAvailableVideoDevices(state = new Array(), action: {
type: string, devices: DeviceInfo[] }): Array<DeviceInfo> {
switch (action.type) {
case UPDATE_AVAILABLE_VIDEO_DEVICES:
return action.devices;
default:
return state;
}
}
function updateAvailableAudioDevices(state = new Array(), action: {
type: string, devices: DeviceInfo[] }): Array<DeviceInfo> {
switch (action.type) {
case UPDATE_AVAILABLE_AUDIO_DEVICES:
return action.devices;
default:
return state;
}
}
function exchangeVideoDevice(state = null, action: {
type: string, deviceInfo: DeviceInfo }) {
switch (action.type) {
case EXCHANGE_VIDEO_DEVICE:
localStorage.setItem('usingVideoDevice', action.deviceInfo.deviceId);
return action.deviceInfo;
default:
return state;
}
}
function exchangeAudioDevice(state = null, action: {
type: string, deviceInfo: DeviceInfo }) {
switch (action.type) {
case EXCHANGE_AUDIO_DEVICE:
localStorage.setItem('usingAudioDevice', action.deviceInfo.deviceId);
return action.deviceInfo;
default:
return state;
}
}
function setAuthToken(state = null, action: {
type: string, token: string }) {
if (action.type === SET_AUTH_TOKEN) return action.token;
return state;
}
function setUnreadMessages(state = {
}, action: {
type: string, payload: any })
: {
[user: string]: ChatMessage[]
} {
switch (action.type) {
case ADD_UNREAD_MESSAGE:
const {
fromId, toId, myId } = action.payload;
const messageOwnerId = fromId === myId ? toId : fromId;
const newArr = state[`${
messageOwnerId}` as keyof typeof state]
? [...state[`${
messageOwnerId}` as keyof typeof state]]
: new Array();
newArr.push(action.payload);
return Object.assign({
}, state, {
[`${
messageOwnerId}`]: newArr,
});
case REMOVE_UNREAD_MESSAGES:
const {
userId } = action.payload;
const newState = Object.assign({
}, state);
delete newState[`${
userId}` as keyof typeof newState];
return newState;
default:
return state;
}
}
function setMessageHistory(state = {
}, action: {
type: string, payload: any }) {
switch (action.type) {
case INIT_MESSAGE_HISTORY:
return action.payload;
case SYNC_CLOUD_MESSAGE_HISTORY:
return Object.assign({
}, state, action.payload);
case GET_MORE_MESSAGE_HISTORY:
const chatId = action.payload.chatId as keyof typeof state
const newArr1 = state[`${
chatId}`] ? [...state[`${
chatId}`]] : new Array();
newArr1.unshift(action.payload);
return Object.assign({
}, state, {
[`${
chatId}`]: newArr1,
});
case ADD_MESSAGE_HISTORY:
const {
fromId, toId, myId } = action.payload;
const messageOwnerId = (fromId === myId ? toId : fromId) as keyof typeof state;
const newArr2 = state[`${
messageOwnerId}`]
? [...state[`${
messageOwnerId}`]]
: new Array();
newArr2.push(action.payload);
const newMessages = Object.assign({
}, state, {
[`${
messageOwnerId}`]: newArr2,
});
return newMessages;
case REMOVE_MESSAGE_HISTORY:
const newState = Object.assign({
}, state);
const userId = action.payload.userId as keyof typeof newState;
delete newState[`${
userId}`];
return newState;
default:
return state;
}
}
function setCallStatus(state = CALL_STATUS_FREE, action: {
type: string, status: ChatWebSocketType }) {
if (action.type === SET_CALL_STATUS) {
return action.status;
}
return state;
}
const reducers = combineReducers({
nowChattingId: setNowChattingId,
nowWebrtcFriendId: setNowWebrtcFriendId,
availableVideoDevices: updateAvailableVideoDevices,
availableAudioDevices: updateAvailableAudioDevices,
usingVideoDevice: exchangeVideoDevice,
usingAudioDevice: exchangeAudioDevice,
authToken: setAuthToken,
unreadMessages: setUnreadMessages,
messageHistory: setMessageHistory,
callStatus: setCallStatus,
});
export default reducers;
store.ts
import {
configureStore } from '@reduxjs/toolkit';
import reducers from './reducers';
const store = configureStore({
reducer: reducers,
});
export default store;
增加新的代码
为了提高开发效率和代码复用能力,我在 src/Utils 文件夹下定义了三个新的文件:
- Constraints.ts:定义了一些全局常量
- Global.ts:定义了一些零散的、但是经常被各种组件引用的全局函数
- Types.ts:为了开发方便而定义的一些类型
Constraints.ts
/** * 这个文件用来存放一些常量 */
// 音视频设备
export const DEVICE_TYPE = {
VIDEO_DEVICE: 'video',
AUDIO_DEVICE: 'audio',
};
/** * 通话状态 */
export const CALL_STATUS_FREE = 0;
export const CALL_STATUS_OFFERING = 1;
export const CALL_STATUS_OFFERED = 2;
export const CALL_STATUS_ANSWERING = 3;
export const CALL_STATUS_CALLING = 4;
/** * 回复好友申请 */
export const ACCEPT_FRIEND_REQUEST = 2;
export const REJECT_FRIEND_REQUEST = 1;
export const NO_OPERATION_FRIEND_REQUEST = -1;
/** * 聊天系统 WebSocket type 参数 */
export enum ChatWebSocketType {
UNDEFINED_0, // 未定义 0 占位
CHAT_SEND_PRIVATE_MESSAGE, // 发送私聊消息
CHAT_READ_MESSAGE, // 签收私聊消息
CHAT_SEND_FRIEND_REQUEST, // 发送好友请求
CHAT_ANSWER_FRIEND_REQUEST, // 响应好友请求
CHAT_PRIVATE_WEBRTC_OFFER, // 发送视频聊天请求 OFFER
CHAT_PRIVATE_WEBRTC_ANSWER, // 响应视频聊天请求 ANSWER
CHAT_PRIVATE_WEBRTC_CANDIDATE, // 视频聊天 ICE 候选者
CHAT_PRIVATE_WEBRTC_DISCONNECT, // 断开视频聊天
}
Global.ts
/** * 这个文件用来存放一些不好分类的全局函数 */
import jwtDecode from 'jwt-decode';
import {
DEVICE_TYPE } from './Constraints';
import store from './Store/store';
import {
DeviceInfo } from './Types';
/** * 用来返回 mainContent 模态屏遮罩层挂载DOM * @returns Id值为'mainContent'的DOM */
function getMainContent() {
const content = document.getElementById('mainContent');
if (content) {
return content
} else {
return document.body
}
}
/** * 由于直接使用 jwtDecode 解析非法 token 会报错,因此进行封装 * @param {string} token * @returns 解析后的 token */
function decodeJWT(token: string): any {
try {
return jwtDecode(token);
} catch (error: any) {
if (error.message === 'Invalid token specified') return undefined;
console.log(error);
}
}
/** * 封装后的获取设备流函数 * @param {string} device 设备类型 DEVICE_TYPE * @returns */
function getDeviceStream(device: string) {
switch (device) {
case DEVICE_TYPE.AUDIO_DEVICE:
const audioDevice = store.getState().usingAudioDevice as DeviceInfo;
const audioConstraints = {
deviceId: {
exact: audioDevice.deviceId,
},
noiseSuppression: localStorage.getItem('noiseSuppression') !== 'false',
echoCancellation: localStorage.getItem('echoCancellation') !== 'false',
};
return navigator.mediaDevices.getUserMedia({
audio: audioConstraints });
case DEVICE_TYPE.VIDEO_DEVICE:
const videoDevice = store.getState().usingVideoDevice as DeviceInfo;
const videoConstraints = {
deviceId: {
exact: videoDevice.deviceId,
},
width: 1920,
height: 1080,
frameRate: {
max: 30,
},
};
return navigator.mediaDevices.getUserMedia({
video: videoConstraints,
});
default:
return Promise.resolve(new MediaStream());
}
}
export const A_SECOND_TIME = 1000;
export const A_MINUTE_TIME = 60 * A_SECOND_TIME;
export const AN_HOUR_TIME = 60 * A_MINUTE_TIME;
export const A_DAY_TIME = 24 * AN_HOUR_TIME;
export const isSameDay = (timeStampA: string | number | Date, timeStampB: string | number | Date) => {
const dateA = new Date(timeStampA);
const dateB = new Date(timeStampB);
return dateA.setHours(0, 0, 0, 0) === dateB.setHours(0, 0, 0, 0);
};
export const isSameWeek = (timeStampA: string | number | Date, timeStampB: string | number | Date) => {
let A = new Date(timeStampA).setHours(0, 0, 0, 0);
let B = new Date(timeStampB).setHours(0, 0, 0, 0);
const timeDistance = Math.abs(A - B);
return timeDistance / A_DAY_TIME;
};
export const isSameYear = (timeStampA: string | number | Date, timeStampB: string | number | Date) => {
const dateA = new Date(timeStampA);
const dateB = new Date(timeStampB);
dateA.setHours(0, 0, 0, 0);
dateB.setHours(0, 0, 0, 0);
dateA.setMonth(0, 1);
dateB.setMonth(0, 1);
return dateA.getFullYear() === dateB.getFullYear();
};
export const translateDayNumberToDayChara = (day: any) => {
if (typeof day === 'number') {
day = day % 7;
}
switch (day) {
case 0:
return '星期天';
case 1:
return '星期一';
case 2:
return '星期二';
case 3:
return '星期三';
case 4:
return '星期四';
case 5:
return '星期五';
case 6:
return '星期六';
default:
return String(day);
}
};
export {
decodeJWT, getMainContent, getDeviceStream };
Types.ts
import {
ReactNode } from "react"
export interface ChatMessage {
date: number,
fromId: number,
id: number,
message: string,
toId: number,
myId?: number,
userId: number
}
export interface DeviceInfo {
webLabel?: ReactNode
deviceId: string,
label: string,
}
export interface UserInfo {
email: string,
exp: number,
iat: number,
id: number,
iss: string,
profile: string,
role: [
{
authority: string,
id: number
}
],
sub: string,
username: string
}
边栏推荐
- Promouvoir l'adaptation compatible et permettre le développement collaboratif du Service Express adaptatif gbase en mai
- 希尔排序的简单理解
- Oracle客户端和服务端的区别
- 微信小程序头像挂件制作
- Cve-2022-0847 (privilege lifting kernel vulnerability)
- 大佬们好,初次使用MySQL cdc 报错
- Applet development - Custom expiration cache
- MongoDB在腾讯零售优码中的应用
- FPGA collects DHT11 temperature and humidity
- 三菱机械臂demo程序
猜你喜欢

FPGA collects DHT11 temperature and humidity

C # implements insertion sorting

Application of mongodb in Tencent retail premium code

Advanced thinking on application scenarios of standardization, maximum normalization and mean normalization

GBASE现身说 “库” 北京金融科技产业联盟创新应用专委会专题培训

Yilian technology rushes to Shenzhen Stock Exchange: annual revenue of RMB 1.4 billion, 65% of which comes from Ningde times

Ultimate efficiency is the foundation for the cloud native database tdsql-c to settle down
New design of databend SQL planner

SDVO:LDSO+语义,直接法语义SLAM(RAL 2022)

Cross border integration, creativity and innovation to help improve the influence of cultural tourism night tour
随机推荐
【newman】postman生成漂亮的测试报告
A simple understanding of hill ordering
Differences between Oracle client and server
Bridging the gap between open source databases and database services
Quickly play ci/cd graphical choreography
uni开发微信小程序自定义相机自动检测(人像+身份证)
Cross border integration, creativity and innovation to help improve the influence of cultural tourism night tour
Discover the number of media new users can insert
vector的模拟实现
普通人怎么在一年内赚到100万?
Discourse 新用户可插入媒体的数量
推进兼容适配,使能协同发展 GBase 5月适配速递
Oracle客户端和服务端的区别
Be an we media video blogger, and share the necessary 32 material websites
ArcGIS JS之 4.23之IIS本地部署与问题解决
Applet development - Custom expiration cache
程序替换函数
标准化、最值归一化、均值归一化应用场景的进阶思考
“软件定义世界,开源共筑未来” 2022开放原子全球开源峰会7月底即将开启
Ml notes matrix fundamental, gradient descent