From e903b27c27c5a3fbb8fd1738c9a94271dfc5b8fe Mon Sep 17 00:00:00 2001 From: Natsumi Date: Tue, 12 Nov 2024 11:33:45 +1300 Subject: [PATCH] User dialog group edit mode --- html/src/app.js | 126 +++++++++++++++++++----- html/src/classes/groups.js | 17 ++++ html/src/localization/en/en.json | 5 +- html/src/mixins/dialogs/userDialog.pug | 130 +++++++++++++++---------- 4 files changed, 203 insertions(+), 75 deletions(-) diff --git a/html/src/app.js b/html/src/app.js index 6ce17755..a161da40 100644 --- a/html/src/app.js +++ b/html/src/app.js @@ -9362,6 +9362,7 @@ speechSynthesis.getVoices(); D.dateFriended = ''; D.unFriended = false; D.dateFriendedInfo = []; + this.userDialogGroupEditMode = false; if (userId === API.currentUser.id) { this.getWorldName(API.currentUser.homeLocation).then( (worldName) => { @@ -16981,9 +16982,37 @@ speechSynthesis.getVoices(); this.saveCurrentUserGroups(); }; + $app.data.inGameGroupOrder = []; + + $app.methods.updateInGameGroupOrder = async function () { + this.inGameGroupOrder = []; + try { + var json = await AppApi.GetVRChatRegistryKey( + `VRC_GROUP_ORDER_${API.currentUser.id}` + ); + this.inGameGroupOrder = JSON.parse(json); + } catch (err) { + console.error(err); + } + }; + + $app.methods.sortGroupsByInGame = function (a, b) { + var aIndex = this.inGameGroupOrder.indexOf(a?.id); + var bIndex = this.inGameGroupOrder.indexOf(b?.id); + if (aIndex === -1 && bIndex === -1) { + return 0; + } + if (aIndex === -1) { + return 1; + } + if (bIndex === -1) { + return -1; + } + return aIndex - bIndex; + }; + $app.methods.sortCurrentUserGroups = async function () { var D = this.userDialog; - var inGameGroupList = []; var sortMethod = function () {}; switch (D.groupSorting.value) { @@ -16994,28 +17023,8 @@ speechSynthesis.getVoices(); 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); - } + sortMethod = this.sortGroupsByInGame; + await this.updateInGameGroupOrder(); break; } @@ -17024,6 +17033,77 @@ speechSynthesis.getVoices(); this.userGroups.remainingGroups.sort(sortMethod); }; + $app.data.userDialogGroupEditMode = false; + $app.data.userDialogGroupEditGroups = []; + + $app.methods.editModeCurrentUserGroups = async function () { + await this.updateInGameGroupOrder(); + this.userDialogGroupEditGroups = Array.from( + API.currentUserGroups.values() + ); + this.userDialogGroupEditGroups.sort(this.sortGroupsByInGame); + this.userDialogGroupEditMode = true; + }; + + $app.methods.exitEditModeCurrentUserGroups = async function () { + this.userDialogGroupEditMode = false; + this.userDialogGroupEditGroups = []; + await this.sortCurrentUserGroups(); + }; + + $app.methods.moveGroupUp = function (groupId) { + var index = this.inGameGroupOrder.indexOf(groupId); + if (index > 0) { + this.inGameGroupOrder.splice(index, 1); + this.inGameGroupOrder.splice(index - 1, 0, groupId); + this.saveInGameGroupOrder(); + } + }; + + $app.methods.moveGroupDown = function (groupId) { + var index = this.inGameGroupOrder.indexOf(groupId); + if (index < this.inGameGroupOrder.length - 1) { + this.inGameGroupOrder.splice(index, 1); + this.inGameGroupOrder.splice(index + 1, 0, groupId); + this.saveInGameGroupOrder(); + } + }; + + $app.methods.moveGroupTop = function (groupId) { + var index = this.inGameGroupOrder.indexOf(groupId); + if (index > 0) { + this.inGameGroupOrder.splice(index, 1); + this.inGameGroupOrder.unshift(groupId); + this.saveInGameGroupOrder(); + } + }; + + $app.methods.moveGroupBottom = function (groupId) { + var index = this.inGameGroupOrder.indexOf(groupId); + if (index < this.inGameGroupOrder.length - 1) { + this.inGameGroupOrder.splice(index, 1); + this.inGameGroupOrder.push(groupId); + this.saveInGameGroupOrder(); + } + }; + + $app.methods.saveInGameGroupOrder = async function () { + this.userDialogGroupEditGroups.sort(this.sortGroupsByInGame); + try { + await AppApi.SetVRChatRegistryKey( + `VRC_GROUP_ORDER_${API.currentUser.id}`, + JSON.stringify(this.inGameGroupOrder), + 3 + ); + } catch (err) { + console.error(err); + this.$message({ + message: 'Failed to save in-game group order', + type: 'error' + }); + } + }; + // #endregion // #region | Gallery diff --git a/html/src/classes/groups.js b/html/src/classes/groups.js index c23e2cfb..165fc6fd 100644 --- a/html/src/classes/groups.js +++ b/html/src/classes/groups.js @@ -2284,6 +2284,23 @@ export default class extends baseClass { }); }, + leaveGroupPrompt(groupId) { + this.$confirm( + 'Are you sure you want to leave this group?', + 'Confirm', + { + confirmButtonText: 'Confirm', + cancelButtonText: 'Cancel', + type: 'info', + callback: (action) => { + if (action === 'confirm') { + this.leaveGroup(groupId); + } + } + } + ); + }, + cancelGroupRequest(groupId) { return API.cancelGroupRequest({ groupId diff --git a/html/src/localization/en/en.json b/html/src/localization/en/en.json index 7c065a0e..30a889ad 100644 --- a/html/src/localization/en/en.json +++ b/html/src/localization/en/en.json @@ -646,6 +646,8 @@ "header": "Groups", "total_count": "Total {count}", "sort_by": "Sort by:", + "edit_mode": "Edit Mode", + "exit_edit_mode": "Exit Edit Mode", "own_groups": "Own Groups", "mutual_groups": "Mutual Groups", "groups": "Groups", @@ -653,7 +655,8 @@ "alphabetical": "Alphabetical", "members": "Members", "in_game": "In-Game Order" - } + }, + "leave_group_tooltip": "Leave Group" }, "worlds": { "header": "Worlds", diff --git a/html/src/mixins/dialogs/userDialog.pug b/html/src/mixins/dialogs/userDialog.pug index 6e75558c..45d58d91 100644 --- a/html/src/mixins/dialogs/userDialog.pug +++ b/html/src/mixins/dialogs/userDialog.pug @@ -299,58 +299,25 @@ mixin userDialog() 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)") + template(v-if="!userDialogGroupEditMode") + 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)") + el-button(v-if="userDialogGroupEditMode" size="small" @click="exitEditModeCurrentUserGroups" icon="el-icon-edit" style="margin-right:5px") {{ $t('dialog.user.groups.exit_edit_mode') }} + el-button(v-else-if="API.currentUser.id === userDialog.id" size="small" @click="editModeCurrentUserGroups" icon="el-icon-edit" style="margin-right:5px") {{ $t('dialog.user.groups.edit_mode') }} 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') }} - span(style="color:#909399;font-size:12px;margin-left:5px") {{ userGroups.ownGroups.length }}/{{ API.cachedConfig?.constants?.GROUPS?.MAX_OWNED }} - .x-friend-list(style="margin-top:10px;margin-bottom:15px;min-height:60px") - .x-friend-item(v-for="group in userGroups.ownGroups" :key="group.id" @click="showGroupDialog(group.id)" class="x-friend-item-border") - .avatar - img(v-lazy="group.iconUrl") - .detail - span.name(v-text="group.name") - span.extra - el-tooltip(v-if="group.isRepresenting" placement="top" :content="$t('dialog.group.members.representing')") - i.el-icon-collection-tag(style="margin-right:5px") - el-tooltip(v-if="group.memberVisibility !== 'visible'" placement="top") - template(#content) - span {{ $t('dialog.group.members.visibility') }} {{ group.memberVisibility }} - i.el-icon-view(style="margin-right:5px") - span ({{ group.memberCount }}) - template(v-if="userGroups.mutualGroups.length > 0") - span(style="font-weight:bold;font-size:16px") {{ $t('dialog.user.groups.mutual_groups') }} - span(style="color:#909399;font-size:12px;margin-left:5px") {{ userGroups.mutualGroups.length }} - .x-friend-list(style="margin-top:10px;margin-bottom:15px;min-height:60px") - .x-friend-item(v-for="group in userGroups.mutualGroups" :key="group.id" @click="showGroupDialog(group.id)" class="x-friend-item-border") - .avatar - img(v-lazy="group.iconUrl") - .detail - span.name(v-text="group.name") - span.extra - el-tooltip(v-if="group.isRepresenting" placement="top" :content="$t('dialog.group.members.representing')") - i.el-icon-collection-tag(style="margin-right:5px") - el-tooltip(v-if="group.memberVisibility !== 'visible'" placement="top") - template(#content) - span {{ $t('dialog.group.members.visibility') }} {{ group.memberVisibility }} - i.el-icon-view(style="margin-right:5px") - span ({{ group.memberCount }}) - template(v-if="userGroups.remainingGroups.length > 0") - span(style="font-weight:bold;font-size:16px") {{ $t('dialog.user.groups.groups') }} - span(style="color:#909399;font-size:12px;margin-left:5px") {{ userGroups.remainingGroups.length }} - template(v-if="API.currentUser.id === userDialog.id") - |/ - template(v-if="API.currentUser.$isVRCPlus") - | {{ API.cachedConfig?.constants?.GROUPS?.MAX_JOINED_PLUS }} - template(v-else) - | {{ API.cachedConfig?.constants?.GROUPS?.MAX_JOINED }} - .x-friend-list(style="margin-top:10px;margin-bottom:15px;min-height:60px") - .x-friend-item(v-for="group in userGroups.remainingGroups" :key="group.id" @click="showGroupDialog(group.id)" class="x-friend-item-border") + template(v-if="userDialogGroupEditMode") + .x-friend-list(style="margin-top:10px;margin-bottom:15px;max-height:unset") + .x-friend-item(v-for="group in userDialogGroupEditGroups" :key="group.id" @click="showGroupDialog(group.id)" class="x-friend-item-border" style="width:100%") + div(@click.stop style="margin-right:3px;margin-left:5px") + el-button(@click="moveGroupUp(group.id)" size="mini" icon="el-icon-arrow-up" style="display:block;padding:7px;font-size:9px;margin-left:0") + el-button(@click="moveGroupDown(group.id)" size="mini" icon="el-icon-arrow-down" style="display:block;padding:7px;font-size:9px;margin-left:0") + div(@click.stop style="margin-right:10px") + el-button(@click="moveGroupTop(group.id)" size="mini" icon="el-icon-top" style="display:block;padding:7px;font-size:9px;margin-left:0") + el-button(@click="moveGroupBottom(group.id)" size="mini" icon="el-icon-bottom" style="display:block;padding:7px;font-size:9px;margin-left:0") .avatar img(v-lazy="group.iconUrl") .detail @@ -363,6 +330,67 @@ mixin userDialog() span {{ $t('dialog.group.members.visibility') }} {{ group.memberVisibility }} i.el-icon-view(style="margin-right:5px") span ({{ group.memberCount }}) + el-tooltip(placement="right" :content="$t('dialog.user.groups.leave_group_tooltip')" :disabled="hideTooltips") + el-button(v-if="shiftHeld" @click.stop="leaveGroupPrompt(group.id)" size="mini" icon="el-icon-close" circle style="color:#f56c6c;margin-left:5px") + el-button(v-else @click.stop="leaveGroupPrompt(group.id)" size="mini" icon="el-icon-delete" circle style="margin-left:5px") + template(v-else) + template(v-if="userGroups.ownGroups.length > 0") + span(style="font-weight:bold;font-size:16px") {{ $t('dialog.user.groups.own_groups') }} + span(style="color:#909399;font-size:12px;margin-left:5px") {{ userGroups.ownGroups.length }}/{{ API.cachedConfig?.constants?.GROUPS?.MAX_OWNED }} + .x-friend-list(style="margin-top:10px;margin-bottom:15px;min-height:60px") + .x-friend-item(v-for="group in userGroups.ownGroups" :key="group.id" @click="showGroupDialog(group.id)" class="x-friend-item-border") + .avatar + img(v-lazy="group.iconUrl") + .detail + span.name(v-text="group.name") + span.extra + el-tooltip(v-if="group.isRepresenting" placement="top" :content="$t('dialog.group.members.representing')") + i.el-icon-collection-tag(style="margin-right:5px") + el-tooltip(v-if="group.memberVisibility !== 'visible'" placement="top") + template(#content) + span {{ $t('dialog.group.members.visibility') }} {{ group.memberVisibility }} + i.el-icon-view(style="margin-right:5px") + span ({{ group.memberCount }}) + template(v-if="userGroups.mutualGroups.length > 0") + span(style="font-weight:bold;font-size:16px") {{ $t('dialog.user.groups.mutual_groups') }} + span(style="color:#909399;font-size:12px;margin-left:5px") {{ userGroups.mutualGroups.length }} + .x-friend-list(style="margin-top:10px;margin-bottom:15px;min-height:60px") + .x-friend-item(v-for="group in userGroups.mutualGroups" :key="group.id" @click="showGroupDialog(group.id)" class="x-friend-item-border") + .avatar + img(v-lazy="group.iconUrl") + .detail + span.name(v-text="group.name") + span.extra + el-tooltip(v-if="group.isRepresenting" placement="top" :content="$t('dialog.group.members.representing')") + i.el-icon-collection-tag(style="margin-right:5px") + el-tooltip(v-if="group.memberVisibility !== 'visible'" placement="top") + template(#content) + span {{ $t('dialog.group.members.visibility') }} {{ group.memberVisibility }} + i.el-icon-view(style="margin-right:5px") + span ({{ group.memberCount }}) + template(v-if="userGroups.remainingGroups.length > 0") + span(style="font-weight:bold;font-size:16px") {{ $t('dialog.user.groups.groups') }} + span(style="color:#909399;font-size:12px;margin-left:5px") {{ userGroups.remainingGroups.length }} + template(v-if="API.currentUser.id === userDialog.id") + |/ + template(v-if="API.currentUser.$isVRCPlus") + | {{ API.cachedConfig?.constants?.GROUPS?.MAX_JOINED_PLUS }} + template(v-else) + | {{ API.cachedConfig?.constants?.GROUPS?.MAX_JOINED }} + .x-friend-list(style="margin-top:10px;margin-bottom:15px;min-height:60px") + .x-friend-item(v-for="group in userGroups.remainingGroups" :key="group.id" @click="showGroupDialog(group.id)" class="x-friend-item-border") + .avatar + img(v-lazy="group.iconUrl") + .detail + span.name(v-text="group.name") + span.extra + el-tooltip(v-if="group.isRepresenting" placement="top" :content="$t('dialog.group.members.representing')") + i.el-icon-collection-tag(style="margin-right:5px") + el-tooltip(v-if="group.memberVisibility !== 'visible'" placement="top") + template(#content) + span {{ $t('dialog.group.members.visibility') }} {{ group.memberVisibility }} + i.el-icon-view(style="margin-right:5px") + span ({{ group.memberCount }}) el-tab-pane(:label="$t('dialog.user.worlds.header')") el-button(type="default" :loading="userDialog.isWorldsLoading" @click="refreshUserDialogWorlds()" size="mini" icon="el-icon-refresh" circle) span(style="margin-left:5px") {{ $t('dialog.user.worlds.total_count', { count: userDialog.worlds.length }) }}