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

@@ -51,8 +51,12 @@ import NavMenu from './views/NavMenu.vue';
// components
import SimpleSwitch from './components/settings/SimpleSwitch.vue';
import PreviousInstanceInfo from './views/dialogs/PreviousInstanceInfo.vue';
import Location from './components/common/Location.vue';
import FavoritesWorldTab from './components/favorites/FavoritesWorldTab.vue';
// dialogs
import WorldDialog from './views/dialogs/WorldDialog.vue';
import PreviousInstanceInfoDialog from './views/dialogs/PreviousInstanceInfoDialog.vue';
// main app classes
import _sharedFeed from './classes/sharedFeed.js';
@@ -189,15 +193,16 @@ console.log(`isLinux: ${LINUX}`);
NavMenu,
// components
// - common
Location,
// - favorites
FavoritesWorldTab,
// - settings
SimpleSwitch,
// components
// - common
Location,
// - dialogs
PreviousInstanceInfo
PreviousInstanceInfoDialog,
WorldDialog
},
provide() {
return {
@@ -206,7 +211,8 @@ console.log(`isLinux: ${LINUX}`);
adjustDialogZ: this.adjustDialogZ,
getWorldName: this.getWorldName,
userImage: this.userImage,
userStatusClass: this.userStatusClass
userStatusClass: this.userStatusClass,
getGroupName: this.getGroupName
};
},
el: '#root',
@@ -6096,25 +6102,6 @@ console.log(`isLinux: ${LINUX}`);
}
};
$app.methods.deleteFavorite = function (objectId) {
API.deleteFavorite({
objectId
});
// FIXME: 메시지 수정
// this.$confirm('Continue? Delete Favorite', 'Confirm', {
// confirmButtonText: 'Confirm',
// cancelButtonText: 'Cancel',
// type: 'info',
// callback: (action) => {
// if (action === 'confirm') {
// API.deleteFavorite({
// objectId
// });
// }
// }
// });
};
$app.methods.deleteFavoriteNoConfirm = function (objectId) {
if (!objectId) {
return;
@@ -6185,21 +6172,6 @@ console.log(`isLinux: ${LINUX}`);
return this.favoriteWorldsSorted;
};
$app.computed.groupedByGroupKeyFavoriteWorlds = function () {
const groupedByGroupKeyFavoriteWorlds = {};
this.favoriteWorlds.forEach((world) => {
if (world.groupKey) {
if (!groupedByGroupKeyFavoriteWorlds[world.groupKey]) {
groupedByGroupKeyFavoriteWorlds[world.groupKey] = [];
}
groupedByGroupKeyFavoriteWorlds[world.groupKey].push(world);
}
});
return groupedByGroupKeyFavoriteWorlds;
};
$app.computed.favoriteAvatars = function () {
if (this.sortFavoriteAvatars) {
this.sortFavoriteAvatars = false;
@@ -10576,9 +10548,8 @@ console.log(`isLinux: ${LINUX}`);
});
$app.methods.showWorldDialog = function (tag, shortName) {
this.$nextTick(() => $app.adjustDialogZ(this.$refs.worldDialog.$el));
var D = this.worldDialog;
var L = $utils.parseLocation(tag);
const D = this.worldDialog;
const L = $utils.parseLocation(tag);
if (L.worldId === '') {
return;
}
@@ -11049,9 +11020,6 @@ console.log(`isLinux: ${LINUX}`);
case 'Refresh':
this.showWorldDialog(D.id);
break;
case 'Share':
this.copyWorldUrl(D.id);
break;
case 'New Instance':
this.showNewInstanceDialog(D.$location.tag);
break;
@@ -11209,30 +11177,6 @@ console.log(`isLinux: ${LINUX}`);
});
};
$app.methods.refreshWorldDialogTreeData = function () {
var D = this.worldDialog;
D.treeData = $utils.buildTreeData(D.ref);
};
$app.computed.worldDialogPlatform = function () {
var { ref } = this.worldDialog;
var platforms = [];
if (ref.unityPackages) {
for (var unityPackage of ref.unityPackages) {
var platform = 'PC';
if (unityPackage.platform === 'standalonewindows') {
platform = 'PC';
} else if (unityPackage.platform === 'android') {
platform = 'Android';
} else if (unityPackage.platform) {
({ platform } = unityPackage);
}
platforms.unshift(`${platform}/${unityPackage.unityVersion}`);
}
}
return platforms.join(', ');
};
// #endregion
// #region | App: Avatar Dialog
@@ -11805,18 +11749,6 @@ console.log(`isLinux: ${LINUX}`);
});
};
$app.methods.moveFavorite = function (ref, group, type) {
API.deleteFavorite({
objectId: ref.id
}).then(() => {
API.addFavorite({
type,
favoriteId: ref.id,
tags: group.name
});
});
};
$app.methods.showFavoriteDialog = function (type, objectId) {
this.$nextTick(() => $app.adjustDialogZ(this.$refs.favoriteDialog.$el));
var D = this.favoriteDialog;
@@ -13264,30 +13196,6 @@ console.log(`isLinux: ${LINUX}`);
this.copyToClipboard(`https://vrchat.com/home/avatar/${avatarId}`);
};
$app.methods.copyWorldId = function (worldId) {
this.$message({
message: 'World ID copied to clipboard',
type: 'success'
});
this.copyToClipboard(worldId);
};
$app.methods.copyWorldUrl = function (worldId) {
this.$message({
message: 'World URL copied to clipboard',
type: 'success'
});
this.copyToClipboard(`https://vrchat.com/home/world/${worldId}`);
};
$app.methods.copyWorldName = function (worldName) {
this.$message({
message: 'World name copied to clipboard',
type: 'success'
});
this.copyToClipboard(worldName);
};
$app.methods.copyUserId = function (userId) {
this.$message({
message: 'User ID copied to clipboard',
@@ -16829,8 +16737,6 @@ console.log(`isLinux: ${LINUX}`);
this.userDialog.isFavoriteWorldsLoading = false;
};
$app.data.worldGroupVisibilityOptions = ['private', 'friends', 'public'];
$app.methods.userFavoriteWorldsStatus = function (visibility) {
var style = {};
if (visibility === 'public') {
@@ -16843,33 +16749,6 @@ console.log(`isLinux: ${LINUX}`);
return style;
};
$app.methods.userFavoriteWorldsStatusForFavTab = function (visibility) {
let style = '';
if (visibility === 'public') {
style = '';
} else if (visibility === 'friends') {
style = 'success';
} else {
style = 'info';
}
return style;
};
$app.methods.changeWorldGroupVisibility = function (name, visibility) {
var params = {
type: 'world',
group: name,
visibility
};
API.saveFavoriteGroup(params).then((args) => {
this.$message({
message: 'Group visibility changed',
type: 'success'
});
return args;
});
};
$app.methods.refreshInstancePlayerCount = function (instance) {
var L = $utils.parseLocation(instance);
if (L.isRealInstance) {
@@ -20545,27 +20424,6 @@ console.log(`isLinux: ${LINUX}`);
return favoriteGroup.length;
};
$app.methods.promptNewLocalWorldFavoriteGroup = function () {
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);
}
}
}
);
};
$app.methods.newLocalWorldFavoriteGroup = function (group) {
if (this.localWorldFavoriteGroups.includes(group)) {
$app.$message({
@@ -20585,35 +20443,6 @@ console.log(`isLinux: ${LINUX}`);
this.sortLocalWorldFavorites();
};
$app.methods.promptLocalWorldFavoriteGroupRename = function (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
);
}
}
}
);
};
$app.methods.renameLocalWorldFavoriteGroup = function (newName, group) {
if (this.localWorldFavoriteGroups.includes(newName)) {
$app.$message({
@@ -20634,19 +20463,6 @@ console.log(`isLinux: ${LINUX}`);
this.sortLocalWorldFavorites();
};
$app.methods.promptLocalWorldFavoriteGroupDelete = function (group) {
this.$confirm(`Delete Group? ${group}`, 'Confirm', {
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
type: 'info',
callback: (action) => {
if (action === 'confirm') {
this.deleteLocalWorldFavoriteGroup(group);
}
}
});
};
$app.methods.sortLocalWorldFavorites = function () {
this.localWorldFavoriteGroups.sort();
if (!this.sortFavorites) {
@@ -20729,11 +20545,10 @@ console.log(`isLinux: ${LINUX}`);
this.refreshingLocalFavorites = false;
};
$app.data.worldFavoriteSearch = '';
$app.data.worldFavoriteSearchResults = [];
$app.methods.searchWorldFavorites = function () {
var search = this.worldFavoriteSearch.toLowerCase();
$app.methods.searchWorldFavorites = function (worldFavoriteSearch) {
var search = worldFavoriteSearch.toLowerCase();
if (search.length < 3) {
this.worldFavoriteSearchResults = [];
return;
@@ -22447,27 +22262,6 @@ console.log(`isLinux: ${LINUX}`);
);
};
// favorites Tab
// - local favorites
// - local world & avatar
$app.data.localFavoriteShowDelayedContent = [false, false];
$app.methods.onFavTabClick = function (el) {
if (el.index === '0') {
this.localFavoriteShowDelayedContent = [false, false];
} else {
setTimeout(() => {
requestAnimationFrame(() => {
if (el.index === '1') {
this.localFavoriteShowDelayedContent = [true, false];
} else if (el.index === '2') {
this.localFavoriteShowDelayedContent = [false, true];
}
});
}, 300);
}
};
// #endregion
// #region | Tab Props

View File

@@ -84,11 +84,6 @@ export default class extends baseClass {
});
},
onWorldMemoChange() {
var D = this.worldDialog;
this.saveWorldMemo(D.id, D.memo);
},
async getWorldMemo(worldId) {
try {
return await database.getWorldMemo(worldId);
@@ -102,18 +97,6 @@ export default class extends baseClass {
}
},
saveWorldMemo(worldId, memo) {
if (memo) {
database.setWorldMemo({
worldId,
editedAt: new Date().toJSON(),
memo
});
} else {
database.deleteWorldMemo(worldId);
}
},
onAvatarMemoChange() {
var D = this.avatarDialog;
this.saveAvatarMemo(D.id, D.memo);

View File

@@ -294,6 +294,7 @@ export default class extends baseClass {
);
},
// remove when finished fav tab split
changeFavoriteGroupName(ctx) {
this.$prompt(
$t('prompt.change_favorite_group_name.description'),

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);

View File

@@ -123,7 +123,7 @@ mixin previousInstances
size='mini'
@click='deleteGameLogWorldInstancePrompt(scope.row)')
previous-instance-info(
previous-instance-info-dialog(
:visible.sync='previousInstanceInfoDialogVisible'
:instance-id='previousInstanceInfoDialogInstanceId'
:game-log-is-friend='gameLogIsFriend'

View File

@@ -1,453 +1,20 @@
mixin worldDialog
el-dialog.x-dialog.x-world-dialog(
:before-close='beforeDialogClose'
@mousedown.native='dialogMouseDown'
@mouseup.native='dialogMouseUp'
ref='worldDialog'
:visible.sync='worldDialog.visible'
:show-close='false'
width='770px')
div(v-loading='worldDialog.loading')
div(style='display: flex')
el-popover(placement='right' width='500px' trigger='click')
img.x-link(
slot='reference'
v-lazy='worldDialog.ref.thumbnailImageUrl'
style='flex: none; width: 160px; height: 120px; border-radius: 12px')
img.x-link(
v-lazy='worldDialog.ref.imageUrl'
style='width: 500px; height: 375px'
@click='showFullscreenImageDialog(worldDialog.ref.imageUrl)')
div(style='flex: 1; display: flex; align-items: center; margin-left: 15px')
div(style='flex: 1')
div
i.el-icon-s-home(
v-show='API.currentUser.$homeLocation && API.currentUser.$homeLocation.worldId === worldDialog.id'
style='margin-right: 5px')
span.dialog-title(v-text='worldDialog.ref.name')
div(style='margin-top: 5px')
span.x-link.x-grey(
v-text='worldDialog.ref.authorName'
@click='showUserDialog(worldDialog.ref.authorId)'
style='font-family: monospace')
div
el-tag(
v-if='worldDialog.ref.$isLabs'
type='primary'
effect='plain'
size='mini'
style='margin-right: 5px; margin-top: 5px') {{ $t('dialog.world.tags.labs') }}
el-tag(
v-else-if='worldDialog.ref.releaseStatus === "public"'
type='success'
effect='plain'
size='mini'
style='margin-right: 5px; margin-top: 5px') {{ $t('dialog.world.tags.public') }}
el-tag(
v-else
type='danger'
effect='plain'
size='mini'
style='margin-right: 5px; margin-top: 5px') {{ $t('dialog.world.tags.private') }}
el-tag.x-tag-platform-pc(
v-if='worldDialog.isPC'
type='info'
effect='plain'
size='mini'
style='margin-right: 5px; margin-top: 5px') PC
span.x-grey(
v-if='worldDialog.bundleSizes["standalonewindows"]'
style='margin-left: 5px; border-left: inherit; padding-left: 5px') {{ worldDialog.bundleSizes['standalonewindows'].fileSize }}
el-tag.x-tag-platform-quest(
v-if='worldDialog.isQuest'
type='info'
effect='plain'
size='mini'
style='margin-right: 5px; margin-top: 5px') Android
span.x-grey(
v-if='worldDialog.bundleSizes["android"]'
style='margin-left: 5px; border-left: inherit; padding-left: 5px') {{ worldDialog.bundleSizes['android'].fileSize }}
el-tag.x-tag-platform-ios(
v-if='worldDialog.isIos'
type='info'
effect='plain'
size='mini'
style='margin-right: 5px; margin-top: 5px') iOS
span.x-grey(
v-if='worldDialog.bundleSizes["ios"]'
style='margin-left: 5px; border-left: inherit; padding-left: 5px') {{ worldDialog.bundleSizes['ios'].fileSize }}
el-tag(
v-if='worldDialog.avatarScalingDisabled'
type='warning'
effect='plain'
size='mini'
style='margin-right: 5px; margin-top: 5px') {{ $t('dialog.world.tags.avatar_scaling_disabled') }}
el-tag(
v-if='worldDialog.focusViewDisabled'
type='warning'
effect='plain'
size='mini'
style='margin-right: 5px; margin-top: 5px') {{ $t('dialog.world.tags.focus_view_disabled') }}
el-tag(
v-if='worldDialog.stickersDisabled'
type='warning'
effect='plain'
size='mini'
style='margin-right: 5px; margin-top: 5px') {{ $t('dialog.world.tags.stickers_disabled') }}
el-tag(
v-if='worldDialog.ref.unityPackageUrl'
type='success'
effect='plain'
size='mini'
style='margin-right: 5px; margin-top: 5px') {{ $t('dialog.world.tags.future_proofing') }}
el-tag.x-link(
v-if='worldDialog.inCache'
type='info'
effect='plain'
size='mini'
style='margin-right: 5px; margin-top: 5px'
@click='openFolderGeneric(worldDialog.cachePath)')
span(v-text='worldDialog.cacheSize')
| {{ $t('dialog.world.tags.cache') }}
div
template(v-for='tag in worldDialog.ref.tags')
el-tag(
v-if='tag.startsWith("content_")'
:key='tag'
effect='plain'
size='mini'
style='margin-right: 5px; margin-top: 5px')
template(v-if='tag === "content_horror"') {{ $t('dialog.world.tags.content_horror') }}
template(v-else-if='tag === "content_gore"') {{ $t('dialog.world.tags.content_gore') }}
template(v-else-if='tag === "content_violence"') {{ $t('dialog.world.tags.content_violence') }}
template(v-else-if='tag === "content_adult"') {{ $t('dialog.world.tags.content_adult') }}
template(v-else-if='tag === "content_sex"') {{ $t('dialog.world.tags.content_sex') }}
template(v-else) {{ tag.replace('content_', '') }}
div(style='margin-top: 5px')
span(
v-show='worldDialog.ref.name !== worldDialog.ref.description'
v-text='worldDialog.ref.description'
style='font-size: 12px')
div(style='flex: none; margin-left: 10px')
el-tooltip(
v-if='worldDialog.inCache'
placement='top'
:content='$t("dialog.world.actions.delete_cache_tooltip")'
:disabled='hideTooltips')
el-button(
icon='el-icon-delete'
circle
@click='deleteVRChatCache(worldDialog.ref)'
:disabled='isGameRunning && worldDialog.cacheLocked')
el-tooltip(
v-if='worldDialog.isFavorite'
placement='top'
:content='$t("dialog.world.actions.favorites_tooltip")'
:disabled='hideTooltips')
el-button(
type='default'
icon='el-icon-star-on'
circle
@click='worldDialogCommand("Add Favorite")'
style='margin-left: 5px')
el-tooltip(
v-else
placement='top'
:content='$t("dialog.world.actions.favorites_tooltip")'
:disabled='hideTooltips')
el-button(
type='default'
icon='el-icon-star-off'
circle
@click='worldDialogCommand("Add Favorite")'
style='margin-left: 5px')
el-dropdown(
trigger='click'
@command='worldDialogCommand'
size='small'
style='margin-left: 5px')
el-button(type='default' icon='el-icon-more' circle)
el-dropdown-menu(#default='dropdown')
el-dropdown-item(icon='el-icon-refresh' command='Refresh') {{ $t('dialog.world.actions.refresh') }}
el-dropdown-item(icon='el-icon-share' command='Share') {{ $t('dialog.world.actions.share') }}
el-dropdown-item(icon='el-icon-s-flag' command='New Instance' divided) {{ $t('dialog.world.actions.new_instance') }}
el-dropdown-item(icon='el-icon-message' command='New Instance and Self Invite') {{ $t('dialog.world.actions.new_instance_and_self_invite') }}
el-dropdown-item(
v-if='API.currentUser.$homeLocation && API.currentUser.$homeLocation.worldId === worldDialog.id'
icon='el-icon-magic-stick'
command='Reset Home'
divided) {{ $t('dialog.world.actions.reset_home') }}
el-dropdown-item(v-else icon='el-icon-s-home' command='Make Home' divided) {{ $t('dialog.world.actions.make_home') }}
el-dropdown-item(icon='el-icon-tickets' command='Previous Instances') {{ $t('dialog.world.actions.show_previous_instances') }}
template(v-if='API.currentUser.id !== worldDialog.ref.authorId')
el-dropdown-item(icon='el-icon-picture-outline' command='Previous Images') {{ $t('dialog.world.actions.show_previous_images') }}
el-dropdown-item(
:disabled='!worldDialog.hasPersistData'
icon='el-icon-upload'
command='Delete Persistent Data') {{ $t('dialog.world.actions.delete_persistent_data') }}
template(v-else)
el-dropdown-item(icon='el-icon-edit' command='Rename') {{ $t('dialog.world.actions.rename') }}
el-dropdown-item(icon='el-icon-edit' command='Change Description') {{ $t('dialog.world.actions.change_description') }}
el-dropdown-item(icon='el-icon-edit' command='Change Capacity') {{ $t('dialog.world.actions.change_capacity') }}
el-dropdown-item(icon='el-icon-edit' command='Change Recommended Capacity') {{ $t('dialog.world.actions.change_recommended_capacity') }}
el-dropdown-item(icon='el-icon-edit' command='Change YouTube Preview') {{ $t('dialog.world.actions.change_preview') }}
el-dropdown-item(icon='el-icon-edit' command='Change Tags') {{ $t('dialog.world.actions.change_tags') }}
el-dropdown-item(icon='el-icon-edit' command='Change Allowed Domains') {{ $t('dialog.world.actions.change_allowed_video_player_domains') }}
el-dropdown-item(icon='el-icon-picture-outline' command='Change Image') {{ $t('dialog.world.actions.change_image') }}
el-dropdown-item(
v-if='worldDialog.ref.unityPackageUrl'
icon='el-icon-download'
command='Download Unity Package') {{ $t('dialog.world.actions.download_package') }}
el-dropdown-item(
v-if='worldDialog.ref.tags.includes("system_approved") || worldDialog.ref.tags.includes("system_labs")'
icon='el-icon-view'
command='Unpublish'
divided) {{ $t('dialog.world.actions.unpublish') }}
el-dropdown-item(v-else icon='el-icon-view' command='Publish' divided) {{ $t('dialog.world.actions.publish_to_labs') }}
el-dropdown-item(
:disabled='!worldDialog.hasPersistData'
icon='el-icon-upload'
command='Delete Persistent Data') {{ $t('dialog.world.actions.delete_persistent_data') }}
el-dropdown-item(icon='el-icon-delete' command='Delete' style='color: #f56c6c') {{ $t('dialog.world.actions.delete') }}
el-tabs
el-tab-pane(:label='$t("dialog.world.instances.header")')
div.
#[i.el-icon-user] {{ $t('dialog.world.instances.public_count', { count: worldDialog.ref.publicOccupants }) }}
#[i.el-icon-user-solid(style='margin-left: 10px')] {{ $t('dialog.world.instances.private_count', { count: worldDialog.ref.privateOccupants }) }}
#[i.el-icon-check(style='margin-left: 10px')] {{ $t('dialog.world.instances.capacity_count', { count: worldDialog.ref.recommendedCapacity, max: worldDialog.ref.capacity }) }}
div(v-for='room in worldDialog.rooms' :key='room.id')
div(style='margin: 5px 0')
location-world(
:locationobject='room.$location'
:currentuserid='API.currentUser.id'
:worlddialogshortname='worldDialog.$location.shortName'
@show-launch-dialog='showLaunchDialog')
launch(
:location='room.tag'
@show-launch-dialog='showLaunchDialog'
style='margin-left: 5px')
el-tooltip(
placement='top'
:content='$t("dialog.world.instances.self_invite_tooltip")'
:disabled='hideTooltips')
invite-yourself(
:location='room.$location.tag'
:shortname='room.$location.shortName'
style='margin-left: 5px')
el-tooltip(
placement='top'
:content='$t("dialog.world.instances.refresh_instance_info")'
:disabled='hideTooltips')
el-button(
@click='refreshInstancePlayerCount(room.tag)'
size='mini'
icon='el-icon-refresh'
style='margin-left: 5px'
circle)
el-tooltip(
v-if='instanceJoinHistory.get(room.$location.tag)'
placement='top'
:content='$t("dialog.previous_instances.info")'
:disabled='hideTooltips')
el-button(
@click='showPreviousInstanceInfoDialog(room.location)'
size='mini'
icon='el-icon-s-data'
style='margin-left: 5px'
plain
circle)
last-join(:location='room.$location.tag' :currentlocation='lastLocation.location')
instance-info(
:location='room.tag'
:instance='room.ref'
:friendcount='room.friendCount'
:updateelement='updateInstanceInfo')
.x-friend-list(
style='margin: 10px 0; max-height: unset'
v-if='room.$location.userId || room.users.length')
.x-friend-item.x-friend-item-border(
v-if='room.$location.userId'
@click='showUserDialog(room.$location.userId)')
template(v-if='room.$location.user')
.avatar(:class='userStatusClass(room.$location.user)')
img(v-lazy='userImage(room.$location.user)')
.detail
span.name(
v-text='room.$location.user.displayName'
:style='{ color: room.$location.user.$userColour }')
span.extra {{ $t('dialog.world.instances.instance_creator') }}
span(v-else v-text='room.$location.userId')
.x-friend-item.x-friend-item-border(
v-for='user in room.users'
:key='user.id'
@click='showUserDialog(user.id)')
.avatar(:class='userStatusClass(user)')
img(v-lazy='userImage(user)')
.detail
span.name(v-text='user.displayName' :style='{ color: user.$userColour }')
span.extra(v-if='user.location === "traveling"')
i.el-icon-loading(style='margin-right: 5px')
timer(:epoch='user.$travelingToTime')
span.extra(v-else)
timer(:epoch='user.$location_at')
el-tab-pane(:label='$t("dialog.world.info.header")' lazy)
.x-friend-list(style='max-height: none')
.x-friend-item(style='width: 100%; cursor: default')
.detail
span.name {{ $t('dialog.world.info.memo') }}
el-input.extra(
v-model='worldDialog.memo'
@change='onWorldMemoChange'
type='textarea'
:rows='2'
:autosize='{ minRows: 1, maxRows: 20 }'
:placeholder='$t("dialog.world.info.memo_placeholder")'
size='mini'
resize='none')
div(style='width: 100%; display: flex')
.x-friend-item(style='width: 100%; cursor: default')
.detail
span.name {{ $t('dialog.world.info.id') }}
span.extra {{ worldDialog.id }}
el-tooltip(
placement='top'
:content='$t("dialog.world.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='copyWorldId(worldDialog.id)') {{ $t('dialog.world.info.copy_id') }}
el-dropdown-item(@click.native='copyWorldUrl(worldDialog.id)') {{ $t('dialog.world.info.copy_url') }}
el-dropdown-item(
@click.native='copyWorldName(worldDialog.ref.name)') {{ $t('dialog.world.info.copy_name') }}
.x-friend-item(
v-if='worldDialog.ref.previewYoutubeId'
style='width: 350px'
@click='openExternalLink(`https://www.youtube.com/watch?v=${worldDialog.ref.previewYoutubeId}`)')
.detail
span.name {{ $t('dialog.world.info.youtube_preview') }}
span.extra https://www.youtube.com/watch?v={{ worldDialog.ref.previewYoutubeId }}
.x-friend-item(style='width: 100%; cursor: default')
.detail
span.name {{ $t('dialog.world.info.author_tags') }}
span.extra(
v-if='worldDialog.ref.tags?.filter((tag) => tag.startsWith("author_tag")).length > 0') {{ worldDialog.ref.tags.filter((tag) => tag.startsWith('author_tag')).map((tag) => tag.replace('author_tag_', '')).join(', ') }}
span.extra(v-else) -
.x-friend-item(style='cursor: default')
.detail
span.name {{ $t('dialog.world.info.players') }}
span.extra {{ worldDialog.ref.occupants | commaNumber }}
.x-friend-item(style='cursor: default')
.detail
span.name {{ $t('dialog.world.info.favorites') }}
span.extra {{ worldDialog.ref.favorites | commaNumber }}
| &nbsp;#[span.extra(v-if='worldDialog.ref.favorites > 0 && worldDialog.ref.visits > 0') ({{ Math.round((((worldDialog.ref.favorites - worldDialog.ref.visits) / worldDialog.ref.visits) * 100 + 100) * 100) / 100 }}%)]
.x-friend-item(style='cursor: default')
.detail
span.name {{ $t('dialog.world.info.visits') }}
span.extra {{ worldDialog.ref.visits | commaNumber }}
.x-friend-item(style='cursor: default')
.detail
span.name {{ $t('dialog.world.info.capacity') }}
span.extra {{ worldDialog.ref.recommendedCapacity | commaNumber }} ({{ worldDialog.ref.capacity | commaNumber }})
.x-friend-item(style='cursor: default')
.detail
span.name {{ $t('dialog.world.info.created_at') }}
span.extra {{ worldDialog.ref.created_at | formatDate('long') }}
.x-friend-item(style='cursor: default')
.detail
span.name {{ $t('dialog.world.info.last_updated') }}
span.extra(v-if='worldDialog.lastUpdated') {{ worldDialog.lastUpdated | formatDate('long') }}
span.extra(v-else) {{ worldDialog.ref.updated_at | formatDate('long') }}
.x-friend-item(v-if='worldDialog.ref.labsPublicationDate !== "none"' style='cursor: default')
.detail
span.name {{ $t('dialog.world.info.labs_publication_date') }}
span.extra {{ worldDialog.ref.labsPublicationDate | formatDate('long') }}
.x-friend-item(v-if='worldDialog.ref.publicationDate !== "none"' style='cursor: default')
.detail
span.name {{ $t('dialog.world.info.publication_date') }}
el-tooltip(
v-if='worldDialog.ref.publicationDate && worldDialog.ref.publicationDate !== "none" && worldDialog.ref.labsPublicationDate && worldDialog.ref.labsPublicationDate !== "none"'
placement='top'
style='margin-left: 5px')
template(#content)
span {{ $t('dialog.world.info.time_in_labs') }} {{ timeToText(new Date(worldDialog.ref.publicationDate) - new Date(worldDialog.ref.labsPublicationDate)) }}
i.el-icon-arrow-down
span.extra {{ worldDialog.ref.publicationDate | formatDate('long') }}
.x-friend-item(style='cursor: default')
.detail
span.name {{ $t('dialog.world.info.version') }}
span.extra(v-text='worldDialog.ref.version')
.x-friend-item(style='cursor: default')
.detail
span.name {{ $t('dialog.world.info.heat') }}
span.extra {{ worldDialog.ref.heat | commaNumber }} {{ '🔥'.repeat(worldDialog.ref.heat) }}
.x-friend-item(style='cursor: default')
.detail
span.name {{ $t('dialog.world.info.popularity') }}
span.extra {{ worldDialog.ref.popularity | commaNumber }} {{ '💖'.repeat(worldDialog.ref.popularity) }}
.x-friend-item(style='width: 100%; cursor: default')
.detail
span.name {{ $t('dialog.world.info.platform') }}
span.extra(v-text='worldDialogPlatform')
.x-friend-item(style='cursor: default')
.detail
span.name {{ $t('dialog.world.info.last_visited') }}
el-tooltip(
v-if='!hideTooltips'
placement='top'
style='margin-left: 5px'
:content='$t("dialog.world.info.accuracy_notice")')
i.el-icon-warning
span.extra {{ worldDialog.lastVisit | formatDate('long') }}
el-tooltip(
:disabled='hideTooltips'
placement='top'
:content='$t("dialog.user.info.open_previouse_instance")')
.x-friend-item(@click='showPreviousInstancesWorldDialog(worldDialog.ref)')
.detail
span.name {{ $t('dialog.world.info.visit_count') }}
el-tooltip(
v-if='!hideTooltips'
placement='top'
style='margin-left: 5px'
:content='$t("dialog.world.info.accuracy_notice")')
i.el-icon-warning
span.extra(v-text='worldDialog.visitCount')
.x-friend-item(style='cursor: default')
.detail
span.name {{ $t('dialog.world.info.time_spent') }}
el-tooltip(
v-if='!hideTooltips'
placement='top'
style='margin-left: 5px'
:content='$t("dialog.world.info.accuracy_notice")')
i.el-icon-warning
span.extra(v-if='worldDialog.timeSpent === 0') -
span.extra(v-else) {{ timeToText(worldDialog.timeSpent) }}
el-tab-pane(:label='$t("dialog.world.json.header")' lazy)
el-button(
type='default'
@click='refreshWorldDialogTreeData()'
size='mini'
icon='el-icon-refresh'
circle)
el-button(
type='default'
@click='downloadAndSaveJson(worldDialog.id, worldDialog.ref)'
size='mini'
icon='el-icon-download'
circle
style='margin-left: 5px')
el-tree(:data='worldDialog.treeData' style='margin-top: 5px; 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')
world-dialog(
:world-dialog='worldDialog'
:hide-tooltips='hideTooltips'
:is-game-running='isGameRunning'
:last-location='lastLocation'
:instance-join-history='instanceJoinHistory'
:update-instance-info='updateInstanceInfo'
@show-fullscreen-image-dialog='showFullscreenImageDialog'
@open-folder-generic='openFolderGeneric'
@delete-vrchat-cache='deleteVRChatCache'
@world-dialog-command='worldDialogCommand'
@show-launch-dialog='showLaunchDialog'
@refresh-instance-player-count='refreshInstancePlayerCount'
@show-previous-instance-info-dialog='showPreviousInstanceInfoDialog'
@show-previous-instances-world-dialog='showPreviousInstancesWorldDialog'
@download-and-save-json='downloadAndSaveJson')
//- dialog: change Allowed Video Player Domains
el-dialog.x-dialog(

View File

@@ -16,7 +16,7 @@ mixin favoritesTab
size='small'
icon='el-icon-refresh'
circle)
el-tabs(type='card' v-loading='API.isFavoriteLoading' style='height: 100%' @tab-click='onFavTabClick')
el-tabs(type='card' v-loading='API.isFavoriteLoading' style='height: 100%')
el-tab-pane(:label='$t("view.favorite.friends.header")' lazy)
div(style='display: flex; align-items: center; justify-content: space-between')
div
@@ -132,302 +132,28 @@ mixin favoritesTab
style='padding-top: 25px; width: 100%; display: flex; align-items: center; justify-content: center; color: rgb(144, 147, 153)')
span No Data
el-tab-pane(:label='$t("view.favorite.worlds.header")' lazy)
div(style='display: flex; align-items: center; justify-content: space-between')
div
el-button(size='small' @click='showWorldExportDialog') {{ $t('view.favorite.export') }}
el-button(size='small' @click='showWorldImportDialog' style='margin-left: 5px') {{ $t('view.favorite.import') }}
div(style='display: flex; align-items: center; font-size: 13px; margin-right: 10px')
span.name(style='margin-right: 5px; line-height: 10px') {{ $t('view.favorite.sort_by') }}
el-radio-group(
v-model='sortFavorites'
@change='saveSortFavoritesOption'
style='margin-right: 12px')
el-radio(:label='false') {{ $t('view.settings.appearance.appearance.sort_favorite_by_name') }}
el-radio(:label='true') {{ $t('view.settings.appearance.appearance.sort_favorite_by_date') }}
el-input(
v-model='worldFavoriteSearch'
@input='searchWorldFavorites'
clearable
size='mini'
:placeholder='$t("view.favorite.worlds.search")'
style='width: 200px')
.x-friend-list(style='margin-top: 10px')
div(
style='display: inline-block; width: 300px; margin-right: 15px'
v-for='favorite in worldFavoriteSearchResults'
: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')
template(v-else)
.avatar
.detail
span(v-text='favorite.id')
span(style='display: block; margin-top: 20px') {{ $t('view.favorite.worlds.vrchat_favorites') }}
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(
v-text='group.displayName'
style='font-weight: bold; font-size: 14px; margin-left: 10px')
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) }}
span(style='color: #909399; font-size: 12px; margin-left: 10px') {{ group.count }}/{{ group.capacity }}
el-dropdown(trigger='click' @click.native.stop size='mini' style='margin-left: 10px')
el-tooltip(
placement='top'
:content='$t("view.favorite.visibility_tooltip")'
:disabled='hideTooltips')
el-button(type='default' icon='el-icon-view' size='mini' circle)
el-dropdown-menu(#default='dropdown')
el-dropdown-item(
v-if='group.visibility !== visibility'
v-for='visibility in worldGroupVisibilityOptions'
:key='visibility'
style='display: block; margin: 10px 0'
v-text='visibility.charAt(0).toUpperCase() + visibility.slice(1)'
@click.native='changeWorldGroupVisibility(group.name, visibility)')
el-tooltip(
placement='top'
:content='$t("view.favorite.rename_tooltip")'
:disabled='hideTooltips')
el-button(
@click.stop='changeFavoriteGroupName(group)'
size='mini'
icon='el-icon-edit'
circle
style='margin-left: 5px')
el-tooltip(
placement='right'
:content='$t("view.favorite.clear_tooltip")'
:disabled='hideTooltips')
el-button(
@click.stop='clearFavoriteGroup(group)'
size='mini'
icon='el-icon-delete'
circle
style='margin-left: 5px')
.x-friend-list(v-if='group.count' style='margin-top: 10px')
div(
style='display: inline-block; width: 300px; margin-right: 15px'
v-for='favorite in groupedByGroupKeyFavoriteWorlds[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')
template(v-if='editFavoritesMode')
el-dropdown(
trigger='click'
@click.native.stop
size='mini'
style='margin-left: 5px')
el-tooltip(
placement='left'
:content='$t("view.favorite.move_tooltip")'
:disabled='hideTooltips')
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')
el-dropdown-item(
:key='groupAPI.name'
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-button(type='text' size='mini' @click.stop style='margin-left: 5px')
el-checkbox(v-model='favorite.$selected')
template(v-else)
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.self_invite_tooltip")'
:disabled='hideTooltips')
el-button(
@click.stop='newInstanceSelfInvite(favorite.id)'
size='mini'
icon='el-icon-message'
circle
style='margin-left: 5px')
el-tooltip(
placement='right'
:content='$t("view.favorite.unfavorite_tooltip")'
:disabled='hideTooltips')
el-button(
v-if='shiftHeld'
@click.stop='deleteFavorite(favorite.id)'
size='mini'
icon='el-icon-close'
circle
style='color: #f56c6c; margin-left: 5px')
el-button(
v-else
@click.stop='showFavoriteDialog("world", favorite.id)'
type='default'
icon='el-icon-star-on'
size='mini'
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')
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(style='display: block; margin-top: 20px') {{ $t('view.favorite.worlds.local_favorites') }}
br
el-button(size='small' @click='promptNewLocalWorldFavoriteGroup') {{ $t('view.favorite.worlds.new_group') }}
el-button(
v-if='!refreshingLocalFavorites'
size='small'
@click='refreshLocalWorldFavorites'
style='margin-left: 5px') {{ $t('view.favorite.worlds.refresh') }}
el-button(v-else size='small' @click='refreshingLocalFavorites = false' style='margin-left: 5px')
i.el-icon-loading(style='margin-right: 5px')
span {{ $t('view.favorite.worlds.cancel_refresh') }}
el-collapse-item(
v-for='group in localWorldFavoriteGroups'
v-if='localWorldFavorites[group]'
:key='group')
template(slot='title')
span(v-text='group' style='font-weight: bold; font-size: 14px; margin-left: 10px')
span(style='color: #909399; font-size: 12px; margin-left: 10px') {{ getLocalWorldFavoriteGroupLength(group) }}
el-tooltip(
placement='top'
:content='$t("view.favorite.rename_tooltip")'
:disabled='hideTooltips')
el-button(
@click.stop='promptLocalWorldFavoriteGroupRename(group)'
size='mini'
icon='el-icon-edit'
circle
style='margin-left: 10px')
el-tooltip(
placement='right'
:content='$t("view.favorite.delete_tooltip")'
:disabled='hideTooltips')
el-button(
@click.stop='promptLocalWorldFavoriteGroupDelete(group)'
size='mini'
icon='el-icon-delete'
circle
style='margin-left: 5px')
.x-friend-list(
style='margin-top: 10px'
v-if='localFavoriteShowDelayedContent[0] && localWorldFavorites[group].length')
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')
template(v-if='editFavoritesMode')
el-dropdown(
trigger='click'
@click.native.stop
size='mini'
style='margin-left: 5px')
el-tooltip(
placement='left'
:content='$t("view.favorite.copy_tooltip")'
:disabled='hideTooltips')
el-button(type='default' icon='el-icon-back' size='mini' circle)
el-dropdown-menu(#default='dropdown')
template(v-for='groupAPI in API.favoriteWorldGroups')
el-dropdown-item(
:key='groupAPI.name'
style='display: block; margin: 10px 0'
@click.native='addFavoriteWorld(favorite, groupAPI, true)'
:disabled='groupAPI.count >= groupAPI.capacity') {{ groupAPI.displayName }} ({{ groupAPI.count }} / {{ groupAPI.capacity }})
template(v-else)
el-tooltip(
placement='left'
:content='$t("view.favorite.self_invite_tooltip")'
:disabled='hideTooltips')
el-button(
@click.stop='newInstanceSelfInvite(favorite.id)'
size='mini'
icon='el-icon-message'
circle
style='margin-left: 5px')
el-tooltip(
placement='right'
:content='$t("view.favorite.unfavorite_tooltip")'
:disabled='hideTooltips')
el-button(
v-if='shiftHeld'
@click.stop='removeLocalWorldFavorite(favorite.id, group)'
size='mini'
icon='el-icon-close'
circle
style='color: #f56c6c; margin-left: 5px')
el-button(
v-else
@click.stop='showFavoriteDialog("world", favorite.id)'
type='default'
icon='el-icon-star-on'
size='mini'
circle
style='margin-left: 5px')
template(v-else)
.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')
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
favorites-world-tab(
@show-world-export-dialog='showWorldExportDialog'
@show-world-import-dialog='showWorldImportDialog'
@save-sort-favorites-option='saveSortFavoritesOption'
@show-world-dialog='showWorldDialog'
@change-favorite-group-name='changeFavoriteGroupName'
@clear-favorite-group='clearFavoriteGroup'
@new-instance-self-invite='newInstanceSelfInvite'
@show-favorite-dialog='showFavoriteDialog'
@refresh-local-world-favorite='refreshLocalWorldFavorites'
@delete-local-world-favorite-group='deleteLocalWorldFavoriteGroup'
@search-world-favorites='searchWorldFavorites'
@remove-local-world-favorite='removeLocalWorldFavorite'
:sort-favorites.sync='sortFavorites'
:world-favorite-search-results='worldFavoriteSearchResults'
:hide-tooltips='hideTooltips'
:favorite-worlds='favoriteWorlds'
:edit-favorites-mode='editFavoritesMode'
:shift-held='shiftHeld'
:refresh-local-world-favorites='refreshLocalWorldFavorites'
:local-world-favorite-groups='localWorldFavoriteGroups'
:local-world-favorites='localWorldFavorites')
el-tab-pane(:label='$t("view.favorite.avatars.header")' lazy)
div(style='display: flex; align-items: center; justify-content: space-between')
div
@@ -681,9 +407,7 @@ mixin favoritesTab
icon='el-icon-delete'
circle
style='margin-left: 5px')
.x-friend-list(
style='margin-top: 10px'
v-if='localFavoriteShowDelayedContent[1] && localAvatarFavorites[group].length')
.x-friend-list(style='margin-top: 10px' v-if='localAvatarFavorites[group].length')
div(
style='display: inline-block; width: 300px; margin-right: 15px'
v-for='favorite in localAvatarFavorites[group]'

View File

@@ -105,7 +105,7 @@ mixin notificationsTab
@click='showFullscreenImageDialog(scope.row.imageUrl)')
el-table-column(:label='$t("table.notification.message")' prop='message')
template(#default='scope')
span.x-link(v-if='scope.row.type === "invite"')
span.x-link(v-if='scope.row.type === "invite"' style='display: flex')
location(
v-if='scope.row.details'
:location='scope.row.details.worldId'

View File

@@ -146,7 +146,3 @@
}
};
</script>
<style scoped>
/* your component styles here */
</style>

View File

@@ -64,7 +64,7 @@
import Location from '../../components/common/Location.vue';
export default {
name: 'PreviousInstanceInfo',
name: 'PreviousInstanceInfoDialog',
components: {
Location
},

View File

@@ -0,0 +1,931 @@
<template>
<el-dialog
ref="worldDialog"
class="x-dialog x-world-dialog"
:visible.sync="worldDialog.visible"
:show-close="false"
width="770px">
<div v-loading="worldDialog.loading">
<div style="display: flex">
<el-popover placement="right" width="500px" trigger="click">
<img
slot="reference"
v-lazy="worldDialog.ref.thumbnailImageUrl"
class="x-link"
style="flex: none; width: 160px; height: 120px; border-radius: 12px" />
<img
v-lazy="worldDialog.ref.imageUrl"
class="x-link"
style="width: 500px; height: 375px"
@click="showFullscreenImageDialog(worldDialog.ref.imageUrl)" />
</el-popover>
<div style="flex: 1; display: flex; align-items: center; margin-left: 15px">
<div style="flex: 1">
<div>
<i
v-show="
API.currentUser.$homeLocation &&
API.currentUser.$homeLocation.worldId === worldDialog.id
"
class="el-icon-s-home"
style="margin-right: 5px" />
<span class="dialog-title" v-text="worldDialog.ref.name" />
</div>
<div style="margin-top: 5px">
<span
class="x-link x-grey"
style="font-family: monospace"
@click="showUserDialog(worldDialog.ref.authorId)"
v-text="worldDialog.ref.authorName" />
</div>
<div>
<el-tag
v-if="worldDialog.ref.$isLabs"
type="primary"
effect="plain"
size="mini"
style="margin-right: 5px; margin-top: 5px">
{{ $t('dialog.world.tags.labs') }}
</el-tag>
<el-tag
v-else-if="worldDialog.ref.releaseStatus === 'public'"
type="success"
effect="plain"
size="mini"
style="margin-right: 5px; margin-top: 5px">
{{ $t('dialog.world.tags.public') }}
</el-tag>
<el-tag
v-else
type="danger"
effect="plain"
size="mini"
style="margin-right: 5px; margin-top: 5px">
{{ $t('dialog.world.tags.private') }}
</el-tag>
<el-tag
v-if="worldDialog.isPC"
class="x-tag-platform-pc"
type="info"
effect="plain"
size="mini"
style="margin-right: 5px; margin-top: 5px">
PC
</el-tag>
<span
v-if="worldDialog.bundleSizes['standalonewindows']"
class="x-grey"
style="margin-left: 5px; border-left: inherit; padding-left: 5px">
{{ worldDialog.bundleSizes['standalonewindows'].fileSize }}
</span>
<el-tag
v-if="worldDialog.isQuest"
class="x-tag-platform-quest"
type="info"
effect="plain"
size="mini"
style="margin-right: 5px; margin-top: 5px">
Android
</el-tag>
<span
v-if="worldDialog.bundleSizes['android']"
class="x-grey"
style="margin-left: 5px; border-left: inherit; padding-left: 5px">
{{ worldDialog.bundleSizes['android'].fileSize }}
</span>
<el-tag
v-if="worldDialog.isIos"
class="x-tag-platform-ios"
type="info"
effect="plain"
size="mini"
style="margin-right: 5px; margin-top: 5px">
iOS
</el-tag>
<span
v-if="worldDialog.bundleSizes['ios']"
class="x-grey"
style="margin-left: 5px; border-left: inherit; padding-left: 5px">
{{ worldDialog.bundleSizes['ios'].fileSize }}
</span>
<el-tag
v-if="worldDialog.avatarScalingDisabled"
type="warning"
effect="plain"
size="mini"
style="margin-right: 5px; margin-top: 5px">
{{ $t('dialog.world.tags.avatar_scaling_disabled') }}
</el-tag>
<el-tag
v-if="worldDialog.focusViewDisabled"
type="warning"
effect="plain"
size="mini"
style="margin-right: 5px; margin-top: 5px">
{{ $t('dialog.world.tags.focus_view_disabled') }}
</el-tag>
<el-tag
v-if="worldDialog.stickersDisabled"
type="warning"
effect="plain"
size="mini"
style="margin-right: 5px; margin-top: 5px">
{{ $t('dialog.world.tags.stickers_disabled') }}
</el-tag>
<el-tag
v-if="worldDialog.ref.unityPackageUrl"
type="success"
effect="plain"
size="mini"
style="margin-right: 5px; margin-top: 5px">
{{ $t('dialog.world.tags.future_proofing') }}
</el-tag>
<el-tag
v-if="worldDialog.inCache"
class="x-link"
type="info"
effect="plain"
size="mini"
style="margin-right: 5px; margin-top: 5px"
@click="openFolderGeneric(worldDialog.cachePath)">
<span v-text="worldDialog.cacheSize" />
| {{ $t('dialog.world.tags.cache') }}
</el-tag>
</div>
<div>
<template v-for="tag in worldDialog.ref.tags">
<el-tag
v-if="tag.startsWith('content_')"
:key="tag"
effect="plain"
size="mini"
style="margin-right: 5px; margin-top: 5px">
<template v-if="tag === 'content_horror'">
{{ $t('dialog.world.tags.content_horror') }}
</template>
<template v-else-if="tag === 'content_gore'">
{{ $t('dialog.world.tags.content_gore') }}
</template>
<template v-else-if="tag === 'content_violence'">
{{ $t('dialog.world.tags.content_violence') }}
</template>
<template v-else-if="tag === 'content_adult'">
{{ $t('dialog.world.tags.content_adult') }}
</template>
<template v-else-if="tag === 'content_sex'">
{{ $t('dialog.world.tags.content_sex') }}
</template>
<template v-else>
{{ tag.replace('content_', '') }}
</template>
</el-tag>
</template>
</div>
<div style="margin-top: 5px">
<span
v-show="worldDialog.ref.name !== worldDialog.ref.description"
style="font-size: 12px"
>{{ worldDialog.ref.description }}</span
>
</div>
</div>
<div style="flex: none; margin-left: 10px">
<el-tooltip
v-if="worldDialog.inCache"
placement="top"
:content="$t('dialog.world.actions.delete_cache_tooltip')"
:disabled="hideTooltips">
<el-button
icon="el-icon-delete"
circle
:disabled="isGameRunning && worldDialog.cacheLocked"
@click="deleteVRChatCache(worldDialog.ref)" />
</el-tooltip>
<el-tooltip
v-if="worldDialog.isFavorite"
placement="top"
:content="$t('dialog.world.actions.favorites_tooltip')"
:disabled="hideTooltips">
<el-button
type="default"
:icon="worldDialog.isFavorite ? 'el-icon-star-on' : 'el-icon-star-off'"
circle
style="margin-left: 5px"
@click="worldDialogCommand('Add Favorite')" />
</el-tooltip>
<el-dropdown
trigger="click"
size="small"
style="margin-left: 5px"
@command="worldDialogCommand">
<el-button type="default" icon="el-icon-more" circle />
<el-dropdown-menu slot="dropdown">
<el-dropdown-item icon="el-icon-refresh" command="Refresh">
{{ $t('dialog.world.actions.refresh') }}
</el-dropdown-item>
<el-dropdown-item icon="el-icon-share" command="Share">
{{ $t('dialog.world.actions.share') }}
</el-dropdown-item>
<el-dropdown-item icon="el-icon-s-flag" command="New Instance" divided>
{{ $t('dialog.world.actions.new_instance') }}
</el-dropdown-item>
<el-dropdown-item icon="el-icon-message" command="New Instance and Self Invite">
{{ $t('dialog.world.actions.new_instance_and_self_invite') }}
</el-dropdown-item>
<el-dropdown-item
v-if="
API.currentUser.$homeLocation &&
API.currentUser.$homeLocation.worldId === worldDialog.id
"
icon="el-icon-magic-stick"
command="Reset Home"
divided>
{{ $t('dialog.world.actions.reset_home') }}
</el-dropdown-item>
<el-dropdown-item v-else icon="el-icon-s-home" command="Make Home" divided>
{{ $t('dialog.world.actions.make_home') }}
</el-dropdown-item>
<el-dropdown-item icon="el-icon-tickets" command="Previous Instances">
{{ $t('dialog.world.actions.show_previous_instances') }}
</el-dropdown-item>
<template v-if="API.currentUser.id !== worldDialog.ref.authorId">
<el-dropdown-item icon="el-icon-picture-outline" command="Previous Images">
{{ $t('dialog.world.actions.show_previous_images') }}
</el-dropdown-item>
<el-dropdown-item
:disabled="!worldDialog.hasPersistData"
icon="el-icon-upload"
command="Delete Persistent Data">
{{ $t('dialog.world.actions.delete_persistent_data') }}
</el-dropdown-item>
</template>
<template v-else>
<el-dropdown-item icon="el-icon-edit" command="Rename">
{{ $t('dialog.world.actions.rename') }}
</el-dropdown-item>
<el-dropdown-item icon="el-icon-edit" command="Change Description">
{{ $t('dialog.world.actions.change_description') }}
</el-dropdown-item>
<el-dropdown-item icon="el-icon-edit" command="Change Capacity">
{{ $t('dialog.world.actions.change_capacity') }}
</el-dropdown-item>
<el-dropdown-item icon="el-icon-edit" command="Change Recommended Capacity">
{{ $t('dialog.world.actions.change_recommended_capacity') }}
</el-dropdown-item>
<el-dropdown-item icon="el-icon-edit" command="Change YouTube Preview">
{{ $t('dialog.world.actions.change_preview') }}
</el-dropdown-item>
<el-dropdown-item icon="el-icon-edit" command="Change Tags">
{{ $t('dialog.world.actions.change_tags') }}
</el-dropdown-item>
<el-dropdown-item icon="el-icon-edit" command="Change Allowed Domains">
{{ $t('dialog.world.actions.change_allowed_video_player_domains') }}
</el-dropdown-item>
<el-dropdown-item icon="el-icon-picture-outline" command="Change Image">
{{ $t('dialog.world.actions.change_image') }}
</el-dropdown-item>
<el-dropdown-item
v-if="worldDialog.ref.unityPackageUrl"
icon="el-icon-download"
command="Download Unity Package">
{{ $t('dialog.world.actions.download_package') }}
</el-dropdown-item>
<el-dropdown-item
v-if="
worldDialog.ref?.tags?.includes('system_approved') ||
worldDialog.ref?.tags?.includes('system_labs')
"
icon="el-icon-view"
command="Unpublish"
divided>
{{ $t('dialog.world.actions.unpublish') }}
</el-dropdown-item>
<el-dropdown-item v-else icon="el-icon-view" command="Publish" divided>
{{ $t('dialog.world.actions.publish_to_labs') }}
</el-dropdown-item>
<el-dropdown-item
:disabled="!worldDialog.hasPersistData"
icon="el-icon-upload"
command="Delete Persistent Data">
{{ $t('dialog.world.actions.delete_persistent_data') }}
</el-dropdown-item>
<el-dropdown-item icon="el-icon-delete" command="Delete" style="color: #f56c6c">
{{ $t('dialog.world.actions.delete') }}
</el-dropdown-item>
</template>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</div>
<el-tabs>
<el-tab-pane :label="$t('dialog.world.instances.header')">
<div class="">
<i class="el-icon-user" />
{{ $t('dialog.world.instances.public_count', { count: worldDialog.ref.publicOccupants }) }}
<i class="el-icon-user-solid" style="margin-left: 10px" />
{{ $t('dialog.world.instances.private_count', { count: worldDialog.ref.privateOccupants }) }}
<i class="el-icon-check" style="margin-left: 10px" />
{{
$t('dialog.world.instances.capacity_count', {
count: worldDialog.ref.recommendedCapacity,
max: worldDialog.ref.capacity
})
}}
</div>
<div v-for="room in worldDialog.rooms" :key="room.id">
<div style="margin: 5px 0">
<location-world
:locationobject="room.$location"
:currentuserid="API.currentUser.id"
:worlddialogshortname="worldDialog.$location.shortName"
@show-launch-dialog="showLaunchDialog" />
<launch
:location="room.tag"
style="margin-left: 5px"
@show-launch-dialog="showLaunchDialog" />
<el-tooltip
placement="top"
:content="$t('dialog.world.instances.self_invite_tooltip')"
:disabled="hideTooltips">
<invite-yourself
:location="room.$location.tag"
:shortname="room.$location.shortName"
style="margin-left: 5px" />
</el-tooltip>
<el-tooltip
placement="top"
:content="$t('dialog.world.instances.refresh_instance_info')"
:disabled="hideTooltips">
<el-button
size="mini"
icon="el-icon-refresh"
style="margin-left: 5px"
circle
@click="refreshInstancePlayerCount(room.tag)" />
</el-tooltip>
<el-tooltip
v-if="instanceJoinHistory.get(room.$location.tag)"
placement="top"
:content="$t('dialog.previous_instances.info')"
:disabled="hideTooltips">
<el-button
size="mini"
icon="el-icon-s-data"
style="margin-left: 5px"
plain
circle
@click="showPreviousInstanceInfoDialog(room.location)" />
</el-tooltip>
<last-join :location="room.$location.tag" :currentlocation="lastLocation.location" />
<instance-info
:location="room.tag"
:instance="room.ref"
:friendcount="room.friendCount"
:updateelement="updateInstanceInfo" />
<div
v-if="room.$location.userId || room.users.length"
class="x-friend-list"
style="margin: 10px 0; max-height: unset">
<div
v-if="room.$location.userId"
class="x-friend-item x-friend-item-border"
@click="showUserDialog(room.$location.userId)">
<template v-if="room.$location.user">
<div class="avatar" :class="userStatusClass(room.$location.user)">
<img v-lazy="userImage(room.$location.user)" />
</div>
<div class="detail">
<span
class="name"
:style="{ color: room.$location.user.$userColour }"
v-text="room.$location.user.displayName" />
<span class="extra">
{{ $t('dialog.world.instances.instance_creator') }}
</span>
</div>
</template>
<span v-else v-text="room.$location.userId" />
</div>
<div
v-for="user in room.users"
:key="user.id"
class="x-friend-item x-friend-item-border"
@click="showUserDialog(user.id)">
<div class="avatar" :class="userStatusClass(user)">
<img v-lazy="userImage(user)" />
</div>
<div class="detail">
<span
class="name"
:style="{ color: user.$userColour }"
v-text="user.displayName" />
<span v-if="user.location === 'traveling'" class="extra">
<i class="el-icon-loading" style="margin-right: 5px" />
<timer :epoch="user.$travelingToTime" />
</span>
<span v-else class="extra">
<timer :epoch="user.$location_at" />
</span>
</div>
</div>
</div>
</div>
</div>
</el-tab-pane>
<el-tab-pane :label="$t('dialog.world.info.header')" lazy>
<div class="x-friend-list" style="max-height: none">
<div class="x-friend-item" style="width: 100%; cursor: default">
<div class="detail">
<span class="name">
{{ $t('dialog.world.info.memo') }}
</span>
<el-input
v-model="worldDialog.memo"
class="extra"
type="textarea"
:rows="2"
:autosize="{ minRows: 1, maxRows: 20 }"
:placeholder="$t('dialog.world.info.memo_placeholder')"
size="mini"
resize="none"
@change="onWorldMemoChange" />
</div>
</div>
<div style="width: 100%; display: flex">
<div class="x-friend-item" style="width: 100%; cursor: default">
<div class="detail">
<span class="name">
{{ $t('dialog.world.info.id') }}
</span>
<span class="extra" style="display: inline">
{{ worldDialog.id }}
</span>
<el-tooltip
placement="top"
:content="$t('dialog.world.info.id_tooltip')"
:disabled="hideTooltips">
<el-dropdown
trigger="click"
size="mini"
style="margin-left: 5px"
@click.native.stop>
<el-button type="default" icon="el-icon-s-order" size="mini" circle />
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="copyWorldId(worldDialog.id)">
{{ $t('dialog.world.info.copy_id') }}
</el-dropdown-item>
<el-dropdown-item @click.native="copyWorldUrl(worldDialog.id)">
{{ $t('dialog.world.info.copy_url') }}
</el-dropdown-item>
<el-dropdown-item @click.native="copyWorldName(worldDialog.ref.name)">
{{ $t('dialog.world.info.copy_name') }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-tooltip>
</div>
</div>
</div>
<div
v-if="worldDialog.ref.previewYoutubeId"
class="x-friend-item"
style="width: 350px"
@click="
openExternalLink(`https://www.youtube.com/watch?v=${worldDialog.ref.previewYoutubeId}`)
">
<div class="detail">
<span class="name">
{{ $t('dialog.world.info.youtube_preview') }}
</span>
<span class="extra">
https://www.youtube.com/watch?v={{ worldDialog.ref.previewYoutubeId }}
</span>
</div>
</div>
<div class="x-friend-item" style="width: 100%; cursor: default">
<div class="detail">
<span class="name">
{{ $t('dialog.world.info.author_tags') }}
</span>
<span
v-if="
worldDialog.ref.tags?.filter((tag) => tag.startsWith('author_tag')).length > 0
"
class="extra">
{{ worldTags }}
</span>
<span v-else class="extra"> - </span>
</div>
</div>
<div class="x-friend-item" style="cursor: default">
<div class="detail">
<span class="name">
{{ $t('dialog.world.info.players') }}
</span>
<span class="extra">
{{ worldDialog.ref.occupants | commaNumber }}
</span>
</div>
</div>
<div class="x-friend-item" style="cursor: default">
<div class="detail">
<span class="name">
{{ $t('dialog.world.info.favorites') }}
</span>
<span class="extra">
{{ worldDialog.ref.favorites | commaNumber
}}<span
v-if="worldDialog.ref?.favorites > 0 && worldDialog.ref?.visits > 0"
class="extra">
({{ favoriteRate }}%)
</span>
</span>
</div>
</div>
<div class="x-friend-item" style="cursor: default">
<div class="detail">
<span class="name">
{{ $t('dialog.world.info.visits') }}
</span>
<span class="extra">
{{ worldDialog.ref.visits | commaNumber }}
</span>
</div>
</div>
<div class="x-friend-item" style="cursor: default">
<div class="detail">
<span class="name">
{{ $t('dialog.world.info.capacity') }}
</span>
<span class="extra">
{{ worldDialog.ref.recommendedCapacity | commaNumber }} ({{
worldDialog.ref.capacity | commaNumber
}})
</span>
</div>
</div>
<div class="x-friend-item" style="cursor: default">
<div class="detail">
<span class="name">
{{ $t('dialog.world.info.created_at') }}
</span>
<span class="extra">
{{ worldDialog.ref.created_at | formatDate('long') }}
</span>
</div>
</div>
<div class="x-friend-item" style="cursor: default">
<div class="detail">
<span class="name">
{{ $t('dialog.world.info.last_updated') }}
</span>
<span v-if="worldDialog.lastUpdated" class="extra">
{{ worldDialog.lastUpdated | formatDate('long') }}
</span>
<span v-else class="extra">
{{ worldDialog.ref.updated_at | formatDate('long') }}
</span>
</div>
</div>
<div
v-if="worldDialog.ref.labsPublicationDate !== 'none'"
class="x-friend-item"
style="cursor: default">
<div class="detail">
<span class="name">
{{ $t('dialog.world.info.labs_publication_date') }}
</span>
<span class="extra">
{{ worldDialog.ref.labsPublicationDate | formatDate('long') }}
</span>
</div>
</div>
<div
v-if="worldDialog.ref.publicationDate !== 'none'"
class="x-friend-item"
style="cursor: default">
<div class="detail">
<span class="name" style="display: inline">
{{ $t('dialog.world.info.publication_date') }}
</span>
<el-tooltip v-if="isTimeInLabVisible" placement="top" style="margin-left: 5px">
<template slot="content">
<span>
{{ $t('dialog.world.info.time_in_labs') }}
{{ timeInLab }}
</span>
</template>
<i class="el-icon-arrow-down" />
</el-tooltip>
<span class="extra">
{{ worldDialog.ref.publicationDate | formatDate('long') }}
</span>
</div>
</div>
<div class="x-friend-item" style="cursor: default">
<div class="detail">
<span class="name">
{{ $t('dialog.world.info.version') }}
</span>
<span class="extra" v-text="worldDialog.ref.version" />
</div>
</div>
<div class="x-friend-item" style="cursor: default">
<div class="detail">
<span class="name">
{{ $t('dialog.world.info.heat') }}
</span>
<span class="extra">
{{ worldDialog.ref.heat | commaNumber }} {{ '🔥'.repeat(worldDialog.ref.heat) }}
</span>
</div>
</div>
<div class="x-friend-item" style="cursor: default">
<div class="detail">
<span class="name">
{{ $t('dialog.world.info.popularity') }}
</span>
<span class="extra">
{{ worldDialog.ref.popularity | commaNumber }}
{{ '💖'.repeat(worldDialog.ref.popularity) }}
</span>
</div>
</div>
<div class="x-friend-item" style="width: 100%; cursor: default">
<div class="detail">
<span class="name">
{{ $t('dialog.world.info.platform') }}
</span>
<span class="extra" style="white-space: normal">{{ worldDialogPlatform }}</span>
</div>
</div>
<div class="x-friend-item" style="cursor: default">
<div class="detail">
<span class="name">
{{ $t('dialog.world.info.last_visited') }}
<el-tooltip
v-if="!hideTooltips"
placement="top"
style="margin-left: 5px"
:content="$t('dialog.world.info.accuracy_notice')"
><i class="el-icon-warning"></i
></el-tooltip>
</span>
<span class="extra">{{ worldDialog.lastVisit | formatDate('long') }}</span>
</div>
</div>
<el-tooltip
:disabled="hideTooltips"
placement="top"
:content="$t('dialog.user.info.open_previouse_instance')">
<div class="x-friend-item" @click="showPreviousInstancesWorldDialog(worldDialog.ref)">
<div class="detail">
<span class="name">
{{ $t('dialog.world.info.visit_count') }}
<el-tooltip
v-if="!hideTooltips"
placement="top"
style="margin-left: 5px"
:content="$t('dialog.world.info.accuracy_notice')"
><i class="el-icon-warning"></i
></el-tooltip>
</span>
<span class="extra">
{{ worldDialog.visitCount }}
</span>
</div>
</div>
</el-tooltip>
<div class="x-friend-item" style="cursor: default">
<div class="detail">
<span class="name"
>{{ $t('dialog.world.info.time_spent') }}
<el-tooltip
v-if="!hideTooltips"
placement="top"
style="margin-left: 5px"
:content="$t('dialog.world.info.accuracy_notice')">
<i class="el-icon-warning"></i>
</el-tooltip>
</span>
<span class="extra">
{{ worldDialog.timeSpent === 0 ? ' - ' : timeSpent }}
</span>
</div>
</div>
</div>
</el-tab-pane>
<el-tab-pane :label="$t('dialog.world.json.header')" style="max-height: 50vh" lazy>
<el-button
type="default"
size="mini"
icon="el-icon-refresh"
circle
@click="refreshWorldDialogTreeData()"></el-button>
<el-button
type="default"
size="mini"
icon="el-icon-download"
circle
style="margin-left: 5px"
@click="downloadAndSaveJson(worldDialog.id, worldDialog.ref)"></el-button>
<el-tree :data="treeData" style="margin-top: 5px; font-size: 12px">
<template slot-scope="scope">
<span>
<span style="font-weight: bold; margin-right: 5px" v-text="scope.data.key"></span>
<span v-if="!scope.data.children" v-text="scope.data.value"></span>
</span>
</template>
</el-tree>
</el-tab-pane>
</el-tabs>
</div>
</el-dialog>
</template>
<script>
import utils from '../../classes/utils';
import database from '../../repository/database.js';
export default {
name: 'WorldDialog',
inject: ['API', 'showUserDialog', 'userStatusClass', 'userImage', 'adjustDialogZ'],
props: {
worldDialog: Object,
hideTooltips: Boolean,
isGameRunning: Boolean,
lastLocation: Object,
instanceJoinHistory: Map,
// ok
updateInstanceInfo: Number
},
data() {
return {
treeData: []
};
},
computed: {
isTimeInLabVisible() {
return (
this.worldDialog.ref.publicationDate &&
this.worldDialog.ref.publicationDate !== 'none' &&
this.worldDialog.ref.labsPublicationDate &&
this.worldDialog.ref.labsPublicationDate !== 'none'
);
},
timeInLab() {
return utils.timeToText(
new Date(this.worldDialog.ref?.publicationDate) -
new Date(this.worldDialog.ref?.labsPublicationDate)
);
},
favoriteRate() {
return (
Math.round(
(((this.worldDialog.ref?.favorites - this.worldDialog.ref?.visits) /
this.worldDialog.ref?.visits) *
100 +
100) *
100
) / 100
);
},
worldTags() {
return this.worldDialog.ref?.tags
.filter((tag) => tag.startsWith('author_tag'))
.map((tag) => tag.replace('author_tag_', ''))
.join(', ');
},
timeSpent() {
return utils.timeToText(this.worldDialog.timeSpent);
},
worldDialogPlatform() {
const { ref } = this.worldDialog;
const platforms = [];
if (ref.unityPackages) {
for (const unityPackage of ref.unityPackages) {
let platform = 'PC';
if (unityPackage.platform === 'standalonewindows') {
platform = 'PC';
} else if (unityPackage.platform === 'android') {
platform = 'Android';
} else if (unityPackage.platform) {
({ platform } = unityPackage);
}
platforms.unshift(`${platform}/${unityPackage.unityVersion}`);
}
}
return platforms.join(', ');
}
},
watch: {
'worldDialog.visible'(value) {
if (value) {
this.$nextTick(() => this.adjustDialogZ(this.$refs.worldDialog.$el));
}
}
},
methods: {
showFullscreenImageDialog(imageUrl) {
this.$emit('show-fullscreen-image-dialog', imageUrl);
},
openFolderGeneric(path) {
this.$emit('open-folder-generic', path);
},
deleteVRChatCache(world) {
this.$emit('delete-vrchat-cache', world);
},
worldDialogCommand(command) {
if (command === 'Share') {
this.copyWorldUrl();
} else {
this.$emit('world-dialog-command', command);
}
},
showLaunchDialog(location, shortName) {
this.$emit('show-launch-dialog', location, shortName);
},
refreshInstancePlayerCount(tag) {
this.$emit('refresh-instance-player-count', tag);
},
showPreviousInstanceInfoDialog(location) {
this.$emit('show-previous-instance-info-dialog', location);
},
onWorldMemoChange() {
const worldId = this.worldDialog.id;
const memo = this.worldDialog.memo;
if (memo) {
database.setWorldMemo({
worldId,
editedAt: new Date().toJSON(),
memo
});
} else {
database.deleteWorldMemo(id);
}
},
showPreviousInstancesWorldDialog(world) {
this.$emit('show-previous-instances-world-dialog', world);
},
refreshWorldDialogTreeData() {
this.treeData = utils.buildTreeData(this.worldDialog.ref);
},
downloadAndSaveJson(id, ref) {
this.$emit('download-and-save-json', id, ref);
},
copyWorldId() {
navigator.clipboard
.writeText(this.worldDialog.id)
.then(() => {
this.$message({
message: 'World ID copied to clipboard',
type: 'success'
});
})
.catch((err) => {
console.error('copy failed:', err);
this.$message({
message: 'Copy failed',
type: 'error'
});
});
},
copyWorldUrl() {
navigator.clipboard
.writeText(`https://vrchat.com/home/world/${this.worldDialog.id}`)
.then(() => {
this.$message({
message: 'World URL copied to clipboard',
type: 'success'
});
})
.catch((err) => {
console.error('copy failed:', err);
this.$message({
message: 'Copy failed',
type: 'error'
});
});
},
copyWorldName() {
navigator.clipboard
.writeText(this.worldDialog.ref.name)
.then(() => {
this.$message({
message: 'World name copied to clipboard',
type: 'success'
});
})
.catch((err) => {
console.error('copy failed:', err);
this.$message({
message: 'Copy failed',
type: 'error'
});
});
}
}
};
</script>