diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..55712c1 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib" +} \ No newline at end of file diff --git a/bin/register.ts b/bin/register.ts index bdd9ea7..6f7d2ea 100644 --- a/bin/register.ts +++ b/bin/register.ts @@ -4,9 +4,8 @@ */ import bcrypt from 'bcryptjs'; -import { promisify } from 'util'; -import User from '../server/models/user'; +import User, { UserDocument } from '../server/models/user'; import Group from '../server/models/group'; import options from '../utils/commandOptions'; @@ -37,10 +36,11 @@ connectDB() const defaultGroup = await Group.findOne({ isDefault: true }); if (!defaultGroup) { exitWithError('默认群组不存在'); + return; } - const salt = await promisify(bcrypt.genSalt)(saltRounds); - const hash = await promisify(bcrypt.hash)(password, salt); + const salt = await bcrypt.genSalt(saltRounds); + const hash = await bcrypt.hash(password, salt); let newUser = null; try { @@ -52,21 +52,24 @@ connectDB() }); } catch (createError) { if (createError.name === 'ValidationError') { - return exitWithError('用户名包含不支持的字符或者长度超过限制'); + exitWithError('用户名包含不支持的字符或者长度超过限制'); + return; } console.error(createError); exitWithError('创建新用户失败'); } if (!defaultGroup.creator) { - defaultGroup.creator = newUser; + defaultGroup.creator = newUser as UserDocument; + } + if (newUser) { + defaultGroup.members.push(newUser._id); } - defaultGroup.members.push(newUser); await defaultGroup.save(); console.log('注册成功'); - return process.exit(0); + process.exit(0); }) .catch((err) => { console.error('connect database error!'); diff --git a/build/build.ts b/build/build.ts index 549010a..4ac9d8d 100644 --- a/build/build.ts +++ b/build/build.ts @@ -14,9 +14,9 @@ process.env.NODE_ENV = 'production'; const spinner = ora('building for production...'); spinner.start(); -rm(path.join(config.build.assetsRoot), (err) => { +rm(path.join(config.build.assetsRoot), (err: any) => { if (err) throw err; - webpack(webpackConfig, (wErr, stats) => { + webpack(webpackConfig, (wErr: any, stats: any) => { spinner.stop(); if (wErr) throw wErr; process.stdout.write(`${stats.toString({ diff --git a/build/check-versions.ts b/build/check-versions.ts index d2e20b3..5250f93 100644 --- a/build/check-versions.ts +++ b/build/check-versions.ts @@ -6,14 +6,14 @@ import cp from 'child_process'; import packageJson from '../package.json'; -function exec(cmd) { +function exec(cmd: any) { return cp.execSync(cmd).toString().trim(); } const versionRequirements = [ { name: 'node', - currentVersion: semver.clean(process.version), + currentVersion: semver.clean(process.version) as string, versionRequirement: packageJson.engines.node, }, { diff --git a/build/dev-client.ts b/build/dev-client.ts index eb1b7e5..0c2f988 100644 --- a/build/dev-client.ts +++ b/build/dev-client.ts @@ -5,7 +5,7 @@ import 'eventsource-polyfill'; // @ts-ignore import hotClient from 'webpack-hot-middleware/client?noInfo=true&reload=true'; // eslint-disable-line import/no-unresolved -hotClient.subscribe((event) => { +hotClient.subscribe((event: any) => { if (event.action === 'reload') { window.location.reload(); } diff --git a/build/dev-server.ts b/build/dev-server.ts index ad93335..698aced 100644 --- a/build/dev-server.ts +++ b/build/dev-server.ts @@ -5,7 +5,6 @@ import opn from 'opn'; import path from 'path'; import express from 'express'; import webpack from 'webpack'; -import proxyMiddleware from 'http-proxy-middleware'; import webpackDevMiddleware from 'webpack-dev-middleware'; import webpackHotMiddleware from 'webpack-hot-middleware'; import connectionHistoryApiFallback from 'connect-history-api-fallback'; @@ -20,7 +19,6 @@ if (!process.env.NODE_ENV) { const host = process.env.HOST || config.dev.host; const port = process.env.PORT || config.dev.port; const autoOpenBrowser = !!config.dev.autoOpenBrowser; -const { proxyTable } = config.dev; const app = express(); const compiler = webpack(webpackConfig); @@ -36,22 +34,14 @@ const hotMiddleware = webpackHotMiddleware(compiler, { }); -compiler.plugin('compilation', (compilation) => { - compilation.plugin('html-webpack-plugin-after-emit', (data, cb) => { +compiler.plugin('compilation', (compilation: any) => { + compilation.plugin('html-webpack-plugin-after-emit', (data: any, cb: any) => { if (cb) { cb(); } }); }); -Object.keys(proxyTable).forEach((context) => { - let options = proxyTable[context]; - if (typeof options === 'string') { - options = { target: options }; - } - app.use(proxyMiddleware(options.filter || context, options)); -}); - app.use(connectionHistoryApiFallback()); app.use(devMiddleware); diff --git a/build/utils.ts b/build/utils.ts index 876bf96..3322ac3 100644 --- a/build/utils.ts +++ b/build/utils.ts @@ -5,7 +5,7 @@ import LessPluginAutoPrefix from 'less-plugin-autoprefix'; import ExtractTextPlugin from 'extract-text-webpack-plugin'; import config from '../config/webpack'; -export function assetsPath(_path) { +export function assetsPath(_path: any) { return path.posix.join('', _path); } diff --git a/build/webpack.base.conf.ts b/build/webpack.base.conf.ts index ceb0da8..b5c181a 100644 --- a/build/webpack.base.conf.ts +++ b/build/webpack.base.conf.ts @@ -3,12 +3,12 @@ import * as utils from './utils'; import config from '../config/webpack'; import pages from '../config/pages'; -const entry = {}; +const entry: { [key: string]: string } = {}; pages.forEach((page) => { entry[page.entry.key] = page.entry.file; }); -function resolve(dir) { +function resolve(dir: any) { return path.join(__dirname, '..', dir); } @@ -36,10 +36,7 @@ export default { { test: /\.tsx?$/, exclude: /node_modules/, - use: [ - 'babel-loader', - 'ts-loader', - ], + use: ['babel-loader', 'ts-loader'], }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, diff --git a/build/webpack.dev.conf.ts b/build/webpack.dev.conf.ts index e42bd91..d8e3963 100644 --- a/build/webpack.dev.conf.ts +++ b/build/webpack.dev.conf.ts @@ -14,6 +14,7 @@ import pages from '../config/pages'; const htmlPlugins = pages.map((page) => new HtmlWebpackPlugin(page)); Object.keys(baseWebpackConfig.entry).forEach((name) => { + // @ts-ignore baseWebpackConfig.entry[name] = ['react-hot-loader/patch', './build/dev-client'].concat(baseWebpackConfig.entry[name]); }); diff --git a/build/webpack.prod.conf.ts b/build/webpack.prod.conf.ts index a28f850..817d3e5 100644 --- a/build/webpack.prod.conf.ts +++ b/build/webpack.prod.conf.ts @@ -35,18 +35,6 @@ const webpackConfig = merge(baseWebpackConfig, { rules: utils.getStyleLoaders(), }, devtool: config.build.productionSourceMap ? '#source-map' : false, - // optimization: { - // splitChunks: { - // cacheGroups: { - // vendor: { - // test: module => /node_modules/.test(module.context), - // chunks: 'initial', - // name: 'vendor', - // enforce: true, - // }, - // }, - // }, - // }, plugins: [ // @ts-ignore new webpack.DefinePlugin({ diff --git a/client/App.tsx b/client/App.tsx index 66f7d19..17b6095 100644 --- a/client/App.tsx +++ b/client/App.tsx @@ -66,6 +66,7 @@ function App() { setHeight(getHeightPercent()); }; + // @ts-ignore inobounce($app.current); }, []); @@ -121,11 +122,11 @@ function App() { const [groupInfo, setGroupInfo] = useState(null); const contextValue = useMemo(() => ({ - showUserInfo(user) { + showUserInfo(user: any) { setUserInfo(user); toggleUserInfoDialog(true); }, - showGroupInfo(group) { + showGroupInfo(group: any) { setGroupInfo(group); toggleGroupInfoDialog(true); }, @@ -135,7 +136,7 @@ function App() {
- + @@ -145,11 +146,13 @@ function App() { toggleUserInfoDialog(false)} + // @ts-ignore user={userInfo} /> toggleGroupInfoDialog(false)} + // @ts-ignore group={groupInfo} />
diff --git a/client/components/Input.tsx b/client/components/Input.tsx index 9c1be9f..811837c 100644 --- a/client/components/Input.tsx +++ b/client/components/Input.tsx @@ -24,7 +24,7 @@ function Input(props: InputProps) { onFocus = () => {}, } = props; - function handleInput(e) { + function handleInput(e: any) { onChange(e.target.value); } @@ -35,18 +35,19 @@ function Input(props: InputProps) { function handleIMEEnd() { setLockEnter(false); } - function handleKeyDown(e) { + function handleKeyDown(e: any) { if (lockEnter) { return; } if (e.key === 'Enter') { - onEnter(value); + onEnter(value as string); } } const $input = useRef(null); function handleClickClear() { onChange(''); + // @ts-ignore $input.current.focus(); } diff --git a/client/components/Message.tsx b/client/components/Message.tsx index 52bea7e..2b02536 100644 --- a/client/components/Message.tsx +++ b/client/components/Message.tsx @@ -5,7 +5,7 @@ import 'rc-notification/dist/rc-notification.min.css'; import Style from './Message.less'; function showMessage(text: string, duration = 1500, type = 'success') { - Notification.newInstance({}, (notification) => { + Notification.newInstance({}, (notification: any) => { notification.notice({ content: (
diff --git a/client/hooks/useAction.ts b/client/hooks/useAction.ts index 0ca09be..85c0ab8 100644 --- a/client/hooks/useAction.ts +++ b/client/hooks/useAction.ts @@ -47,7 +47,7 @@ export default function useAction() { }); }, - removeLinkman(linkmanId) { + removeLinkman(linkmanId: string) { dispatch({ type: ActionTypes.RemoveLinkman, payload: linkmanId, @@ -120,7 +120,7 @@ export default function useAction() { window.localStorage.setItem(key, value); }, - toggleLoginRegisterDialog(visible) { + toggleLoginRegisterDialog(visible: boolean) { dispatch({ type: ActionTypes.SetStatus, payload: { diff --git a/client/localStorage.ts b/client/localStorage.ts index 49554d5..cab97e5 100644 --- a/client/localStorage.ts +++ b/client/localStorage.ts @@ -47,7 +47,9 @@ export default function getData() { backgroundImage: '', aero: false, }; + // @ts-ignore if (theme && config.theme[theme]) { + // @ts-ignore themeConfig = config.theme[theme]; } else { themeConfig = { diff --git a/client/modules/Chat/Chat.tsx b/client/modules/Chat/Chat.tsx index f05ddf1..a314554 100644 --- a/client/modules/Chat/Chat.tsx +++ b/client/modules/Chat/Chat.tsx @@ -72,6 +72,7 @@ function Chat() { action.setLinkmanProperty(focus, 'onlineMembers', onlineMembers); toggleGroupManagePanel(true); } else { + // @ts-ignore context.showUserInfo(linkman); } } diff --git a/client/modules/Chat/ChatInput.tsx b/client/modules/Chat/ChatInput.tsx index 1f48719..a3a7dc9 100644 --- a/client/modules/Chat/ChatInput.tsx +++ b/client/modules/Chat/ChatInput.tsx @@ -29,10 +29,10 @@ function ChatInput() { const action = useAction(); const isLogin = useIsLogin(); const connect = useSelector((state: State) => state.connect); - const selfId = useSelector((state: State) => state.user._id); - const username = useSelector((state: State) => state.user.username); - const avatar = useSelector((state: State) => state.user.avatar); - const tag = useSelector((state: State) => state.user.tag); + const selfId = useSelector((state: State) => state.user?._id); + const username = useSelector((state: State) => state.user?.username); + const avatar = useSelector((state: State) => state.user?.avatar); + const tag = useSelector((state: State) => state.user?.tag); const focus = useSelector((state: State) => state.focus); const linkman = useSelector((state: State) => state.linkmans[focus]); const selfVoiceSwitch = useSelector((state: State) => state.status.selfVoiceSwitch); @@ -52,6 +52,7 @@ function ChatInput() { return; } e.preventDefault(); + // @ts-ignore $input.current.focus(e); } useEffect(() => { @@ -63,13 +64,17 @@ function ChatInput() { (async () => { if (expressionDialog && !Expression) { // @ts-ignore - const ExpressionModule = await import(/* webpackChunkName: "expression" */ './Expression'); + const ExpressionModule = await import( + /* webpackChunkName: "expression" */ './Expression', + ); Expression = ExpressionModule.default; setTimestamp(Date.now()); } if (codeEditorDialog && !CodeEditor) { // @ts-ignore - const CodeEditorModule = await import(/* webpackChunkName: "code-editor" */ './CodeEditor'); + const CodeEditorModule = await import( + /* webpackChunkName: "code-editor" */ './CodeEditor', + ); CodeEditor = CodeEditorModule.default; setTimestamp(Date.now()); } @@ -99,14 +104,14 @@ function ChatInput() { * @param value 要插入的文本 */ function insertAtCursor(value: string) { - const input = $input.current; + const input = ($input.current as unknown) as HTMLInputElement; if (input.selectionStart || input.selectionStart === 0) { const startPos = input.selectionStart; const endPos = input.selectionEnd; const restoreTop = input.scrollTop; input.value = input.value.substring(0, startPos) + value - + input.value.substring(endPos, input.value.length); + + input.value.substring(endPos as number, input.value.length); if (restoreTop > 0) { input.scrollTop = restoreTop; } @@ -124,7 +129,7 @@ function ChatInput() { insertAtCursor(`#(${expression})`); } - function addSelfMessage(type, content) { + function addSelfMessage(type: string, content: string) { const _id = focus + Date.now(); const message = { _id, @@ -152,7 +157,7 @@ function ChatInput() { .replace(/#/g, ''); if (text.length > 0 && text.length <= 100) { - voice.push(text, Math.random()); + voice.push(text, Math.random().toString()); } } @@ -160,7 +165,12 @@ function ChatInput() { } // eslint-disable-next-line react/destructuring-assignment - async function handleSendMessage(localId, type, content, linkmanId = focus) { + async function handleSendMessage( + localId: string, + type: string, + content: string, + linkmanId = focus, + ) { const [error, message] = await sendMessage(linkmanId, type, content); if (error) { action.deleteMessage(focus, localId); @@ -185,6 +195,7 @@ function ChatInput() { return; } + // @ts-ignore const ext = image.type .split('/') .pop() @@ -234,7 +245,7 @@ function ChatInput() { handleSendMessage(id, 'image', huaji); } - function handleFeatureMenuClick({ key, domEvent }) { + function handleFeatureMenuClick({ key, domEvent }: {key: string, domEvent: any}) { // Quickly hitting the Enter key causes the button to repeatedly trigger the problem if (domEvent.keyCode === 13) { return; @@ -261,7 +272,7 @@ function ChatInput() { } } - async function handlePaste(e) { + async function handlePaste(e: any) { // eslint-disable-next-line react/destructuring-assignment if (!connect) { e.preventDefault(); @@ -281,11 +292,12 @@ function ChatInput() { const image = new Image(); image.onload = async () => { const imageBlob = await compressImage(image, file.type, 0.8); + // @ts-ignore sendImageMessage({ filename: file.name, - ext: imageBlob.type.split('/').pop(), - length: imageBlob.size, - type: imageBlob.type, + ext: imageBlob?.type.split('/').pop(), + length: imageBlob?.size, + type: imageBlob?.type, result: imageBlob, }); }; @@ -305,6 +317,7 @@ function ChatInput() { return Message.error('发送消息失败, 您当前处于离线状态'); } + // @ts-ignore const message = $input.current.value.trim(); if (message.length === 0) { return null; @@ -325,11 +338,12 @@ function ChatInput() { const id = addSelfMessage('text', xss(message)); handleSendMessage(id, 'text', message); } + // @ts-ignore $input.current.value = ''; return null; } - async function handleInputKeyDown(e) { + async function handleInputKeyDown(e: any) { if (e.key === 'Tab') { e.preventDefault(); } else if (e.key === 'Enter' && !inputIME) { @@ -342,6 +356,7 @@ function ChatInput() { e.preventDefault(); } else if (e.key === '@') { // 如果按下@建, 则进入@计算模式 + // @ts-ignore if (!/@/.test($input.current.value)) { setAt({ enable: true, @@ -357,6 +372,7 @@ function ChatInput() { // 延时, 以便拿到新的value和ime状态 setTimeout(() => { // 如果@已经被删掉了, 退出@计算模式 + // @ts-ignore if (!/@/.test($input.current.value)) { setAt({ enable: false, content: '' }); return; @@ -375,6 +391,7 @@ function ChatInput() { if (inputIME) { return; } + // @ts-ignore const regexResult = /@([^ ]*)/.exec($input.current.value); if (regexResult) { setAt({ enable: true, content: regexResult[1] }); @@ -396,7 +413,8 @@ function ChatInput() { }); } - function replaceAt(targetUsername) { + function replaceAt(targetUsername: string) { + // @ts-ignore $input.current.value = $input.current.value.replace( `@${at.content}`, `@${targetUsername} `, @@ -405,6 +423,7 @@ function ChatInput() { enable: false, content: '', }); + // @ts-ignore $input.current.focus(); } @@ -475,8 +494,11 @@ function ChatInput() { iconSize={32} /> -
e.preventDefault()}> - + e.preventDefault()} + > {!isMobile && !inputFocus && ( - 支持粘贴图片发图
全局按 i 键聚焦}> + + 支持粘贴图片发图 +
+ 全局按 i 键聚焦 + + )} + >
)} diff --git a/client/modules/Chat/CodeEditor.tsx b/client/modules/Chat/CodeEditor.tsx index 7d43afa..baa79bd 100644 --- a/client/modules/Chat/CodeEditor.tsx +++ b/client/modules/Chat/CodeEditor.tsx @@ -180,6 +180,7 @@ function CodeEditor(props: CodeEditorProps) {