diff --git a/ProcessMonitor.cs b/ProcessMonitor.cs index 4547cc53..d0207f0c 100644 --- a/ProcessMonitor.cs +++ b/ProcessMonitor.cs @@ -68,8 +68,6 @@ namespace VRCX foreach (var keyValuePair in monitoredProcesses) { var monitoredProcess = keyValuePair.Value; - var process = monitoredProcess.Process; - var name = monitoredProcess.ProcessName; if (monitoredProcess.IsRunning) { @@ -93,11 +91,11 @@ namespace VRCX foreach (var monitoredProcess in processesNeedingUpdate) { var process = processes.FirstOrDefault(p => string.Equals(p.ProcessName, monitoredProcess.ProcessName, StringComparison.OrdinalIgnoreCase)); - if (process != null) - { - monitoredProcess.ProcessStarted(process); - ProcessStarted?.Invoke(monitoredProcess); - } + if (process == null) + continue; + + monitoredProcess.ProcessStarted(process); + ProcessStarted?.Invoke(monitoredProcess); } } @@ -110,17 +108,14 @@ namespace VRCX public bool IsProcessRunning(string processName, bool ensureCheck = false) { processName = processName.ToLower(); - if (monitoredProcesses.ContainsKey(processName)) - { - var process = monitoredProcesses[processName]; + if (!monitoredProcesses.TryGetValue(processName, out var process)) + return false; - if (ensureCheck && process.Process == null) - return Process.GetProcessesByName(processName).FirstOrDefault() != null; + if (ensureCheck && process.Process == null) + return Process.GetProcessesByName(processName).FirstOrDefault() != null; - return process.IsRunning; - } + return process.IsRunning; - return false; } /// @@ -129,10 +124,11 @@ namespace VRCX /// public void AddProcess(Process process) { - if (monitoredProcesses.ContainsKey(process.ProcessName.ToLower())) + var processName = process.ProcessName.ToLower(); + if (monitoredProcesses.ContainsKey(processName)) return; - monitoredProcesses.Add(process.ProcessName.ToLower(), new MonitoredProcess(process)); + monitoredProcesses.Add(processName, new MonitoredProcess(process)); } /// @@ -141,10 +137,9 @@ namespace VRCX /// public void AddProcess(string processName) { - if (monitoredProcesses.ContainsKey(processName.ToLower())) - { + processName = processName.ToLower(); + if (monitoredProcesses.ContainsKey(processName)) return; - } monitoredProcesses.Add(processName, new MonitoredProcess(processName)); } @@ -155,10 +150,8 @@ namespace VRCX /// public void RemoveProcess(string processName) { - if (monitoredProcesses.ContainsKey(processName.ToLower())) - { - monitoredProcesses.Remove(processName); - } + processName = processName.ToLower(); + monitoredProcesses.Remove(processName); } } @@ -169,7 +162,7 @@ namespace VRCX Process = process; ProcessName = process.ProcessName.ToLower(); - if (process != null && !WinApi.HasProcessExited(process.Id)) + if (!WinApi.HasProcessExited(process.Id)) IsRunning = true; } @@ -182,6 +175,7 @@ namespace VRCX public Process Process { get; private set; } public string ProcessName { get; private set; } public bool IsRunning { get; private set; } + public DateTime LastExitTime { get; private set; } public bool HasName(string processName) { @@ -193,10 +187,15 @@ namespace VRCX IsRunning = false; Process?.Dispose(); Process = null; + LastExitTime = DateTime.Now; } public void ProcessStarted(Process process) { + // check last exit time to prevent status flapping + if (LastExitTime != DateTime.MinValue && DateTime.Now - LastExitTime < TimeSpan.FromSeconds(5)) + return; + Process = process; ProcessName = process.ProcessName.ToLower(); IsRunning = true; diff --git a/VRCX.csproj b/VRCX.csproj index 22156f86..842d99e8 100644 --- a/VRCX.csproj +++ b/VRCX.csproj @@ -189,13 +189,13 @@ - 116.0.150 + 116.0.230 - 116.0.150 + 116.0.230 - 116.0.150 + 116.0.230 1.2.1.24 @@ -207,7 +207,7 @@ 13.0.3 - 5.2.3 + 5.2.4 4.2.0 diff --git a/WorldDBManager.cs b/WorldDBManager.cs index 7cd327a2..15c2c0b5 100644 --- a/WorldDBManager.cs +++ b/WorldDBManager.cs @@ -703,9 +703,9 @@ namespace VRCX public void Stop() { - listener.Stop(); - listener.Close(); - worldDB.Close(); + listener?.Stop(); + listener?.Close(); + worldDB?.Close(); } } } \ No newline at end of file diff --git a/html/src/app.js b/html/src/app.js index 68dbd4e8..be1ed04f 100644 --- a/html/src/app.js +++ b/html/src/app.js @@ -4899,6 +4899,25 @@ speechSynthesis.getVoices(); $app.instanceQueueReady(instanceId); break; + case 'content-refresh': + var contentType = content.contentType; + if (contentType === 'icon') { + if ($app.galleryDialogVisible) { + $app.refreshVRCPlusIconsTable(); + } + } else if (contentType === 'gallery') { + if ($app.galleryDialogVisible) { + $app.refreshGalleryTable(); + } + } else if (contentType === 'emoji') { + if ($app.galleryDialogVisible) { + $app.refreshEmojiTable(); + } + } else { + console.log('Unknown content-refresh', content); + } + break; + default: console.log('Unknown pipeline type', args.json); } @@ -15311,6 +15330,7 @@ speechSynthesis.getVoices(); return true; } else if (/^[A-Za-z0-9]{3,6}\.[0-9]{4}$/g.test(input)) { this.showGroupDialogShortCode(input); + return true; } else if ( input.substring(0, 4) === 'usr_' || /^[A-Za-z0-9]{10}$/g.test(input) @@ -17268,8 +17288,6 @@ speechSynthesis.getVoices(); this.showPreviousInstancesUserDialog(D.ref); } else if (command === 'Manage Gallery') { this.showGalleryDialog(); - } else if (command === 'Copy User') { - this.copyUser(D.id); } else if (command === 'Invite To Group') { this.showInviteGroupDialog('', D.id); } else if (command === 'Hide Avatar') { @@ -19531,7 +19549,15 @@ speechSynthesis.getVoices(); this.copyToClipboard(worldName); }; - $app.methods.copyUser = function (userId) { + $app.methods.copyUserId = function (userId) { + this.$message({ + message: 'User ID copied to clipboard', + type: 'success' + }); + this.copyToClipboard(userId); + }; + + $app.methods.copyUserURL = function (userId) { this.$message({ message: 'User URL copied to clipboard', type: 'success' @@ -19539,6 +19565,14 @@ speechSynthesis.getVoices(); this.copyToClipboard(`https://vrchat.com/home/user/${userId}`); }; + $app.methods.copyUserDisplayName = function (displayName) { + this.$message({ + message: 'User DisplayName copied to clipboard', + type: 'success' + }); + this.copyToClipboard(displayName); + }; + $app.methods.copyGroupId = function (groupId) { this.$message({ message: 'Group ID copied to clipboard', @@ -22728,7 +22762,7 @@ speechSynthesis.getVoices(); return; } // wait a bit for SteamVR to potentially close before deciding to relaunch - var restartDelay = 5000; + var restartDelay = 8000; if (this.isGameNoVR) { // wait for game to close before relaunching restartDelay = 2000; diff --git a/html/src/index.pug b/html/src/index.pug index 7295054c..49975ea4 100644 --- a/html/src/index.pug +++ b/html/src/index.pug @@ -270,7 +270,6 @@ html el-button(:type="(userDialog.incomingRequest || userDialog.outgoingRequest) ? 'success' : (userDialog.isBlock || userDialog.isMute) ? 'danger' : 'default'" icon="el-icon-more" circle style="margin-left:5px") el-dropdown-menu(#default="dropdown") el-dropdown-item(icon="el-icon-refresh" command="Refresh") {{ $t('dialog.user.actions.refresh') }} - el-dropdown-item(icon="el-icon-s-order" command="Copy User") {{ $t('dialog.user.actions.copy_url') }} template(v-if="userDialog.ref.id === API.currentUser.id") el-dropdown-item(icon="el-icon-picture-outline" command="Manage Gallery" divided) {{ $t('dialog.user.actions.manage_gallery_icon') }} el-dropdown-item(icon="el-icon-s-custom" command="Show Avatar Author") {{ $t('dialog.user.actions.show_avatar_author') }} @@ -366,7 +365,7 @@ html .x-friend-item(style="width:100%;cursor:default") .detail span.name {{ $t('dialog.user.info.represented_group') }} - .extra(v-if="userDialog.representedGroup.isRepresenting") + .extra(v-if="userDialog.representedGroup?.isRepresenting") div(style="display:inline-block;flex:none;margin-right:5px") el-popover(placement="right" width="500px" trigger="click") img.x-link(slot="reference" v-lazy="userDialog.representedGroup.iconUrl" style="flex:none;width:60px;height:60px;border-radius:4px;object-fit:cover") @@ -458,6 +457,17 @@ html span.extra span(v-text="userDialog.$homeLocationName") el-button(@click.stop="resetHome()" size="mini" icon="el-icon-delete" circle style="margin-left:5px") + .x-friend-item(style="width:100%;cursor:default") + .detail + span.name {{ $t('dialog.user.info.id') }} + span.extra {{ userDialog.id }} + el-tooltip(placement="top" :content="$t('dialog.user.info.id_tooltip')" :disabled="hideTooltips") + el-dropdown(trigger="click" @click.native.stop size="mini" style="margin-left:5px") + el-button(type="default" icon="el-icon-s-order" size="mini" circle) + el-dropdown-menu(#default="dropdown") + el-dropdown-item(@click.native="copyUserId(userDialog.id)") {{ $t('dialog.user.info.copy_id') }} + el-dropdown-item(@click.native="copyUserURL(userDialog.id)") {{ $t('dialog.user.info.copy_url') }} + el-dropdown-item(@click.native="copyUserDisplayName(userDialog.ref.displayName)") {{ $t('dialog.user.info.copy_display_name') }} el-tab-pane(:label="$t('dialog.user.groups.header')") el-button(type="default" :loading="userDialog.isGroupsLoading" @click="getUserGroups(userDialog.id)" size="mini" icon="el-icon-refresh" circle) span(style="margin-left:5px") {{ $t('dialog.user.groups.total_count', { count: userGroups.groups.length }) }} diff --git a/html/src/localization/strings/en.json b/html/src/localization/strings/en.json index 82fb00a8..c7b9360b 100644 --- a/html/src/localization/strings/en.json +++ b/html/src/localization/strings/en.json @@ -104,6 +104,7 @@ "clear_tooltip": "Clear", "delete_tooltip": "Delete", "unavailable_tooltip": "Unavailable", + "private": "Private", "sort_by": "Sort By" }, "friend_log": { @@ -528,7 +529,6 @@ "favorite_tooltip": "Add to favorites", "unfavorite_tooltip": "Remove from favorites", "refresh": "Refresh", - "copy_url": "Copy User URL", "invite": "Invite", "invite_with_message": "Invite With Message", "request_invite": "Request Invite", @@ -590,6 +590,11 @@ "avatar_cloning_allow": "Allowed", "avatar_cloning_deny": "Deny", "home_location": "Home Location", + "id": "User ID", + "id_tooltip": "Copy to clipboard", + "copy_id": "Copy ID", + "copy_url": "Copy URL", + "copy_display_name": "Copy DisplayName", "accuracy_notice": "Info from local database may not be accurate", "instance_full": "full" }, diff --git a/html/src/mixins/tabs/favorites.pug b/html/src/mixins/tabs/favorites.pug index 52909c57..cf633bdc 100644 --- a/html/src/mixins/tabs/favorites.pug +++ b/html/src/mixins/tabs/favorites.pug @@ -47,7 +47,9 @@ mixin favoritesTab() el-tooltip(placement="right" :content="$t('view.favorite.unfavorite_tooltip')" :disabled="hideTooltips") el-button(@click.stop="deleteFavorite(favorite.id)" size="mini" icon="el-icon-delete" circle style="margin-left:5px") template(v-else) - span(v-text="favorite.name || favorite.id") + .avatar + .detail + span(v-text="favorite.name || favorite.id") el-button(type="text" icon="el-icon-close" size="mini" @click.stop="deleteFavorite(favorite.id)" style="margin-left:5px") el-tab-pane(:label="$t('view.favorite.worlds.header')") el-collapse(v-if="$refs.menu && $refs.menu.activeIndex === 'favorite' && $refs.favoriteTabRef && $refs.favoriteTabRef.currentName === '1'" style="border:0") @@ -75,30 +77,34 @@ mixin favoritesTab() div(style="display:inline-block;width:300px;margin-right:15px" v-for="favorite in favoriteWorlds" v-if="favorite.groupKey === group.key" :key="favorite.id" @click="showWorldDialog(favorite.id)") .x-friend-item template(v-if="favorite.ref") - .avatar - img(v-lazy="favorite.ref.thumbnailImageUrl") - .detail - span.name(v-text="favorite.ref.name") - span.extra(v-if="favorite.ref.occupants") {{ favorite.ref.authorName }} ({{ favorite.ref.occupants }}) - span.extra(v-else v-text="favorite.ref.authorName") + .avatar + img(v-lazy="favorite.ref.thumbnailImageUrl") + .detail + span.name(v-text="favorite.ref.name") + span.extra(v-if="favorite.ref.occupants") {{ favorite.ref.authorName }} ({{ favorite.ref.occupants }}) + span.extra(v-else v-text="favorite.ref.authorName") template(v-if="bulkUnfavoriteMode") el-button(type="text" size="mini" @click.stop style="margin-left:5px") el-checkbox(v-model="favorite.$selected") template(v-else-if="favorite.ref") - el-tooltip(v-if="favorite.deleted" placement="left" :content="$t('view.favorite.unavailable_tooltip')") - i.el-icon-warning(style="color:#f56c6c;margin-left:5px") - el-tooltip(placement="left" :content="$t('view.favorite.move_tooltip')" :disabled="hideTooltips") - el-dropdown(trigger="click" @click.native.stop size="mini" style="margin-left:5px") - el-button(type="default" icon="el-icon-back" size="mini" circle) - el-dropdown-menu(#default="dropdown") - template(v-if="groupAPI.name !== group.name" v-for="groupAPI in API.favoriteWorldGroups" :key="groupAPI.name") - el-dropdown-item(style="display:block;margin:10px 0" @click.native="moveFavorite(favorite.ref, groupAPI, 'world')" :disabled="groupAPI.count >= groupAPI.capacity") {{ groupAPI.displayName }} ({{ groupAPI.count }} / {{ groupAPI.capacity }}) - el-tooltip(placement="right" :content="$t('view.favorite.unfavorite_tooltip')" :disabled="hideTooltips") - el-button(@click.stop="deleteFavorite(favorite.id)" size="mini" icon="el-icon-delete" circle style="margin-left:5px") - template(v-else) - span(v-text="favorite.name || favorite.id") el-tooltip(v-if="favorite.deleted" placement="left" :content="$t('view.favorite.unavailable_tooltip')") i.el-icon-warning(style="color:#f56c6c;margin-left:5px") + el-tooltip(v-if="favorite.ref.releaseStatus === 'private'" placement="left" :content="$t('view.favorite.private')") + i.el-icon-warning(style="color:#e6a23c;margin-left:5px") + el-tooltip(placement="left" :content="$t('view.favorite.move_tooltip')" :disabled="hideTooltips") + el-dropdown(trigger="click" @click.native.stop size="mini" style="margin-left:5px") + el-button(type="default" icon="el-icon-back" size="mini" circle) + el-dropdown-menu(#default="dropdown") + template(v-if="groupAPI.name !== group.name" v-for="groupAPI in API.favoriteWorldGroups" :key="groupAPI.name") + el-dropdown-item(style="display:block;margin:10px 0" @click.native="moveFavorite(favorite.ref, groupAPI, 'world')" :disabled="groupAPI.count >= groupAPI.capacity") {{ groupAPI.displayName }} ({{ groupAPI.count }} / {{ groupAPI.capacity }}) + el-tooltip(placement="right" :content="$t('view.favorite.unfavorite_tooltip')" :disabled="hideTooltips") + el-button(@click.stop="deleteFavorite(favorite.id)" size="mini" icon="el-icon-delete" circle style="margin-left:5px") + template(v-else) + .avatar + .detail + span(v-text="favorite.name || favorite.id") + el-tooltip(v-if="favorite.deleted" placement="left" :content="$t('view.favorite.unavailable_tooltip')") + i.el-icon-warning(style="color:#f56c6c;margin-left:5px") el-button(type="text" icon="el-icon-close" size="mini" @click.stop="deleteFavorite(favorite.id)" style="margin-left:5px") span(style="display:block;margin-top:20px") {{ $t('view.favorite.worlds.local_favorites') }} el-button(size="small" @click="promptNewLocalWorldFavoriteGroup" style="display:block;margin-top:10px") {{ $t('view.favorite.worlds.new_group') }} @@ -114,16 +120,18 @@ mixin favoritesTab() div(style="display:inline-block;width:300px;margin-right:15px" v-for="favorite in localWorldFavorites[group]" :key="favorite.id" @click="showWorldDialog(favorite.id)") .x-friend-item template(v-if="favorite.name") - .avatar - img(v-lazy="favorite.thumbnailImageUrl") - .detail - span.name(v-text="favorite.name") - span.extra(v-if="favorite.occupants") {{ favorite.authorName }} ({{ favorite.occupants }}) - span.extra(v-else v-text="favorite.authorName") - el-tooltip(placement="right" :content="$t('view.favorite.unfavorite_tooltip')" :disabled="hideTooltips") - el-button(@click.stop="removeLocalWorldFavorite(favorite.id, group)" size="mini" icon="el-icon-delete" circle style="margin-left:5px") + .avatar + img(v-lazy="favorite.thumbnailImageUrl") + .detail + span.name(v-text="favorite.name") + span.extra(v-if="favorite.occupants") {{ favorite.authorName }} ({{ favorite.occupants }}) + span.extra(v-else v-text="favorite.authorName") + el-tooltip(placement="right" :content="$t('view.favorite.unfavorite_tooltip')" :disabled="hideTooltips") + el-button(@click.stop="removeLocalWorldFavorite(favorite.id, group)" size="mini" icon="el-icon-delete" circle style="margin-left:5px") template(v-else) - span(v-text="favorite.id") + .avatar + .detail + span(v-text="favorite.id") el-button(type="text" icon="el-icon-close" size="mini" @click.stop="removeLocalWorldFavorite(favorite.id, group)" style="margin-left:5px") el-tab-pane(:label="$t('view.favorite.avatars.header')") el-collapse(v-if="$refs.menu && $refs.menu.activeIndex === 'favorite' && $refs.favoriteTabRef && $refs.favoriteTabRef.currentName === '2'" style="border:0") @@ -155,6 +163,8 @@ mixin favoritesTab() template(v-else-if="favorite.ref") el-tooltip(v-if="favorite.deleted" placement="left" :content="$t('view.favorite.unavailable_tooltip')") i.el-icon-warning(style="color:#f56c6c;margin-left:5px") + el-tooltip(v-if="favorite.ref.releaseStatus === 'private'" placement="left" :content="$t('view.favorite.private')") + i.el-icon-warning(style="color:#e6a23c;margin-left:5px") el-tooltip(placement="left" :content="$t('view.favorite.move_tooltip')" :disabled="hideTooltips") el-dropdown(trigger="click" @click.native.stop size="mini" style="margin-left:5px") el-button(type="default" icon="el-icon-back" size="mini" circle) @@ -164,6 +174,7 @@ mixin favoritesTab() el-tooltip(placement="right" :content="$t('view.favorite.unfavorite_tooltip')" :disabled="hideTooltips") el-button(@click.stop="deleteFavorite(favorite.id)" size="mini" icon="el-icon-delete" circle style="margin-left:5px") template(v-else) + .avatar .detail span.name(v-text="favorite.name || favorite.id") el-button(type="text" icon="el-icon-close" size="mini" @click.stop="deleteFavorite(favorite.id)" style="margin-left:5px")