diff --git a/html/src/app.js b/html/src/app.js index c5d34bfb..ef9ad7a1 100644 --- a/html/src/app.js +++ b/html/src/app.js @@ -434,6 +434,9 @@ speechSynthesis.getVoices(); this.$throw(0, err, endpoint); }) .then((response) => { + if (!response.data) { + return response; + } try { response.data = JSON.parse(response.data); if ($app.debugWebRequests) { @@ -499,7 +502,7 @@ speechSynthesis.getVoices(); } throw new Error('401: Unauthorized'); } - if (status === 403 && endpoint.substring(0, 6) === 'config') { + if (status === 403 && endpoint === 'config') { $app.$alert( 'VRChat currently blocks most VPNs. Please disable any connected VPNs and try again.', 'Login Error 403' @@ -507,7 +510,11 @@ speechSynthesis.getVoices(); this.logout(); throw new Error(`403: ${endpoint}`); } - if (status === 404 && endpoint.substring(0, 8) === 'avatars/') { + if ( + init.method === 'GET' && + status === 404 && + endpoint.startsWith('avatars/') + ) { $app.$message({ message: 'Avatar private or deleted', type: 'error' @@ -515,6 +522,9 @@ speechSynthesis.getVoices(); $app.avatarDialog.visible = false; throw new Error(`404: ${data.error.message} ${endpoint}`); } + if (status === 404 && endpoint.endsWith('/persist/exists')) { + return false; + } if ( init.method === 'GET' && (status === 404 || status === 403) && @@ -522,7 +532,12 @@ speechSynthesis.getVoices(); ) { this.failedGetRequests.set(endpoint, Date.now()); } - if (status === 404 && endpoint.startsWith('users/')) { + if ( + init.method === 'GET' && + status === 404 && + endpoint.startsWith('users/') && + endpoint.split('/').length - 1 === 1 + ) { throw new Error(`404: ${data.error.message} ${endpoint}`); } if ( @@ -19168,7 +19183,8 @@ speechSynthesis.getVoices(); timeSpent: 0, isPC: false, isQuest: false, - isIos: false + isIos: false, + hasPersistData: false }; $app.data.ignoreWorldMemoSave = false; @@ -19342,6 +19358,7 @@ speechSynthesis.getVoices(); D.isPC = false; D.isQuest = false; D.isIos = false; + D.hasPersistData = false; this.ignoreWorldMemoSave = true; D.memo = ''; var LL = API.parseLocation(this.lastLocation.location); @@ -19408,6 +19425,7 @@ speechSynthesis.getVoices(); D.isQuest = isQuest; D.isIos = isIos; this.updateVRChatWorldCache(); + API.hasWorldPersistData({ worldId: D.id }); if (args.cache) { API.getWorld(args.params) .catch((err) => { @@ -19882,6 +19900,18 @@ speechSynthesis.getVoices(); return args; }); break; + case 'Delete Persistent Data': + API.deleteWorldPersistData({ + worldId: D.id + }).then((args) => { + this.$message({ + message: + 'Persistent data has been deleted', + type: 'success' + }); + return args; + }); + break; case 'Delete': API.deleteWorld({ worldId: D.id @@ -34051,6 +34081,72 @@ speechSynthesis.getVoices(); // #endregion + // #region persistent data + + /** + * @param {{ + worldId: string + }} params + * @returns {Promise<{json: any, params}>} + */ + API.deleteWorldPersistData = function (params) { + return this.call( + `users/${this.currentUser.id}/${params.worldId}/persist`, + { + method: 'DELETE' + } + ).then((json) => { + var args = { + json, + params + }; + this.$emit('WORLD:PERSIST:DELETE', args); + return args; + }); + }; + + /** + * @param {{ + worldId: string + }} params + * @returns {Promise<{json: any, params}>} + */ + API.hasWorldPersistData = function (params) { + return this.call( + `users/${this.currentUser.id}/${params.worldId}/persist/exists`, + { + method: 'GET' + } + ).then((json) => { + var args = { + json, + params + }; + this.$emit('WORLD:PERSIST:HAS', args); + return args; + }); + }; + + API.$on('WORLD:PERSIST:HAS', function (args) { + if ( + args.params.worldId === $app.worldDialog.id && + $app.worldDialog.visible + ) { + $app.worldDialog.hasPersistData = args.json !== false; + } + }); + + API.$on('WORLD:PERSIST:DELETE', function (args) { + if ( + args.params.worldId === $app.worldDialog.id && + $app.worldDialog.visible + ) { + $app.worldDialog.hasPersistData = false; + } + }); + + // #endregion + $app = new Vue($app); window.$app = $app; })(); diff --git a/html/src/index.pug b/html/src/index.pug index 4328bb65..e34f5f7d 100644 --- a/html/src/index.pug +++ b/html/src/index.pug @@ -691,6 +691,7 @@ html el-dropdown-item(icon="el-icon-tickets" command="Previous Instances") {{ $t('dialog.world.actions.show_previous_instances') }} template(v-if="API.currentUser.id !== worldDialog.ref.authorId") el-dropdown-item(icon="el-icon-picture-outline" command="Previous Images") {{ $t('dialog.world.actions.show_previous_images') }} + el-dropdown-item(:disabled="!worldDialog.hasPersistData" icon="el-icon-upload" command="Delete Persistent Data") {{ $t('dialog.world.actions.delete_persistent_data') }} template(v-else) el-dropdown-item(icon="el-icon-edit" command="Rename") {{ $t('dialog.world.actions.rename') }} el-dropdown-item(icon="el-icon-edit" command="Change Description") {{ $t('dialog.world.actions.change_description') }} @@ -702,6 +703,7 @@ html el-dropdown-item(v-if="worldDialog.ref.unityPackageUrl" icon="el-icon-download" command="Download Unity Package") {{ $t('dialog.world.actions.download_package') }} el-dropdown-item(v-if="worldDialog.ref.tags.includes('system_approved') || worldDialog.ref.tags.includes('system_labs')" icon="el-icon-view" command="Unpublish" divided) {{ $t('dialog.world.actions.unpublish') }} el-dropdown-item(v-else icon="el-icon-view" command="Publish" divided) {{ $t('dialog.world.actions.publish_to_labs') }} + el-dropdown-item(:disabled="!worldDialog.hasPersistData" icon="el-icon-upload" command="Delete Persistent Data") {{ $t('dialog.world.actions.delete_persistent_data') }} el-dropdown-item(icon="el-icon-delete" command="Delete" style="color:#F56C6C") {{ $t('dialog.world.actions.delete') }} el-tabs el-tab-pane(:label="$t('dialog.world.instances.header')") diff --git a/html/src/localization/en/en.json b/html/src/localization/en/en.json index 55d37b35..0d9d3030 100644 --- a/html/src/localization/en/en.json +++ b/html/src/localization/en/en.json @@ -715,6 +715,7 @@ "download_package": "Download Unity Package", "publish_to_labs": "Publish To Labs", "unpublish": "Unpublish", + "delete_persistent_data": "Delete Persistent Data", "delete": "Delete" }, "instances": {