diff --git a/html/src/app.js b/html/src/app.js index 3983a1cc..27e0049d 100644 --- a/html/src/app.js +++ b/html/src/app.js @@ -2071,6 +2071,21 @@ speechSynthesis.getVoices(); }); }; + API.sendInvitePhoto = function (params, receiverUserId) { + return this.call(`invite/${receiverUserId}/photo`, { + uploadImage: true, + postData: JSON.stringify(params), + imageData: $app.uploadImage + }).then((json) => { + var args = { + json, + params + }; + this.$emit('NOTIFICATION:INVITE:PHOTO:SEND', args); + return args; + }); + }; + API.sendRequestInvite = function (params, receiverUserId) { return this.call(`requestInvite/${receiverUserId}`, { method: 'POST', @@ -2085,6 +2100,52 @@ speechSynthesis.getVoices(); }); }; + API.sendRequestInvitePhoto = function (params, receiverUserId) { + return this.call(`requestInvite/${receiverUserId}/photo`, { + uploadImage: true, + postData: JSON.stringify(params), + imageData: $app.uploadImage + }).then((json) => { + var args = { + json, + params + }; + this.$emit('NOTIFICATION:REQUESTINVITE:PHOTO:SEND', args); + return args; + }); + }; + + 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; + }); + }; + + API.sendInviteResponsePhoto = function (params, inviteID) { + return this.call(`invite/${inviteID}/response/photo`, { + uploadImage: true, + postData: JSON.stringify(params), + imageData: $app.uploadImage + }).then((json) => { + var args = { + json, + params, + inviteID + }; + this.$emit('INVITE:RESPONSE:PHOTO:SEND', args); + return args; + }); + }; + /* params: { notificationId: string @@ -4051,6 +4112,9 @@ speechSynthesis.getVoices(); message = noty.details[messageList[i]]; } } + if (message) { + message = `, ${message}`; + } if ((this.notificationTTS) && (this.isGameRunning)) { this.playNotyTTS(noty, message); } @@ -4086,16 +4150,16 @@ speechSynthesis.getVoices(); this.speak(`${noty.displayName} status is now ${noty.status[0].status} ${noty.status[0].statusDescription}`); break; case 'invite': - this.speak(`${noty.senderUsername} has invited you to ${noty.details.worldName} ${message}`); + this.speak(`${noty.senderUsername} has invited you to ${noty.details.worldName}${message}`); break; case 'requestInvite': - this.speak(`${noty.senderUsername} has requested an invite ${message}`); + this.speak(`${noty.senderUsername} has requested an invite${message}`); break; case 'inviteResponse': - this.speak(`${noty.senderUsername} has responded to your invite ${message}`); + this.speak(`${noty.senderUsername} has responded to your invite${message}`); break; case 'requestInviteResponse': - this.speak(`${noty.senderUsername} has responded to your invite request ${message}`); + this.speak(`${noty.senderUsername} has responded to your invite request${message}`); break; case 'friendRequest': this.speak(`${noty.senderUsername} has sent you a friend request`); @@ -4149,7 +4213,9 @@ speechSynthesis.getVoices(); } } } - if (userId) { + if ((noty.details) && (noty.details.imageUrl)) { + imageURL = noty.details.imageUrl; + } else if (userId) { imageURL = await API.getCachedUser({ userId: userId }).catch((err) => { @@ -4184,16 +4250,16 @@ speechSynthesis.getVoices(); AppApi.DesktopNotification(noty.displayName, `status is now ${noty.status[0].status} ${noty.status[0].statusDescription}`, imageURL); break; case 'invite': - AppApi.DesktopNotification(noty.senderUsername, `has invited you to ${noty.details.worldName} ${message}`, imageURL); + AppApi.DesktopNotification(noty.senderUsername, `has invited you to ${noty.details.worldName}${message}`, imageURL); break; case 'requestInvite': - AppApi.DesktopNotification(noty.senderUsername, `has requested an invite ${message}`, imageURL); + AppApi.DesktopNotification(noty.senderUsername, `has requested an invite${message}`, imageURL); break; case 'inviteResponse': - AppApi.DesktopNotification(noty.senderUsername, `has responded to your invite ${message}`, imageURL); + AppApi.DesktopNotification(noty.senderUsername, `has responded to your invite${message}`, imageURL); break; case 'requestInviteResponse': - AppApi.DesktopNotification(noty.senderUsername, `has responded to your invite request ${message}`, imageURL); + AppApi.DesktopNotification(noty.senderUsername, `has responded to your invite request${message}`, imageURL); break; case 'friendRequest': AppApi.DesktopNotification(noty.senderUsername, 'has sent you a friend request', imageURL); @@ -8782,6 +8848,41 @@ speechSynthesis.getVoices(); }); }; + $app.data.uploadImage = ''; + + $app.methods.inviteImageUpload = function (e) { + var files = e.target.files || e.dataTransfer.files; + if (!files.length) { + return; + } + if (files[0].size >= 10485760) { //10MB + $app.$message({ + message: 'File size too large', + type: 'error' + }); + return; + } + if (!files[0].type.match(/image.*/)) { + $app.$message({ + message: 'File isn\'t an image', + type: 'error' + }); + return; + } + var r = new FileReader(); + r.onload = function () { + $app.uploadImage = btoa(r.result); + }; + r.readAsBinaryString(files[0]); + }; + + $app.methods.clearInviteImageUpload = function () { + if (document.querySelector('#InviteImageUploadButton')) { + document.querySelector('#InviteImageUploadButton').value = ''; + } + this.uploadImage = ''; + }; + $app.methods.userOnlineFor = function (ctx) { if ((ctx.ref.state === 'online') && (ctx.ref.$online_for)) { return timeToText(Date.now() - ctx.ref.$online_for); @@ -8883,10 +8984,17 @@ speechSynthesis.getVoices(); API.editInviteMessage(params, messageType, slot).catch((err) => { throw err; }).then((args) => { - this.$message('Invite message updated'); + API.$emit(`INVITE:${messageType.toUpperCase()}`, args); + if (args.json[slot].message !== D.newMessage) { + this.$message({ + message: 'VRChat API didn\'t update message, try again', + type: 'error' + }); + throw new Error('VRChat API didn\'t update message, try again'); + } else { + this.$message('Invite message updated'); + } return args; - }).finally(() => { - API.refreshInviteMessageTableData(messageType); }); } }; @@ -8926,7 +9034,7 @@ speechSynthesis.getVoices(); await API.editInviteMessage(params, messageType, slot).catch((err) => { throw err; }).then((args) => { - this.$emit(`INVITE:${messageType.toUpperCase()}`, args); + API.$emit(`INVITE:${messageType.toUpperCase()}`, args); if (args.json[slot].message !== D.newMessage) { this.$message({ message: 'VRChat API didn\'t update message, try again', @@ -8944,20 +9052,37 @@ speechSynthesis.getVoices(); responseSlot: slot, rsvp: true }; - API.sendInviteResponse(params, I.invite.id).catch((err) => { - throw err; - }).then((args) => { - API.hideNotification({ - notificationId: I.invite.id + if ($app.uploadImage) { + API.sendInviteResponsePhoto(params, I.invite.id).catch((err) => { + throw err; + }).then((args) => { + API.hideNotification({ + notificationId: I.invite.id + }); + this.$message({ + message: 'Invite response message sent', + type: 'success' + }); + this.sendInviteResponseDialogVisible = false; + this.sendInviteRequestResponseDialogVisible = false; + return args; }); - this.$message({ - message: 'Invite response message sent', - type: 'success' + } else { + API.sendInviteResponse(params, I.invite.id).catch((err) => { + throw err; + }).then((args) => { + API.hideNotification({ + notificationId: I.invite.id + }); + this.$message({ + message: 'Invite response message sent', + type: 'success' + }); + this.sendInviteResponseDialogVisible = false; + this.sendInviteRequestResponseDialogVisible = false; + return args; }); - this.sendInviteResponseDialogVisible = false; - this.sendInviteRequestResponseDialogVisible = false; - return args; - }); + } }; $app.methods.cancelEditAndSendInviteResponse = function () { @@ -8987,6 +9112,7 @@ speechSynthesis.getVoices(); }; API.refreshInviteMessageTableData('response'); this.$nextTick(() => adjustDialogZ(this.$refs.sendInviteResponseDialog.$el)); + this.clearInviteImageUpload(); this.sendInviteResponseDialogVisible = true; }; @@ -9013,38 +9139,38 @@ speechSynthesis.getVoices(); 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 + if ($app.uploadImage) { + API.sendInviteResponsePhoto(params, D.invite.id, D.messageType).catch((err) => { + throw err; + }).then((args) => { + API.hideNotification({ + notificationId: D.invite.id + }); + this.$message({ + message: 'Invite response photo message sent', + type: 'success' + }); + return args; }); - this.$message({ - message: 'Invite response message sent', - type: 'success' + } else { + 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; }); - 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; @@ -9064,6 +9190,7 @@ speechSynthesis.getVoices(); }; API.refreshInviteMessageTableData('requestResponse'); this.$nextTick(() => adjustDialogZ(this.$refs.sendInviteRequestResponseDialog.$el)); + this.clearInviteImageUpload(); this.sendInviteRequestResponseDialogVisible = true; }; @@ -9098,7 +9225,7 @@ speechSynthesis.getVoices(); await API.editInviteMessage(params, messageType, slot).catch((err) => { throw err; }).then((args) => { - this.$emit(`INVITE:${messageType.toUpperCase()}`, args); + API.$emit(`INVITE:${messageType.toUpperCase()}`, args); if (args.json[slot].message !== D.newMessage) { this.$message({ message: 'VRChat API didn\'t update message, try again', @@ -9125,12 +9252,21 @@ speechSynthesis.getVoices(); var inviteLoop = () => { if (J.userIds.length > 0) { var receiverUserId = J.userIds.shift(); - API.sendInvite({ - instanceId: J.worldId, - worldId: J.worldId, - worldName: J.worldName, - messageSlot: slot - }, receiverUserId).finally(inviteLoop); + if ($app.uploadImage) { + API.sendInvitePhoto({ + instanceId: J.worldId, + worldId: J.worldId, + worldName: J.worldName, + messageSlot: slot + }, receiverUserId).finally(inviteLoop); + } else { + API.sendInvite({ + instanceId: J.worldId, + worldId: J.worldId, + worldName: J.worldName, + messageSlot: slot + }, receiverUserId).finally(inviteLoop); + } } else { J.loading = false; J.visible = false; @@ -9144,26 +9280,51 @@ speechSynthesis.getVoices(); } else { if (I.messageType === 'invite') { I.params.messageSlot = slot; - API.sendInvite(I.params, I.userId).catch((err) => { - throw err; - }).then((args) => { - this.$message({ - message: 'Invite message sent', - type: 'success' + if ($app.uploadImage) { + API.sendInvitePhoto(I.params, I.userId).catch((err) => { + throw err; + }).then((args) => { + this.$message({ + message: 'Invite photo message sent', + type: 'success' + }); + return args; }); - return args; - }); + } else { + API.sendInvite(I.params, I.userId).catch((err) => { + throw err; + }).then((args) => { + this.$message({ + message: 'Invite message sent', + type: 'success' + }); + return args; + }); + } } else if (I.messageType === 'requestInvite') { I.params.requestSlot = slot; - API.sendRequestInvite(I.params, I.userId).catch((err) => { - throw err; - }).then((args) => { - this.$message({ - message: 'Request invite message sent', - type: 'success' + if ($app.uploadImage) { + API.sendRequestInvitePhoto(I.params, I.userId).catch((err) => { + this.clearInviteImageUpload(); + throw err; + }).then((args) => { + this.$message({ + message: 'Request invite photo message sent', + type: 'success' + }); + return args; }); - return args; - }); + } else { + API.sendRequestInvite(I.params, I.userId).catch((err) => { + throw err; + }).then((args) => { + this.$message({ + message: 'Request invite message sent', + type: 'success' + }); + return args; + }); + } } } this.sendInviteDialogVisible = false; @@ -9201,6 +9362,7 @@ speechSynthesis.getVoices(); }; API.refreshInviteMessageTableData('message'); this.$nextTick(() => adjustDialogZ(this.$refs.sendInviteDialog.$el)); + this.clearInviteImageUpload(); this.sendInviteDialogVisible = true; }; @@ -9236,12 +9398,21 @@ speechSynthesis.getVoices(); var inviteLoop = () => { if (J.userIds.length > 0) { var receiverUserId = J.userIds.shift(); - API.sendInvite({ - instanceId: J.worldId, - worldId: J.worldId, - worldName: J.worldName, - messageSlot: D.messageSlot - }, receiverUserId).finally(inviteLoop); + if ($app.uploadImage) { + API.sendInvitePhoto({ + instanceId: J.worldId, + worldId: J.worldId, + worldName: J.worldName, + messageSlot: D.messageSlot + }, receiverUserId).finally(inviteLoop); + } else { + API.sendInvite({ + instanceId: J.worldId, + worldId: J.worldId, + worldName: J.worldName, + messageSlot: D.messageSlot + }, receiverUserId).finally(inviteLoop); + } } else { J.loading = false; J.visible = false; @@ -9255,24 +9426,51 @@ speechSynthesis.getVoices(); } else { if (D.messageType === 'invite') { D.params.messageSlot = D.messageSlot; - API.sendInvite(D.params, D.userId).catch((err) => { - throw err; - }).then((args) => { - this.$message({ - message: 'Invite message sent', - type: 'success' + if ($app.uploadImage) { + API.sendInvitePhoto(D.params, D.userId).catch((err) => { + throw err; + }).then((args) => { + this.$message({ + message: 'Invite photo message sent', + type: 'success' + }); + return args; }); - return args; - }); + } else { + API.sendInvite(D.params, D.userId).catch((err) => { + throw err; + }).then((args) => { + this.$message({ + message: 'Invite message sent', + type: 'success' + }); + return args; + }); + } } else if (D.messageType === 'requestInvite') { D.params.requestSlot = D.messageSlot; - API.sendRequestInvite(D.params, D.userId).then((args) => { - this.$message({ - message: 'Request invite message sent', - type: 'success' + if ($app.uploadImage) { + API.sendRequestInvitePhoto(D.params, D.userId).catch((err) => { + this.clearInviteImageUpload(); + throw err; + }).then((args) => { + this.$message({ + message: 'Request invite photo message sent', + type: 'success' + }); + return args; }); - return args; - }); + } else { + API.sendRequestInvite(D.params, D.userId).catch((err) => { + throw err; + }).then((args) => { + this.$message({ + message: 'Request invite message sent', + type: 'success' + }); + return args; + }); + } } } this.sendInviteDialogVisible = false; @@ -9301,6 +9499,7 @@ speechSynthesis.getVoices(); }; API.refreshInviteMessageTableData('request'); this.$nextTick(() => adjustDialogZ(this.$refs.sendInviteRequestDialog.$el)); + this.clearInviteImageUpload(); this.sendInviteRequestDialogVisible = true; }; diff --git a/html/src/index.pug b/html/src/index.pug index 4ce4274e..69b834b2 100644 --- a/html/src/index.pug +++ b/html/src/index.pug @@ -536,12 +536,13 @@ html template(#content) span Clear results el-button(type="default" @click="VRCPlusIconsTable = {}" size="mini" icon="el-icon-delete" circle style="margin-left:0") - el-tooltip(placement="top") - template(#content) - span Upload icon - div(style="display:inline-block") - el-button(type="default" @click="displayVRCPlusIconUpload" size="mini" icon="el-icon-upload2" circle style="margin-left:0") - input(type="file" multiple accept="image/*" @change="onFileChangeVRCPlusIcon" id="VRCPlusIconUploadButton" style="display:none") + template(v-if="API.currentUser.$isVRCPlus") + el-tooltip(placement="top") + template(#content) + span Upload icon + div(style="display:inline-block") + el-button(type="default" @click="displayVRCPlusIconUpload" size="mini" icon="el-icon-upload2" circle style="margin-left:0") + input(type="file" multiple accept="image/*" @change="onFileChangeVRCPlusIcon" id="VRCPlusIconUploadButton" style="display:none") el-tooltip(placement="top") template(#content) span Reset icon @@ -1511,6 +1512,8 @@ html //- dialog Table: Send Invite Response Message el-dialog.x-dialog(ref="sendInviteResponseDialog" :visible.sync="sendInviteResponseDialogVisible" title="Send Invite Response Message" width="800px") + template(v-if="API.currentUser.$isVRCPlus") + input(type="file" multiple accept="image/*" @change="inviteImageUpload" id="InviteImageUploadButton") data-tables(v-bind="inviteResponseMessageTable" @row-click="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") @@ -1526,6 +1529,8 @@ html //- dialog Table: Send Invite Request Response Message el-dialog.x-dialog(ref="sendInviteRequestResponseDialog" :visible.sync="sendInviteRequestResponseDialogVisible" title="Send Invite Request Response Message" width="800px") + template(v-if="API.currentUser.$isVRCPlus") + input(type="file" multiple accept="image/*" @change="inviteImageUpload" id="InviteImageUploadButton") data-tables(v-bind="inviteRequestResponseMessageTable" @row-click="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") @@ -1549,6 +1554,8 @@ html //- dialog Table: Send Invite Message el-dialog.x-dialog(ref="sendInviteDialog" :visible.sync="sendInviteDialogVisible" title="Send Invite Message" width="800px") + template(v-if="API.currentUser.$isVRCPlus") + input(type="file" multiple accept="image/*" @change="inviteImageUpload" id="InviteImageUploadButton") data-tables(v-bind="inviteMessageTable" @row-click="showSendInviteConfirmDialog" style="margin-top:10px;cursor:pointer") el-table-column(label="Slot" prop="slot" sortable="custom" width="70") el-table-column(label="Message" prop="message") @@ -1564,6 +1571,8 @@ html //- dialog Table: Send Invite Request Message el-dialog.x-dialog(ref="sendInviteRequestDialog" :visible.sync="sendInviteRequestDialogVisible" title="Send Invite Request Message" width="800px") + template(v-if="API.currentUser.$isVRCPlus") + input(type="file" multiple accept="image/*" @change="inviteImageUpload" id="InviteImageUploadButton") data-tables(v-bind="inviteRequestMessageTable" @row-click="showSendInviteConfirmDialog" style="margin-top:10px;cursor:pointer") el-table-column(label="Slot" prop="slot" sortable="custom" width="70") el-table-column(label="Message" prop="message")