diff --git a/Dotnet/AssetBundleCacher.cs b/Dotnet/AssetBundleCacher.cs index a23b12e0..481793f0 100644 --- a/Dotnet/AssetBundleCacher.cs +++ b/Dotnet/AssetBundleCacher.cs @@ -71,8 +71,8 @@ namespace VRCX return 0; // it's cooked try { - var versionHexString = hexString.Substring(16, 8); // 16..24 - var variantVersionHexString = hexString.Substring(24, 8); // 24..32 + var variantVersionHexString = hexString.Substring(16, 8); // 16..24 + var versionHexString = hexString.Substring(24, 8); // 24..32 var versionBytes = new byte[4]; var variantVersionBytes = new byte[4]; for (var i = 0; i < 4; i++) diff --git a/html/src/app.js b/html/src/app.js index 6b2b818e..a1896da8 100644 --- a/html/src/app.js +++ b/html/src/app.js @@ -4412,6 +4412,7 @@ speechSynthesis.getVoices(); if (typeof ctx === 'undefined') { return; } + var ref = API.cachedUsers.get(id); if (stateInput) { ctx.pendingState = stateInput; if (typeof ref !== 'undefined') { @@ -4426,7 +4427,6 @@ speechSynthesis.getVoices(); ctx.pendingOffline = false; ctx.pendingOfflineTime = ''; } - var ref = API.cachedUsers.get(id); var isVIP = this.localFavoriteFriends.has(id); var location = ''; var $location_at = ''; @@ -4513,6 +4513,9 @@ speechSynthesis.getVoices(); } return; } + if (this.debugFriendState) { + console.log(ctx.name, 'pendingOfflineBegin'); + } ctx.pendingOffline = true; ctx.pendingOfflineTime = Date.now(); // wait 2minutes then check if user came back online @@ -4534,6 +4537,9 @@ speechSynthesis.getVoices(); } return; } + if (this.debugFriendState) { + console.log(ctx.name, 'pendingOfflineEnd'); + } this.updateFriendDelayedCheck(ctx, location, $location_at); }, this.pendingOfflineDelay); } else { @@ -4772,6 +4778,16 @@ speechSynthesis.getVoices(); return 0; }; + var compareByMemberCount = function (a, b) { + if ( + typeof a.memberCount !== 'number' || + typeof b.memberCount !== 'number' + ) { + return 0; + } + return a.memberCount - b.memberCount; + }; + // private var compareByPrivate = function (a, b) { if (typeof a.ref === 'undefined' || typeof b.ref === 'undefined') { @@ -8897,6 +8913,10 @@ speechSynthesis.getVoices(); name: $t('dialog.user.worlds.order.descending'), value: 'descending' }, + groupSorting: { + name: $t('dialog.user.groups.sorting.alphabetical'), + value: 'alphabetical' + }, avatarSorting: 'update', avatarReleaseStatus: 'all', @@ -8949,6 +8969,15 @@ speechSynthesis.getVoices(); await this.refreshUserDialogWorlds(); }; + $app.methods.setUserDialogGroupSorting = async function (sortOrder) { + var D = this.userDialog; + if (D.groupSorting === sortOrder) { + return; + } + D.groupSorting = sortOrder; + await this.sortCurrentUserGroups(); + }; + $app.methods.getFaviconUrl = function (resource) { try { var url = new URL(resource); @@ -9293,10 +9322,7 @@ speechSynthesis.getVoices(); ); this.setUserDialogAvatars(userId); this.userDialogLastAvatar = userId; - if ( - userId === API.currentUser.id && - D.avatars.length === 0 - ) { + if (userId === API.currentUser.id) { this.refreshUserDialogAvatars(); } this.setUserDialogAvatarsRemote(userId); @@ -9935,6 +9961,7 @@ speechSynthesis.getVoices(); $app.methods.setUserDialogAvatarsRemote = async function (userId) { if (this.avatarRemoteDatabase && userId !== API.currentUser.id) { + this.userDialog.isAvatarsLoading = true; var data = await this.lookupAvatars('authorId', userId); var avatars = new Set(); this.userDialogAvatars.forEach((avatar) => { @@ -9943,12 +9970,19 @@ speechSynthesis.getVoices(); if (data && typeof data === 'object') { data.forEach((avatar) => { if (avatar.id && !avatars.has(avatar.id)) { - this.userDialog.avatars.push(avatar); + if (avatar.authorId === userId) { + this.userDialog.avatars.push(avatar); + } else { + console.error( + `Avatar authorId mismatch for ${avatar.id} - ${avatar.name}` + ); + } } }); } this.userDialog.avatarSorting = 'name'; this.userDialog.avatarReleaseStatus = 'all'; + this.userDialog.isAvatarsLoading = false; } this.sortUserDialogAvatars(this.userDialog.avatars); }; @@ -10146,6 +10180,8 @@ speechSynthesis.getVoices(); if (fileId) { D.loading = true; } + D.avatarSorting = 'update'; + D.avatarReleaseStatus = 'all'; var params = { n: 50, offset: 0, @@ -15521,10 +15557,7 @@ speechSynthesis.getVoices(); this.setUserDialogAvatars(userId); if (this.userDialogLastAvatar !== userId) { this.userDialogLastAvatar = userId; - if ( - userId === API.currentUser.id && - this.userDialog.avatars.length === 0 - ) { + if (userId === API.currentUser.id) { this.refreshUserDialogAvatars(); } else { this.setUserDialogAvatarsRemote(userId); @@ -16766,10 +16799,18 @@ speechSynthesis.getVoices(); this.userGroups.remainingGroups.unshift(group); } } - this.userDialog.isGroupsLoading = false; if (userId === API.currentUser.id) { - this.sortCurrentUserGroups(); + this.userDialog.groupSorting = + this.userDialogGroupSortingOptions.inGame; + } else if ( + this.userDialog.groupSorting === + this.userDialogGroupSortingOptions.inGame + ) { + this.userDialog.groupSorting = + this.userDialogGroupSortingOptions.alphabetical; } + await this.sortCurrentUserGroups(); + this.userDialog.isGroupsLoading = false; }; $app.methods.getCurrentUserGroups = async function () { @@ -16783,28 +16824,47 @@ speechSynthesis.getVoices(); this.saveCurrentUserGroups(); }; - $app.methods.sortCurrentUserGroups = function () { - var groupList = []; - var sortGroups = function (a, b) { - var aIndex = groupList.indexOf(a?.id); - var bIndex = groupList.indexOf(b?.id); - if (aIndex === -1 && bIndex === -1) { - return 0; - } - if (aIndex === -1) { - return 1; - } - if (bIndex === -1) { - return -1; - } - return aIndex - bIndex; - }; - AppApi.GetVRChatRegistryKey( - `VRC_GROUP_ORDER_${API.currentUser.id}` - ).then((json) => { - groupList = JSON.parse(json); - this.userGroups.remainingGroups.sort(sortGroups); - }); + $app.methods.sortCurrentUserGroups = async function () { + var D = this.userDialog; + var inGameGroupList = []; + var sortMethod = function () {}; + + switch (D.groupSorting.value) { + case 'alphabetical': + sortMethod = compareByName; + break; + case 'members': + sortMethod = compareByMemberCount; + break; + case 'inGame': + sortMethod = function (a, b) { + var aIndex = inGameGroupList.indexOf(a?.id); + var bIndex = inGameGroupList.indexOf(b?.id); + if (aIndex === -1 && bIndex === -1) { + return 0; + } + if (aIndex === -1) { + return 1; + } + if (bIndex === -1) { + return -1; + } + return aIndex - bIndex; + }; + try { + var json = await AppApi.GetVRChatRegistryKey( + `VRC_GROUP_ORDER_${API.currentUser.id}` + ); + inGameGroupList = JSON.parse(json); + } catch (err) { + console.error(err); + } + break; + } + + this.userGroups.ownGroups.sort(sortMethod); + this.userGroups.mutualGroups.sort(sortMethod); + this.userGroups.remainingGroups.sort(sortMethod); }; // #endregion @@ -20779,6 +20839,21 @@ speechSynthesis.getVoices(); value: 'ascending' } }; + + this.userDialogGroupSortingOptions = { + alphabetical: { + name: $t('dialog.user.groups.sorting.alphabetical'), + value: 'alphabetical' + }, + members: { + name: $t('dialog.user.groups.sorting.members'), + value: 'members' + }, + inGame: { + name: $t('dialog.user.groups.sorting.in_game'), + value: 'inGame' + } + }; }; $app.methods.applyGroupDialogSortingStrings = function () { @@ -20817,6 +20892,9 @@ speechSynthesis.getVoices(); this.userDialogWorldSortingOptions.updated; this.userDialog.worldOrder = this.userDialogWorldOrderOptions.descending; + this.userDialog.groupSorting = + this.userDialogGroupSortingOptions.alphabetical; + this.groupDialog.memberFilter = this.groupDialogFilterOptions.everyone; this.groupDialog.memberSortOrder = this.groupDialogSortingOptions.joinedAtDesc; diff --git a/html/src/localization/en/en.json b/html/src/localization/en/en.json index eeae304f..d64b8d4f 100644 --- a/html/src/localization/en/en.json +++ b/html/src/localization/en/en.json @@ -640,9 +640,15 @@ "groups": { "header": "Groups", "total_count": "Total {count}", + "sort_by": "Sort by:", "own_groups": "Own Groups", "mutual_groups": "Mutual Groups", - "groups": "Groups" + "groups": "Groups", + "sorting": { + "alphabetical": "Alphabetical", + "members": "Members", + "in_game": "In-Game Order" + } }, "worlds": { "header": "Worlds", diff --git a/html/src/mixins/dialogs/userDialog.pug b/html/src/mixins/dialogs/userDialog.pug index 9e04844b..6e75558c 100644 --- a/html/src/mixins/dialogs/userDialog.pug +++ b/html/src/mixins/dialogs/userDialog.pug @@ -298,6 +298,13 @@ mixin userDialog() 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 }) }} + div(style="float:right") + span(style="margin-right:5px") {{ $t('dialog.user.groups.sort_by') }} + el-dropdown(@click.native.stop trigger="click" size="small" style="margin-right:5px" :disabled="userDialog.isGroupsLoading") + el-button(size="mini") + span {{ userDialog.groupSorting.name }} #[i.el-icon-arrow-down.el-icon--right] + el-dropdown-menu(#default="dropdown") + el-dropdown-item(:disabled="item === userDialogGroupSortingOptions.inGame && userDialog.id !== API.currentUser.id" v-for="(item) in userDialogGroupSortingOptions" v-text="item.name" @click.native="setUserDialogGroupSorting(item)") div(v-loading="userDialog.isGroupsLoading" style="margin-top:10px") template(v-if="userGroups.ownGroups.length > 0") span(style="font-weight:bold;font-size:16px") {{ $t('dialog.user.groups.own_groups') }} @@ -396,8 +403,8 @@ mixin userDialog() span.name(v-text="world.name") span.extra(v-if="world.occupants") ({{ world.occupants }}) el-tab-pane(:label="$t('dialog.user.avatars.header')") - template(v-if="userDialog.ref.id === API.currentUser.id") - el-button(type="default" :loading="userDialog.isAvatarsLoading" @click="refreshUserDialogAvatars()" size="mini" icon="el-icon-refresh" circle) + el-button(v-if="userDialog.ref.id === API.currentUser.id" type="default" :loading="userDialog.isAvatarsLoading" @click="refreshUserDialogAvatars()" size="mini" icon="el-icon-refresh" circle) + el-button(v-else type="default" :loading="userDialog.isAvatarsLoading" @click="setUserDialogAvatarsRemote(userDialog.id)" size="mini" icon="el-icon-refresh" circle) span(style="margin-left:5px") {{ $t('dialog.user.avatars.total_count', { count: userDialogAvatars.length }) }} el-radio-group(v-if="userDialog.ref.id === API.currentUser.id" v-model="userDialog.avatarSorting" size="mini" style="margin-left:30px;margin-right:30px" @change="changeUserDialogAvatarSorting") el-radio(label="name") {{ $t('dialog.user.avatars.sort_by_name') }} diff --git a/html/src/mixins/tabs/friendLog.pug b/html/src/mixins/tabs/friendLog.pug index 9b3cdc60..47e62eab 100644 --- a/html/src/mixins/tabs/friendLog.pug +++ b/html/src/mixins/tabs/friendLog.pug @@ -6,7 +6,7 @@ mixin friendLogTab() el-select(v-model="friendLogTable.filters[0].value" @change="saveTableFilters" multiple clearable collapse-tags style="flex:1" :placeholder="$t('view.friend_log.filter_placeholder')") el-option(v-once v-for="type in ['Friend', 'Unfriend', 'FriendRequest', 'CancelFriendRequest', 'DisplayName', 'TrustLevel']" :key="type" :label="type" :value="type") el-input(v-model="friendLogTable.filters[1].value" :placeholder="$t('view.friend_log.search_placeholder')" style="flex:none;width:150px;margin-left:10px") - el-table-column(:label="$t('table.friendLog.date')" prop="created_at" sortable="custom" width="150") + el-table-column(:label="$t('table.friendLog.date')" prop="created_at" sortable="custom" width="200") template(v-once #default="scope") span {{ scope.row.created_at | formatDate('long') }} el-table-column(:label="$t('table.friendLog.type')" prop="type" width="150")