diff --git a/.vscode/settings.json b/.vscode/settings.json index 17dee1bc..4f8c231c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,10 @@ { - "i18n-ally.localesPaths": ["html/src/localization/strings"], - "i18n-ally.keystyle": "nested", - "i18n-ally.sourceLanguage": "en" + "i18n-ally.localesPaths": ["html/src/localization/strings"], + "i18n-ally.keystyle": "nested", + "i18n-ally.sourceLanguage": "en", + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + } } diff --git a/AppApi.cs b/AppApi.cs index 2036e111..7820a04f 100644 --- a/AppApi.cs +++ b/AppApi.cs @@ -255,7 +255,7 @@ namespace VRCX return ImageCache.GetImage(url, fileId, version); } - public void DesktopNotification(string BoldText, string Text, string Image) + public void DesktopNotification(string BoldText, string Text = "", string Image = "") { XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastImageAndText02); XmlNodeList stringElements = toastXml.GetElementsByTagName("text"); @@ -288,7 +288,7 @@ namespace VRCX public string sourceApp { get; set; } } - public void XSNotification(string Title, string Content, int Timeout, string Image) + public void XSNotification(string Title, string Content, int Timeout, string Image = "") { bool UseBase64Icon; string Icon; diff --git a/html/src/app.js b/html/src/app.js index 03a6bc69..440bd4b5 100644 --- a/html/src/app.js +++ b/html/src/app.js @@ -826,9 +826,9 @@ speechSynthesis.getVoices(); template: "" + '' + - '{{ text }}' + - '({{ groupName }})' + - '' + + '{{ text }}' + + '({{ groupName }})' + + '' + '', props: { location: String, @@ -955,9 +955,9 @@ speechSynthesis.getVoices(); template: '' + '' + - ' #{{ instanceName }} {{ accessType }}' + - '({{ groupName }})' + - '' + + ' #{{ instanceName }} {{ accessType }}' + + '({{ groupName }})' + + '' + '', props: { locationobject: Object, @@ -1009,9 +1009,11 @@ speechSynthesis.getVoices(); this.groupName = this.grouphint; } else if (this.locationobject.groupId) { this.groupName = this.locationobject.groupId; - $app.getGroupName(this.location).then((groupName) => { - this.groupName = groupName; - }); + $app.getGroupName(this.locationobject.groupId).then( + (groupName) => { + this.groupName = groupName; + } + ); } }, showLaunchDialog() { @@ -1188,14 +1190,19 @@ speechSynthesis.getVoices(); args.ref = this.applyCurrentUser(json); var location = ''; var travelingToLocation = ''; - if (json.presence?.world && $app.isRealInstance(json.presence.world)) { - location = `${json.presence.world}:${json.presence.instance}`; + if (json.presence?.world) { + if ($app.isRealInstance(json.presence.world)) { + location = `${json.presence.world}:${json.presence.instance}`; + } else { + location = json.presence.world; + } } - if ( - json.presence?.travelingToWorld && - $app.isRealInstance(json.presence.travelingToWorld) - ) { - travelingToLocation = `${json.presence.travelingToWorld}:${json.presence.travelingToInstance}`; + if (json.presence?.travelingToWorld) { + if ($app.isRealInstance(json.presence.travelingToWorld)) { + travelingToLocation = `${json.presence.travelingToWorld}:${json.presence.travelingToInstance}`; + } else { + travelingToLocation = json.presence.travelingToWorld; + } } this.applyUser({ id: json.id, @@ -1892,6 +1899,24 @@ speechSynthesis.getVoices(); }); }; + /* + params: { + userId: string + } + */ + API.getUserFeedback = function (params) { + return this.call(`users/${params.userId}/feedback`, { + method: 'GET' + }).then((json) => { + var args = { + json, + params + }; + this.$emit('USER:FEEDBACK', args); + return args; + }); + }; + // API: World API.cachedWorlds = new Map(); @@ -3785,7 +3810,11 @@ speechSynthesis.getVoices(); return; } this.isFavoriteLoading = true; - await this.getFavoriteLimits(); + try { + await this.getFavoriteLimits(); + } catch (err) { + console.error(err); + } this.expireFavorites(); this.bulk({ fn: 'getFavorites', @@ -7760,10 +7789,9 @@ speechSynthesis.getVoices(); var groupName = ''; var groupId = data; if (!data.startsWith('grp_')) { - var L = API.parseLocation(location); - if (L.groupId) { - groupId = L.groupId; - } else { + var L = API.parseLocation(data); + groupId = L.groupId; + if (!L.groupId) { return ''; } } @@ -9489,7 +9517,7 @@ speechSynthesis.getVoices(); if (!this.photonLobbyWatcherLoop) { return; } - if (this.photonLobbyCurrent.size <= 1) { + if (this.photonLobbyCurrent.size === 0) { this.photonLobbyWatcherLoopStop(); return; } @@ -10466,8 +10494,12 @@ speechSynthesis.getVoices(); type: 'ChangeStatus', status: photonUser.status, previousStatus: ref.status, - statusDescription: photonUser.statusDescription, - previousStatusDescription: ref.statusDescription, + statusDescription: this.replaceBioSymbols( + photonUser.statusDescription + ), + previousStatusDescription: this.replaceBioSymbols( + ref.statusDescription + ), created_at: Date.parse(gameLogDate) }); } @@ -10479,6 +10511,8 @@ speechSynthesis.getVoices(); return; } var avatar = user.avatarDict; + avatar.name = this.replaceBioSymbols(avatar.name); + avatar.description = this.replaceBioSymbols(avatar.description); var platform = ''; if (user.last_platform === 'android') { platform = 'Quest'; @@ -10613,6 +10647,8 @@ speechSynthesis.getVoices(); oldAvatarId !== avatar.id && photonId !== this.photonLobbyCurrentUser ) { + avatar.name = this.replaceBioSymbols(avatar.name); + avatar.description = this.replaceBioSymbols(avatar.description); this.checkVRChatCache(avatar).then((cacheInfo) => { var inCache = false; if (cacheInfo[0] > 0) { @@ -10662,7 +10698,7 @@ speechSynthesis.getVoices(); } var {groupOnNameplate} = this.photonLobbyJointime.get(photonId); if ( - groupOnNameplate && + typeof groupOnNameplate !== 'undefined' && groupOnNameplate !== groupId && photonId !== this.photonLobbyCurrentUser ) { @@ -11412,11 +11448,6 @@ speechSynthesis.getVoices(); this.showUserDialog(ref.userId); return; } - if (ref.displayName === 'F⁄A-18E Super Hornet') { - // :eyes: - this.showUserDialog('usr_5cd9007b-1802-45fe-92e2-0b6617639344'); - return; - } if (!ref.displayName || ref.displayName.substring(0, 3) === 'ID:') { return; } @@ -12683,6 +12714,7 @@ speechSynthesis.getVoices(); $app.data.configTreeData = []; $app.data.currentUserTreeData = []; + $app.data.currentUserFeedbackData = []; $app.data.pastDisplayNameTable = { data: [], tableProps: { @@ -19768,7 +19800,7 @@ speechSynthesis.getVoices(); var name = ref.displayName; if (ref.statusDescription) { var statusRegex = - /(?:^|\n*)(?:(?:[^\n:]|\|)*(?::|˸|discord)[\t\v\f\r]*)?([^\n]*(#|#)(?: )?\d{4})/gi.exec( + /(?:^|\n*)(?:(?:[^\n:])*(?::|˸|discord)[\t\v\f\r]*)?([^\n]*(#|#)(?: )?\d{4})/gi.exec( ref.statusDescription ); if (statusRegex) { @@ -19777,7 +19809,7 @@ speechSynthesis.getVoices(); } if (!discord && ref.bio) { var bioRegex = - /(?:^|\n*)(?:(?:[^\n:]|\|)*(?::|˸|discord)[\t\v\f\r]*)?([^\n]*(#|#)(?: )?\d{4})/gi.exec( + /(?:^|\n*)(?:(?:[^\n:])*(?::|˸|discord)[\t\v\f\r]*)?([^\n]*(#|#)(?: )?\d{4})/gi.exec( ref.bio ); if (bioRegex) { @@ -21842,11 +21874,9 @@ speechSynthesis.getVoices(); $app.methods.isRealInstance = function (instanceId) { switch (instanceId) { case 'offline': - return false; case 'private': - return false; case 'traveling': - return false; + case 'local': case '': return false; } @@ -24567,6 +24597,16 @@ speechSynthesis.getVoices(); this.updateVRConfigVars(); }; + API.$on('USER:FEEDBACK', function (args) { + if (args.params.userId === this.currentUser.id) { + $app.currentUserFeedbackData = buildTreeData(args.json); + } + }); + + $app.methods.getCurrentUserFeedback = function () { + return API.getUserFeedback({userId: API.currentUser.id}); + }; + $app = new Vue($app); window.$app = $app; })(); diff --git a/html/src/index.pug b/html/src/index.pug index 66e1f9f1..24502668 100644 --- a/html/src/index.pug +++ b/html/src/index.pug @@ -856,7 +856,7 @@ html el-button(type="text" icon="el-icon-delete" size="mini" style="margin-left:5px" @click="deleteNotificationLog(scope.row)") //- profile - .x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'profile'") + .x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'profile'" v-if="$refs.menu && $refs.menu.activeIndex === 'profile'") div.options-container(style="margin-top:0") span.header {{ $t('view.profile.profile.header') }} .x-friend-list(style="margin-top:10px") @@ -994,6 +994,17 @@ html span span(v-text="scope.data.key" style="font-weight:bold;margin-right:5px") span(v-if="!scope.data.children" v-text="scope.data.value") + div.options-container + span.header {{ $t('view.profile.feedback') }} + el-tooltip(placement="top" :content="$t('view.profile.refresh_tooltip')" :disabled="hideTooltips") + el-button(type="default" @click="getCurrentUserFeedback()" size="mini" icon="el-icon-refresh" circle style="margin-left:5px") + el-tooltip(placement="top" :content="$t('view.profile.clear_results_tooltip')" :disabled="hideTooltips") + el-button(type="default" @click="currentUserFeedbackData = []" size="mini" icon="el-icon-delete" circle style="margin-left:5px") + el-tree(v-if="currentUserFeedbackData.length > 0" :data="currentUserFeedbackData" style="margin-top:10px;font-size:12px") + template(#default="scope") + span + span(v-text="scope.data.key" style="font-weight:bold;margin-right:5px") + span(v-if="!scope.data.children" v-text="scope.data.value") //- friends list .x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'friendsList'" v-if="$refs.menu && $refs.menu.activeIndex === 'friendsList'") @@ -2722,7 +2733,7 @@ html el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="VRCXUpdateDialog" :visible.sync="VRCXUpdateDialog.visible" :title="$t('dialog.vrcx_updater.header')" width="400px") div(v-loading="checkingForVRCXUpdate" style="margin-top:15px") div(v-if="VRCXUpdateDialog.updatePending" style="margin-bottom:15px") - span(v-text="pendingVRCXUpdate") + span(v-text="pendingVRCXInstall") br span {{ $t('dialog.vrcx_updater.ready_for_update') }} el-select(v-model="branch" @change="loadBranchVersions" style="display:inline-block;width:150px;margin-right:15px") @@ -2732,7 +2743,7 @@ html div(v-if="!VRCXUpdateDialog.updatePending && VRCXUpdateDialog.release === appVersion" style="margin-top:15px") span {{ $t('dialog.vrcx_updater.latest_version') }} template(#footer) - el-button(v-if="(VRCXUpdateDialog.updatePending && VRCXUpdateDialog.release !== pendingVRCXUpdate) || VRCXUpdateDialog.release !== appVersion" type="primary" size="small" @click="installVRCXUpdate") {{ $t('dialog.vrcx_updater.download') }} + el-button(v-if="(VRCXUpdateDialog.updatePending && VRCXUpdateDialog.release !== pendingVRCXInstall) || VRCXUpdateDialog.release !== appVersion" type="primary" size="small" @click="installVRCXUpdate") {{ $t('dialog.vrcx_updater.download') }} el-button(v-if="VRCXUpdateDialog.updatePending" type="primary" size="small" @click="restartVRCX") {{ $t('dialog.vrcx_updater.install') }} //- dialog: launch diff --git a/html/src/localization/strings/en.json b/html/src/localization/strings/en.json index 137f3bf0..4fcbfe0e 100644 --- a/html/src/localization/strings/en.json +++ b/html/src/localization/strings/en.json @@ -163,6 +163,7 @@ "past_display_names": "Past Display Names", "config_json": "Config JSON", "current_user_json": "Current User JSON", + "feedback": "Feedback", "refresh_tooltip": "Refresh", "clear_results_tooltip": "Clear results" }, @@ -202,9 +203,9 @@ }, "legal_notice": { "header": "Legal Notice", - "info": "VRCX is an assistant application for provide information about manage friendship. this application uses unofficial VRChat API (VRCSDK).", + "info": "VRCX is an assistant application for VRChat that provides information about and managing friendship. This application makes use of the unofficial VRChat API SDK.", "disclaimer1": "VRCX isn't endorsed by VRChat and doesn't reflect the views or opinions of VRChat or anyone officially involved in producing or managing VRChat. VRChat is trademark of VRChat Inc. VRChat © VRChat Inc.", - "disclaimer2": "pypy or Natsumi aren't responsible for any problems caused by VRCX. Use at your own risk!", + "disclaimer2": "pypy & Natsumi are not responsible for any problems caused by VRCX. Use at your own risk!", "open_source_software_notice": "Open Source Software Notice" } }, diff --git a/html/src/vr.js b/html/src/vr.js index 9d8d7b02..e7b4e6ae 100644 --- a/html/src/vr.js +++ b/html/src/vr.js @@ -90,9 +90,9 @@ Vue.component('marquee-text', MarqueeText); Vue.component('location', { template: - '{{ text }}' + - '({{ groupName }})' + - '' + + '{{ text }}' + + '({{ groupName }})' + + '' + '', props: { location: String, diff --git a/html/src/vr.pug b/html/src/vr.pug index 6a05a6d0..5490d4c6 100644 --- a/html/src/vr.pug +++ b/html/src/vr.pug @@ -488,12 +488,12 @@ html span(v-else-if="feed.avatar.releaseStatus === 'private'" style="margin-left:10px;color:#e6a23c") (Private) template(v-else-if="feed.type === 'ChangeStatus'") span(style="margin-left:10px;color:#a3a3a3") ChangeStatus - template(v-if="feed.status !== feed.previousStatus") - i.x-user-status(:class="statusClass(feed.previousStatus)" style="margin-left:10px;width:20px;height:20px") - span - i.el-icon-right - i.x-user-status(:class="statusClass(feed.status)" style="width:20px;height:20px") - span(v-if="feed.statusDescription !== feed.previousStatusDescription" v-text="feed.statusDescription" style="margin-left:10px") + span(v-if="feed.status !== feed.previousStatus") + i.x-user-status(:class="statusClass(feed.previousStatus)" style="margin-left:10px;width:20px;height:20px") + span + i.el-icon-right + i.x-user-status(:class="statusClass(feed.status)" style="width:20px;height:20px") + span(v-if="feed.statusDescription !== feed.previousStatusDescription" v-text="feed.statusDescription" style="margin-left:10px") template(v-else-if="feed.type === 'ChangeGroup'") span(style="margin-left:10px;color:#a3a3a3") ChangeGroup span(v-text="feed.groupName" style="margin-left:10px")