diff --git a/frontend/src/lang/en.js b/frontend/src/lang/en.js index 636d4e0..e0407f5 100644 --- a/frontend/src/lang/en.js +++ b/frontend/src/lang/en.js @@ -51,6 +51,7 @@ export default { addEmoticon: 'Add emote', emoticonFileTooLarge: 'File size is too large. Max size is 1MB', + urlTooLong: 'The room URL is too long, and will be truncated by Livehime (but not by OBS)', roomUrl: 'Room URL', enterRoom: 'Enter room', enterTestRoom: 'Enter test room', diff --git a/frontend/src/lang/ja.js b/frontend/src/lang/ja.js index 90be9a8..423103f 100644 --- a/frontend/src/lang/ja.js +++ b/frontend/src/lang/ja.js @@ -51,6 +51,7 @@ export default { addEmoticon: 'スタンプを追加', emoticonFileTooLarge: 'ファイルサイズが大きすぎます。最大サイズは1MBです', + urlTooLong: 'ルームのURLが長すぎて、直播姬によって切り詰められます(ただし、OBSでは切り詰められません)', roomUrl: 'ルームのURL', enterRoom: 'ルームに入る', enterTestRoom: 'テストルームに入る', diff --git a/frontend/src/lang/zh.js b/frontend/src/lang/zh.js index 2104cee..23b6933 100644 --- a/frontend/src/lang/zh.js +++ b/frontend/src/lang/zh.js @@ -51,6 +51,7 @@ export default { addEmoticon: '添加表情', emoticonFileTooLarge: '文件尺寸太大,最大1MB', + urlTooLong: '房间URL太长了,会被直播姬截断(OBS不会)', roomUrl: '房间URL', enterRoom: '进入房间', enterTestRoom: '进入测试房间', diff --git a/frontend/src/main.js b/frontend/src/main.js index 240d827..bf380e1 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -1,7 +1,7 @@ import Vue from 'vue' import VueRouter from 'vue-router' import { - Aside, Autocomplete, Badge, Button, ButtonGroup, Card, Col, ColorPicker, Container, Divider, Form, FormItem, Image, + Alert, Aside, Autocomplete, Badge, Button, ButtonGroup, Card, Col, ColorPicker, Container, Divider, Form, FormItem, Image, Input, Link, Main, Menu, MenuItem, Message, Option, OptionGroup, Radio, RadioGroup, Row, Select, Scrollbar, Slider, Submenu, Switch, Table, TableColumn, TabPane, Tabs, Tooltip } from 'element-ui' @@ -20,6 +20,7 @@ axios.defaults.timeout = 10 * 1000 Vue.use(VueRouter) // 初始化element +Vue.use(Alert) Vue.use(Aside) Vue.use(Autocomplete) Vue.use(Badge) diff --git a/frontend/src/views/Home.vue b/frontend/src/views/Home.vue index cb7aac3..9488e59 100644 --- a/frontend/src/views/Home.vue +++ b/frontend/src/views/Home.vue @@ -4,37 +4,36 @@ - - - - - - - - - - - - - {{ $t('home.useAuthCodeWarning') }} - + - @@ -193,6 +192,9 @@

+

+ +

@@ -221,8 +223,6 @@ export default { name: 'Home', data() { return { - AUTH_CODE_REG: /^[0-9A-Z]{12,14}$/, - serverConfig: { enableTranslate: true, enableUploadFile: true, @@ -233,7 +233,10 @@ export default { roomKeyType: parseInt(window.localStorage.roomKeyType || '2'), roomId: parseInt(window.localStorage.roomId || '1'), authCode: window.localStorage.authCode || '', - } + }, + // 因为$refs.form.validate是异步的所以不能直接用计算属性 + // getUnvalidatedRoomUrl -> unvalidatedRoomUrl -> updateRoomUrl -> roomUrl + roomUrl: '', } }, computed: { @@ -244,8 +247,8 @@ export default { return this.form.authCode } }, - roomUrl() { - return this.getRoomUrl(false) + unvalidatedRoomUrl() { + return this.getUnvalidatedRoomUrl(false) }, obsRoomUrl() { if (this.roomUrl === '') { @@ -260,6 +263,7 @@ export default { } }, watch: { + unvalidatedRoomUrl: 'updateRoomUrl', roomUrl: _.debounce(function() { window.localStorage.roomKeyType = this.form.roomKeyType window.localStorage.roomId = this.form.roomId @@ -269,6 +273,7 @@ export default { }, mounted() { this.updateServerConfig() + this.updateRoomUrl() }, methods: { async updateServerConfig() { @@ -279,6 +284,18 @@ export default { throw e } }, + async updateRoomUrl() { + // 防止切换roomKeyType时校验的还是老规则 + await this.$nextTick() + try { + await this.$refs.form.validate() + } catch { + this.roomUrl = '' + return + } + // 没有异步的校验规则,应该不需要考虑竞争条件 + this.roomUrl = this.unvalidatedRoomUrl + }, addEmoticon() { this.form.emoticons.push({ @@ -316,20 +333,38 @@ export default { window.open(this.roomUrl, `room ${this.roomKeyValue}`, 'menubar=0,location=0,scrollbars=0,toolbar=0,width=600,height=600') }, enterTestRoom() { - window.open(this.getRoomUrl(true), 'test room', 'menubar=0,location=0,scrollbars=0,toolbar=0,width=600,height=600') + window.open(this.getUnvalidatedRoomUrl(true), 'test room', 'menubar=0,location=0,scrollbars=0,toolbar=0,width=600,height=600') }, - getRoomUrl(isTestRoom) { - if (!isTestRoom && !this.validateForm()) { - return '' + getUnvalidatedRoomUrl(isTestRoom) { + // 重要的字段放在前面,因为如果被截断就连接不了房间了 + let frontFields = { + roomKeyType: this.form.roomKeyType } - - let query = { - ...this.form, - emoticons: JSON.stringify(this.form.emoticons), - lang: this.$i18n.locale + let backFields = { + emoticons: JSON.stringify(this.form.emoticons) + } + let ignoredNames = new Set(['roomId', 'authCode']) + let query = { ...frontFields } + for (let name in this.form) { + if (!(name in frontFields || name in backFields || ignoredNames.has(name))) { + query[name] = this.form[name] + } } - delete query.roomId - delete query.authCode + Object.assign(query, backFields) + + // 去掉和默认值相同的字段,缩短URL长度 + query = Object.fromEntries(Object.entries(query).filter( + ([name, value]) => { + let defaultValue = chatConfig.DEFAULT_CONFIG[name] + if (defaultValue === undefined) { + return true + } + if (typeof defaultValue === 'object') { + defaultValue = JSON.stringify(defaultValue) + } + return value !== defaultValue + } + )) let resolved if (isTestRoom) { @@ -339,19 +374,11 @@ export default { } return `${window.location.protocol}//${window.location.host}${resolved.href}` }, - // 因为要用在计算属性里,所以不能用this.$refs.form.validate - validateForm() { - if (this.form.roomKeyType === 1) { - return this.roomKeyValue > 0 - } else if (this.form.roomKeyType === 2) { - return this.AUTH_CODE_REG.test(this.roomKeyValue) - } - return true - }, copyUrl() { this.$refs.roomUrlInput.select() document.execCommand('Copy') }, + exportConfig() { let cfg = mergeConfig(this.form, chatConfig.DEFAULT_CONFIG) download(JSON.stringify(cfg, null, 2), 'blivechat.json', 'application/json')