refactor: dialogs (#1200)

This commit is contained in:
pa
2025-03-26 21:18:05 +09:00
committed by GitHub
parent c4bbb45403
commit 242bcfed22
31 changed files with 1694 additions and 1135 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -77,18 +77,12 @@ doctype html
include ./mixins/dialogs/userDialog.pug
+userDialog
include ./mixins/dialogs/worldDialog.pug
+worldDialog
include ./mixins/dialogs/avatarDialog.pug
+avatarDialog
include ./mixins/dialogs/groupDialog.pug
+groupDialog
include ./mixins/dialogs/favoritesDialog.pug
+favoritesDialog
include ./mixins/dialogs/images.pug
+images
@@ -119,23 +113,35 @@ doctype html
include ./mixins/dialogs/settings.pug
+settings
include ./mixins/dialogs/previousInstances.pug
+previousInstances
include ./mixins/dialogs/tags.pug
+tags
include ./mixins/dialogs/boops.pug
+boops
//- previous instances
previous-instances-info-dialog(v-bind='previousInstancesInfoDialogBind' v-on='previousInstancesInfoDialogEvent')
previous-instances-user-dialog(v-bind='previousInstancesUserDialogBind' v-on='previousInstancesUserDialogEvent')
//- favorites
friend-import-dialog(v-bind='friendImportDialogBind' v-on='friendImportDialogEvent')
world-import-dialog(v-bind='worldImportDialogBind' v-on='worldImportDialogEvent')
avatar-import-dialog(v-bind='avatarImportDialogBind' v-on='avatarImportDialogEvent')
//- favorites dialog
favorite-dialog(v-bind='favoriteDialogBind' v-on='favoriteDialogEvent')
export-friends-list-dialog(v-bind='exportFriendsListDialogBind' v-on='exportFriendsListDialogEvent')
export-avatars-list-dialog(v-bind='exportAvatarsListDialogBind' v-on='exportAvatarsListDialogEvent')
//- launch
launch-dialog(v-bind='launchDialogBind' v-on='launchDialogEvent')
new-instance-dialog(v-bind='newInstanceDialogBind')
//- world
world-dialog(v-bind='worldDialogBind' v-on='worldDialogEvent')
//- el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="templateDialog" :visible.sync="templateDialog.visible" :title="$t('dialog.template_dialog.header')" width="450px")

View File

@@ -76,7 +76,7 @@ export default class extends baseClass {
}
switch (L.accessType) {
case 'public':
L.joinUrl = this.getLaunchURL(L);
L.joinUrl = $utils.getLaunchURL(L);
L.accessName = `Public #${L.instanceName} (${platform})`;
break;
case 'invite+':

View File

@@ -424,5 +424,29 @@ export default {
}
// no match return origin url
return url;
},
replaceVrcPackageUrl(url) {
if (!url) {
return '';
}
return url.replace('https://api.vrchat.cloud/', 'https://vrchat.com/');
},
getLaunchURL(instance) {
var L = instance;
if (L.instanceId) {
if (L.shortName) {
return `https://vrchat.com/home/launch?worldId=${encodeURIComponent(
L.worldId
)}&instanceId=${encodeURIComponent(
L.instanceId
)}&shortName=${encodeURIComponent(L.shortName)}`;
}
return `https://vrchat.com/home/launch?worldId=${encodeURIComponent(
L.worldId
)}&instanceId=${encodeURIComponent(L.instanceId)}`;
}
return `https://vrchat.com/home/launch?worldId=${encodeURIComponent(
L.worldId
)}`;
}
};

View File

@@ -76,7 +76,7 @@
size="mini"
circle
style="margin-left: 5px"
@click.stop="showFavoriteDialog"></el-button>
@click.stop="showFavoriteDialog('avatar', favorite.id)"></el-button>
</el-tooltip>
</template>
<template v-else>
@@ -112,7 +112,7 @@
size="mini"
circle
style="margin-left: 5px"
@click.stop="showFavoriteDialog"
@click.stop="showFavoriteDialog('avatar', favorite.id)"
/></el-tooltip>
</template>
<template v-else>
@@ -136,7 +136,7 @@
export default {
name: 'FavoritesAvatarItem',
inject: ['API'],
inject: ['API', 'showFavoriteDialog'],
props: {
favorite: Object,
group: [Object, String],
@@ -226,9 +226,6 @@
this.moveFavorite(this.favorite.ref, groupAPI, 'avatar');
}
},
showFavoriteDialog() {
this.$emit('show-favorite-dialog', 'avatar', this.favorite.id);
},
removeLocalAvatarFavorite() {
this.$emit('remove-local-avatar-favorite', this.favorite.id, this.group);
}

View File

@@ -46,7 +46,7 @@
<script>
export default {
name: 'FavoritesAvatarLocalHistoryItem',
inject: ['API'],
inject: ['API', 'showFavoriteDialog'],
props: {
favorite: {
type: Object,
@@ -62,9 +62,6 @@
methods: {
selectAvatarWithConfirmation() {
this.$emit('select-avatar-with-confirmation', this.favorite.id);
},
showFavoriteDialog() {
this.$emit('show-favorite-dialog', 'avatar', this.favorite.id);
}
}
};

View File

@@ -93,7 +93,6 @@
:edit-favorites-mode="editFavoritesMode"
style="display: inline-block; width: 300px; margin-right: 15px"
@handle-select="favorite.$selected = $event"
@show-favorite-dialog="showFavoriteDialog"
@remove-local-avatar-favorite="removeLocalAvatarFavorite"
@select-avatar-with-confirmation="selectAvatarWithConfirmation"
@click="showAvatarDialog(favorite.id)" />
@@ -134,7 +133,6 @@
:favorite="favorite"
:hide-tooltips="hideTooltips"
@select-avatar-with-confirmation="selectAvatarWithConfirmation"
@show-favorite-dialog="showFavoriteDialog"
@click="showAvatarDialog(favorite.id)"></favorites-avatar-local-history-item>
</div>
<div
@@ -207,7 +205,6 @@
:shift-held="shiftHeld"
:edit-favorites-mode="editFavoritesMode"
@handle-select="favorite.$selected = $event"
@show-favorite-dialog="showFavoriteDialog"
@remove-local-avatar-favorite="removeLocalAvatarFavorite"
@select-avatar-with-confirmation="selectAvatarWithConfirmation"
@click="showAvatarDialog(favorite.id)"></favorites-avatar-item>
@@ -367,9 +364,6 @@
changeFavoriteGroupName(group) {
this.$emit('change-favorite-group-name', group);
},
showFavoriteDialog(type, id) {
this.$emit('show-favorite-dialog', type, id);
},
removeLocalAvatarFavorite(id, group) {
this.$emit('remove-local-avatar-favorite', id, group);
},

View File

@@ -87,7 +87,7 @@
import { favoriteRequest } from '../../classes/request';
export default {
components: { Location },
inject: ['showUserDialog', 'userImage', 'userStatusClass', 'API'],
inject: ['showUserDialog', 'userImage', 'userStatusClass', 'API', 'showFavoriteDialog'],
props: {
favorite: {
type: Object,
@@ -138,9 +138,6 @@
// }
// }
// });
},
showFavoriteDialog(param1, param2) {
this.$emit('show-favorite-dialog', param1, param2);
}
}
};

View File

@@ -81,7 +81,7 @@
circle
style="margin-left: 5px"
type="default"
@click.stop="showFavoriteDialog(favorite.id)"></el-button>
@click.stop="showFavoriteDialog('world', favorite.id)"></el-button>
</el-tooltip>
</template>
<el-tooltip
@@ -103,7 +103,7 @@
circle
style="margin-left: 5px"
type="default"
@click.stop="showFavoriteDialog(favorite.id)"></el-button>
@click.stop="showFavoriteDialog('world', favorite.id)"></el-button>
</el-tooltip>
</template>
<template v-else>
@@ -133,7 +133,7 @@
export default {
name: 'FavoritesWorldItem',
inject: ['API'],
inject: ['API', 'showFavoriteDialog'],
props: {
group: [Object, String],
favorite: Object,
@@ -225,9 +225,6 @@
}
return args;
});
},
showFavoriteDialog(favoriteId) {
this.$emit('show-favorite-dialog', 'world', favoriteId);
}
}
};

View File

@@ -128,8 +128,7 @@
:shift-held="shiftHeld"
@click="showWorldDialog(favorite.id)"
@handle-select="favorite.$selected = $event"
@new-instance-self-invite="newInstanceSelfInvite"
@show-favorite-dialog="showFavoriteDialog" />
@new-instance-self-invite="newInstanceSelfInvite" />
</div>
<div
v-else
@@ -200,8 +199,7 @@
:shift-held="shiftHeld"
@click="showWorldDialog(favorite.id)"
@new-instance-self-invite="newInstanceSelfInvite"
@remove-local-world-favorite="removeLocalWorldFavorite"
@show-favorite-dialog="showFavoriteDialog" />
@remove-local-world-favorite="removeLocalWorldFavorite" />
</div>
<div
v-else
@@ -431,9 +429,7 @@
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);
}

View File

@@ -1,100 +0,0 @@
mixin favoritesDialog
//- dialog: favorite
el-dialog.x-dialog(
:before-close='beforeDialogClose'
@mousedown.native='dialogMouseDown'
@mouseup.native='dialogMouseUp'
ref='favoriteDialog'
:visible.sync='favoriteDialog.visible'
:title='$t("dialog.favorite.header")'
width='300px')
div(v-if='favoriteDialog.visible' v-loading='favoriteDialog.loading')
span(style='display: block; text-align: center') {{ $t('dialog.favorite.vrchat_favorites') }}
template(v-if='favoriteDialog.currentGroup && favoriteDialog.currentGroup.key')
el-button(
style='display: block; width: 100%; margin: 10px 0'
@click='deleteFavoriteNoConfirm(favoriteDialog.objectId)') #[i.el-icon-check] {{ favoriteDialog.currentGroup.displayName }} ({{ favoriteDialog.currentGroup.count }} / {{ favoriteDialog.currentGroup.capacity }})
template(v-else)
el-button(
v-for='group in favoriteDialog.groups'
:key='group.key'
style='display: block; width: 100%; margin: 10px 0'
@click='addFavorite(group)') {{ group.displayName }} ({{ group.count }} / {{ group.capacity }})
div(v-if='favoriteDialog.visible && favoriteDialog.type === "world"' style='margin-top: 20px')
span(style='display: block; text-align: center') {{ $t('dialog.favorite.local_favorites') }}
template(v-for='group in localWorldFavoriteGroups')
el-button(
v-if='hasLocalWorldFavorite(favoriteDialog.objectId, group)'
:key='group'
style='display: block; width: 100%; margin: 10px 0'
@click='removeLocalWorldFavorite(favoriteDialog.objectId, group)') #[i.el-icon-check] {{ group }} ({{ getLocalWorldFavoriteGroupLength(group) }})
el-button(
v-else
style='display: block; width: 100%; margin: 10px 0'
:key='group'
@click='addLocalWorldFavorite(favoriteDialog.objectId, group)') {{ group }} ({{ getLocalWorldFavoriteGroupLength(group) }})
div(v-if='favoriteDialog.visible && favoriteDialog.type === "avatar"' style='margin-top: 20px')
span(style='display: block; text-align: center') {{ $t('dialog.favorite.local_avatar_favorites') }}
template(v-for='group in localAvatarFavoriteGroups')
el-button(
v-if='hasLocalAvatarFavorite(favoriteDialog.objectId, group)'
:key='group'
style='display: block; width: 100%; margin: 10px 0'
@click='removeLocalAvatarFavorite(favoriteDialog.objectId, group)') #[i.el-icon-check] {{ group }} ({{ getLocalAvatarFavoriteGroupLength(group) }})
el-button(
v-else
:key='group'
style='display: block; width: 100%; margin: 10px 0'
:disabled='!isLocalUserVrcplusSupporter()'
@click='addLocalAvatarFavorite(favoriteDialog.objectId, group)') {{ group }} ({{ getLocalAvatarFavoriteGroupLength(group) }})
//- dialog: export friends list
el-dialog.x-dialog(
:before-close='beforeDialogClose'
@mousedown.native='dialogMouseDown'
@mouseup.native='dialogMouseUp'
:visible.sync='exportFriendsListDialog'
:title='$t("dialog.export_friends_list.header")'
width='650px')
el-tabs(type='card')
el-tab-pane(:label='$t("dialog.export_friends_list.csv")')
el-input(
type='textarea'
v-if='exportFriendsListDialog'
v-model='exportFriendsListCsv'
size='mini'
rows='15'
resize='none'
readonly
style='margin-top: 15px'
@click.native='$event.target.tagName === "TEXTAREA" && $event.target.select()')
el-tab-pane(:label='$t("dialog.export_friends_list.json")')
el-input(
type='textarea'
v-if='exportFriendsListDialog'
v-model='exportFriendsListJson'
size='mini'
rows='15'
resize='none'
readonly
style='margin-top: 15px'
@click.native='$event.target.tagName === "TEXTAREA" && $event.target.select()')
//- dialog: export avatars list
el-dialog.x-dialog(
:before-close='beforeDialogClose'
@mousedown.native='dialogMouseDown'
@mouseup.native='dialogMouseUp'
:visible.sync='exportAvatarsListDialog'
:title='$t("dialog.export_own_avatars.header")'
width='650px')
el-input(
type='textarea'
v-if='exportAvatarsListDialog'
v-model='exportAvatarsListCsv'
size='mini'
rows='15'
resize='none'
readonly
style='margin-top: 15px'
@click.native='$event.target.tagName === "TEXTAREA" && $event.target.select()')

View File

@@ -1,132 +0,0 @@
mixin previousInstances
//- dialog Table: Previous Instances User
el-dialog.x-dialog(
:before-close='beforeDialogClose'
@mousedown.native='dialogMouseDown'
@mouseup.native='dialogMouseUp'
ref='previousInstancesUserDialog'
:visible.sync='previousInstancesUserDialog.visible'
:title='$t("dialog.previous_instances.header")'
width='1000px')
div(style='display: flex; align-items: center; justify-content: space-between')
span(v-text='previousInstancesUserDialog.userRef.displayName' style='font-size: 14px')
el-input(
v-model='previousInstancesUserDialogTable.filters[0].value'
:placeholder='$t("dialog.previous_instances.search_placeholder")'
style='display: block; width: 150px')
data-tables(
v-if='previousInstancesUserDialog.visible'
v-bind='previousInstancesUserDialogTable'
v-loading='previousInstancesUserDialog.loading'
style='margin-top: 10px')
el-table-column(:label='$t("table.previous_instances.date")' prop='created_at' sortable width='170')
template(#default='scope')
span {{ scope.row.created_at | formatDate('long') }}
el-table-column(:label='$t("table.previous_instances.world")' prop='name' sortable)
template(#default='scope')
location(
:location='scope.row.location'
:hint='scope.row.worldName'
:grouphint='scope.row.groupName')
el-table-column(:label='$t("table.previous_instances.instance_creator")' prop='location' width='170')
template(#default='scope')
display-name(
:userid='scope.row.$location.userId'
:location='scope.row.$location.tag'
:force-update-key='previousInstancesUserDialog.forceUpdate')
el-table-column(:label='$t("table.previous_instances.time")' prop='time' width='100' sortable)
template(#default='scope')
span(v-text='scope.row.timer')
el-table-column(:label='$t("table.previous_instances.action")' width='90' align='right')
template(#default='scope')
el-button(
type='text'
icon='el-icon-switch-button'
size='mini'
@click='showLaunchDialog(scope.row.location)')
el-button(
type='text'
icon='el-icon-s-data'
size='mini'
@click='showPreviousInstanceInfoDialog(scope.row.location)')
el-button(
v-if='shiftHeld'
style='color: #f56c6c'
type='text'
icon='el-icon-close'
size='mini'
@click='deleteGameLogUserInstance(scope.row)')
el-button(
v-else
type='text'
icon='el-icon-close'
size='mini'
@click='deleteGameLogUserInstancePrompt(scope.row)')
//- dialog Table: Previous Instances World
el-dialog.x-dialog(
:before-close='beforeDialogClose'
@mousedown.native='dialogMouseDown'
@mouseup.native='dialogMouseUp'
ref='previousInstancesWorldDialog'
:visible.sync='previousInstancesWorldDialog.visible'
:title='$t("dialog.previous_instances.header")'
width='1000px')
div(style='display: flex; align-items: center; justify-content: space-between')
span(v-text='previousInstancesWorldDialog.worldRef.name' style='font-size: 14px')
el-input(
v-model='previousInstancesWorldDialogTable.filters[0].value'
:placeholder='$t("dialog.previous_instances.search_placeholder")'
style='display: block; width: 150px')
data-tables(
v-if='previousInstancesWorldDialog.visible'
v-bind='previousInstancesWorldDialogTable'
v-loading='previousInstancesWorldDialog.loading'
style='margin-top: 10px')
el-table-column(:label='$t("table.previous_instances.date")' prop='created_at' sortable width='170')
template(#default='scope')
span {{ scope.row.created_at | formatDate('long') }}
el-table-column(:label='$t("table.previous_instances.instance_name")' prop='name')
template(#default='scope')
location-world(
:locationobject='scope.row.$location'
:grouphint='scope.row.groupName'
:currentuserid='API.currentUser.id'
@show-launch-dialog='showLaunchDialog')
el-table-column(:label='$t("table.previous_instances.instance_creator")' prop='location')
template(#default='scope')
display-name(
:userid='scope.row.$location.userId'
:location='scope.row.$location.tag'
:force-update-key='previousInstancesWorldDialog.forceUpdate')
el-table-column(:label='$t("table.previous_instances.time")' prop='time' width='100' sortable)
template(#default='scope')
span(v-text='scope.row.timer')
el-table-column(:label='$t("table.previous_instances.action")' width='90' align='right')
template(#default='scope')
el-button(
type='text'
icon='el-icon-s-data'
size='mini'
@click='showPreviousInstanceInfoDialog(scope.row.location)')
el-button(
v-if='shiftHeld'
style='color: #f56c6c'
type='text'
icon='el-icon-close'
size='mini'
@click='deleteGameLogWorldInstance(scope.row)')
el-button(
v-else
type='text'
icon='el-icon-close'
size='mini'
@click='deleteGameLogWorldInstancePrompt(scope.row)')
previous-instance-info-dialog(
:visible.sync='previousInstanceInfoDialogVisible'
:instance-id='previousInstanceInfoDialogInstanceId'
:game-log-is-friend='gameLogIsFriend'
:game-log-is-favorite='gameLogIsFavorite'
:lookup-user='lookupUser'
:is-dark-mode='isDarkMode')

View File

@@ -1,56 +1,4 @@
mixin tags
//- dialog: Set World Tags
el-dialog.x-dialog(
:before-close='beforeDialogClose'
@mousedown.native='dialogMouseDown'
@mouseup.native='dialogMouseUp'
ref='setWorldTagsDialog'
:visible.sync='setWorldTagsDialog.visible'
:title='$t("dialog.set_world_tags.header")'
width='400px')
el-checkbox(v-model='setWorldTagsDialog.avatarScalingDisabled') {{ $t('dialog.set_world_tags.avatar_scaling_disabled') }}
br
el-checkbox(v-model='setWorldTagsDialog.focusViewDisabled') {{ $t('dialog.set_world_tags.focus_view_disabled') }}
br
el-checkbox(v-model='setWorldTagsDialog.debugAllowed') {{ $t('dialog.set_world_tags.enable_debugging') }}
div(style='font-size: 12px; margin-top: 10px')
| {{ $t('dialog.set_world_tags.author_tags') }} #[br]
el-input(
type='textarea'
v-model='setWorldTagsDialog.authorTags'
size='mini'
show-word-limit
:autosize='{ minRows: 2, maxRows: 5 }'
placeholder=''
style='margin-top: 10px')
div(style='font-size: 12px; margin-top: 10px')
| {{ $t('dialog.set_world_tags.content_tags') }} #[br]
el-checkbox(v-model='setWorldTagsDialog.contentHorror') {{ $t('dialog.set_world_tags.content_horror') }}
br
el-checkbox(v-model='setWorldTagsDialog.contentGore') {{ $t('dialog.set_world_tags.content_gore') }}
br
el-checkbox(v-model='setWorldTagsDialog.contentViolence') {{ $t('dialog.set_world_tags.content_violence') }}
br
el-checkbox(v-model='setWorldTagsDialog.contentAdult') {{ $t('dialog.set_world_tags.content_adult') }}
br
el-checkbox(v-model='setWorldTagsDialog.contentSex') {{ $t('dialog.set_world_tags.content_sex') }}
div(style='font-size: 12px; margin-top: 10px')
| {{ $t('dialog.set_world_tags.default_content_settings') }} #[br]
el-checkbox(v-model='setWorldTagsDialog.emoji') {{ $t('dialog.new_instance.content_emoji') }}
br
el-checkbox(v-model='setWorldTagsDialog.stickers') {{ $t('dialog.new_instance.content_stickers') }}
br
el-checkbox(v-model='setWorldTagsDialog.pedestals') {{ $t('dialog.new_instance.content_pedestals') }}
br
el-checkbox(v-model='setWorldTagsDialog.prints') {{ $t('dialog.new_instance.content_prints') }}
br
el-checkbox(v-model='setWorldTagsDialog.drones') {{ $t('dialog.new_instance.content_drones') }}
//- el-input(type="textarea" v-model="setWorldTagsDialog.contentTags" size="mini" show-word-limit :autosize="{ minRows:2, maxRows:5 }" placeholder="" style="margin-top:10px")
template(#footer)
div(style='display: flex')
el-button(size='small' @click='setWorldTagsDialog.visible = false') {{ $t('dialog.set_world_tags.cancel') }}
el-button(type='primary' size='small' @click='saveSetWorldTagsDialog') {{ $t('dialog.set_world_tags.save') }}
//- dialog: Set Avatar Tags
el-dialog.x-dialog(
:before-close='beforeDialogClose'

View File

@@ -1,44 +0,0 @@
mixin worldDialog
world-dialog(
:world-dialog='worldDialog'
:hide-tooltips='hideTooltips'
:is-game-running='isGameRunning'
:last-location='lastLocation'
:instance-join-history='instanceJoinHistory'
:update-instance-info='updateInstanceInfo'
:is-age-gated-instances-visible='isAgeGatedInstancesVisible'
@open-folder-generic='openFolderGeneric'
@delete-vrchat-cache='deleteVRChatCache'
@world-dialog-command='worldDialogCommand'
@refresh-instance-player-count='refreshInstancePlayerCount'
@show-previous-instances-world-dialog='showPreviousInstancesWorldDialog'
@download-and-save-json='downloadAndSaveJson')
//- dialog: change Allowed Video Player Domains
el-dialog.x-dialog(
:before-close='beforeDialogClose'
@mousedown.native='dialogMouseDown'
@mouseup.native='dialogMouseUp'
ref='worldAllowedDomainsDialog'
:visible.sync='worldAllowedDomainsDialog.visible'
:title='$t("dialog.allowed_video_player_domains.header")'
width='600px')
div(v-loading='bioDialog.loading')
el-input(
v-for='(domain, index) in worldAllowedDomainsDialog.urlList'
:key='index'
:value='domain'
v-model='worldAllowedDomainsDialog.urlList[index]'
size='small'
style='margin-top: 5px')
el-button(
slot='append'
icon='el-icon-delete'
@click='worldAllowedDomainsDialog.urlList.splice(index, 1)')
el-button(@click='worldAllowedDomainsDialog.urlList.push("")' size='mini' style='margin-top: 5px') {{ $t('dialog.allowed_video_player_domains.add_domain') }}
template(#footer)
el-button(
type='primary'
size='small'
:disabled='!worldAllowedDomainsDialog.worldId'
@click='saveWorldAllowedDomains') {{ $t('dialog.allowed_video_player_domains.save') }}

View File

@@ -38,7 +38,7 @@ mixin gameLogTab
template(#default='scope')
el-tooltip(placement='right' :open-delay='500' :disabled='hideTooltips')
template(#content)
span {{ $t("view.game_log.filters." + scope.row.type) }}
span {{ $t('view.game_log.filters.' + scope.row.type) }}
span.x-link(
v-if='scope.row.location && scope.row.type !== "Location"'
v-text='$t("view.game_log.filters." + scope.row.type)'
@@ -115,4 +115,4 @@ mixin gameLogTab
type='text'
icon='el-icon-s-data'
size='mini'
@click='showPreviousInstanceInfoDialog(scope.row.location)')
@click='showPreviousInstancesInfoDialog(scope.row.location)')

View File

@@ -38,17 +38,10 @@ mixin notificationsTab
span {{ scope.row.created_at | formatDate('short') }}
el-table-column(:label='$t("table.notification.type")' prop='type' width='180')
template(#default='scope')
el-tooltip(v-if='scope.row.type === "invite"' placement='top')
template(#content)
location(
v-if='scope.row.details'
:location='scope.row.details.worldId'
:hint='scope.row.details.worldName'
:grouphint='scope.row.details.groupName'
:link='false')
span.x-link(
v-text='$t("view.notification.filters." + scope.row.type)'
@click='showWorldDialog(scope.row.details.worldId)')
span.x-link(
v-if='scope.row.type === "invite"'
v-text='$t("view.notification.filters." + scope.row.type)'
@click='showWorldDialog(scope.row.details.worldId)')
el-tooltip(
v-else-if='scope.row.type === "group.queueReady" || scope.row.type === "instance.closed"'
placement='top')

View File

@@ -1,5 +1,11 @@
<template>
<el-dialog :visible.sync="isDialogVisible" :title="$t('dialog.avatar_export.header')" width="650px">
<el-dialog
:before-close="beforeDialogClose"
:visible.sync="isDialogVisible"
:title="$t('dialog.avatar_export.header')"
width="650px"
@mousedown.native="dialogMouseDown"
@mouseup.native="dialogMouseUp">
<el-checkbox-group
v-model="exportSelectedOptions"
style="margin-bottom: 10px"
@@ -82,7 +88,7 @@
<script>
export default {
name: 'AvatarExportDialog',
inject: ['API'],
inject: ['API', 'beforeDialogClose', 'dialogMouseDown', 'dialogMouseUp'],
props: {
avatarExportDialogVisible: Boolean,
favoriteAvatars: Array,

View File

@@ -1,10 +1,13 @@
<template>
<el-dialog
:before-close="beforeDialogClose"
:visible.sync="isDialogVisible"
class="x-dialog"
:title="$t('dialog.friend_export.header')"
width="650px"
destroy-on-close>
destroy-on-close
@mousedown.native="dialogMouseDown"
@mouseup.native="dialogMouseUp">
<el-dropdown trigger="click" size="small" @click.native.stop>
<el-button size="mini">
<span v-if="friendExportFavoriteGroup">
@@ -45,7 +48,7 @@
<script>
export default {
name: 'FriendExportDialog',
inject: ['API'],
inject: ['API', 'beforeDialogClose', 'dialogMouseDown', 'dialogMouseUp'],
props: {
friendExportDialogVisible: Boolean,
favoriteFriends: Array

View File

@@ -1,5 +1,11 @@
<template>
<el-dialog :visible.sync="isDialogVisible" :title="$t('dialog.world_export.header')" width="650px">
<el-dialog
:before-close="beforeDialogClose"
:visible.sync="isDialogVisible"
:title="$t('dialog.world_export.header')"
width="650px"
@mousedown.native="dialogMouseDown"
@mouseup.native="dialogMouseUp">
<el-checkbox-group
v-model="exportSelectedOptions"
style="margin-bottom: 10px"
@@ -84,7 +90,7 @@
<script>
export default {
name: 'WorldExportDialog',
inject: ['API'],
inject: ['API', 'beforeDialogClose', 'dialogMouseDown', 'dialogMouseUp'],
props: {
favoriteWorlds: Array,
worldExportDialogVisible: Boolean,

View File

@@ -0,0 +1,105 @@
<template>
<el-dialog
:before-close="beforeDialogClose"
:visible.sync="isVisible"
:title="$t('dialog.export_own_avatars.header')"
width="650px"
@mousedown.native="dialogMouseDown"
@mouseup.native="dialogMouseUp">
<el-input
v-model="exportAvatarsListCsv"
v-loading="loading"
type="textarea"
size="mini"
rows="15"
resize="none"
readonly
style="margin-top: 15px"
@click.native="$event.target.tagName === 'TEXTAREA' && $event.target.select()" />
</el-dialog>
</template>
<script>
import { avatarRequest } from '../../../classes/request';
export default {
name: 'ExportAvatarsListDialog',
inject: ['API', 'beforeDialogClose', 'dialogMouseDown', 'dialogMouseUp'],
props: {
isExportAvatarsListDialogVisible: Boolean
},
data() {
return {
exportAvatarsListCsv: '',
loading: false
};
},
computed: {
isVisible: {
get() {
return this.isExportAvatarsListDialogVisible;
},
set(value) {
this.$emit('update:is-export-avatars-list-dialog-visible', value);
}
}
},
watch: {
isExportAvatarsListDialogVisible(value) {
if (value) {
this.initExportAvatarsListDialog();
}
}
},
methods: {
initExportAvatarsListDialog() {
this.loading = true;
for (const ref of this.API.cachedAvatars.values()) {
if (ref.authorId === this.API.currentUser.id) {
this.API.cachedAvatars.delete(ref.id);
}
}
const params = {
n: 50,
offset: 0,
sort: 'updated',
order: 'descending',
releaseStatus: 'all',
user: 'me'
};
const map = new Map();
this.API.bulk({
fn: avatarRequest.getAvatars,
N: -1,
params,
handle: (args) => {
for (const json of args.json) {
const $ref = this.API.cachedAvatars.get(json.id);
if (typeof $ref !== 'undefined') {
map.set($ref.id, $ref);
}
}
},
done: () => {
const avatars = Array.from(map.values());
if (Array.isArray(avatars) === false) {
return;
}
const lines = ['AvatarID,AvatarName'];
const _ = function (str) {
if (/[\x00-\x1f,"]/.test(str) === true) {
return `"${str.replace(/"/g, '""')}"`;
}
return str;
};
for (const avatar of avatars) {
lines.push(`${_(avatar.id)},${_(avatar.name)}`);
}
this.exportAvatarsListCsv = lines.join('\n');
this.loading = false;
}
});
}
}
};
</script>

View File

@@ -0,0 +1,93 @@
<template>
<el-dialog
:before-close="beforeDialogClose"
:title="$t('dialog.export_friends_list.header')"
:visible.sync="isVisible"
width="650px"
@mousedown.native="dialogMouseDown"
@mouseup.native="dialogMouseUp">
<el-tabs type="card">
<el-tab-pane :label="$t('dialog.export_friends_list.csv')">
<el-input
v-model="exportFriendsListCsv"
type="textarea"
size="mini"
rows="15"
resize="none"
readonly
style="margin-top: 15px"
@click.native="$event.target.tagName === 'TEXTAREA' && $event.target.select()" />
</el-tab-pane>
<el-tab-pane :label="$t('dialog.export_friends_list.json')">
<el-input
v-model="exportFriendsListJson"
type="textarea"
size="mini"
rows="15"
resize="none"
readonly
style="margin-top: 15px"
@click.native="$event.target.tagName === 'TEXTAREA' && $event.target.select()" />
</el-tab-pane>
</el-tabs>
</el-dialog>
</template>
<script>
export default {
name: 'ExportFriendsListDialog',
inject: ['API', 'beforeDialogClose', 'dialogMouseDown', 'dialogMouseUp'],
props: {
friends: Map,
isExportFriendsListDialogVisible: Boolean
},
data() {
return {
exportFriendsListCsv: '',
exportFriendsListJson: ''
};
},
computed: {
isVisible: {
get() {
return this.isExportFriendsListDialogVisible;
},
set(value) {
this.$emit('update:is-export-friends-list-dialog-visible', value);
}
}
},
watch: {
isExportFriendsListDialogVisible(value) {
if (value) {
this.initExportFriendsListDialog();
}
}
},
methods: {
initExportFriendsListDialog() {
const { friends } = this.API.currentUser;
if (Array.isArray(friends) === false) {
return;
}
const lines = ['UserID,DisplayName,Memo'];
const _ = function (str) {
if (/[\x00-\x1f,"]/.test(str) === true) {
return `"${str.replace(/"/g, '""')}"`;
}
return str;
};
const friendsList = [];
for (const userId of friends) {
const ref = this.friends.get(userId);
const name = (typeof ref !== 'undefined' && ref.name) || '';
const memo = (typeof ref !== 'undefined' && ref.memo.replace(/\n/g, ' ')) || '';
lines.push(`${_(userId)},${_(name)},${_(memo)}`);
friendsList.push(userId);
}
this.exportFriendsListJson = JSON.stringify({ friends: friendsList }, null, 4);
this.exportFriendsListCsv = lines.join('\n');
}
}
};
</script>

View File

@@ -0,0 +1,193 @@
<template>
<el-dialog
ref="favoriteDialog"
:before-close="beforeDialogClose"
:visible.sync="isVisible"
:title="$t('dialog.favorite.header')"
width="300px"
@mousedown.native="dialogMouseDown"
@mouseup.native="dialogMouseUp">
<div v-loading="loading">
<span style="display: block; text-align: center">{{ $t('dialog.favorite.vrchat_favorites') }}</span>
<template v-if="favoriteDialog.currentGroup && favoriteDialog.currentGroup.key">
<el-button
style="display: block; width: 100%; margin: 10px 0"
@click="deleteFavoriteNoConfirm(favoriteDialog.objectId)">
<i class="el-icon-check"></i>
{{ favoriteDialog.currentGroup.displayName }} ({{ favoriteDialog.currentGroup.count }} /
{{ favoriteDialog.currentGroup.capacity }})
</el-button>
</template>
<template v-else>
<el-button
v-for="group in groups"
:key="group.key"
style="display: block; width: 100%; margin: 10px 0"
@click="addFavorite(group)">
{{ group.displayName }} ({{ group.count }} / {{ group.capacity }})
</el-button>
</template>
</div>
<div v-if="favoriteDialog.type === 'world'" style="margin-top: 20px">
<span style="display: block; text-align: center">{{ $t('dialog.favorite.local_favorites') }}</span>
<template v-for="group in localWorldFavoriteGroups">
<el-button
v-if="hasLocalWorldFavorite(favoriteDialog.objectId, group)"
:key="group"
style="display: block; width: 100%; margin: 10px 0"
@click="removeLocalWorldFavorite(favoriteDialog.objectId, group)">
<i class="el-icon-check"></i>
{{ group }} ({{ getLocalWorldFavoriteGroupLength(group) }})
</el-button>
<el-button
v-else
:key="group"
style="display: block; width: 100%; margin: 10px 0"
@click="addLocalWorldFavorite(favoriteDialog.objectId, group)">
{{ group }} ({{ getLocalWorldFavoriteGroupLength(group) }})
</el-button>
</template>
</div>
<div v-if="favoriteDialog.type === 'avatar'" style="margin-top: 20px">
<span style="display: block; text-align: center">{{ $t('dialog.favorite.local_avatar_favorites') }}</span>
<template v-for="group in localAvatarFavoriteGroups">
<el-button
v-if="hasLocalAvatarFavorite(favoriteDialog.objectId, group)"
:key="group"
style="display: block; width: 100%; margin: 10px 0"
@click="removeLocalAvatarFavorite(favoriteDialog.objectId, group)">
<i class="el-icon-check"></i>
{{ group }} ({{ getLocalAvatarFavoriteGroupLength(group) }})
</el-button>
<el-button
v-else
:key="group"
style="display: block; width: 100%; margin: 10px 0"
:disabled="!isLocalUserVrcplusSupporter"
@click="addLocalAvatarFavorite(favoriteDialog.objectId, group)">
{{ group }} ({{ getLocalAvatarFavoriteGroupLength(group) }})
</el-button>
</template>
</div>
</el-dialog>
</template>
<script>
import { favoriteRequest } from '../../../classes/request';
import Noty from 'noty';
export default {
name: 'FavoriteDialog',
inject: ['API', 'beforeDialogClose', 'dialogMouseDown', 'dialogMouseUp', 'adjustDialogZ'],
props: {
favoriteDialog: {
type: Object,
default: () => ({
visible: false,
type: '',
objectId: '',
currentGroup: {}
})
},
localWorldFavoriteGroups: {
type: Array,
default: () => []
},
localAvatarFavoriteGroups: {
type: Array,
default: () => []
},
hasLocalWorldFavorite: {
type: Function,
default: () => () => false
},
getLocalWorldFavoriteGroupLength: {
type: Function,
default: () => () => 0
},
hasLocalAvatarFavorite: {
type: Function,
default: () => () => false
},
getLocalAvatarFavoriteGroupLength: {
type: Function,
default: () => () => 0
}
},
data() {
return {
groups: [],
loading: false
};
},
computed: {
isVisible: {
get() {
return this.favoriteDialog.visible;
},
set(value) {
this.$emit('update:favorite-dialog', { ...this.favoriteDialog, visible: value });
}
},
isLocalUserVrcplusSupporter() {
return this.API.currentUser.$isVRCPlus;
}
},
watch: {
'favoriteDialog.visible'(value) {
if (value) {
this.initFavoriteDialog();
this.$nextTick(() => {
this.adjustDialogZ(this.$refs.favoriteDialog.$el);
});
}
}
},
methods: {
initFavoriteDialog() {
if (this.favoriteDialog.type === 'friend') {
this.groups = this.API.favoriteFriendGroups;
} else if (this.favoriteDialog.type === 'world') {
this.groups = this.API.favoriteWorldGroups;
} else if (this.favoriteDialog.type === 'avatar') {
this.groups = this.API.favoriteAvatarGroups;
}
},
addFavorite(group) {
const D = this.favoriteDialog;
this.loading = true;
favoriteRequest
.addFavorite({
type: D.type,
favoriteId: D.objectId,
tags: group.name
})
.then(() => {
this.isVisible = false;
new Noty({
type: 'success',
text: 'Favorite added'
}).show();
})
.finally(() => {
this.loading = false;
});
},
addLocalWorldFavorite(...args) {
this.$emit('add-local-world-favorite', ...args);
},
removeLocalWorldFavorite(...args) {
this.$emit('remove-local-world-favorite', ...args);
},
addLocalAvatarFavorite(...args) {
this.$emit('add-local-avatar-favorite', ...args);
},
removeLocalAvatarFavorite(...args) {
this.$emit('remove-local-avatar-favorite', ...args);
},
deleteFavoriteNoConfirm(...args) {
this.$emit('delete-favorite-no-confirm', ...args);
}
}
};
</script>

View File

@@ -67,7 +67,7 @@
{{ $t('dialog.launch.start_as_desktop') }}
</el-checkbox>
<template slot="footer">
<el-button size="small" @click="showPreviousInstanceInfoDialog(launchDialog.location)">
<el-button size="small" @click="showPreviousInstancesInfoDialog(launchDialog.location)">
{{ $t('dialog.launch.info') }}
</el-button>
<el-button
@@ -98,7 +98,7 @@
'beforeDialogClose',
'dialogMouseDown',
'dialogMouseUp',
'showPreviousInstanceInfoDialog',
'showPreviousInstancesInfoDialog',
'showInviteDialog',
'adjustDialogZ'
],
@@ -108,10 +108,6 @@
checkCanInvite: {
type: Function,
required: true
},
getLaunchURL: {
type: Function,
required: true
}
},
data() {
@@ -182,7 +178,7 @@
} else {
D.location = L.worldId;
}
D.url = this.getLaunchURL(L);
D.url = utils.getLaunchURL(L);
if (!shortName) {
const res = await instanceRequest.getInstanceShortName({
worldId: L.worldId,
@@ -197,14 +193,14 @@
if (resLocation === this.launchDialog.tag) {
const resShortName = res.json.shortName;
const secureOrShortName = res.json.shortName || res.json.secureName;
const parsedL = utils.parseLocation(location);
const parsedL = utils.parseLocation(resLocation);
parsedL.shortName = resShortName;
this.launchDialog.shortName = resShortName;
this.launchDialog.secureOrShortName = secureOrShortName;
if (resShortName) {
this.launchDialog.shortUrl = `https://vrch.at/${resShortName}`;
}
this.launchDialog.url = this.getLaunchURL(parsedL);
this.launchDialog.url = utils.getLaunchURL(parsedL);
}
}
},

View File

@@ -1,12 +1,15 @@
<template>
<el-dialog
ref="dialog"
:before-close="beforeDialogClose"
:visible="visible"
:title="$t('dialog.previous_instances.info')"
width="800px"
@close="$emit('update:visible', false)"
:fullscreen="fullscreen"
destroy-on-close>
destroy-on-close
@mousedown.native="dialogMouseDown"
@mouseup.native="dialogMouseUp"
@close="$emit('update:visible', false)">
<div style="display: flex; align-items: center; justify-content: space-between">
<location :location="location.tag" style="font-size: 14px"></location>
<el-input
@@ -64,11 +67,11 @@
import Location from '../../../components/common/Location.vue';
export default {
name: 'PreviousInstanceInfoDialog',
name: 'PreviousInstancesInfoDialog',
components: {
Location
},
inject: ['adjustDialogZ'],
inject: ['adjustDialogZ', 'beforeDialogClose', 'dialogMouseDown', 'dialogMouseUp'],
props: {
visible: {
type: Boolean,
@@ -113,19 +116,6 @@
fullscreen: false
};
},
watch: {
visible(value) {
if (value) {
this.$nextTick(() => {
this.init();
this.refreshPreviousInstanceInfoTable();
});
utils.loadEcharts().then((echarts) => {
this.echarts = echarts;
});
}
}
},
computed: {
activityDetailData() {
return this.dataTable.data.map((item) => ({
@@ -137,13 +127,26 @@
}));
}
},
watch: {
visible(value) {
if (value) {
this.$nextTick(() => {
this.init();
this.refreshPreviousInstancesInfoTable();
});
utils.loadEcharts().then((echarts) => {
this.echarts = echarts;
});
}
}
},
methods: {
init() {
this.adjustDialogZ(this.$refs.dialog.$el);
this.loading = true;
this.location = utils.parseLocation(this.instanceId);
},
refreshPreviousInstanceInfoTable() {
refreshPreviousInstancesInfoTable() {
database.getPlayersFromInstance(this.location.tag).then((data) => {
const array = [];
for (const entry of Array.from(data.values())) {

View File

@@ -0,0 +1,214 @@
<template>
<el-dialog
ref="previousInstancesUserDialog"
:before-close="beforeDialogClose"
:visible.sync="isVisible"
:title="$t('dialog.previous_instances.header')"
width="1000px"
@mousedown.native="dialogMouseDown"
@mouseup.native="dialogMouseUp">
<div style="display: flex; align-items: center; justify-content: space-between">
<span style="font-size: 14px" v-text="previousInstancesUserDialog.userRef.displayName"></span>
<el-input
v-model="previousInstancesUserDialogTable.filters[0].value"
:placeholder="$t('dialog.previous_instances.search_placeholder')"
style="display: block; width: 150px"></el-input>
</div>
<data-tables v-loading="loading" v-bind="previousInstancesUserDialogTable" style="margin-top: 10px">
<el-table-column :label="$t('table.previous_instances.date')" prop="created_at" sortable width="170">
<template slot-scope="scope">
<span>{{ scope.row.created_at | formatDate('long') }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('table.previous_instances.world')" prop="name" sortable>
<template slot-scope="scope">
<location
:location="scope.row.location"
:hint="scope.row.worldName"
:grouphint="scope.row.groupName"></location>
</template>
</el-table-column>
<el-table-column :label="$t('table.previous_instances.instance_creator')" prop="location" width="170">
<template slot-scope="scope">
<display-name
:userid="scope.row.$location.userId"
:location="scope.row.$location.tag"></display-name>
</template>
</el-table-column>
<el-table-column :label="$t('table.previous_instances.time')" prop="time" width="100" sortable>
<template slot-scope="scope">
<span v-text="scope.row.timer"></span>
</template>
</el-table-column>
<el-table-column :label="$t('table.previous_instances.action')" width="90" align="right">
<template slot-scope="scope">
<el-button
type="text"
icon="el-icon-switch-button"
size="mini"
@click="showLaunchDialog(scope.row.location)"></el-button>
<el-button
type="text"
icon="el-icon-s-data"
size="mini"
@click="showPreviousInstancesInfoDialog(scope.row.location)"></el-button>
<el-button
v-if="shiftHeld"
style="color: #f56c6c"
type="text"
icon="el-icon-close"
size="mini"
@click="deleteGameLogUserInstance(scope.row)"></el-button>
<el-button
v-else
type="text"
icon="el-icon-close"
size="mini"
@click="deleteGameLogUserInstancePrompt(scope.row)"></el-button>
</template>
</el-table-column>
</data-tables>
</el-dialog>
</template>
<script>
import utils from '../../../classes/utils';
import database from '../../../repository/database';
import Location from '../../../components/common/Location.vue';
export default {
name: 'PreviousInstancesUserDialog',
components: {
Location
},
inject: [
'beforeDialogClose',
'dialogMouseDown',
'dialogMouseUp',
'adjustDialogZ',
'showLaunchDialog',
'showPreviousInstancesInfoDialog'
],
props: {
previousInstancesUserDialog: {
type: Object,
default: () => ({
visible: false,
userRef: {},
loading: false,
forceUpdate: 0,
previousInstances: [],
previousInstancesTable: {
data: [],
filters: [
{
prop: 'displayName',
value: ''
}
],
tableProps: {
stripe: true,
size: 'mini',
height: '400px'
}
}
})
},
shiftHeld: {
type: Boolean,
default: false
}
},
data() {
return {
previousInstancesUserDialogTable: {
data: [],
filters: [
{
prop: 'worldName',
value: ''
}
],
tableProps: {
stripe: true,
size: 'mini',
defaultSort: {
prop: 'created_at',
order: 'descending'
}
},
pageSize: 10,
paginationProps: {
small: true,
layout: 'sizes,prev,pager,next,total',
pageSizes: [10, 25, 50, 100]
}
},
loading: false
};
},
computed: {
isVisible: {
get() {
return this.previousInstancesUserDialog.visible;
},
set(value) {
this.$emit('update:previous-instances-user-dialog', {
...this.previousInstancesUserDialog,
visible: value
});
}
}
},
watch: {
'previousInstancesUserDialog.openFlg'() {
if (this.previousInstancesUserDialog.visible) {
this.$nextTick(() => {
this.adjustDialogZ(this.$refs.previousInstancesUserDialog.$el);
});
this.refreshPreviousInstancesUserTable();
}
}
},
methods: {
refreshPreviousInstancesUserTable() {
this.loading = true;
database.getpreviousInstancesByUserId(this.previousInstancesUserDialog.userRef).then((data) => {
const array = [];
for (const ref of data.values()) {
ref.$location = utils.parseLocation(ref.location);
if (ref.time > 0) {
ref.timer = utils.timeToText(ref.time);
} else {
ref.timer = '';
}
array.push(ref);
}
array.sort(utils.compareByCreatedAt);
this.previousInstancesUserDialogTable.data = array;
this.loading = false;
});
},
deleteGameLogUserInstance(row) {
database.deleteGameLogInstance({
id: this.previousInstancesUserDialog.userRef.id,
displayName: this.previousInstancesUserDialog.userRef.displayName,
location: row.location
});
utils.removeFromArray(this.previousInstancesUserDialogTable.data, row);
},
deleteGameLogUserInstancePrompt(row) {
this.$confirm('Continue? Delete User From GameLog Instance', 'Confirm', {
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
type: 'info',
callback: (action) => {
if (action === 'confirm') {
this.deleteGameLogUserInstance(row);
}
}
});
}
}
};
</script>

View File

@@ -0,0 +1,186 @@
<template>
<el-dialog
ref="previousInstancesWorldDialog"
:before-close="beforeDialogClose"
:visible.sync="isVisible"
:title="$t('dialog.previous_instances.header')"
width="1000px"
append-to-body
@mousedown.native="dialogMouseDown"
@mouseup.native="dialogMouseUp">
<div style="display: flex; align-items: center; justify-content: space-between">
<span style="font-size: 14px" v-text="previousInstancesWorldDialog.worldRef.name"></span>
<el-input
v-model="previousInstancesWorldDialogTable.filters[0].value"
:placeholder="$t('dialog.previous_instances.search_placeholder')"
style="display: block; width: 150px"></el-input>
</div>
<data-tables v-loading="loading" v-bind="previousInstancesWorldDialogTable" style="margin-top: 10px">
<el-table-column :label="$t('table.previous_instances.date')" prop="created_at" sortable width="170">
<template slot-scope="scope">
<span>{{ scope.row.created_at | formatDate('long') }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('table.previous_instances.instance_name')" prop="name">
<template slot-scope="scope">
<location-world
:locationobject="scope.row.$location"
:grouphint="scope.row.groupName"
:currentuserid="API.currentUser.id"
@show-launch-dialog="showLaunchDialog"></location-world>
</template>
</el-table-column>
<el-table-column :label="$t('table.previous_instances.instance_creator')" prop="location">
<template slot-scope="scope">
<display-name
:userid="scope.row.$location.userId"
:location="scope.row.$location.tag"
:force-update-key="previousInstancesWorldDialog.forceUpdate"></display-name>
</template>
</el-table-column>
<el-table-column :label="$t('table.previous_instances.time')" prop="time" width="100" sortable>
<template slot-scope="scope">
<span v-text="scope.row.timer"></span>
</template>
</el-table-column>
<el-table-column :label="$t('table.previous_instances.action')" width="90" align="right">
<template slot-scope="scope">
<el-button
type="text"
icon="el-icon-s-data"
size="mini"
@click="showPreviousInstancesInfoDialog(scope.row.location)"></el-button>
<el-button
v-if="shiftHeld"
style="color: #f56c6c"
type="text"
icon="el-icon-close"
size="mini"
@click="deleteGameLogWorldInstance(scope.row)"></el-button>
<el-button
v-else
type="text"
icon="el-icon-close"
size="mini"
@click="deleteGameLogWorldInstancePrompt(scope.row)"></el-button>
</template>
</el-table-column>
</data-tables>
</el-dialog>
</template>
<script>
import utils from '../../../classes/utils';
import database from '../../../repository/database';
export default {
name: 'PreviousInstancesWorldDialog',
inject: [
'API',
'showLaunchDialog',
'showPreviousInstancesInfoDialog',
'adjustDialogZ',
'beforeDialogClose',
'dialogMouseDown',
'dialogMouseUp'
],
props: {
previousInstancesWorldDialog: {
type: Object,
required: true
},
shiftHeld: Boolean
},
data() {
return {
previousInstancesWorldDialogTable: {
data: [],
filters: [
{
prop: 'groupName',
value: ''
}
],
tableProps: {
stripe: true,
size: 'mini',
defaultSort: {
prop: 'created_at',
order: 'descending'
}
},
pageSize: 10,
paginationProps: {
small: true,
layout: 'sizes,prev,pager,next,total',
pageSizes: [10, 25, 50, 100]
}
},
loading: false
};
},
computed: {
isVisible: {
get() {
return this.previousInstancesWorldDialog.visible;
},
set(value) {
this.$emit('update:previous-instances-world-dialog', {
...this.previousInstancesWorldDialog,
visible: value
});
}
}
},
watch: {
'previousInstancesWorldDialog.openFlg'() {
if (this.previousInstancesWorldDialog.visible) {
this.$nextTick(() => {
this.adjustDialogZ(this.$refs.previousInstancesWorldDialog.$el);
});
this.refreshPreviousInstancesWorldTable();
}
}
},
methods: {
refreshPreviousInstancesWorldTable() {
this.loading = true;
const D = this.previousInstancesWorldDialog;
database.getpreviousInstancesByWorldId(D.worldRef).then((data) => {
const array = [];
for (const ref of data.values()) {
ref.$location = utils.parseLocation(ref.location);
if (ref.time > 0) {
ref.timer = utils.timeToText(ref.time);
} else {
ref.timer = '';
}
array.push(ref);
}
array.sort(utils.compareByCreatedAt);
this.previousInstancesWorldDialogTable.data = array;
this.loading = false;
});
},
deleteGameLogWorldInstance(row) {
database.deleteGameLogInstanceByInstanceId({
location: row.location
});
utils.removeFromArray(this.previousInstancesWorldDialogTable.data, row);
},
deleteGameLogWorldInstancePrompt(row) {
this.$confirm('Continue? Delete GameLog Instance', 'Confirm', {
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
type: 'info',
callback: (action) => {
if (action === 'confirm') {
this.deleteGameLogWorldInstance(row);
}
}
});
}
}
};
</script>

View File

@@ -5,6 +5,7 @@
:visible.sync="newInstanceDialog.visible"
:title="$t('dialog.new_instance.header')"
width="650px"
append-to-body
@mousedown.native="dialogMouseDown"
@mouseup.native="dialogMouseUp">
<el-tabs v-model="newInstanceDialog.selectedTab" type="card" @tab-click="newInstanceTabClick">
@@ -511,10 +512,6 @@
type: Function,
required: true
},
getLaunchURL: {
type: Function,
required: true
},
vipFriends: {
type: Array,
required: true
@@ -695,7 +692,7 @@
} else {
D.shortName = '';
}
D.url = this.getLaunchURL(L);
D.url = utils.getLaunchURL(L);
},
selfInvite(location) {
const L = utils.parseLocation(location);
@@ -854,7 +851,7 @@
this.updateNewInstanceDialog(true);
}
}
const newUrl = this.getLaunchURL(L);
const newUrl = utils.getLaunchURL(L);
this.copyToClipboard(newUrl);
},
async copyToClipboard(newUrl) {

View File

@@ -0,0 +1,297 @@
<template>
<el-dialog
:before-close="beforeDialogClose"
:visible.sync="isVisible"
:title="$t('dialog.set_world_tags.header')"
width="400px"
destroy-on-close
append-to-body
@mousedown.native="dialogMouseDown"
@mouseup.native="dialogMouseUp">
<el-checkbox v-model="setWorldTagsDialog.avatarScalingDisabled">
{{ $t('dialog.set_world_tags.avatar_scaling_disabled') }}
</el-checkbox>
<br />
<el-checkbox v-model="setWorldTagsDialog.focusViewDisabled">
{{ $t('dialog.set_world_tags.focus_view_disabled') }}
</el-checkbox>
<br />
<el-checkbox v-model="setWorldTagsDialog.debugAllowed">
{{ $t('dialog.set_world_tags.enable_debugging') }}
</el-checkbox>
<div style="font-size: 12px; margin-top: 10px">{{ $t('dialog.set_world_tags.author_tags') }}<br /></div>
<el-input
v-model="setWorldTagsDialog.authorTags"
type="textarea"
size="mini"
show-word-limit
:autosize="{ minRows: 2, maxRows: 5 }"
placeholder=""
style="margin-top: 10px"></el-input>
<div style="font-size: 12px; margin-top: 10px">{{ $t('dialog.set_world_tags.content_tags') }}<br /></div>
<el-checkbox v-model="setWorldTagsDialog.contentHorror">
{{ $t('dialog.set_world_tags.content_horror') }}
</el-checkbox>
<br />
<el-checkbox v-model="setWorldTagsDialog.contentGore">
{{ $t('dialog.set_world_tags.content_gore') }}
</el-checkbox>
<br />
<el-checkbox v-model="setWorldTagsDialog.contentViolence">
{{ $t('dialog.set_world_tags.content_violence') }}
</el-checkbox>
<br />
<el-checkbox v-model="setWorldTagsDialog.contentAdult">
{{ $t('dialog.set_world_tags.content_adult') }}
</el-checkbox>
<br />
<el-checkbox v-model="setWorldTagsDialog.contentSex">
{{ $t('dialog.set_world_tags.content_sex') }}
</el-checkbox>
<div style="font-size: 12px; margin-top: 10px">
{{ $t('dialog.set_world_tags.default_content_settings') }}<br />
</div>
<el-checkbox v-model="setWorldTagsDialog.emoji">
{{ $t('dialog.new_instance.content_emoji') }}
</el-checkbox>
<br />
<el-checkbox v-model="setWorldTagsDialog.stickers">
{{ $t('dialog.new_instance.content_stickers') }}
</el-checkbox>
<br />
<el-checkbox v-model="setWorldTagsDialog.pedestals">
{{ $t('dialog.new_instance.content_pedestals') }}
</el-checkbox>
<br />
<el-checkbox v-model="setWorldTagsDialog.prints">
{{ $t('dialog.new_instance.content_prints') }}
</el-checkbox>
<br />
<el-checkbox v-model="setWorldTagsDialog.drones">
{{ $t('dialog.new_instance.content_drones') }}
</el-checkbox>
<template #footer>
<div style="display: flex">
<el-button size="small" @click="setWorldTagsDialog.visible = false">
{{ $t('dialog.set_world_tags.cancel') }}
</el-button>
<el-button type="primary" size="small" @click="saveSetWorldTagsDialog">
{{ $t('dialog.set_world_tags.save') }}
</el-button>
</div>
</template>
</el-dialog>
</template>
<script>
import { worldRequest } from '../../../classes/request';
export default {
name: 'SetWorldTagsDialog',
inject: ['beforeDialogClose', 'dialogMouseDown', 'dialogMouseUp', 'showWorldDialog'],
props: {
oldTags: {
type: Array,
default: () => []
},
isSetWorldTagsDialogVisible: {
type: Boolean,
required: true
},
worldId: {
type: String,
required: true
},
isWorldDialogVisible: {
type: Boolean,
required: true
}
},
data() {
return {
setWorldTagsDialog: {
authorTags: [],
contentTags: [],
debugAllowed: false,
avatarScalingDisabled: false,
focusViewDisabled: false,
contentHorror: false,
contentGore: false,
contentViolence: false,
contentAdult: false,
contentSex: false,
emoji: true,
stickers: true,
pedestals: true,
prints: true,
drones: true
}
};
},
computed: {
isVisible: {
get() {
return this.isSetWorldTagsDialogVisible;
},
set(val) {
this.$emit('update:is-set-world-tags-dialog-visible', val);
}
}
},
watch: {
isSetWorldTagsDialogVisible(val) {
if (val) {
this.showSetWorldTagsDialog();
}
}
},
methods: {
showSetWorldTagsDialog() {
const D = this.setWorldTagsDialog;
D.visible = true;
D.debugAllowed = false;
D.avatarScalingDisabled = false;
D.focusViewDisabled = false;
D.contentHorror = false;
D.contentGore = false;
D.contentViolence = false;
D.contentAdult = false;
D.contentSex = false;
const authorTags = [];
const contentTags = [];
this.oldTags.forEach((tag) => {
if (tag.startsWith('author_tag_')) {
authorTags.unshift(tag.substring(11));
}
if (tag.startsWith('content_')) {
contentTags.unshift(tag.substring(8));
}
switch (tag) {
case 'content_horror':
D.contentHorror = true;
break;
case 'content_gore':
D.contentGore = true;
break;
case 'content_violence':
D.contentViolence = true;
break;
case 'content_adult':
D.contentAdult = true;
break;
case 'content_sex':
D.contentSex = true;
break;
case 'debug_allowed':
D.debugAllowed = true;
break;
case 'feature_avatar_scaling_disabled':
D.avatarScalingDisabled = true;
break;
case 'feature_focus_view_disabled':
D.focusViewDisabled = true;
break;
case 'feature_emoji_disabled':
D.emoji = false;
break;
case 'feature_stickers_disabled':
D.stickers = false;
break;
case 'feature_pedestals_disabled':
D.pedestals = false;
break;
case 'feature_prints_disabled':
D.prints = false;
break;
case 'feature_drones_disabled':
D.drones = false;
break;
}
});
D.authorTags = authorTags.toString();
D.contentTags = contentTags.toString();
},
saveSetWorldTagsDialog() {
const D = this.setWorldTagsDialog;
const authorTags = D.authorTags.trim().split(',');
const contentTags = D.contentTags.trim().split(',');
const tags = [];
authorTags.forEach((tag) => {
if (tag) {
tags.unshift(`author_tag_${tag}`);
}
});
// add back custom tags
contentTags.forEach((tag) => {
switch (tag) {
case 'horror':
case 'gore':
case 'violence':
case 'adult':
case 'sex':
case '':
break;
default:
tags.unshift(`content_${tag}`);
break;
}
});
if (D.contentHorror) {
tags.unshift('content_horror');
}
if (D.contentGore) {
tags.unshift('content_gore');
}
if (D.contentViolence) {
tags.unshift('content_violence');
}
if (D.contentAdult) {
tags.unshift('content_adult');
}
if (D.contentSex) {
tags.unshift('content_sex');
}
if (D.debugAllowed) {
tags.unshift('debug_allowed');
}
if (D.avatarScalingDisabled) {
tags.unshift('feature_avatar_scaling_disabled');
}
if (D.focusViewDisabled) {
tags.unshift('feature_focus_view_disabled');
}
if (!D.emoji) {
tags.unshift('feature_emoji_disabled');
}
if (!D.stickers) {
tags.unshift('feature_stickers_disabled');
}
if (!D.pedestals) {
tags.unshift('feature_pedestals_disabled');
}
if (!D.prints) {
tags.unshift('feature_prints_disabled');
}
if (!D.drones) {
tags.unshift('feature_drones_disabled');
}
worldRequest
.saveWorld({
id: this.worldId,
tags
})
.then((args) => {
this.$message({
message: 'Tags updated',
type: 'success'
});
this.$emit('update:is-set-world-tags-dialog-visible', false);
if (this.isWorldDialogVisible) {
this.showWorldDialog(args.json.id);
}
return args;
});
}
}
};
</script>

View File

@@ -0,0 +1,93 @@
<template>
<el-dialog
:before-close="beforeDialogClose"
:visible.sync="isVisible"
:title="$t('dialog.allowed_video_player_domains.header')"
width="600px"
destroy-on-close
append-to-body
@mousedown.native="dialogMouseDown"
@mouseup.native="dialogMouseUp">
<div>
<el-input
v-for="(domain, index) in urlList"
:key="index"
v-model="urlList[index]"
:value="domain"
size="small"
style="margin-top: 5px">
<el-button slot="append" icon="el-icon-delete" @click="urlList.splice(index, 1)"></el-button>
</el-input>
<el-button size="mini" style="margin-top: 5px" @click="urlList.push('')">
{{ $t('dialog.allowed_video_player_domains.add_domain') }}
</el-button>
</div>
<template #footer>
<el-button
type="primary"
size="small"
:disabled="!worldAllowedDomainsDialog.worldId"
@click="saveWorldAllowedDomains">
{{ $t('dialog.allowed_video_player_domains.save') }}
</el-button>
</template>
</el-dialog>
</template>
<script>
import { worldRequest } from '../../../classes/request';
export default {
name: 'WorldAllowedDomainsDialog',
inject: ['beforeDialogClose', 'dialogMouseDown', 'dialogMouseUp'],
props: {
worldAllowedDomainsDialog: {
type: Object,
required: true
}
},
data() {
return {
urlList: []
};
},
computed: {
isVisible: {
get() {
return this.worldAllowedDomainsDialog.visible;
},
set(val) {
this.$emit('update:world-allowed-domains-dialog', {
...this.worldAllowedDomainsDialog,
visible: val
});
}
}
},
watch: {
'worldAllowedDomainsDialog.visible'(val) {
if (val) {
this.urlList = this.worldAllowedDomainsDialog.urlList;
}
}
},
methods: {
saveWorldAllowedDomains() {
const D = this.worldAllowedDomainsDialog;
worldRequest
.saveWorld({
id: D.worldId,
urlList: D.urlList
})
.then((args) => {
this.$message({
message: 'Allowed Video Player Domains updated',
type: 'success'
});
return args;
});
D.visible = false;
}
}
};
</script>

View File

@@ -1,10 +1,13 @@
<template>
<el-dialog
ref="worldDialog"
:before-close="beforeDialogClose"
class="x-dialog x-world-dialog"
:visible.sync="worldDialog.visible"
:visible.sync="isDialogVisible"
:show-close="false"
width="770px">
width="770px"
@mousedown.native="dialogMouseDown"
@mouseup.native="dialogMouseUp">
<div v-loading="worldDialog.loading">
<div style="display: flex">
<el-popover placement="right" width="500px" trigger="click">
@@ -368,7 +371,7 @@
style="margin-left: 5px"
plain
circle
@click="showPreviousInstanceInfoDialog(room.location)" />
@click="showPreviousInstancesInfoDialog(room.location)" />
</el-tooltip>
<last-join :location="room.$location.tag" :currentlocation="lastLocation.location" />
<instance-info
@@ -435,7 +438,7 @@
{{ $t('dialog.world.info.memo') }}
</span>
<el-input
v-model="worldDialog.memo"
v-model="memo"
class="extra"
type="textarea"
:rows="2"
@@ -735,41 +738,111 @@
</el-tab-pane>
</el-tabs>
</div>
<!-- Nested Hmm-->
<world-allowed-domains-dialog :world-allowed-domains-dialog.sync="worldAllowedDomainsDialog" />
<set-world-tags-dialog
:is-set-world-tags-dialog-visible.sync="isSetWorldTagsDialogVisible"
:old-tags="worldDialog.ref?.tags"
:world-id="worldDialog.id"
:is-world-dialog-visible="worldDialog.visible" />
<previous-instances-world-dialog
:previous-instances-world-dialog.sync="previousInstancesWorldDialog"
:shift-held="shiftHeld" />
<new-instance-dialog
:new-instance-dialog-location-tag="newInstanceDialogLocationTag"
:create-new-instance="createNewInstance"
:instance-content-settings="instanceContentSettings"
:offline-friends="offlineFriends"
:active-friends="activeFriends"
:online-friends="onlineFriends"
:vip-friends="vipFriends"
:has-group-permission="hasGroupPermission" />
</el-dialog>
</template>
<script>
import utils from '../../../classes/utils';
import database from '../../../repository/database.js';
import WorldAllowedDomainsDialog from './WorldAllowedDomainsDialog.vue';
import SetWorldTagsDialog from './SetWorldTagsDialog.vue';
import PreviousInstancesWorldDialog from '../previousInstances/PreviousInstancesWorldDialog.vue';
import NewInstanceDialog from './NewInstanceDialog.vue';
import { favoriteRequest, miscRequest, worldRequest } from '../../../classes/request';
export default {
name: 'WorldDialog',
components: { SetWorldTagsDialog, WorldAllowedDomainsDialog, PreviousInstancesWorldDialog, NewInstanceDialog },
inject: [
'API',
'showUserDialog',
'userStatusClass',
'userImage',
'adjustDialogZ',
'showPreviousInstanceInfoDialog',
'showPreviousInstancesInfoDialog',
'showLaunchDialog',
'showFullscreenImageDialog'
'showFullscreenImageDialog',
'beforeDialogClose',
'dialogMouseDown',
'dialogMouseUp',
'displayPreviousImages',
'showWorldDialog',
'showFavoriteDialog'
],
props: {
worldDialog: Object,
hideTooltips: Boolean,
shiftHeld: Boolean,
isGameRunning: Boolean,
lastLocation: Object,
instanceJoinHistory: Map,
isAgeGatedInstancesVisible: Boolean,
createNewInstance: Function,
instanceContentSettings: Array,
offlineFriends: Array,
activeFriends: Array,
onlineFriends: Array,
vipFriends: Array,
hasGroupPermission: Function,
// TODO: Remove
updateInstanceInfo: Number
},
data() {
return {
treeData: []
treeData: [],
worldAllowedDomainsDialog: {
visible: false,
worldId: '',
urlList: []
},
isSetWorldTagsDialogVisible: false,
previousInstancesWorldDialog: {
visible: false,
openFlg: false,
worldRef: {}
},
newInstanceDialogLocationTag: ''
};
},
computed: {
isDialogVisible: {
get() {
return this.worldDialog.visible;
},
set(value) {
this.$emit('update:world-dialog', { ...this.worldDialog, visible: value });
}
},
memo: {
get() {
return this.worldDialog.memo;
},
set(value) {
this.$emit('update:world-dialog', { ...this.worldDialog, memo: value });
}
},
isTimeInLabVisible() {
return (
this.worldDialog.ref.publicationDate &&
@@ -778,7 +851,6 @@
this.worldDialog.ref.labsPublicationDate !== 'none'
);
},
timeInLab() {
return utils.timeToText(
new Date(this.worldDialog.ref?.publicationDate) -
@@ -832,6 +904,11 @@
}
},
methods: {
showNewInstanceDialog(tag) {
// trigger watcher
this.newInstanceDialogLocationTag = '';
this.$nextTick(() => (this.newInstanceDialogLocationTag = tag));
},
openFolderGeneric(path) {
this.$emit('open-folder-generic', path);
},
@@ -839,10 +916,144 @@
this.$emit('delete-vrchat-cache', world);
},
worldDialogCommand(command) {
if (command === 'Share') {
this.copyWorldUrl();
} else {
this.$emit('world-dialog-command', command);
const D = this.worldDialog;
if (D.visible === false) {
return;
}
switch (command) {
case 'Delete Favorite':
case 'Make Home':
case 'Reset Home':
case 'Publish':
case 'Unpublish':
case 'Delete Persistent Data':
case 'Delete':
this.$confirm(`Continue? ${command}`, 'Confirm', {
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
type: 'info',
callback: (action) => {
if (action !== 'confirm') {
return;
}
switch (command) {
case 'Delete Favorite':
favoriteRequest.deleteFavorite({
objectId: D.id
});
break;
case 'Make Home':
this.API.saveCurrentUser({
homeLocation: D.id
}).then((args) => {
this.$message({
message: 'Home world updated',
type: 'success'
});
return args;
});
break;
case 'Reset Home':
this.API.saveCurrentUser({
homeLocation: ''
}).then((args) => {
this.$message({
message: 'Home world has been reset',
type: 'success'
});
return args;
});
break;
case 'Publish':
worldRequest
.publishWorld({
worldId: D.id
})
.then((args) => {
this.$message({
message: 'World has been published',
type: 'success'
});
return args;
});
break;
case 'Unpublish':
worldRequest
.unpublishWorld({
worldId: D.id
})
.then((args) => {
this.$message({
message: 'World has been unpublished',
type: 'success'
});
return args;
});
break;
case 'Delete Persistent Data':
miscRequest
.deleteWorldPersistData({
worldId: D.id
})
.then((args) => {
this.$message({
message: 'Persistent data has been deleted',
type: 'success'
});
return args;
});
break;
case 'Delete':
worldRequest
.deleteWorld({
worldId: D.id
})
.then((args) => {
this.$message({
message: 'World has been deleted',
type: 'success'
});
D.visible = false;
return args;
});
break;
}
}
});
break;
case 'Previous Instances':
this.showPreviousInstancesWorldDialog(D.ref);
break;
case 'Share':
this.copyWorldUrl();
break;
case 'Change Allowed Domains':
this.showWorldAllowedDomainsDialog();
break;
case 'Change Tags':
this.isSetWorldTagsDialogVisible = true;
break;
case 'Download Unity Package':
utils.openExternalLink(this.replaceVrcPackageUrl(this.worldDialog.ref.unityPackageUrl));
break;
case 'Change Image':
this.displayPreviousImages('World', 'Change');
break;
case 'Previous Images':
this.displayPreviousImages('World', 'Display');
break;
case 'Refresh':
this.showWorldDialog(D.id);
break;
case 'New Instance':
this.showNewInstanceDialog(D.$location.tag);
break;
case 'Add Favorite':
this.showFavoriteDialog('world', D.id);
break;
default:
this.$emit('world-dialog-command', command);
break;
}
},
refreshInstancePlayerCount(tag) {
@@ -861,8 +1072,13 @@
database.deleteWorldMemo(worldId);
}
},
showPreviousInstancesWorldDialog(world) {
this.$emit('show-previous-instances-world-dialog', world);
showPreviousInstancesWorldDialog(worldRef) {
const D = this.previousInstancesWorldDialog;
D.worldRef = worldRef;
D.visible = true;
// trigger watcher
D.openFlg = true;
this.$nextTick(() => (D.openFlg = false));
},
refreshWorldDialogTreeData() {
this.treeData = utils.buildTreeData(this.worldDialog.ref);
@@ -920,6 +1136,12 @@
type: 'error'
});
});
},
showWorldAllowedDomainsDialog() {
const D = this.worldAllowedDomainsDialog;
D.worldId = this.worldDialog.id;
D.urlList = this.worldDialog.ref?.urlList ?? [];
D.visible = true;
}
}
};

View File

@@ -43,7 +43,6 @@
@save-sort-favorites-option="saveSortFavoritesOption"
@change-favorite-group-name="changeFavoriteGroupName"
@new-instance-self-invite="newInstanceSelfInvite"
@show-favorite-dialog="showFavoriteDialog"
@refresh-local-world-favorite="refreshLocalWorldFavorites"
@delete-local-world-favorite-group="deleteLocalWorldFavoriteGroup"
@remove-local-world-favorite="removeLocalWorldFavorite"
@@ -73,7 +72,6 @@
:local-avatar-favorites-list="localAvatarFavoritesList"
@show-avatar-import-dialog="showAvatarImportDialog"
@save-sort-favorites-option="saveSortFavoritesOption"
@show-favorite-dialog="showFavoriteDialog"
@change-favorite-group-name="changeFavoriteGroupName"
@remove-local-avatar-favorite="removeLocalAvatarFavorite"
@select-avatar-with-confirmation="selectAvatarWithConfirmation"
@@ -282,9 +280,6 @@
newInstanceSelfInvite(worldId) {
this.$emit('new-instance-self-invite', worldId);
},
showFavoriteDialog(type, objectId) {
this.$emit('show-favorite-dialog', type, objectId);
},
deleteLocalWorldFavoriteGroup(group) {
this.$emit('delete-local-world-favorite-group', group);
},