refactor: resolve lag issues when opening the world dialog in the favorites worlds tab (#1156) (#1168)

* refactor: resolve lag issues when opening the world dialog in the favorite world tab (#1156)

* fix
This commit is contained in:
pa
2025-03-05 16:48:26 +09:00
committed by GitHub
parent 747a7ca683
commit 1fef4dee57
15 changed files with 1682 additions and 1051 deletions

View File

@@ -17,8 +17,7 @@
<i
slot="reference"
class="el-icon-info"
style="margin-left: 5px; font-size: 12px; opacity: 0.7"
></i>
style="margin-left: 5px; font-size: 12px; opacity: 0.7"></i>
</el-popover>
</div>
@@ -36,8 +35,7 @@
v-model.lazy="barWidth"
:max="50"
:min="1"
@change="changeBarWidth"
></el-slider>
@change="changeBarWidth"></el-slider>
</div>
</div>
<div>
@@ -54,8 +52,7 @@
<span>{{ $t('view.charts.instance_activity.settings.show_no_friend_instance') }}</span>
<el-switch
v-model="isNoFriendInstanceVisible"
@change="changeIsNoFriendInstanceVisible"
>
@change="changeIsNoFriendInstanceVisible">
</el-switch>
</div>
</div>
@@ -68,8 +65,7 @@
<el-button
icon="el-icon-arrow-left"
:disabled="isPrevDayBtnDisabled"
@click="changeSelectedDateFromBtn(false)"
></el-button>
@click="changeSelectedDateFromBtn(false)"></el-button>
</el-tooltip>
<el-tooltip :content="$t('view.charts.instance_activity.next_day')" placement="top">
<el-button :disabled="isNextDayBtnDisabled" @click="changeSelectedDateFromBtn(true)"
@@ -85,8 +81,7 @@
:picker-options="{
disabledDate: (time) => getDatePickerDisabledDate(time)
}"
@change="reloadData"
></el-date-picker>
@change="reloadData"></el-date-picker>
</div>
</div>
<div style="position: relative">
@@ -115,8 +110,7 @@
:is-dark-mode="isDarkMode"
:dt-hour12="dtHour12"
:bar-width="barWidth"
@open-previous-instance-info-dialog="$emit('open-previous-instance-info-dialog', $event)"
/>
@open-previous-instance-info-dialog="$emit('open-previous-instance-info-dialog', $event)" />
</div>
</template>
@@ -292,46 +286,79 @@
initEcharts() {
const chartsHeight = this.activityData.length * (this.barWidth + 10) + 200;
const chartDom = this.$refs.activityChartRef;
if (!this.echartsInstance) {
const afterInit = () => {
this.echartsInstance.resize({
height: chartsHeight,
animation: {
duration: 300
}
});
const handleClickYAxisLabel = (params) => {
const detailDataIdx = this.filteredActivityDetailData.findIndex((arr) => {
const sameLocation = arr[0]?.location === this.activityData[params?.dataIndex]?.location;
const sameJoinTime = arr
.find((item) => item.user_id === this.API.currentUser.id)
?.joinTime.isSame(this.activityData[params?.dataIndex].joinTime);
return sameLocation && sameJoinTime;
});
if (detailDataIdx === -1) {
// no detail chart down below, it's hidden, so can't find instance data index
console.error(
"handleClickYAxisLabel failed, likely current user wasn't in this instance.",
params
);
} else {
this.$refs.activityDetailChartRef[detailDataIdx].$el.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
};
const options = this.activityData.length ? this.getNewOption() : {};
this.echartsInstance.setOption(options, { lazyUpdate: true });
this.echartsInstance.on('click', 'yAxis', handleClickYAxisLabel);
this.isLoading = false;
};
const initEchartsInstance = () => {
this.echartsInstance = this.echarts.init(chartDom, `${this.isDarkMode ? 'dark' : null}`, {
height: chartsHeight
});
this.resizeObserver.observe(chartDom);
}
this.echartsInstance.resize({
height: chartsHeight,
animation: {
duration: 300
}
});
const handleClickYAxisLabel = (params) => {
const detailDataIdx = this.filteredActivityDetailData.findIndex((arr) => {
const sameLocation = arr[0]?.location === this.activityData[params?.dataIndex]?.location;
const sameJoinTime = arr
.find((item) => item.user_id === this.API.currentUser.id)
?.joinTime.isSame(this.activityData[params?.dataIndex].joinTime);
return sameLocation && sameJoinTime;
});
if (detailDataIdx === -1) {
console.error(
"handleClickYAxisLabel failed, likely current user wasn't in this instance",
params
);
} else {
this.$refs.activityDetailChartRef[detailDataIdx].$el.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
};
const options = this.activityData.length ? this.getNewOption() : {};
const loadEchartsWithTimeout = () => {
const timeout = 5000;
let time = 0;
const timer = setInterval(() => {
if (this.echarts) {
initEchartsInstance();
afterInit();
clearInterval(timer);
return;
}
time += 100;
if (time >= timeout) {
clearInterval(timer);
console.error('echarts init timeout');
}
}, 100);
};
this.echartsInstance.setOption(options, { lazyUpdate: true });
this.echartsInstance.on('click', 'yAxis', handleClickYAxisLabel);
this.isLoading = false;
if (!this.echartsInstance) {
if (!this.echarts) {
loadEchartsWithTimeout();
} else {
initEchartsInstance();
afterInit();
}
} else {
afterInit();
}
},
getNewOption() {
const getTooltip = (params) => {

View File

@@ -21,7 +21,13 @@
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: 'Location',
inject: ['API'],
inject: {
// prevent randomly error
// not good idea, it's temporary
API: { default: window.API },
getWorldName: { default: window.$app?.getWorldName },
getGroupName: { default: window.$app?.getGroupName }
},
props: {
location: String,
traveling: String,
@@ -82,8 +88,7 @@
} else if (L.worldId) {
var ref = this.API.cachedWorlds.get(L.worldId);
if (typeof ref === 'undefined') {
// TODO: USE props
$app.getWorldName(L.worldId).then((worldName) => {
this.getWorldName(L.worldId).then((worldName) => {
if (L.tag === instanceId) {
if (L.instanceId) {
this.text = `${worldName} #${L.instanceName} ${L.accessTypeName}`;
@@ -102,8 +107,7 @@
this.groupName = this.grouphint;
} else if (L.groupId) {
this.groupName = L.groupId;
// TODO: USE props
$app.getGroupName(instanceId).then((groupName) => {
this.getGroupName(instanceId).then((groupName) => {
if (L.tag === instanceId) {
this.groupName = groupName;
}
@@ -153,5 +157,3 @@
}
};
</script>
<style scoped></style>

View File

@@ -0,0 +1,222 @@
<template>
<div @click="$emit('click')" :style="{ display: 'inline-block', width: '300px', marginRight: '15px' }">
<div class="x-friend-item">
<template v-if="isLocalFavorite ? favorite.name : favorite.ref">
<div class="avatar">
<img v-lazy="localFavFakeRef.thumbnailImageUrl" />
</div>
<div class="detail">
<span class="name">{{ localFavFakeRef.name }}</span>
<span v-if="localFavFakeRef.occupants" class="extra"
>{{ localFavFakeRef.authorName }} ({{ localFavFakeRef.occupants }})</span
>
<span v-else class="extra">{{ localFavFakeRef.authorName }}</span>
</div>
<template v-if="editFavoritesMode">
<el-dropdown trigger="click" size="mini" style="margin-left: 5px" @click.native.stop>
<el-tooltip
placement="left"
:content="$t(localFavFakeRef ? 'view.favorite.copy_tooltip' : 'view.favorite.move_tooltip')"
:disabled="hideTooltips">
<el-button type="default" icon="el-icon-back" size="mini" circle></el-button>
</el-tooltip>
<el-dropdown-menu slot="dropdown">
<template v-for="groupAPI in API.favoriteWorldGroups">
<el-dropdown-item
v-if="isLocalFavorite || groupAPI.name !== group.name"
:key="groupAPI.name"
style="display: block; margin: 10px 0"
:disabled="groupAPI.count >= groupAPI.capacity"
@click.native="handleDropdownItemClick(groupAPI)">
{{ groupAPI.displayName }} ({{ groupAPI.count }} / {{ groupAPI.capacity }})
</el-dropdown-item>
</template>
</el-dropdown-menu>
<el-button v-if="!isLocalFavorite" type="text" size="mini" @click.stop style="margin-left: 5px">
<el-checkbox v-model="isSelected"></el-checkbox>
</el-button>
</el-dropdown>
</template>
<template v-else>
<el-tooltip
v-if="!isLocalFavorite && favorite.deleted"
placement="left"
:content="$t('view.favorite.unavailable_tooltip')">
<i class="el-icon-warning" style="color: #f56c6c; margin-left: 5px"></i>
</el-tooltip>
<el-tooltip
v-if="!isLocalFavorite && favorite.ref.releaseStatus === 'private'"
placement="left"
:content="$t('view.favorite.private')">
<i class="el-icon-warning" style="color: #e6a23c; margin-left: 5px"></i>
</el-tooltip>
<el-tooltip
placement="left"
:content="$t('view.favorite.self_invite_tooltip')"
:disabled="hideTooltips">
<el-button
size="mini"
icon="el-icon-message"
style="margin-left: 5px"
@click.stop="$emit('new-instance-self-invite', favorite.id)"
circle></el-button>
</el-tooltip>
<el-tooltip
v-if="!isLocalFavorite"
placement="right"
:content="$t('view.favorite.unfavorite_tooltip')"
:disabled="hideTooltips">
<el-button
v-if="shiftHeld"
size="mini"
icon="el-icon-close"
circle
style="color: #f56c6c; margin-left: 5px"
@click.stop="deleteFavorite(favorite.id)"></el-button>
<el-button
v-else
icon="el-icon-star-on"
size="mini"
circle
style="margin-left: 5px"
type="default"
@click.stop="showFavoriteDialog(favorite.id)"></el-button>
</el-tooltip>
</template>
<el-tooltip
v-if="isLocalFavorite"
placement="right"
:content="$t('view.favorite.unfavorite_tooltip')"
:disabled="hideTooltips">
<el-button
v-if="shiftHeld"
size="mini"
icon="el-icon-close"
circle
style="color: #f56c6c; margin-left: 5px"
@click.stop="$emit('remove-local-world-favorite', favorite.id, group)"></el-button>
<el-button
v-else
icon="el-icon-star-on"
size="mini"
circle
style="margin-left: 5px"
type="default"
@click.stop="showFavoriteDialog(favorite.id)"></el-button>
</el-tooltip>
</template>
<template v-else>
<div class="avatar"></div>
<div class="detail">
<span>{{ favorite.name || favorite.id }}</span>
<el-tooltip
v-if="!isLocalFavorite && favorite.deleted"
placement="left"
:content="$t('view.favorite.unavailable_tooltip')">
<i class="el-icon-warning" style="color: #f56c6c; margin-left: 5px"></i>
</el-tooltip>
<el-button
type="text"
icon="el-icon-close"
size="mini"
style="margin-left: 5px"
@click.stop="handleDeleteFavorite"></el-button>
</div>
</template>
</div>
</div>
</template>
<script>
export default {
name: 'FavoritesWorldItem',
inject: ['API'],
props: {
group: [Object, String],
favorite: Object,
editFavoritesMode: Boolean,
hideTooltips: Boolean,
shiftHeld: Boolean,
isLocalFavorite: { type: Boolean, required: false }
},
computed: {
isSelected: {
get() {
return this.favorite.$selected;
},
set(value) {
this.$emit('handle-select', value);
}
},
localFavFakeRef() {
// local favorite no "ref" property
return this.isLocalFavorite ? this.favorite : this.favorite.ref;
}
},
methods: {
handleDropdownItemClick(groupAPI) {
if (this.isLocalFavorite) {
this.addFavoriteWorld(this.localFavFakeRef, groupAPI, true);
} else {
this.moveFavorite(this.localFavFakeRef, groupAPI, 'world');
}
},
handleDeleteFavorite() {
if (this.isLocalFavorite) {
this.$emit('remove-local-world-favorite', this.favorite.id, this.group);
} else {
this.deleteFavorite(this.favorite.id);
}
},
moveFavorite(ref, group, type) {
this.API.deleteFavorite({
objectId: ref.id
}).then(() => {
this.API.addFavorite({
type,
favoriteId: ref.id,
tags: group.name
});
});
},
deleteFavorite(objectId) {
this.API.deleteFavorite({
objectId
});
// FIXME: 메시지 수정
// this.$confirm('Continue? Delete Favorite', 'Confirm', {
// confirmButtonText: 'Confirm',
// cancelButtonText: 'Cancel',
// type: 'info',
// callback: (action) => {
// if (action === 'confirm') {
// API.deleteFavorite({
// objectId
// });
// }
// }
// });
},
addFavoriteWorld(ref, group, message) {
// wait API splitting PR Merged
return this.API.addFavorite({
type: 'world',
favoriteId: ref.id,
tags: group.name
}).then((args) => {
if (message) {
this.$message({
message: 'World added to favorites',
type: 'success'
});
}
return args;
});
},
showFavoriteDialog(favoriteId) {
this.$emit('show-favorite-dialog', 'world', favoriteId);
}
}
};
</script>

View File

@@ -0,0 +1,382 @@
<template>
<div>
<div style="display: flex; align-items: center; justify-content: space-between">
<div>
<el-button size="small" @click="$emit('show-world-export-dialog')">{{
$t('view.favorite.export')
}}</el-button>
<el-button size="small" style="margin-left: 5px" @click="$emit('show-world-import-dialog')">{{
$t('view.favorite.import')
}}</el-button>
</div>
<div style="display: flex; align-items: center; font-size: 13px; margin-right: 10px">
<span class="name" style="margin-right: 5px; line-height: 10px">{{ $t('view.favorite.sort_by') }}</span>
<el-radio-group
v-model="sortFav"
style="margin-right: 12px"
@change="$emit('save-sort-favorites-option')">
<el-radio :label="false">{{
$t('view.settings.appearance.appearance.sort_favorite_by_name')
}}</el-radio>
<el-radio :label="true">{{
$t('view.settings.appearance.appearance.sort_favorite_by_date')
}}</el-radio>
</el-radio-group>
<el-input
v-model="worldFavoriteSearch"
clearable
size="mini"
:placeholder="$t('view.favorite.worlds.search')"
style="width: 200px"
@input="searchWorldFavorites" />
</div>
</div>
<div class="x-friend-list" style="margin-top: 10px">
<div
v-for="favorite in worldFavoriteSearchResults"
:key="favorite.id"
style="display: inline-block; width: 300px; margin-right: 15px"
@click="showWorldDialog(favorite.id)">
<div class="x-friend-item">
<template v-if="favorite.name">
<div class="avatar">
<img v-lazy="favorite.thumbnailImageUrl" />
</div>
<div class="detail">
<span class="name" v-text="favorite.name"></span>
<span v-if="favorite.occupants" class="extra"
>{{ favorite.authorName }} ({{ favorite.occupants }})</span
>
<span v-else class="extra" v-text="favorite.authorName"></span>
</div>
</template>
<template v-else>
<div class="avatar"></div>
<div class="detail">
<span v-text="favorite.id"></span>
</div>
</template>
</div>
</div>
</div>
<span style="display: block; margin-top: 20px">{{ $t('view.favorite.worlds.vrchat_favorites') }}</span>
<el-collapse style="border: 0">
<el-collapse-item v-for="group in API.favoriteWorldGroups" :key="group.name">
<template slot="title">
<div style="display: flex; align-items: center">
<span
style="font-weight: bold; font-size: 14px; margin-left: 10px"
v-text="group.displayName" />
<el-tag
style="margin: 1px 0 0 5px"
size="mini"
:type="userFavoriteWorldsStatusForFavTab(group.visibility)"
effect="plain"
>{{ group.visibility.charAt(0).toUpperCase() + group.visibility.slice(1) }}</el-tag
>
<span style="color: #909399; font-size: 12px; margin-left: 10px"
>{{ group.count }}/{{ group.capacity }}</span
>
<el-dropdown trigger="click" size="mini" style="margin-left: 10px" @click.native.stop>
<el-tooltip
placement="top"
:content="$t('view.favorite.visibility_tooltip')"
:disabled="hideTooltips">
<el-button type="default" icon="el-icon-view" size="mini" circle />
</el-tooltip>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
v-for="visibility in worldGroupVisibilityOptions"
v-if="group.visibility !== visibility"
:key="visibility"
style="display: block; margin: 10px 0"
@click.native="changeWorldGroupVisibility(group.name, visibility)"
>{{ visibility.charAt(0).toUpperCase() + visibility.slice(1) }}</el-dropdown-item
>
</el-dropdown-menu>
<el-tooltip
placement="top"
:content="$t('view.favorite.rename_tooltip')"
:disabled="hideTooltips">
<el-button
size="mini"
icon="el-icon-edit"
circle
style="margin-left: 5px"
@click.stop="$emit('change-favorite-group-name', group)" />
</el-tooltip>
<el-tooltip
placement="right"
:content="$t('view.favorite.clear_tooltip')"
:disabled="hideTooltips">
<el-button
size="mini"
icon="el-icon-delete"
circle
style="margin-left: 5px"
@click.stop="$emit('clear-favorite-group', group)" />
</el-tooltip>
</el-dropdown>
</div>
</template>
<div v-if="group.count" class="x-friend-list" style="margin-top: 10px">
<favorites-world-item
v-for="favorite in groupedByGroupKeyFavoriteWorlds[group.key]"
:key="favorite.id"
:group="group"
:favorite="favorite"
:edit-favorites-mode="editFavoritesMode"
:hide-tooltips="hideTooltips"
:shift-held="shiftHeld"
@click="showWorldDialog(favorite.id)"
@handle-select="favorite.$selected = $event"
@new-instance-self-invite="newInstanceSelfInvite"
@show-favorite-dialog="showFavoriteDialog" />
</div>
<div
v-else
style="
padding-top: 25px;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
color: rgb(144, 147, 153);
">
<span>No Data</span>
</div>
</el-collapse-item>
</el-collapse>
<span style="display: block; margin-top: 20px">{{ $t('view.favorite.worlds.local_favorites') }}</span>
<br />
<el-button size="small" @click="promptNewLocalWorldFavoriteGroup">{{
$t('view.favorite.worlds.new_group')
}}</el-button>
<el-button
v-if="!refreshingLocalFavorites"
size="small"
style="margin-left: 5px"
@click="$emit('refresh-local-world-favorite')"
>{{ $t('view.favorite.worlds.refresh') }}</el-button
>
<el-button v-else size="small" style="margin-left: 5px" @click="refreshingLocalFavorites = false">
<i class="el-icon-loading" style="margin-right: 5px" />
<span>{{ $t('view.favorite.worlds.cancel_refresh') }}</span>
</el-button>
<el-collapse style="border: 0">
<el-collapse-item v-for="group in localWorldFavoriteGroups" v-if="localWorldFavorites[group]" :key="group">
<template slot="title">
<span style="font-weight: bold; font-size: 14px; margin-left: 10px" v-text="group" />
<span style="color: #909399; font-size: 12px; margin-left: 10px">{{
getLocalWorldFavoriteGroupLength(group)
}}</span>
<el-tooltip placement="top" :content="$t('view.favorite.rename_tooltip')" :disabled="hideTooltips">
<el-button
size="mini"
icon="el-icon-edit"
circle
style="margin-left: 10px"
@click.stop="promptLocalWorldFavoriteGroupRename(group)" />
</el-tooltip>
<el-tooltip
placement="right"
:content="$t('view.favorite.delete_tooltip')"
:disabled="hideTooltips">
<el-button
size="mini"
icon="el-icon-delete"
circle
style="margin-left: 5px"
@click.stop="promptLocalWorldFavoriteGroupDelete(group)" />
</el-tooltip>
</template>
<div v-if="localWorldFavorites[group].length" class="x-friend-list" style="margin-top: 10px">
<favorites-world-item
v-for="favorite in localWorldFavorites[group]"
:key="favorite.id"
is-local-favorite
:group="group"
:favorite="favorite"
:edit-favorites-mode="editFavoritesMode"
:hide-tooltips="hideTooltips"
:shift-held="shiftHeld"
@click="showWorldDialog(favorite.id)"
@new-instance-self-invite="newInstanceSelfInvite"
@remove-local-world-favorite="removeLocalWorldFavorite"
@show-favorite-dialog="showFavoriteDialog" />
</div>
<div
v-else
style="
padding-top: 25px;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
color: rgb(144, 147, 153);
">
<span>No Data</span>
</div>
</el-collapse-item>
</el-collapse>
</div>
</template>
<script>
import FavoritesWorldItem from './FavoritesWorldItem.vue';
export default {
name: 'FavoritesWorldTab',
components: {
FavoritesWorldItem
},
inject: ['API'],
props: {
sortFavorites: Boolean,
worldFavoriteSearchResults: Array,
hideTooltips: Boolean,
favoriteWorlds: Array,
editFavoritesMode: Boolean,
shiftHeld: Boolean,
refreshingLocalFavorites: Boolean,
localWorldFavoriteGroups: Array,
localWorldFavorites: Object
// removeLocalWorldFavorite: Function
},
data() {
return {
worldGroupVisibilityOptions: ['private', 'friends', 'public'],
worldFavoriteSearch: ''
};
},
computed: {
groupedByGroupKeyFavoriteWorlds() {
const groupedByGroupKeyFavoriteWorlds = {};
this.favoriteWorlds.forEach((world) => {
if (world.groupKey) {
if (!groupedByGroupKeyFavoriteWorlds[world.groupKey]) {
groupedByGroupKeyFavoriteWorlds[world.groupKey] = [];
}
groupedByGroupKeyFavoriteWorlds[world.groupKey].push(world);
}
});
return groupedByGroupKeyFavoriteWorlds;
},
sortFav: {
get() {
return this.sortFavorites;
},
set(value) {
this.$emit('update:sort-favorites', value);
}
}
},
methods: {
userFavoriteWorldsStatusForFavTab(visibility) {
let style = '';
if (visibility === 'public') {
style = '';
} else if (visibility === 'friends') {
style = 'success';
} else {
style = 'info';
}
return style;
},
changeWorldGroupVisibility(name, visibility) {
const params = {
type: 'world',
group: name,
visibility
};
this.API.saveFavoriteGroup(params).then((args) => {
this.$message({
message: 'Group visibility changed',
type: 'success'
});
return args;
});
},
promptNewLocalWorldFavoriteGroup() {
this.$prompt(
$t('prompt.new_local_favorite_group.description'),
$t('prompt.new_local_favorite_group.header'),
{
distinguishCancelAndClose: true,
confirmButtonText: $t('prompt.new_local_favorite_group.ok'),
cancelButtonText: $t('prompt.new_local_favorite_group.cancel'),
inputPattern: /\S+/,
inputErrorMessage: $t('prompt.new_local_favorite_group.input_error'),
callback: (action, instance) => {
if (action === 'confirm' && instance.inputValue) {
this.newLocalWorldFavoriteGroup(instance.inputValue);
}
}
}
);
},
promptLocalWorldFavoriteGroupRename(group) {
this.$prompt(
$t('prompt.local_favorite_group_rename.description'),
$t('prompt.local_favorite_group_rename.header'),
{
distinguishCancelAndClose: true,
confirmButtonText: $t('prompt.local_favorite_group_rename.save'),
cancelButtonText: $t('prompt.local_favorite_group_rename.cancel'),
inputPattern: /\S+/,
inputErrorMessage: $t('prompt.local_favorite_group_rename.input_error'),
inputValue: group,
callback: (action, instance) => {
if (action === 'confirm' && instance.inputValue) {
this.renameLocalWorldFavoriteGroup(instance.inputValue, group);
}
}
}
);
},
promptLocalWorldFavoriteGroupDelete(group) {
this.$confirm(`Delete Group? ${group}`, 'Confirm', {
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
type: 'info',
callback: (action) => {
if (action === 'confirm') {
this.deleteLocalWorldFavoriteGroup(group);
}
}
});
},
searchWorldFavorites() {
this.$emit('search-world-favorites', this.worldFavoriteSearch);
},
getLocalWorldFavoriteGroupLength(group) {
const favoriteGroup = this.localWorldFavorites[group];
if (!favoriteGroup) {
return 0;
}
return favoriteGroup.length;
},
showWorldDialog(event) {
this.$emit('show-world-dialog', event);
},
newInstanceSelfInvite(event) {
this.$emit('new-instance-self-invite', event);
},
showFavoriteDialog(param1, param2) {
this.$emit('show-favorite-dialog', param1, param2);
},
removeLocalWorldFavorite(param1, param2) {
this.$emit('remove-local-world-favorite', param1, param2);
}
}
};
</script>
<style scoped></style>

View File

@@ -318,9 +318,7 @@
}
},
created() {
configRepository.getBool('VRCX_sidebarGroupByInstanceCollapsed', false).then((value) => {
this.isSidebarGroupByInstanceCollapsed = value;
});
this.loadFriendsGroupStates();
},
methods: {
saveFriendsGroupStates() {
@@ -336,6 +334,10 @@
this.isOnlineFriends = await configRepository.getBool('VRCX_isFriendsGroupOnline', true);
this.isActiveFriends = await configRepository.getBool('VRCX_isFriendsGroupActive', false);
this.isOfflineFriends = await configRepository.getBool('VRCX_isFriendsGroupOffline', false);
this.isSidebarGroupByInstanceCollapsed = await configRepository.getBool(
'VRCX_sidebarGroupByInstanceCollapsed',
false
);
},
isRealInstance(locationTag) {
return utils.isRealInstance(locationTag);