diff --git a/html/src/app.js b/html/src/app.js index d07889eb..4e47e273 100644 --- a/html/src/app.js +++ b/html/src/app.js @@ -5814,39 +5814,44 @@ speechSynthesis.getVoices(); order: 'descending' } }, - pageSize: 10, - paginationProps: { - small: true, - layout: 'sizes,prev,pager,next,total', - pageSizes: [ - 10, - 25, - 50, - 100 - ] - } + layout: 'table' }; $app.data.VRCPlusIconsTable = {}; $app.data.inviteMessageTable = { + visible: false, data: [], tableProps: { stripe: true, size: 'mini' - } + }, + layout: 'table' }; $app.data.inviteResponseMessageTable = { + visible: false, data: [], tableProps: { stripe: true, size: 'mini' - } + }, + layout: 'table' }; $app.data.inviteRequestMessageTable = { + visible: false, data: [], tableProps: { stripe: true, size: 'mini' - } + }, + layout: 'table' + }; + $app.data.inviteRequestResponseMessageTable = { + visible: false, + data: [], + tableProps: { + stripe: true, + size: 'mini' + }, + layout: 'table' }; $app.data.visits = 0; $app.data.openVR = configRepository.getBool('openVR'); @@ -6911,29 +6916,6 @@ speechSynthesis.getVoices(); this.showBioDialog(); } else if (command === 'Logout') { this.logout(); - } else if (command === 'Message') { - this.$prompt('Enter a message', 'Send Message', { - distinguishCancelAndClose: true, - confirmButtonText: 'Send', - cancelButtonText: 'Cancel', - inputPattern: /\S+/, - inputErrorMessage: 'Message is required', - callback: (action, instance) => { - if (action === 'confirm' && - instance.inputValue) { - API.sendNotification({ - receiverUserId: D.id, - type: 'message', - message: instance.inputValue, - seen: false, - details: '{}' - }).then((args) => { - this.$message('Message sent'); - return args; - }); - } - } - }); } else if (command === 'Request Invite') { API.sendNotification({ receiverUserId: D.id, @@ -8123,7 +8105,8 @@ speechSynthesis.getVoices(); method: 'DELETE' }).then((json) => { var args = { - json + json, + userIcon }; return args; }); @@ -8198,14 +8181,24 @@ speechSynthesis.getVoices(); $app.inviteMessageTable.data = []; $app.inviteResponseMessageTable.data = []; $app.inviteRequestMessageTable.data = []; + $app.inviteRequestResponseMessageTable.data = []; + $app.inviteMessageTable.visible = false; + $app.inviteResponseMessageTable.visible = false; + $app.inviteRequestMessageTable.visible = false; + $app.inviteRequestResponseMessageTable.visible = false; }); + $app.methods.refreshInviteMessageTable = function (messageType) { + API.refreshInviteMessageTableData(messageType); + } + API.refreshInviteMessageTableData = function (messageType) { return this.call(`message/${this.currentUser.id}/${messageType}`, { method: 'GET' }).then((json) => { var args = { - json + json, + messageType }; this.$emit(`INVITE:${messageType.toUpperCase()}`, args); return args; @@ -8224,6 +8217,10 @@ speechSynthesis.getVoices(); $app.inviteRequestMessageTable.data = args.json; }); + API.$on('INVITE:REQUESTRESPONSE', function (args) { + $app.inviteRequestResponseMessageTable.data = args.json; + }); + API.editInviteMessage = function (params, messageType, slot) { return this.call(`message/${this.currentUser.id}/${messageType}/${slot}`, { method: 'PUT', @@ -8231,7 +8228,9 @@ speechSynthesis.getVoices(); }).then((json) => { var args = { json, - params + params, + messageType, + slot }; return args; }); @@ -8240,19 +8239,22 @@ speechSynthesis.getVoices(); // App: Edit Invite Message Dialog $app.data.editInviteMessageDialog = { - visible: false + visible: false, + inviteMessage: '', + messageType: '', + newMessage: '' }; $app.methods.showEditInviteMessageDialog = function (messageType, inviteMessage) { this.$nextTick(() => adjustDialogZ(this.$refs.editInviteMessageDialog.$el)); var D = this.editInviteMessageDialog; - //D.newMessage = inviteMessage.message; + D.newMessage = inviteMessage.message; D.visible = true; D.inviteMessage = inviteMessage; D.messageType = messageType; }; - $app.methods.saveInviteMessage = function () { + $app.methods.saveEditInviteMessage = function () { var D = this.editInviteMessageDialog; D.visible = false; if (D.inviteMessage.message !== D.newMessage) { @@ -8273,13 +8275,189 @@ speechSynthesis.getVoices(); API.refreshInviteMessageTableData(messageType); }); } - //D.newMessage = ''; }; - $app.methods.cancelInviteMessage = function () { - var D = this.editInviteMessageDialog; + $app.methods.cancelEditInviteMessage = function () { + this.editInviteMessageDialog.visible = false; + }; + + // App: Edit and Send Invite Response Message Dialog + + $app.data.editAndSendInviteResponseDialog = { + visible: false, + inviteMessage: '', + messageType: '', + newMessage: '' + }; + + $app.methods.showEditAndSendInviteResponseDialog = function (messageType, inviteMessage) { + this.$nextTick(() => adjustDialogZ(this.$refs.editAndSendInviteResponseDialog.$el)); + this.editAndSendInviteResponseDialog = { + newMessage: inviteMessage.message, + visible: true, + messageType, + inviteMessage + }; + }; + + $app.methods.saveEditAndSendInviteResponse = async function () { + var D = this.editAndSendInviteResponseDialog; D.visible = false; - //D.newMessage = ''; + var messageType = D.messageType; + var slot = D.inviteMessage.slot; + if (D.inviteMessage.message !== D.newMessage) { + var params = { + message: D.newMessage + }; + await API.editInviteMessage(params, messageType, slot).catch((err) => { + throw err; + }).then((args) => { + this.$message({ + message: 'Invite message updated', + type: 'success' + }); + return args; + }); + await API.refreshInviteMessageTableData(messageType).catch((err) => { + throw err; + }).then((args) => { + if (args.json[slot].message !== D.newMessage) { + this.$message({ + message: 'VRC API didn\'t update message, try again', + type: 'error' + }); + throw 'VRC API didn\'t update message, try again'; + } + }); + } + + var I = this.sendInviteResponseDialog; + var params = { + responseSlot: slot, + rsvp: true + }; + API.sendInviteResponse(params, I.invite.id).catch((err) => { + D.visible = false; + throw err; + }).then((args) => { + API.hideNotification({ + notificationId: I.invite.id + }); + this.$message({ + message: 'Invite response message sent', + type: 'success' + }); + D.visible = false; + this.sendInviteResponseDialogVisible = false; + this.sendInviteRequestResponseDialogVisible = false; + return args; + }); + }; + + $app.methods.cancelEditAndSendInviteResponse = function () { + this.editAndSendInviteResponseDialog.visible = false; + }; + + $app.data.sendInviteResponseDialog = { + message: '', + invite: '' + }; + + $app.data.sendInviteResponseDialogVisible = false; + + $app.data.sendInviteResponseConfirmDialog = { + visible: false + }; + + API.$on('LOGIN', function () { + $app.sendInviteResponseDialogVisible = false; + $app.sendInviteResponseConfirmDialog.visible = false; + }); + + $app.methods.showSendInviteResponseDialog = function (invite) { + this.sendInviteResponseDialog = { + invite + }; + API.refreshInviteMessageTableData('response'); + this.$nextTick(() => adjustDialogZ(this.$refs.sendInviteResponseDialog.$el)); + this.sendInviteResponseDialogVisible = true; + }; + + $app.methods.showSendInviteResponseConfirmDialog = function (val) { + if (this.editAndSendInviteResponseDialog.visible === true || val === null) { + return; + } + this.$nextTick(() => adjustDialogZ(this.$refs.sendInviteResponseConfirmDialog.$el)); + this.sendInviteResponseConfirmDialog.visible = true; + this.sendInviteResponseDialog.messageSlot = val.slot; + }; + + $app.methods.cancelSendInviteResponse = function () { + this.sendInviteResponseDialogVisible = false; + }; + + $app.methods.cancelInviteResponseConfirm = function () { + this.sendInviteResponseConfirmDialog.visible = false; + }; + + $app.methods.sendInviteResponseConfirm = function () { + var D = this.sendInviteResponseDialog; + var params = { + responseSlot: D.messageSlot, + rsvp: true + }; + API.sendInviteResponse(params, D.invite.id, D.messageType).catch((err) => { + throw err; + }).then((args) => { + API.hideNotification({ + notificationId: D.invite.id + }); + this.$message({ + message: 'Invite response message sent', + type: 'success' + }); + return args; + }); + this.sendInviteResponseDialogVisible = false; + this.sendInviteRequestResponseDialogVisible = false; + this.sendInviteResponseConfirmDialog.visible = false; + }; + + API.sendInviteResponse = function (params, inviteID) { + return this.call(`invite/${inviteID}/response`, { + method: 'POST', + params + }).then((json) => { + var args = { + json, + params, + inviteID + }; + this.$emit('INVITE:RESPONSE:SEND', args); + return args; + }); + }; + + // App: Invite Request Response Message Dialog + + $app.data.sendInviteRequestResponseDialogVisible = false; + + $app.methods.cancelSendInviteRequestResponse = function () { + this.sendInviteRequestResponseDialogVisible = false; + }; + + API.$on('LOGIN', function () { + $app.sendInviteRequestResponseDialogVisible = false; + $app.showSendInviteResponseConfirmDialog.visible = false; + }); + + $app.methods.showSendInviteRequestResponseDialog = function (invite) { + this.sendInviteResponseDialog = { + invite + }; + API.refreshInviteMessageTableData('requestResponse'); + this.$nextTick(() => adjustDialogZ(this.$refs.sendInviteRequestResponseDialog.$el)); + this.sendInviteRequestResponseDialogVisible = true; }; $app = new Vue($app); diff --git a/html/src/index.pug b/html/src/index.pug index b62c2d34..9c02bfe5 100644 --- a/html/src/index.pug +++ b/html/src/index.pug @@ -406,6 +406,8 @@ html el-table-column(label="Action" width="80" align="right") template(v-once #default="scope") el-button(v-if="scope.row.type === 'friendRequest'" type="text" icon="el-icon-check" size="mini" @click="acceptNotification(scope.row)") + el-button(v-else-if="scope.row.type === 'invite'" type="text" icon="el-icon-chat-line-square" size="mini" @click="showSendInviteResponseDialog(scope.row)") + el-button(v-else-if="scope.row.type === 'requestInvite'" type="text" icon="el-icon-chat-line-square" size="mini" @click="showSendInviteRequestResponseDialog(scope.row)") el-button(type="text" icon="el-icon-close" size="mini" @click="hideNotification(scope.row)") //- profile @@ -446,17 +448,17 @@ html el-button(size="small" @click="promptUserDialog()") User el-button(size="small" @click="promptWorldDialog()") World el-button(size="small" @click="promptAvatarDialog()") Avatar - div.options-container(style="margin-top:30px") + div.options-container span.header Invite Messages el-tooltip(placement="top") template(#content) span Refresh - el-button(type="default" @click="API.refreshInviteMessageTableData('message')" size="mini" icon="el-icon-refresh" circle style="margin-left:5px") + el-button(type="default" @click="inviteMessageTable.visible = true; refreshInviteMessageTable('message')" size="mini" icon="el-icon-refresh" circle style="margin-left:5px") el-tooltip(placement="top") template(#content) span Clear results - el-button(type="default" @click="inviteMessageTable.data = []" size="mini" icon="el-icon-delete" circle style="margin-left:0") - data-tables(v-if="inviteMessageTable.data.length > 0" v-bind="inviteMessageTable" style="margin-top:10px") + el-button(type="default" @click="inviteMessageTable.visible = false" size="mini" icon="el-icon-delete" circle style="margin-left:0") + data-tables(v-if="inviteMessageTable.visible" v-bind="inviteMessageTable" style="margin-top:10px") el-table-column(label="Slot" prop="slot" sortable="custom" width="70") el-table-column(label="Message" prop="message") el-table-column(label="Cool Down" prop="updatedAt" sortable="custom" width="110" align="right") @@ -465,17 +467,17 @@ html el-table-column(label="Action" width="60" align="right") template(v-once #default="scope") el-button(type="text" icon="el-icon-edit" size="mini" @click="showEditInviteMessageDialog('message', scope.row)") - div.options-container(style="margin-top:30px") + div.options-container span.header Invite Response Messages el-tooltip(placement="top") template(#content) span Refresh - el-button(type="default" @click="API.refreshInviteMessageTableData('response')" size="mini" icon="el-icon-refresh" circle style="margin-left:5px") + el-button(type="default" @click="inviteResponseMessageTable.visible = true; refreshInviteMessageTable('response')" size="mini" icon="el-icon-refresh" circle style="margin-left:5px") el-tooltip(placement="top") template(#content) span Clear results - el-button(type="default" @click="inviteResponseMessageTable.data = []" size="mini" icon="el-icon-delete" circle style="margin-left:0") - data-tables(v-if="inviteResponseMessageTable.data.length > 0" v-bind="inviteResponseMessageTable" style="margin-top:10px") + el-button(type="default" @click="inviteResponseMessageTable.visible = false" size="mini" icon="el-icon-delete" circle style="margin-left:0") + data-tables(v-if="inviteResponseMessageTable.visible" v-bind="inviteResponseMessageTable" style="margin-top:10px") el-table-column(label="Slot" prop="slot" sortable="custom" width="70") el-table-column(label="Message" prop="message") el-table-column(label="Cool Down" prop="updatedAt" sortable="custom" width="110" align="right") @@ -484,17 +486,17 @@ html el-table-column(label="Action" width="60" align="right") template(v-once #default="scope") el-button(type="text" icon="el-icon-edit" size="mini" @click="showEditInviteMessageDialog('response', scope.row)") - div.options-container(style="margin-top:30px") + div.options-container span.header Invite Request Messages el-tooltip(placement="top") template(#content) span Refresh - el-button(type="default" @click="API.refreshInviteMessageTableData('request')" size="mini" icon="el-icon-refresh" circle style="margin-left:5px") + el-button(type="default" @click="inviteRequestMessageTable.visible = true; refreshInviteMessageTable('request')" size="mini" icon="el-icon-refresh" circle style="margin-left:5px") el-tooltip(placement="top") template(#content) span Clear results - el-button(type="default" @click="inviteRequestMessageTable.data = []" size="mini" icon="el-icon-delete" circle style="margin-left:0") - data-tables(v-if="inviteRequestMessageTable.data.length > 0" v-bind="inviteRequestMessageTable" style="margin-top:10px") + el-button(type="default" @click="inviteRequestMessageTable.visible = false" size="mini" icon="el-icon-delete" circle style="margin-left:0") + data-tables(v-if="inviteRequestMessageTable.visible" v-bind="inviteRequestMessageTable" style="margin-top:10px") el-table-column(label="Slot" prop="slot" sortable="custom" width="70") el-table-column(label="Message" prop="message") el-table-column(label="Cool Down" prop="updatedAt" sortable="custom" width="110" align="right") @@ -503,7 +505,26 @@ html el-table-column(label="Action" width="60" align="right") template(v-once #default="scope") el-button(type="text" icon="el-icon-edit" size="mini" @click="showEditInviteMessageDialog('request', scope.row)") - div.options-container(v-if="API.currentUser.$isVRCPlus" style="margin-top:30px") + div.options-container + span.header Invite Request Response Messages + el-tooltip(placement="top") + template(#content) + span Refresh + el-button(type="default" @click="inviteRequestResponseMessageTable.visible = true; refreshInviteMessageTable('requestResponse')" size="mini" icon="el-icon-refresh" circle style="margin-left:5px") + el-tooltip(placement="top") + template(#content) + span Clear results + el-button(type="default" @click="inviteRequestResponseMessageTable.visible = false" size="mini" icon="el-icon-delete" circle style="margin-left:0") + data-tables(v-if="inviteRequestResponseMessageTable.visible" v-bind="inviteRequestResponseMessageTable" style="margin-top:10px") + el-table-column(label="Slot" prop="slot" sortable="custom" width="70") + el-table-column(label="Message" prop="message") + el-table-column(label="Cool Down" prop="updatedAt" sortable="custom" width="110" align="right") + template(v-once #default="scope") + countdown-timer(:datetime="scope.row.updatedAt" :hours="1") + el-table-column(label="Action" width="60" align="right") + template(v-once #default="scope") + el-button(type="text" icon="el-icon-edit" size="mini" @click="showEditInviteMessageDialog('requestResponse', scope.row)") + div.options-container span.header VRCPlus Icons el-tooltip(placement="top") template(#content) @@ -543,7 +564,7 @@ html template(#content) span Clear results el-button(type="default" @click="configTreeData = []" size="mini" icon="el-icon-delete" circle style="margin-left:0") - el-tree(:data="configTreeData" style="margin-top:10px;font-size:12px") + el-tree(v-if="configTreeData.length > 0" :data="configTreeData" style="margin-top:10px;font-size:12px") template(#default="scope") span span(v-text="scope.data.key" style="font-weight:bold;margin-right:5px") @@ -558,7 +579,7 @@ html template(#content) span Clear results el-button(type="default" @click="currentUserTreeData = []" size="mini" icon="el-icon-delete" circle style="margin-left:0") - el-tree(:data="currentUserTreeData" style="margin-top:10px;font-size:12px") + el-tree(v-if="currentUserTreeData.length > 0" :data="currentUserTreeData" style="margin-top:10px;font-size:12px") template(#default="scope") span span(v-text="scope.data.key" style="font-weight:bold;margin-right:5px") @@ -1452,11 +1473,58 @@ html //- dialog: Edit Invite Message el-dialog.x-dialog(ref="editInviteMessageDialog" :visible.sync="editInviteMessageDialog.visible" title="Edit Invite Message" width="400px") div(style='font-size:12px') - span Edit cool down time 1 hour. + span 1 hour edit cool down time. el-input(type="textarea" v-model="editInviteMessageDialog.newMessage" size="mini" maxlength="64" show-word-limit :autosize="{ minRows:2, maxRows:5 }" placeholder="" style="margin-top:10px") template(#footer) - el-button(type="small" @click="cancelInviteMessage") Cancel - el-button(type="primary" size="small" @click="saveInviteMessage") Save + el-button(type="small" @click="cancelEditInviteMessage") Cancel + el-button(type="primary" size="small" @click="saveEditInviteMessage") Save + + //- dialog: Edit And Send Invite Response Message + el-dialog.x-dialog(ref="editAndSendInviteResponseDialog" :visible.sync="editAndSendInviteResponseDialog.visible" title="Edit and Send Invite Message" width="400px") + div(style='font-size:12px') + span 1 hour edit cool down time. + el-input(type="textarea" v-model="editAndSendInviteResponseDialog.newMessage" size="mini" maxlength="64" show-word-limit :autosize="{ minRows:2, maxRows:5 }" placeholder="" style="margin-top:10px") + template(#footer) + el-button(type="small" @click="cancelEditAndSendInviteResponse") Cancel + el-button(type="primary" size="small" @click="saveEditAndSendInviteResponse") Send + + //- dialog Table: Send Invite Response Message + el-dialog.x-dialog(ref="sendInviteResponseDialog" :visible.sync="sendInviteResponseDialogVisible" title="Send Invite Response Message" width="800px") + data-tables(v-bind="inviteResponseMessageTable" @current-change="showSendInviteResponseConfirmDialog" style="margin-top:10px;cursor:pointer") + el-table-column(label="Slot" prop="slot" sortable="custom" width="70") + el-table-column(label="Message" prop="message") + el-table-column(label="Cool Down" prop="updatedAt" sortable="custom" width="110" align="right") + template(v-once #default="scope") + countdown-timer(:datetime="scope.row.updatedAt" :hours="1") + el-table-column(label="Action" width="60" align="right") + template(v-once #default="scope") + el-button(type="text" icon="el-icon-edit" size="mini" @click="showEditAndSendInviteResponseDialog('response', scope.row)") + template(#footer) + el-button(type="small" @click="API.refreshInviteMessageTableData('response')") Refresh + el-button(type="small" @click="cancelSendInviteResponse") Cancel + + //- dialog Table: Send Invite Response Message + el-dialog.x-dialog(ref="sendInviteRequestResponseDialog" :visible.sync="sendInviteRequestResponseDialogVisible" title="Send Invite Request Response Message" width="800px") + data-tables(v-bind="inviteRequestResponseMessageTable" @current-change="showSendInviteResponseConfirmDialog" style="margin-top:10px;cursor:pointer") + el-table-column(label="Slot" prop="slot" sortable="custom" width="70") + el-table-column(label="Message" prop="message") + el-table-column(label="Cool Down" prop="updatedAt" sortable="custom" width="110" align="right") + template(v-once #default="scope") + countdown-timer(:datetime="scope.row.updatedAt" :hours="1") + el-table-column(label="Action" width="60" align="right") + template(v-once #default="scope") + el-button(type="text" icon="el-icon-edit" size="mini" @click="showEditAndSendInviteResponseDialog('requestResponse', scope.row)") + template(#footer) + el-button(type="small" @click="API.refreshInviteMessageTableData('requestResponse')") Refresh + el-button(type="small" @click="cancelSendInviteRequestResponse") Cancel + + //- dialog: Send Invite Message Confirm + el-dialog.x-dialog(ref="sendInviteResponseConfirmDialog" :visible.sync="sendInviteResponseConfirmDialog.visible" title="Send Invite Message" width="400px") + div(style='font-size:12px') + span Are you sure you want to send? + template(#footer) + el-button(type="small" @click="cancelInviteResponseConfirm") Cancel + el-button(type="primary" size="small" @click="sendInviteResponseConfirm") Confirm //- dialog: open source software notice el-dialog.x-dialog(:visible.sync="ossDialog" title="Open Source Software Notice" width="650px") @@ -1740,6 +1808,30 @@ html OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. div(style="margin-top:15px") + p(style="font-weight:bold") vue-swatches + pre(style="font-size:12px;white-space:pre-line"). + MIT License + + Copyright (c) 2018 - Present Diego Jara + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + div(style="margin-top:15px") p(style="font-weight:bold") vuejs-toggle-switch pre(style="font-size:12px;white-space:pre-line"). MIT License