Electron support for Linux (#1074)

* init

* SQLite changes

* Move html folder, edit build scripts

* AppApi interface

* Build flags

* AppApi inheritance

* Finishing touches

* Merge upstream changes

* Test CI

* Fix class inits

* Rename AppApi

* Merge upstream changes

* Fix SQLiteLegacy on Linux, Add Linux interop, build tools

* Linux specific localisation strings

* Make it run

* Bring back most of Linux functionality

* Clean up

* Fix TTS voices

* Fix UI var

* Changes

* Electron minimise to tray

* Remove separate toggle for WlxOverlay

* Fixes

* Touchups

* Move csproj

* Window zoom, Desktop Notifications, VR check on Linux

* Fix desktop notifications, VR check spam

* Fix building on Linux

* Clean up

* Fix WebApi headers

* Rewrite VRCX updater

* Clean up

* Linux updater

* Add Linux to build action

* init

* SQLite changes

* Move html folder, edit build scripts

* AppApi interface

* Build flags

* AppApi inheritance

* Finishing touches

* Merge upstream changes

* Test CI

* Fix class inits

* Rename AppApi

* Merge upstream changes

* Fix SQLiteLegacy on Linux, Add Linux interop, build tools

* Linux specific localisation strings

* Make it run

* Bring back most of Linux functionality

* Clean up

* Fix TTS voices

* Changes

* Electron minimise to tray

* Remove separate toggle for WlxOverlay

* Fixes

* Touchups

* Move csproj

* Window zoom, Desktop Notifications, VR check on Linux

* Fix desktop notifications, VR check spam

* Fix building on Linux

* Clean up

* Fix WebApi headers

* Rewrite VRCX updater

* Clean up

* Linux updater

* Add Linux to build action

* Test updater

* Rebase and handle merge conflicts

* Fix Linux updater

* Fix Linux app restart

* Fix friend order

* Handle AppImageInstaller, show an install message on Linux

* Updates to the AppImage installer

* Fix Linux updater, fix set version, check for .NET, copy wine prefix

* Handle random errors

* Rotate tall prints

* try fix Linux restart bug

* Final

---------

Co-authored-by: rs189 <35667100+rs189@users.noreply.github.com>
This commit is contained in:
Natsumi
2025-01-11 13:09:44 +13:00
committed by GitHub
parent a39eb9d5ed
commit 938fff63d0
223 changed files with 15841 additions and 9562 deletions

View File

@@ -0,0 +1,326 @@
mixin favoritesTab()
.x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'favorite'")
div(style="font-size:13px;position:absolute;display:flex;right:0;z-index:1;margin-right:15px")
div(v-if="editFavoritesMode" style="display:inline-block;margin-right:10px")
el-button(size="small" @click="clearBulkFavoriteSelection ") {{ $t('view.favorite.clear') }}
el-button(size="small" @click="bulkCopyFavoriteSelection") {{ $t('view.favorite.copy') }}
el-button(size="small" @click="showBulkUnfavoriteSelectionConfirm") {{ $t('view.favorite.bulk_unfavorite') }}
div(style="display:flex;align-items: center;margin-right:10px")
span.name {{ $t('view.favorite.edit_mode') }}
el-switch(v-model="editFavoritesMode" style="margin-left:5px")
el-tooltip(placement="bottom" :content="$t('view.favorite.refresh_tooltip')" :disabled="hideTooltips")
el-button(type="default" :loading="API.isFavoriteLoading" @click="API.refreshFavorites(); getLocalWorldFavorites()" size="small" icon="el-icon-refresh" circle)
el-tabs(ref="favoriteTabRef" type="card" v-loading="API.isFavoriteLoading" style="height:100%")
el-tab-pane(:label="$t('view.favorite.friends.header')")
el-collapse(v-if="$refs.menu && $refs.menu.activeIndex === 'favorite' && $refs.favoriteTabRef && $refs.favoriteTabRef.currentName === '0'" style="border:0")
div(style="display:flex;align-items:center;justify-content:space-between")
div
el-button(size="small" @click="showFriendExportDialog") {{ $t('view.favorite.export') }}
el-button(size="small" @click="showFriendImportDialog" 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")
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-collapse-item(v-for="group in API.favoriteFriendGroups" :key="group.name")
template(slot="title")
span(v-text="group.displayName ? group.displayName : group.name" style="font-weight:bold;font-size:14px;margin-left:10px")
span(style="color:#909399;font-size:12px;margin-left:10px") {{ group.count }}/{{ group.capacity }}
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:10px")
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 favoriteFriends" v-if="favorite.groupKey === group.key" :key="favorite.id" @click="showUserDialog(favorite.id)")
.x-friend-item
template(v-if="favorite.ref")
.avatar(:class="userStatusClass(favorite.ref)")
img(v-lazy="userImage(favorite.ref)")
.detail
span.name(v-text="favorite.ref.displayName" :style="{'color':favorite.ref.$userColour}")
location.extra(v-if="favorite.ref.location !== 'offline'" :location="favorite.ref.location" :traveling="favorite.ref.travelingToLocation" :link="false")
span(v-else v-text="favorite.ref.statusDescription")
template(v-if="editFavoritesMode")
el-tooltip(placement="left" :content="$t('view.favorite.move_tooltip')" :disabled="hideTooltips")
el-dropdown(trigger="click" @click.native.stop size="mini" style="margin-left:5px")
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.favoriteFriendGroups" :key="groupAPI.name")
el-dropdown-item(style="display:block;margin:10px 0" @click.native="moveFavorite(favorite.ref, groupAPI, 'friend')" :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(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('friend', 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-button(type="text" icon="el-icon-close" size="mini" @click.stop="deleteFavorite(favorite.id)" style="margin-left:5px")
div(v-else style="height:20px;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')")
el-collapse(v-if="$refs.menu && $refs.menu.activeIndex === 'favorite' && $refs.favoriteTabRef && $refs.favoriteTabRef.currentName === '1'" style="border:0")
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-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 ? group.displayName : group.name" 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-tooltip(placement="top" :content="$t('view.favorite.visibility_tooltip')" :disabled="hideTooltips")
el-dropdown(trigger="click" @click.native.stop size="mini" style="margin-left:10px")
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 favoriteWorlds" v-if="favorite.groupKey === 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-tooltip(placement="left" :content="$t('view.favorite.move_tooltip')" :disabled="hideTooltips")
el-dropdown(trigger="click" @click.native.stop size="mini" style="margin-left:5px")
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" :key="groupAPI.name")
el-dropdown-item(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="height:20px;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="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-tooltip(placement="left" :content="$t('view.favorite.copy_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")
template(v-for="groupAPI in API.favoriteWorldGroups" :key="groupAPI.name")
el-dropdown-item(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="height:20px;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.avatars.header')")
el-collapse(v-if="$refs.menu && $refs.menu.activeIndex === 'favorite' && $refs.favoriteTabRef && $refs.favoriteTabRef.currentName === '2'" style="border:0")
div(style="display:flex;align-items:center;justify-content:space-between")
div
el-button(size="small" @click="showAvatarExportDialog") {{ $t('view.favorite.export') }}
el-button(size="small" @click="showAvatarImportDialog" 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="avatarFavoriteSearch" @input="searchAvatarFavorites" clearable size="mini" :placeholder="$t('view.favorite.avatars.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 avatarFavoriteSearchResults" :key="favorite.id" @click="showAvatarDialog(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-text="favorite.authorName")
template(v-else)
.avatar
.detail
span.name(v-text="favorite.id")
span(style="display:block;margin-top:20px") {{ $t('view.favorite.avatars.vrchat_favorites') }}
el-collapse-item(v-for="group in API.favoriteAvatarGroups" :key="group.name")
template(slot="title")
span(v-text="group.displayName ? group.displayName : group.name" style="font-weight:bold;font-size:14px;margin-left:10px")
span(style="color:#909399;font-size:12px;margin-left:10px") {{ group.count }}/{{ group.capacity }}
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:10px")
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 favoriteAvatars" v-if="favorite.groupKey === group.key" :key="favorite.id" @click="showAvatarDialog(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-text="favorite.ref.authorName")
template(v-if="editFavoritesMode")
el-tooltip(placement="top" :content="$t('view.favorite.move_tooltip')" :disabled="hideTooltips")
el-dropdown(trigger="click" @click.native.stop size="mini" style="margin-left:5px")
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.favoriteAvatarGroups" :key="groupAPI.name")
el-dropdown-item(style="display:block;margin:10px 0" @click.native="moveFavorite(favorite.ref, groupAPI, 'avatar')" :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(v-if="favorite.ref.releaseStatus !== 'private' && !favorite.deleted" placement="left" :content="$t('view.favorite.select_avatar_tooltip')" :disabled="hideTooltips")
el-button(@click.stop="selectAvatarWithConfirmation(favorite.id)" :disabled="API.currentUser.currentAvatar === favorite.id" size="mini" icon="el-icon-check" 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('avatar', favorite.id)" type="default" icon="el-icon-star-on" size="mini" circle style="margin-left:5px")
template(v-else)
.avatar
.detail
span.name(v-text="favorite.name || favorite.id")
el-button(type="text" icon="el-icon-close" size="mini" @click.stop="deleteFavorite(favorite.id)" style="margin-left:5px")
div(v-else style="height:20px;width:100%;display:flex;align-items:center;justify-content:center;color:rgb(144, 147, 153)")
span No Data
el-collapse-item
template(slot="title")
span(style="font-weight:bold;font-size:14px;margin-left:10px") Local History
span(style="color:#909399;font-size:12px;margin-left:10px") {{ avatarHistoryArray.length }}/100
el-tooltip(placement="right" content="Clear" :disabled="hideTooltips")
el-button(@click.stop="promptClearAvatarHistory" size="mini" icon="el-icon-delete" circle style="margin-left:5px")
.x-friend-list(v-if="avatarHistoryArray.length" style="margin-top:10px")
div(style="display:inline-block;width:300px;margin-right:15px" v-for="favorite in avatarHistoryArray" :key="favorite.id" @click="showAvatarDialog(favorite.id)")
.x-friend-item
.avatar
img(v-lazy="favorite.thumbnailImageUrl")
.detail
span.name(v-text="favorite.name")
span.extra(v-text="favorite.authorName")
el-tooltip(placement="left" :content="$t('view.favorite.select_avatar_tooltip')" :disabled="hideTooltips")
el-button(@click.stop="selectAvatarWithConfirmation(favorite.id)" :disabled="API.currentUser.currentAvatar === favorite.id" size="mini" icon="el-icon-check" circle style="margin-left:5px")
template(v-if="API.cachedFavoritesByObjectId.has(favorite.id)")
el-tooltip(placement="right" content="Unfavorite" :disabled="hideTooltips")
el-button(@click.stop="showFavoriteDialog('avatar', favorite.id)" type="default" icon="el-icon-star-on" size="mini" circle style="margin-left:5px")
template(v-else)
el-tooltip(placement="right" content="Favorite" :disabled="hideTooltips")
el-button(@click.stop="showFavoriteDialog('avatar', favorite.id)" type="default" icon="el-icon-star-off" size="mini" circle style="margin-left:5px")
div(v-else style="height:20px;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.avatars.local_favorites') }}
br
el-button(size="small" :disabled="!isLocalUserVrcplusSupporter()" @click="promptNewLocalAvatarFavoriteGroup") {{ $t('view.favorite.avatars.new_group') }}
el-button(v-if="!refreshingLocalFavorites" size="small" @click="refreshLocalAvatarFavorites" style=";margin-left:5px") {{ $t('view.favorite.avatars.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.avatars.cancel_refresh') }}
el-collapse-item(v-for="group in localAvatarFavoriteGroups" v-if="localAvatarFavorites[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") {{ getLocalAvatarFavoriteGroupLength(group) }}
el-tooltip(placement="top" :content="$t('view.favorite.rename_tooltip')" :disabled="hideTooltips")
el-button(@click.stop="promptLocalAvatarFavoriteGroupRename(group)" size="mini" icon="el-icon-edit" circle style="margin-left:5px")
el-tooltip(placement="right" :content="$t('view.favorite.delete_tooltip')" :disabled="hideTooltips")
el-button(@click.stop="promptLocalAvatarFavoriteGroupDelete(group)" size="mini" icon="el-icon-delete" circle style="margin-left:5px")
.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]" :key="favorite.id" @click="showAvatarDialog(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-text="favorite.authorName")
template(v-if="editFavoritesMode")
el-tooltip(placement="left" :content="$t('view.favorite.copy_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")
template(v-for="groupAPI in API.favoriteAvatarGroups" :key="groupAPI.name")
el-dropdown-item(style="display:block;margin:10px 0" @click.native="addFavoriteAvatar(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.select_avatar_tooltip')" :disabled="hideTooltips")
el-button(@click.stop="selectAvatarWithConfirmation(favorite.id)" :disabled="API.currentUser.currentAvatar === favorite.id" size="mini" icon="el-icon-check" circle style="margin-left:5px")
el-tooltip(placement="right" :content="$t('view.favorite.unfavorite_tooltip')" :disabled="hideTooltips")
el-button(v-if="shiftHeld" @click.stop="removeLocalAvatarFavorite(favorite.id, group)" size="mini" icon="el-icon-close" circle style="color:#f56c6c;margin-left:5px")
el-button(v-else @click.stop="showFavoriteDialog('avatar', 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="removeLocalAvatarFavorite(favorite.id, group)" style="margin-left:5px")
div(v-else style="height:20px;width:100%;display:flex;align-items:center;justify-content:center;color:rgb(144, 147, 153)")
span No Data

120
src/mixins/tabs/feed.pug Normal file
View File

@@ -0,0 +1,120 @@
mixin feedTab()
.x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'feed'")
data-tables(v-bind="feedTable" v-loading="feedTable.loading")
template(#tool)
div(style="margin:0 0 10px;display:flex;align-items:center")
div(style="flex:none;margin-right:10px;display:flex;align-items:center;")
el-tooltip(placement="bottom" :content="$t('view.feed.favorites_only_tooltip')" :disabled="hideTooltips")
el-switch(v-model="feedTable.vip" @change="feedTableLookup" active-color="#13ce66")
el-select(v-model="feedTable.filter" @change="feedTableLookup" multiple clearable style="flex:1;height:40px;" :placeholder="$t('view.feed.filter_placeholder')")
el-option(v-once v-for="type in ['GPS', 'Online', 'Offline', 'Status', 'Avatar', 'Bio']" :key="type" :label="$t('view.feed.filters.' + type)" :value="type")
el-input(v-model="feedTable.search" :placeholder="$t('view.feed.search_placeholder')" @keyup.native.13="feedTableLookup" @change="feedTableLookup" clearable style="flex:none;width:150px;margin:0 10px")
el-table-column(type="expand" width="20")
template(v-once #default="scope")
div(style="position:relative;font-size:14px")
template(v-if="scope.row.type === 'GPS'")
location(v-if="scope.row.previousLocation" :location="scope.row.previousLocation")
el-tag(type="info" effect="plain" size="mini" style="margin-left:5px") {{ timeToText(scope.row.time) }}
br
span(style="margin-right:5px")
i.el-icon-right
location(v-if="scope.row.location" :location="scope.row.location" :hint="scope.row.worldName" :grouphint="scope.row.groupName")
template(v-else-if="scope.row.type === 'Offline'")
template(v-if="scope.row.location")
location(:location="scope.row.location" :hint="scope.row.worldName" :grouphint="scope.row.groupName")
el-tag(type="info" effect="plain" size="mini" style="margin-left:5px") {{ timeToText(scope.row.time) }}
template(v-else-if="scope.row.type === 'Online'")
location(v-if="scope.row.location" :location="scope.row.location" :hint="scope.row.worldName" :grouphint="scope.row.groupName")
template(v-else-if="scope.row.type === 'Avatar'")
el-popover(placement="right" width="500px" trigger="click")
div(slot="reference" style="display:inline-block;vertical-align:top;width:160px")
template(v-if="scope.row.previousCurrentAvatarThumbnailImageUrl")
img.x-link(v-lazy="scope.row.previousCurrentAvatarThumbnailImageUrl" style="flex:none;width:160px;height:120px;border-radius:4px")
br
avatar-info(:imageurl="scope.row.previousCurrentAvatarThumbnailImageUrl" :userid="scope.row.userId" :hintownerid="scope.row.previousOwnerId" :hintavatarname="scope.row.previousAvatarName" :avatartags="scope.row.previousCurrentAvatarTags")
img.x-link(v-lazy="scope.row.previousCurrentAvatarImageUrl" style="width:500px;height:375px" @click="showFullscreenImageDialog(scope.row.previousCurrentAvatarImageUrl)")
span(style="position:relative;vertical-align:top;margin:0 5px")
i.el-icon-right
el-popover(placement="right" width="500px" trigger="click")
div(slot="reference" style="display:inline-block;vertical-align:top;width:160px")
template(v-if="scope.row.currentAvatarThumbnailImageUrl")
img.x-link(v-lazy="scope.row.currentAvatarThumbnailImageUrl" style="flex:none;width:160px;height:120px;border-radius:4px")
br
avatar-info(:imageurl="scope.row.currentAvatarThumbnailImageUrl" :userid="scope.row.userId" :hintownerid="scope.row.ownerId" :hintavatarname="scope.row.avatarName" :avatartags="scope.row.currentAvatarTags")
img.x-link(v-lazy="scope.row.currentAvatarImageUrl" style="width:500px;height:375px" @click="showFullscreenImageDialog(scope.row.currentAvatarImageUrl)")
template(v-else-if="scope.row.type === 'Status'")
el-tooltip(placement="top")
template(#content)
span(v-if="scope.row.previousStatus === 'active'") {{ $t('dialog.user.status.active') }}
span(v-else-if="scope.row.previousStatus === 'join me'") {{ $t('dialog.user.status.join_me') }}
span(v-else-if="scope.row.previousStatus === 'ask me'") {{ $t('dialog.user.status.ask_me') }}
span(v-else-if="scope.row.previousStatus === 'busy'") {{ $t('dialog.user.status.busy') }}
span(v-else) {{ $t('dialog.user.status.offline') }}
i.x-user-status(:class="statusClass(scope.row.previousStatus)")
span(v-text="scope.row.previousStatusDescription" style="margin-left:5px")
br
span
i.el-icon-right
el-tooltip(placement="top")
template(#content)
span(v-if="scope.row.status === 'active'") {{ $t('dialog.user.status.active') }}
span(v-else-if="scope.row.status === 'join me'") {{ $t('dialog.user.status.join_me') }}
span(v-else-if="scope.row.status === 'ask me'") {{ $t('dialog.user.status.ask_me') }}
span(v-else-if="scope.row.status === 'busy'") {{ $t('dialog.user.status.busy') }}
span(v-else) {{ $t('dialog.user.status.offline') }}
i.x-user-status(:class="statusClass(scope.row.status)" style="margin:0 5px")
span(v-text="scope.row.statusDescription")
template(v-else-if="scope.row.type === 'Bio'")
pre(v-html="formatDifference(scope.row.previousBio, scope.row.bio)" style="font-family:inherit;font-size:12px;white-space:pre-wrap;line-height:25px;line-height: 22px;")
el-table-column(:label="$t('table.feed.date')" prop="created_at" sortable="custom" width="120")
template(v-once #default="scope")
el-tooltip(placement="right")
template(#content)
span {{ scope.row.created_at | formatDate('long') }}
span {{ scope.row.created_at | formatDate('short') }}
el-table-column(:label="$t('table.feed.type')" prop="type" width="70")
template(v-once #default="scope")
span.x-link(v-text="$t('view.feed.filters.' + scope.row.type)")
el-table-column(:label="$t('table.feed.user')" prop="displayName" width="180")
template(v-once #default="scope")
span.x-link(v-text="scope.row.displayName" @click="showUserDialog(scope.row.userId)" style="padding-right:10px")
el-table-column(:label="$t('table.feed.detail')")
template(v-once #default="scope")
template(v-if="scope.row.type === 'GPS'")
location(v-if="scope.row.location" :location="scope.row.location" :hint="scope.row.worldName" :grouphint="scope.row.groupName")
template(v-else-if="scope.row.type === 'Offline' || scope.row.type === 'Online'")
location(v-if="scope.row.location" :location="scope.row.location" :hint="scope.row.worldName" :grouphint="scope.row.groupName")
template(v-else-if="scope.row.type === 'Status'")
template(v-if="scope.row.statusDescription === scope.row.previousStatusDescription")
el-tooltip(placement="top")
template(#content)
span(v-if="scope.row.previousStatus === 'active'") {{ $t('dialog.user.status.active') }}
span(v-else-if="scope.row.previousStatus === 'join me'") {{ $t('dialog.user.status.join_me') }}
span(v-else-if="scope.row.previousStatus === 'ask me'") {{ $t('dialog.user.status.ask_me') }}
span(v-else-if="scope.row.previousStatus === 'busy'") {{ $t('dialog.user.status.busy') }}
span(v-else) {{ $t('dialog.user.status.offline') }}
i.x-user-status(:class="statusClass(scope.row.previousStatus)")
span(style="margin:0 5px")
i.el-icon-right
el-tooltip(placement="top")
template(#content)
span(v-if="scope.row.status === 'active'") {{ $t('dialog.user.status.active') }}
span(v-else-if="scope.row.status === 'join me'") {{ $t('dialog.user.status.join_me') }}
span(v-else-if="scope.row.status === 'ask me'") {{ $t('dialog.user.status.ask_me') }}
span(v-else-if="scope.row.status === 'busy'") {{ $t('dialog.user.status.busy') }}
span(v-else) {{ $t('dialog.user.status.offline') }}
i.x-user-status(:class="statusClass(scope.row.status)")
template(v-else)
el-tooltip(placement="top")
template(#content)
span(v-if="scope.row.status === 'active'") {{ $t('dialog.user.status.active') }}
span(v-else-if="scope.row.status === 'join me'") {{ $t('dialog.user.status.join_me') }}
span(v-else-if="scope.row.status === 'ask me'") {{ $t('dialog.user.status.ask_me') }}
span(v-else-if="scope.row.status === 'busy'") {{ $t('dialog.user.status.busy') }}
span(v-else) {{ $t('dialog.user.status.offline') }}
i.x-user-status(:class="statusClass(scope.row.status)" style="margin-right:3px")
span(v-text="scope.row.statusDescription")
template(v-else-if="scope.row.type === 'Avatar'")
avatar-info(:imageurl="scope.row.currentAvatarImageUrl" :userid="scope.row.userId" :hintownerid="scope.row.ownerId" :hintavatarname="scope.row.avatarName" :avatartags="scope.row.currentAvatarTags")
template(v-else-if="scope.row.type === 'Bio'")
span(v-text="scope.row.bio")

View File

@@ -0,0 +1,24 @@
mixin friendLogTab()
.x-container(v-if="$refs.menu && $refs.menu.activeIndex === 'friendLog'")
data-tables(v-bind="friendLogTable" ref="friendLogTableRef")
template(#tool)
div(style="margin:0 0 10px;display:flex;align-items:center")
el-select(v-model="friendLogTable.filters[0].value" @change="saveTableFilters" multiple clearable style="flex:1" :placeholder="$t('view.friend_log.filter_placeholder')")
el-option(v-once v-for="type in ['Friend', 'Unfriend', 'FriendRequest', 'CancelFriendRequest', 'DisplayName', 'TrustLevel']" :key="type" :label="$t('view.friend_log.filters.' + type)" :value="type")
el-input(v-model="friendLogTable.filters[1].value" :placeholder="$t('view.friend_log.search_placeholder')" style="flex:none;width:150px;margin-left:10px")
el-table-column(:label="$t('table.friendLog.date')" prop="created_at" sortable="custom" width="200")
template(v-once #default="scope")
span {{ scope.row.created_at | formatDate('long') }}
el-table-column(:label="$t('table.friendLog.type')" prop="type" width="150")
template(v-once #default="scope")
span(v-text="$t('view.friend_log.filters.' + scope.row.type)")
el-table-column(:label="$t('table.friendLog.user')" prop="displayName")
template(v-once #default="scope")
span(v-if="scope.row.type === 'DisplayName'") {{ scope.row.previousDisplayName }} #[i.el-icon-right]&nbsp;
span.x-link(v-text="scope.row.displayName || scope.row.userId" @click="showUserDialog(scope.row.userId)" style="padding-right:10px")
template(v-if="scope.row.type === 'TrustLevel'")
span ({{ scope.row.previousTrustLevel }} #[i.el-icon-right] {{ scope.row.trustLevel }})
el-table-column(:label="$t('table.friendLog.action')" width="80" align="right")
template(v-once #default="scope")
el-button(v-if="shiftHeld" style="color:#f56c6c" type="text" icon="el-icon-close" size="mini" @click="deleteFriendLog(scope.row)")
el-button(v-else type="text" icon="el-icon-delete" size="mini" @click="deleteFriendLogPrompt(scope.row)")

View File

@@ -0,0 +1,85 @@
mixin friendsListTab()
.x-container(v-if="$refs.menu && $refs.menu.activeIndex === 'friendsList'")
div.options-container(style="margin-top:0")
span.header {{ $t('view.friend_list.header') }}
div(style="float:right;font-size:13px")
div(v-if="friendsListBulkUnfriendMode" style="display:inline-block;margin-right:10px")
el-button(size="small" @click="showBulkUnfriendSelectionConfirm") {{ $t('view.friend_list.bulk_unfriend_selection') }}
//- el-button(size="small" @click="showBulkUnfriendAllConfirm" style="margin-right:5px") Bulk Unfriend All
div(style="display:inline-block;margin-right:10px")
span.name {{ $t('view.friend_list.bulk_unfriend') }}
el-switch(@change="toggleFriendsListBulkUnfriendMode" v-model="friendsListBulkUnfriendMode" style="margin-left:5px")
span {{ $t('view.friend_list.load') }}
template(v-if="friendsListLoading")
span(v-text="friendsListLoadingProgress" style="margin-left:5px")
el-tooltip(placement="top" :content="$t('view.friend_list.cancel_tooltip')" :disabled="hideTooltips")
el-button(@click="friendsListLoading = false" size="mini" icon="el-icon-loading" circle style="margin-left:5px")
template(v-else)
el-tooltip(placement="top" :content="$t('view.friend_list.load_tooltip')" :disabled="hideTooltips")
el-button(@click="friendsListLoadUsers" size="mini" icon="el-icon-refresh-left" circle style="margin-left:5px")
div(style="margin:10px 0 0 10px;display:flex;align-items:center")
div(style="flex:none;margin-right:10px;display:flex;align-items:center;")
el-tooltip(placement="bottom" :content="$t('view.friend_list.favorites_only_tooltip')" :disabled="hideTooltips")
el-switch(v-model="friendsListSearchFilterVIP" @change="friendsListSearchChange" active-color="#13ce66")
el-input(v-model="friendsListSearch" :placeholder="$t('view.friend_list.search_placeholder')" @change="friendsListSearchChange" clearable style="flex:1")
el-select(v-model="friendsListSearchFilters" multiple clearable collapse-tags style="flex:none;width:200px;margin:0 10px" @change="friendsListSearchChange" :placeholder="$t('view.friend_list.filter_placeholder')")
el-option(v-once v-for="type in ['Display Name', 'User Name', 'Rank', 'Status', 'Bio', 'Memo']" :key="type" :label="type" :value="type")
el-tooltip(placement="top" :content="$t('view.friend_list.refresh_tooltip')" :disabled="hideTooltips")
el-button(type="default" @click="friendsListSearchChange" icon="el-icon-refresh" circle style="flex:none")
el-tooltip(placement="top" :content="$t('view.friend_list.clear_tooltip')" :disabled="hideTooltips")
el-button(type="default" @click="friendsListTable.data = []" icon="el-icon-delete" circle style="flex:none;margin-left:5px")
data-tables(v-bind="friendsListTable" @row-click="selectFriendsListRow" style="margin-top:10px;cursor:pointer")
el-table-column(width="55" prop="$selected" v-if="friendsListBulkUnfriendMode" :key="friendsListBulkUnfriendForceUpdate")
template(v-once #default="scope")
el-button(type="text" size="mini" @click.stop)
el-checkbox(v-model="scope.row.$selected" @change="friendsListBulkUnfriendForceUpdate++")
el-table-column(:label="$t('table.friendList.no')" width="70" prop="$friendNumber" sortable="custom")
template(v-once #default="scope")
span {{ scope.row.$friendNumber ? scope.row.$friendNumber : '' }}
el-table-column(:label="$t('table.friendList.avatar')" width="70" prop="photo")
template(v-once #default="scope")
el-popover(placement="right" height="500px" trigger="hover")
img.friends-list-avatar(slot="reference" v-lazy="userImage(scope.row)")
img.friends-list-avatar(v-lazy="userImageFull(scope.row)" style="height:500px;cursor:pointer" @click="showFullscreenImageDialog(userImageFull(scope.row))")
el-table-column(:label="$t('table.friendList.displayName')" min-width="140" prop="displayName" sortable :sort-method="(a, b) => sortAlphabetically(a, b, 'displayName')")
template(v-once #default="scope")
span.name(v-if="randomUserColours" v-text="scope.row.displayName" :style="{'color':scope.row.$userColour}")
span.name(v-else v-text="scope.row.displayName")
el-table-column(:label="$t('table.friendList.rank')" width="110" prop="$trustSortNum" sortable="custom")
template(v-once #default="scope")
span.name(v-if="randomUserColours" v-text="scope.row.$trustLevel" :class="scope.row.$trustClass")
span.name(v-else v-text="scope.row.$trustLevel" :style="{'color':scope.row.$userColour}")
el-table-column(:label="$t('table.friendList.status')" min-width="180" prop="status" sortable :sort-method="(a, b) => sortStatus(a.status, b.status)")
template(v-once #default="scope")
i.x-user-status(v-if="scope.row.status !== 'offline'" :class="statusClass(scope.row.status)")
span
span(v-text="scope.row.statusDescription")
el-table-column(:label="$t('table.friendList.language')" width="110" prop="$languages" sortable :sort-method="(a, b) => sortLanguages(a, b)")
template(v-once #default="scope")
el-tooltip(v-for="item in scope.row.$languages" :key="item.key" placement="top")
template(#content)
span {{ item.value }} ({{ item.key }})
span.flags(:class="languageClass(item.key)" style="display:inline-block;margin-left:5px")
el-table-column(:label="$t('table.friendList.bioLink')" width="100" prop="bioLinks")
template(v-once #default="scope")
el-tooltip(v-if="link" v-for="(link, index) in scope.row.bioLinks" :key="index")
template(#content)
span(v-text="link")
img(:src="getFaviconUrl(link)" style="width:16px;height:16px;vertical-align:middle;margin-right:5px;cursor:pointer" @click.stop="openExternalLink(link)")
el-table-column(:label="$t('table.friendList.joinCount')" width="120" prop="$joinCount" sortable)
el-table-column(:label="$t('table.friendList.timeTogether')" width="140" prop="$timeSpent" sortable)
template(v-once #default="scope")
span(v-if="scope.row.$timeSpent") {{ timeToText(scope.row.$timeSpent) }}
el-table-column(:label="$t('table.friendList.lastSeen')" width="170" prop="$lastSeen" sortable :sort-method="(a, b) => sortAlphabetically(a, b, '$lastSeen')")
template(v-once #default="scope")
span {{ scope.row.$lastSeen | formatDate('long') }}
el-table-column(:label="$t('table.friendList.lastActivity')" width="170" prop="last_activity" sortable :sort-method="(a, b) => sortAlphabetically(a, b, 'last_activity')")
template(v-once #default="scope")
span {{ scope.row.last_activity | formatDate('long') }}
el-table-column(:label="$t('table.friendList.lastLogin')" width="170" prop="last_login" sortable :sort-method="(a, b) => sortAlphabetically(a, b, 'last_login')")
template(v-once #default="scope")
span {{ scope.row.last_login | formatDate('long') }}
el-table-column(:label="$t('table.friendList.dateJoined')" width="120" prop="date_joined" sortable :sort-method="(a, b) => sortAlphabetically(a, b, 'date_joined')")
el-table-column(:label="$t('table.friendList.unfriend')" width="80")
template(v-once #default="scope")
el-button(type="text" icon="el-icon-close" size="mini" @click.stop="confirmDeleteFriend(scope.row.id)")

View File

@@ -0,0 +1,57 @@
mixin gameLogTab()
.x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'gameLog'")
data-tables(v-bind="gameLogTable" v-loading="gameLogTable.loading")
template(#tool)
div(style="margin:0 0 10px;display:flex;align-items:center")
div(style="flex:none;margin-right:10px;display:flex;align-items:center;")
el-tooltip(placement="bottom" :content="$t('view.feed.favorites_only_tooltip')" :disabled="hideTooltips")
el-switch(v-model="gameLogTable.vip" @change="gameLogTableLookup" active-color="#13ce66")
el-select(v-model="gameLogTable.filter" @change="gameLogTableLookup" multiple clearable style="flex:1" :placeholder="$t('view.game_log.filter_placeholder')")
el-option(v-once v-for="type in ['Location', 'OnPlayerJoined', 'OnPlayerLeft', 'PortalSpawn', 'VideoPlay', 'Event', 'External', 'StringLoad', 'ImageLoad']" :key="type" :label="$t('view.game_log.filters.' + type)" :value="type")
el-input(v-model="gameLogTable.search" :placeholder="$t('view.game_log.search_placeholder')" @keyup.native.13="gameLogTableLookup" @change="gameLogTableLookup" clearable style="flex:none;width:150px;margin:0 10px")
el-table-column(:label="$t('table.gameLog.date')" prop="created_at" sortable="custom" width="120")
template(v-once #default="scope")
el-tooltip(placement="right")
template(#content)
span {{ scope.row.created_at | formatDate('long') }}
span {{ scope.row.created_at | formatDate('short') }}
el-table-column(:label="$t('table.gameLog.type')" prop="type" width="120")
template(v-once #default="scope")
span.x-link(v-if="scope.row.location && scope.row.type !== 'Location'" v-text="$t('view.game_log.filters.' + scope.row.type)" @click="showWorldDialog(scope.row.location)")
span(v-else v-text="$t('view.game_log.filters.' + scope.row.type)")
el-table-column(:label="$t('table.gameLog.icon')" prop="isFriend" width="70")
template(v-once #default="scope")
template(v-if="gameLogIsFriend(scope.row)")
el-tooltip(v-if="gameLogIsFavorite(scope.row)" placement="top" content="Favorite")
span ⭐
el-tooltip(v-else placement="top" content="Friend")
span 💚
el-table-column(:label="$t('table.gameLog.user')" prop="displayName" width="180")
template(v-once #default="scope")
span.x-link(v-if="scope.row.displayName" v-text="scope.row.displayName" @click="lookupUser(scope.row)" style="padding-right:10px")
el-table-column(:label="$t('table.gameLog.detail')" prop="data")
template(v-once #default="scope")
location(v-if="scope.row.type === 'Location'" :location="scope.row.location" :hint="scope.row.worldName" :grouphint="scope.row.groupName")
location(v-else-if="scope.row.type === 'PortalSpawn'" :location="scope.row.instanceId" :hint="scope.row.worldName" :grouphint="scope.row.groupName")
template(v-else-if="scope.row.type === 'Event'")
span(v-text="scope.row.data")
template(v-else-if="scope.row.type === 'External'")
span(v-text="scope.row.message")
template(v-else-if="scope.row.type === 'VideoPlay'")
span(v-if="scope.row.videoId" style="margin-right:5px") {{ scope.row.videoId }}:
span(v-if="scope.row.videoId === 'LSMedia'" v-text="scope.row.videoName")
span.x-link(v-else-if="scope.row.videoName" @click="openExternalLink(scope.row.videoUrl)" v-text="scope.row.videoName")
span.x-link(v-else @click="openExternalLink(scope.row.videoUrl)" v-text="scope.row.videoUrl")
template(v-else-if="scope.row.type === 'ImageLoad'")
span.x-link(@click="openExternalLink(scope.row.resourceUrl)" v-text="scope.row.resourceUrl")
template(v-else-if="scope.row.type === 'StringLoad'")
span.x-link(@click="openExternalLink(scope.row.resourceUrl)" v-text="scope.row.resourceUrl")
template(v-else-if="scope.row.type === 'Notification' || scope.row.type === 'OnPlayerJoined' || scope.row.type === 'OnPlayerLeft'")
span.x-link(v-else v-text="scope.row.data")
el-table-column(:label="$t('table.gameLog.action')" width="80" align="right")
template(v-once #default="scope")
template(v-if="scope.row.type !== 'OnPlayerJoined' && scope.row.type !== 'OnPlayerLeft' && scope.row.type !== 'Location' && scope.row.type !== 'PortalSpawn'")
el-button(v-if="shiftHeld" style="color:#f56c6c" type="text" icon="el-icon-close" size="mini" @click="deleteGameLogEntry(scope.row)")
el-button(v-else type="text" icon="el-icon-delete" size="mini" @click="deleteGameLogEntryPrompt(scope.row)")
el-tooltip(placement="top" content="Open Instance Info" :disabled="hideTooltips")
el-button(v-if="scope.row.type === 'Location'" type="text" icon="el-icon-tickets" size="mini" @click="showPreviousInstanceInfoDialog(scope.row.location)")

View File

@@ -0,0 +1,30 @@
mixin moderationTab()
.x-container(v-if="$refs.menu && $refs.menu.activeIndex === 'moderation'")
data-tables(v-bind="playerModerationTable" ref="playerModerationTableRef" v-loading="API.isPlayerModerationsLoading")
template(#tool)
div(style="margin:0 0 10px;display:flex;align-items:center")
el-select(v-model="playerModerationTable.filters[0].value" @change="saveTableFilters" multiple clearable style="flex:1" :placeholder="$t('view.moderation.filter_placeholder')")
el-option(v-once v-for="type in ['block', 'unblock', 'mute', 'unmute', 'interactOn', 'interactOff', 'muteChat']" :key="type" :label="$t('view.moderation.filters.' + type)" :value="type")
el-input(v-model="playerModerationTable.filters[1].value" :placeholder="$t('view.moderation.search_placeholder')" style="flex:none;width:150px;margin:0 10px")
el-tooltip(placement="bottom" :content="$t('view.moderation.refresh_tooltip')" :disabled="hideTooltips")
el-button(type="default" :loading="API.isPlayerModerationsLoading" @click="API.refreshPlayerModerations()" icon="el-icon-refresh" circle style="flex:none")
el-table-column(:label="$t('table.moderation.date')" prop="created" sortable="custom" width="120")
template(v-once #default="scope")
el-tooltip(placement="right")
template(#content)
span {{ scope.row.created | formatDate('long') }}
span {{ scope.row.created | formatDate('short') }}
el-table-column(:label="$t('table.moderation.type')" prop="type" width="100")
template(v-once #default="scope")
span(v-text="$t('view.moderation.filters.' + scope.row.type)")
el-table-column(:label="$t('table.moderation.source')" prop="sourceDisplayName")
template(v-once #default="scope")
span.x-link(v-text="scope.row.sourceDisplayName" @click="showUserDialog(scope.row.sourceUserId)")
el-table-column(:label="$t('table.moderation.target')" prop="targetDisplayName")
template(v-once #default="scope")
span.x-link(v-text="scope.row.targetDisplayName" @click="showUserDialog(scope.row.targetUserId)")
el-table-column(:label="$t('table.moderation.action')" width="80" align="right")
template(v-once #default="scope")
template(v-if="scope.row.sourceUserId === API.currentUser.id")
el-button(v-if="shiftHeld" style="color:#f56c6c" type="text" icon="el-icon-close" size="mini" @click="deletePlayerModeration(scope.row)")
el-button(v-else type="text" icon="el-icon-close" size="mini" @click="deletePlayerModerationPrompt(scope.row)")

View File

@@ -0,0 +1,95 @@
mixin notificationsTab()
.x-container(v-if="$refs.menu && $refs.menu.activeIndex === 'notification'" v-loading="API.isNotificationsLoading")
data-tables(v-bind="notificationTable" ref="notificationTableRef" class="notification-table")
template(#tool)
div(style="margin:0 0 10px;display:flex;align-items:center")
el-select(v-model="notificationTable.filters[0].value" @change="saveTableFilters" multiple clearable style="flex:1" :placeholder="$t('view.notification.filter_placeholder')")
el-option(v-once v-for="type in ['requestInvite', 'invite', 'requestInviteResponse', 'inviteResponse', 'friendRequest', 'ignoredFriendRequest', 'message', 'boop', 'groupChange', 'group.announcement', 'group.informative', 'group.invite', 'group.joinRequest', 'group.transfer', 'group.queueReady', 'moderation.warning.group', 'moderation.report.closed', 'instance.closed']" :key="type" :label="$t('view.notification.filters.' + type)" :value="type")
el-input(v-model="notificationTable.filters[1].value" :placeholder="$t('view.notification.search_placeholder')" style="flex:none;width:150px;margin:0 10px")
el-tooltip(placement="bottom" :content="$t('view.notification.refresh_tooltip')" :disabled="hideTooltips")
el-button(type="default" :loading="API.isNotificationsLoading" @click="API.refreshNotifications()" icon="el-icon-refresh" circle style="flex:none")
el-table-column(:label="$t('table.notification.date')" prop="created_at" sortable="custom" width="120")
template(v-once #default="scope")
el-tooltip(placement="right")
template(#content)
span {{ scope.row.created_at | formatDate('long') }}
span {{ scope.row.created_at | formatDate('short') }}
el-table-column(:label="$t('table.notification.type')" prop="type" width="180")
template(v-once #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)")
el-tooltip(v-else-if="scope.row.type === 'group.queueReady' || scope.row.type === 'instance.closed'" placement="top")
template(#content)
location(v-if="scope.row.location" :location="scope.row.location" :hint="scope.row.worldName" :grouphint="scope.row.groupName" :link="false")
span.x-link(v-text="$t('view.notification.filters.' + scope.row.type)" @click="showWorldDialog(scope.row.location)")
el-tooltip(v-else-if="scope.row.link" placement="top" :content="scope.row.linkText" :disabled="hideTooltips")
span.x-link(v-text="$t('view.notification.filters.' + scope.row.type)" @click="openNotificationLink(scope.row.link)")
span(v-else v-text="$t('view.notification.filters.' + scope.row.type)")
el-table-column(:label="$t('table.notification.user_group')" prop="senderUsername" width="150")
template(v-once #default="scope")
template(v-if="scope.row.type === 'groupChange'")
span.x-link(v-text="scope.row.senderUsername" @click="showGroupDialog(scope.row.senderUserId)")
template(v-else-if="scope.row.senderUserId")
span.x-link(v-text="scope.row.senderUsername" @click="showUserDialog(scope.row.senderUserId)")
template(v-else-if="scope.row.link && scope.row.data?.groupName")
span.x-link(v-text="scope.row.data?.groupName" @click="openNotificationLink(scope.row.link)")
template(v-else-if="scope.row.link")
span.x-link(v-text="scope.row.linkText" @click="openNotificationLink(scope.row.link)")
el-table-column(:label="$t('table.notification.photo')" width="100" prop="photo")
template(v-once #default="scope")
template(v-if="scope.row.details && scope.row.details.imageUrl")
el-popover(placement="right" width="500px" trigger="click")
img.x-link(slot="reference" v-lazy="scope.row.details.imageUrl" style="flex:none;height:50px;border-radius:4px")
img.x-link(v-lazy="scope.row.details.imageUrl" style="width:500px" @click="showFullscreenImageDialog(scope.row.details.imageUrl)")
template(v-else-if="scope.row.imageUrl")
el-popover(placement="right" width="500px" trigger="click")
img.x-link(slot="reference" v-lazy="scope.row.imageUrl" style="flex:none;height:50px;border-radius:4px")
img.x-link(v-lazy="scope.row.imageUrl" style="width:500px" @click="showFullscreenImageDialog(scope.row.imageUrl)")
el-table-column(:label="$t('table.notification.message')" prop="message")
template(v-once #default="scope")
span.x-link(v-if="scope.row.type === 'invite'")
location(v-if="scope.row.details" :location="scope.row.details.worldId" :hint="scope.row.details.worldName" :grouphint="scope.row.details.groupName" :link="true")
br
span(v-if="scope.row.message && scope.row.message !== `This is a generated invite to ${scope.row.details?.worldName}`" v-text="scope.row.message")
span(v-else-if='scope.row.details && scope.row.details.inviteMessage' v-text="scope.row.details.inviteMessage")
span(v-else-if='scope.row.details && scope.row.details.requestMessage' v-text="scope.row.details.requestMessage")
span(v-else-if='scope.row.details && scope.row.details.responseMessage' v-text="scope.row.details.responseMessage")
el-table-column(:label="$t('table.notification.action')" width="100" align="right")
template(v-once #default="scope")
template(v-if="scope.row.senderUserId !== API.currentUser.id && !scope.row.$isExpired")
template(v-if="scope.row.type === 'friendRequest'")
el-tooltip(placement="top" content="Accept" :disabled="hideTooltips")
el-button(type="text" icon="el-icon-check" size="mini" @click="acceptFriendRequestNotification(scope.row)")
template(v-else-if="scope.row.type === 'invite'")
el-tooltip(placement="top" content="Decline with message" :disabled="hideTooltips")
el-button(type="text" icon="el-icon-chat-line-square" size="mini" @click="showSendInviteResponseDialog(scope.row)")
template(v-else-if="scope.row.type === 'requestInvite'")
template(v-if="lastLocation.location && isGameRunning && checkCanInvite(lastLocation.location)")
el-tooltip(placement="top" content="Invite" :disabled="hideTooltips")
el-button(type="text" icon="el-icon-check" size="mini" @click="acceptRequestInvite(scope.row)")
el-tooltip(placement="top" content="Decline with message" :disabled="hideTooltips")
el-button(type="text" icon="el-icon-chat-line-square" size="mini" style="margin-left:5px" @click="showSendInviteRequestResponseDialog(scope.row)")
template(v-if="scope.row.responses")
template(v-for="response in scope.row.responses")
el-tooltip(placement="top" :content="response.text" :disabled="hideTooltips")
el-button(v-if="response.icon === 'check'" type="text" icon="el-icon-check" size="mini" style="margin-left:5px" @click="sendNotificationResponse(scope.row.id, scope.row.responses, response.type)")
el-button(v-else-if="response.icon === 'cancel'" type="text" icon="el-icon-close" size="mini" style="margin-left:5px" @click="sendNotificationResponse(scope.row.id, scope.row.responses, response.type)")
el-button(v-else-if="response.icon === 'ban'" type="text" icon="el-icon-circle-close" size="mini" style="margin-left:5px" @click="sendNotificationResponse(scope.row.id, scope.row.responses, response.type)")
el-button(v-else-if="response.icon === 'bell-slash'" type="text" icon="el-icon-bell" size="mini" style="margin-left:5px" @click="sendNotificationResponse(scope.row.id, scope.row.responses, response.type)")
el-button(v-else-if="response.icon === 'reply' && scope.row.type === 'boop'" type="text" icon="el-icon-chat-line-square" size="mini" style="margin-left:5px" @click="showSendBoopDialog(scope.row.senderUserId)")
el-button(v-else-if="response.icon === 'reply'" type="text" icon="el-icon-chat-line-square" size="mini" style="margin-left:5px" @click="sendNotificationResponse(scope.row.id, scope.row.responses, response.type)")
el-button(v-else type="text" icon="el-icon-collection-tag" size="mini" style="margin-left:5px" @click="sendNotificationResponse(scope.row.id, scope.row.responses, response.type)")
template(v-if="scope.row.type !== 'requestInviteResponse' && scope.row.type !== 'inviteResponse' && scope.row.type !== 'message' && scope.row.type !== 'boop' && scope.row.type !== 'groupChange' && !scope.row.type.includes('group.') && !scope.row.type.includes('moderation.') && !scope.row.type.includes('instance.')")
el-tooltip(placement="top" content="Decline" :disabled="hideTooltips")
el-button(v-if="shiftHeld" style="color:#f56c6c;margin-left:5px" type="text" icon="el-icon-close" size="mini" @click="hideNotification(scope.row)")
el-button(v-else type="text" icon="el-icon-close" size="mini" style="margin-left:5px" @click="hideNotificationPrompt(scope.row)")
template(v-if="scope.row.type === 'group.queueReady'")
el-tooltip(placement="top" content="Delete log" :disabled="hideTooltips")
el-button(v-if="shiftHeld" style="color:#f56c6c;margin-left:5px" type="text" icon="el-icon-close" size="mini" @click="deleteNotificationLog(scope.row)")
el-button(v-else type="text" icon="el-icon-delete" size="mini" style="margin-left:5px" @click="deleteNotificationLogPrompt(scope.row)")
template(v-if="scope.row.type !== 'friendRequest' && scope.row.type !== 'ignoredFriendRequest' && !scope.row.type.includes('group.') && !scope.row.type.includes('moderation.')")
el-tooltip(placement="top" content="Delete log" :disabled="hideTooltips")
el-button(v-if="shiftHeld" style="color:#f56c6c;margin-left:5px" type="text" icon="el-icon-close" size="mini" @click="deleteNotificationLog(scope.row)")
el-button(v-else type="text" icon="el-icon-delete" size="mini" style="margin-left:5px" @click="deleteNotificationLogPrompt(scope.row)")

View File

@@ -0,0 +1,271 @@
mixin playerListTab()
.x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'playerList'" style="padding-top:5px")
div(style="display:flex;flex-direction:column;height:100%")
div(v-if="currentInstanceWorld.ref.id" style="display:flex")
el-popover(placement="right" width="500px" trigger="click" style="height:120px")
img.x-link(slot="reference" v-lazy="currentInstanceWorld.ref.thumbnailImageUrl" style="flex:none;width:160px;height:120px;border-radius:4px")
img.x-link(v-lazy="currentInstanceWorld.ref.imageUrl" style="width:500px;height:375px" @click="showFullscreenImageDialog(currentInstanceWorld.ref.imageUrl)")
div(style="margin-left:10px;display:flex;flex-direction:column;min-width:320px;width:100%")
div
span.x-link(@click="showWorldDialog(currentInstanceWorld.ref.id)" style="font-weight:bold;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1")
| #[i.el-icon-s-home(v-show="API.currentUser.$homeLocation && API.currentUser.$homeLocation.worldId === currentInstanceWorld.ref.id" style="margin-right:5px")] {{ currentInstanceWorld.ref.name }}
div
span.x-link.x-grey(v-text="currentInstanceWorld.ref.authorName" @click="showUserDialog(currentInstanceWorld.ref.authorId)" style="font-family:monospace")
div(style="margin-top:5px")
el-tag(v-if="currentInstanceWorld.ref.$isLabs" type="primary" effect="plain" size="mini" style="margin-right:5px") {{ $t('dialog.world.tags.labs') }}
el-tag(v-else-if="currentInstanceWorld.ref.releaseStatus === 'public'" type="success" effect="plain" size="mini" style="margin-right:5px") {{ $t('dialog.world.tags.public') }}
el-tag(v-else-if="currentInstanceWorld.ref.releaseStatus === 'private'" type="danger" effect="plain" size="mini" style="margin-right:5px") {{ $t('dialog.world.tags.private') }}
el-tag.x-tag-platform-pc(v-if="currentInstanceWorld.isPC" type="info" effect="plain" size="mini" style="margin-right:5px") PC
span.x-grey(v-if="currentInstanceWorld.bundleSizes['standalonewindows']" style="margin-left:5px;border-left:inherit;padding-left:5px") {{ currentInstanceWorld.bundleSizes['standalonewindows'].fileSize }}
el-tag.x-tag-platform-quest(v-if="currentInstanceWorld.isQuest" type="info" effect="plain" size="mini" style="margin-right:5px") Android
span.x-grey(v-if="currentInstanceWorld.bundleSizes['android']" style="margin-left:5px;border-left:inherit;padding-left:5px") {{ currentInstanceWorld.bundleSizes['android'].fileSize }}
el-tag.x-tag-platform-ios(v-if="currentInstanceWorld.isIOS" type="info" effect="plain" size="mini" style="margin-right:5px") iOS
span.x-grey(v-if="currentInstanceWorld.bundleSizes['ios']" style="margin-left:5px;border-left:inherit;padding-left:5px") {{ currentInstanceWorld.bundleSizes['ios'].fileSize }}
el-tag(v-if="currentInstanceWorld.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="currentInstanceWorld.inCache" type="info" effect="plain" size="mini" style="margin-right:5px")
span(v-text="currentInstanceWorld.cacheSize")
| {{ $t('dialog.world.tags.cache') }}
br
location-world(:locationobject="currentInstanceLocation" :currentuserid="API.currentUser.id")
span(v-if="lastLocation.playerList.size > 0" style="margin-left:5px")
| {{ lastLocation.playerList.size }}
| #[template(v-if="lastLocation.friendList.size > 0") ({{ lastLocation.friendList.size }})]
| &nbsp;&horbar; #[timer(v-if="lastLocation.date" :epoch="lastLocation.date")]
div(style="margin-top:5px")
span(v-show="currentInstanceWorld.ref.name !== currentInstanceWorld.ref.description" v-text="currentInstanceWorld.ref.description" style="font-size:12px;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2")
div(style="display:flex;flex-direction:column;margin-left:20px")
.x-friend-item(style="cursor:default")
.detail
span.name {{ $t('dialog.world.info.capacity') }}
span.extra {{ currentInstanceWorld.ref.recommendedCapacity | commaNumber }} ({{ currentInstanceWorld.ref.capacity | commaNumber }})
.x-friend-item(style="cursor:default")
.detail
span.name {{ $t('dialog.world.info.last_updated') }}
span.extra {{ currentInstanceWorld.lastUpdated | formatDate('long') }}
.x-friend-item(style="cursor:default")
.detail
span.name {{ $t('dialog.world.info.created_at') }}
span.extra {{ currentInstanceWorld.ref.created_at | formatDate('long') }}
div.photon-event-table(v-if="photonLoggingEnabled")
div(style="position:absolute;width:600px;margin-left:215px;z-index:1")
el-select(v-model="photonEventTableTypeFilter" @change="photonEventTableFilterChange" multiple clearable collapse-tags style="flex:1;width:220px" :placeholder="$t('view.player_list.photon.filter_placeholder')")
el-option(v-once v-for="type in photonEventTableTypeFilterList" :key="type" :label="type" :value="type")
el-input(v-model="photonEventTableFilter" @input="photonEventTableFilterChange" :placeholder="$t('view.player_list.photon.search_placeholder')" clearable style="width:150px;margin-left:10px")
el-button(@click="showChatboxBlacklistDialog" style="margin-left:10px") {{ $t('view.player_list.photon.chatbox_blacklist') }}
el-tooltip(placement="bottom" :content="$t('view.player_list.photon.status_tooltip')" :disabled="hideTooltips")
div(style="display:inline-block;margin-left:15px;font-size:14px;vertical-align:text-top;margin-top:1px")
span(v-if="ipcEnabled && !photonEventIcon") 🟢
span(v-else-if="ipcEnabled") ⚪
span(v-else) 🔴
el-tabs(type="card")
el-tab-pane(:label="$t('view.player_list.photon.current')")
data-tables(v-bind="photonEventTable" style="margin-bottom:10px")
el-table-column(:label="$t('table.playerList.date')" prop="created_at" width="120")
template(v-once #default="scope")
el-tooltip(placement="right")
template(#content)
span {{ scope.row.created_at | formatDate('long') }}
span {{ scope.row.created_at | formatDate('short') }}
el-table-column(:label="$t('table.playerList.user')" prop="photonId" width="160")
template(v-once #default="scope")
span.x-link(v-text="scope.row.displayName" @click="showUserFromPhotonId(scope.row.photonId)" style="padding-right:10px")
el-table-column(:label="$t('table.playerList.type')" prop="type" width="140")
el-table-column(:label="$t('table.playerList.detail')" prop="text")
template(v-once #default="scope")
template(v-if="scope.row.type === 'ChangeAvatar'")
span.x-link(v-text="scope.row.avatar.name" @click="showAvatarDialog(scope.row.avatar.id)")
| &nbsp;
span(v-if="!scope.row.inCache" style="color:#aaa") #[i.el-icon-download]&nbsp;
span.avatar-info-public(v-if="scope.row.avatar.releaseStatus === 'public'") {{ $t('dialog.avatar.labels.public') }}
span.avatar-info-own(v-else-if="scope.row.avatar.releaseStatus === 'private'") {{ $t('dialog.avatar.labels.private') }}
template(v-if="scope.row.avatar.description && scope.row.avatar.name !== scope.row.avatar.description")
| - {{ scope.row.avatar.description }}
template(v-else-if="scope.row.type === 'ChangeStatus'")
template(v-if="scope.row.status !== scope.row.previousStatus")
el-tooltip(placement="top")
template(#content)
span(v-if="scope.row.previousStatus === 'active'") {{ $t('dialog.user.status.active') }}
span(v-else-if="scope.row.previousStatus === 'join me'") {{ $t('dialog.user.status.join_me') }}
span(v-else-if="scope.row.previousStatus === 'ask me'") {{ $t('dialog.user.status.ask_me') }}
span(v-else-if="scope.row.previousStatus === 'busy'") {{ $t('dialog.user.status.busy') }}
span(v-else) {{ $t('dialog.user.status.offline') }}
i.x-user-status(:class="statusClass(scope.row.previousStatus)")
span
i.el-icon-right
el-tooltip(placement="top")
template(#content)
span(v-if="scope.row.status === 'active'") {{ $t('dialog.user.status.active') }}
span(v-else-if="scope.row.status === 'join me'") {{ $t('dialog.user.status.join_me') }}
span(v-else-if="scope.row.status === 'ask me'") {{ $t('dialog.user.status.ask_me') }}
span(v-else-if="scope.row.status === 'busy'") {{ $t('dialog.user.status.busy') }}
span(v-else) {{ $t('dialog.user.status.offline') }}
i.x-user-status(:class="statusClass(scope.row.status)" style="margin-right:5px")
span(v-if="scope.row.statusDescription !== scope.row.previousStatusDescription" v-text="scope.row.statusDescription")
template(v-else-if="scope.row.type === 'ChangeGroup'")
span.x-link(v-if="scope.row.previousGroupName" v-text="scope.row.previousGroupName" @click="showGroupDialog(scope.row.previousGroupId)" style="margin-right:5px")
span.x-link(v-else v-text="scope.row.previousGroupId" @click="showGroupDialog(scope.row.previousGroupId)" style="margin-right:5px")
span
i.el-icon-right
span.x-link(v-if="scope.row.groupName" v-text="scope.row.groupName" @click="showGroupDialog(scope.row.groupId)" style="margin-left:5px")
span.x-link(v-else v-text="scope.row.groupId" @click="showGroupDialog(scope.row.groupId)" style="margin-left:5px")
span.x-link(v-else-if="scope.row.type === 'PortalSpawn'" @click="showWorldDialog(scope.row.location, scope.row.shortName)")
location(:location="scope.row.location" :hint="scope.row.worldName" :grouphint="scope.row.groupName" :link="false")
span(v-else-if="scope.row.type === 'ChatBoxMessage'" v-text="scope.row.text")
span(v-else-if="scope.row.type === 'OnPlayerJoined'")
span(v-if="scope.row.platform === 'Desktop'" style="color:#409eff") Desktop&nbsp;
span(v-else-if="scope.row.platform === 'VR'" style="color:#409eff") VR&nbsp;
span(v-else-if="scope.row.platform === 'Quest'" style="color:#67c23a") Android&nbsp;
span.x-link(v-text="scope.row.avatar.name" @click="showAvatarDialog(scope.row.avatar.id)")
| &nbsp;
span(v-if="!scope.row.inCache" style="color:#aaa") #[i.el-icon-download]&nbsp;
span.avatar-info-public(v-if="scope.row.avatar.releaseStatus === 'public'") {{ $t('dialog.avatar.labels.public') }}
span.avatar-info-own(v-else-if="scope.row.avatar.releaseStatus === 'private'") {{ $t('dialog.avatar.labels.private') }}
span(v-else-if="scope.row.type === 'SpawnEmoji'")
span(v-if="scope.row.imageUrl")
el-tooltip(placement="right")
template(#content)
img.friends-list-avatar(v-lazy="scope.row.imageUrl" style="height:500px;cursor:pointer" @click="showFullscreenImageDialog(scope.row.imageUrl)")
span(v-text="scope.row.fileId")
span(v-else v-text="scope.row.text")
span(v-else-if="scope.row.color === 'yellow'" v-text="scope.row.text" style="color:yellow")
span(v-else v-text="scope.row.text")
el-tab-pane(:label="$t('view.player_list.photon.previous')")
data-tables(v-bind="photonEventTablePrevious" style="margin-bottom:10px")
el-table-column(:label="$t('table.playerList.date')" prop="created_at" width="120")
template(v-once #default="scope")
el-tooltip(placement="right")
template(#content)
span {{ scope.row.created_at | formatDate('long') }}
span {{ scope.row.created_at | formatDate('short') }}
el-table-column(:label="$t('table.playerList.user')" prop="photonId" width="160")
template(v-once #default="scope")
span.x-link(v-text="scope.row.displayName" @click="lookupUser(scope.row)" style="padding-right:10px")
el-table-column(:label="$t('table.playerList.type')" prop="type" width="140")
el-table-column(:label="$t('table.playerList.detail')" prop="text")
template(v-once #default="scope")
template(v-if="scope.row.type === 'ChangeAvatar'")
span.x-link(v-text="scope.row.avatar.name" @click="showAvatarDialog(scope.row.avatar.id)")
| &nbsp;
span(v-if="!scope.row.inCache" style="color:#aaa") #[i.el-icon-download]&nbsp;
span.avatar-info-public(v-if="scope.row.avatar.releaseStatus === 'public'") {{ $t('dialog.avatar.labels.public') }}
span.avatar-info-own(v-else-if="scope.row.avatar.releaseStatus === 'private'") {{ $t('dialog.avatar.labels.private') }}
template(v-if="scope.row.avatar.description && scope.row.avatar.name !== scope.row.avatar.description")
| - {{ scope.row.avatar.description }}
template(v-else-if="scope.row.type === 'ChangeStatus'")
template(v-if="scope.row.status !== scope.row.previousStatus")
el-tooltip(placement="top")
template(#content)
span(v-if="scope.row.previousStatus === 'active'") {{ $t('dialog.user.status.active') }}
span(v-else-if="scope.row.previousStatus === 'join me'") {{ $t('dialog.user.status.join_me') }}
span(v-else-if="scope.row.previousStatus === 'ask me'") {{ $t('dialog.user.status.ask_me') }}
span(v-else-if="scope.row.previousStatus === 'busy'") {{ $t('dialog.user.status.busy') }}
span(v-else) {{ $t('dialog.user.status.offline') }}
i.x-user-status(:class="statusClass(scope.row.previousStatus)")
span
i.el-icon-right
el-tooltip(placement="top")
template(#content)
span(v-if="scope.row.status === 'active'") {{ $t('dialog.user.status.active') }}
span(v-else-if="scope.row.status === 'join me'") {{ $t('dialog.user.status.join_me') }}
span(v-else-if="scope.row.status === 'ask me'") {{ $t('dialog.user.status.ask_me') }}
span(v-else-if="scope.row.status === 'busy'") {{ $t('dialog.user.status.busy') }}
span(v-else) {{ $t('dialog.user.status.offline') }}
i.x-user-status(:class="statusClass(scope.row.status)" style="margin-right:5px")
span(v-if="scope.row.statusDescription !== scope.row.previousStatusDescription" v-text="scope.row.statusDescription")
template(v-else-if="scope.row.type === 'ChangeGroup'")
span.x-link(v-if="scope.row.previousGroupName" v-text="scope.row.previousGroupName" @click="showGroupDialog(scope.row.previousGroupId)" style="margin-right:5px")
span.x-link(v-else v-text="scope.row.previousGroupId" @click="showGroupDialog(scope.row.previousGroupId)" style="margin-right:5px")
span
i.el-icon-right
span.x-link(v-if="scope.row.groupName" v-text="scope.row.groupName" @click="showGroupDialog(scope.row.groupId)" style="margin-left:5px")
span.x-link(v-else v-text="scope.row.groupId" @click="showGroupDialog(scope.row.groupId)" style="margin-left:5px")
span.x-link(v-else-if="scope.row.type === 'PortalSpawn'" @click="showWorldDialog(scope.row.location, scope.row.shortName)")
location(:location="scope.row.location" :hint="scope.row.worldName" :grouphint="scope.row.groupName" :link="false")
span(v-else-if="scope.row.type === 'ChatBoxMessage'" v-text="scope.row.text")
span(v-else-if="scope.row.type === 'OnPlayerJoined'")
span(v-if="scope.row.platform === 'Desktop'" style="color:#409eff") Desktop&nbsp;
span(v-else-if="scope.row.platform === 'VR'" style="color:#409eff") VR&nbsp;
span(v-else-if="scope.row.platform === 'Quest'" style="color:#67c23a") Android&nbsp;
span.x-link(v-text="scope.row.avatar.name" @click="showAvatarDialog(scope.row.avatar.id)")
| &nbsp;
span(v-if="!scope.row.inCache" style="color:#aaa") #[i.el-icon-download]&nbsp;
span.avatar-info-public(v-if="scope.row.avatar.releaseStatus === 'public'") {{ $t('dialog.avatar.labels.public') }}
span.avatar-info-own(v-else-if="scope.row.avatar.releaseStatus === 'private'") {{ $t('dialog.avatar.labels.private') }}
span(v-else-if="scope.row.type === 'SpawnEmoji'")
span(v-if="scope.row.imageUrl")
el-tooltip(placement="right")
template(#content)
img.friends-list-avatar(v-lazy="scope.row.imageUrl" style="height:500px;cursor:pointer" @click="showFullscreenImageDialog(scope.row.imageUrl)")
span(v-text="scope.row.fileId")
span(v-else v-text="scope.row.text")
span(v-else-if="scope.row.color === 'yellow'" v-text="scope.row.text" style="color:yellow")
span(v-else v-text="scope.row.text")
div.current-instance-table
data-tables(v-bind="currentInstanceUserList" @row-click="selectCurrentInstanceRow" style="margin-top:10px;cursor:pointer")
el-table-column(:label="$t('table.playerList.avatar')" width="70" prop="photo")
template(v-once #default="scope")
template(v-if="userImage(scope.row.ref)")
el-popover(placement="right" height="500px" trigger="hover")
img.friends-list-avatar(slot="reference" v-lazy="userImage(scope.row.ref)")
img.friends-list-avatar(v-lazy="userImageFull(scope.row.ref)" style="height:500px;cursor:pointer" @click="showFullscreenImageDialog(userImageFull(scope.row.ref))")
el-table-column(:label="$t('table.playerList.timer')" width="90" prop="timer" sortable)
template(v-once #default="scope")
timer(:epoch="scope.row.timer")
el-table-column(v-if="photonLoggingEnabled" :label="$t('table.playerList.photonId')" width="110" prop="photonId" sortable)
template(v-once #default="scope")
template(v-if="chatboxUserBlacklist.has(scope.row.ref.id)")
el-tooltip(placement="left" content="Unblock chatbox messages")
el-button(type="text" icon="el-icon-turn-off-microphone" size="mini" style="color:red;margin-right:5px" @click.stop="deleteChatboxUserBlacklist(scope.row.ref.id)")
template(v-else)
el-tooltip(placement="left" content="Block chatbox messages")
el-button(type="text" icon="el-icon-microphone" size="mini" style="margin-right:5px" @click.stop="addChatboxUserBlacklist(scope.row.ref)")
span(v-text="scope.row.photonId")
el-table-column(:label="$t('table.playerList.icon')" prop="isMaster" width="100")
template(v-once #default="scope")
el-tooltip(v-if="scope.row.isMaster" placement="left" content="Instance Master")
span 👑
el-tooltip(v-if="scope.row.isModerator" placement="left" content="Moderator")
span ⚔️
el-tooltip(v-if="scope.row.isFriend" placement="left" content="Friend")
span 💚
el-tooltip(v-if="scope.row.timeoutTime" placement="left" content="Timeout")
span(style="color:red") 🔴{{ scope.row.timeoutTime }}s
el-table-column(:label="$t('table.playerList.platform')" prop="inVRMode" width="80")
template(v-once #default="scope")
template(v-if="scope.row.ref.last_platform")
span(v-if="scope.row.ref.last_platform === 'standalonewindows'" style="color:#409eff") PC
span(v-else-if="scope.row.ref.last_platform === 'android'" style="color:#67c23a") A
span(v-else-if="scope.row.ref.last_platform === 'ios'" style="color:#c7c7ce") iOS
span(v-else) {{ scope.row.ref.last_platform }}
template(v-if="scope.row.inVRMode !== null")
span(v-if="scope.row.inVRMode") VR
span(v-else-if="scope.row.ref.last_platform === 'android' || scope.row.ref.last_platform === 'ios'") M
span(v-else) D
el-table-column(:label="$t('table.playerList.displayName')" min-width="140" prop="displayName" sortable="custom")
template(v-once #default="scope")
span(v-if="randomUserColours" v-text="scope.row.ref.displayName" :style="{'color':scope.row.ref.$userColour}")
span(v-else v-text="scope.row.ref.displayName")
el-table-column(:label="$t('table.playerList.status')" min-width="180" prop="ref.status")
template(v-once #default="scope")
template(v-if="scope.row.ref.status")
i.x-user-status(:class="statusClass(scope.row.ref.status)")
span
span(v-text="scope.row.ref.statusDescription")
//- el-table-column(label="Group" min-width="180" prop="groupOnNameplate" sortable)
//- template(v-once #default="scope")
//- span(v-text="scope.row.groupOnNameplate")
el-table-column(:label="$t('table.playerList.rank')" width="110" prop="$trustSortNum" sortable="custom")
template(v-once #default="scope")
span.name(v-text="scope.row.ref.$trustLevel" :class="scope.row.ref.$trustClass")
el-table-column(:label="$t('table.playerList.language')" width="100" prop="ref.$languages")
template(v-once #default="scope")
el-tooltip(v-for="item in scope.row.ref.$languages" :key="item.key" placement="top")
template(#content)
span {{ item.value }} ({{ item.key }})
span.flags(:class="languageClass(item.key)" style="display:inline-block;margin-left:5px")
el-table-column(:label="$t('table.playerList.bioLink')" width="100" prop="ref.bioLinks")
template(v-once #default="scope")
el-tooltip(v-if="link" v-for="(link, index) in scope.row.ref.bioLinks" :key="index")
template(#content)
span(v-text="link")
img(:src="getFaviconUrl(link)" style="width:16px;height:16px;vertical-align:middle;margin-right:5px;cursor:pointer" @click.stop="openExternalLink(link)")

163
src/mixins/tabs/profile.pug Normal file
View File

@@ -0,0 +1,163 @@
mixin profileTab()
.x-container(v-if="$refs.menu && $refs.menu.activeIndex === 'profile'")
div.options-container(style="margin-top:0")
span.header {{ $t('view.profile.profile.header') }}
.x-friend-list(style="margin-top:10px")
.x-friend-item(@click="showUserDialog(API.currentUser.id)")
.avatar
img(v-lazy="userImage(API.currentUser)")
.detail
span.name(v-text="API.currentUser.displayName")
span.extra(v-text="API.currentUser.username")
.x-friend-item(style="cursor:default")
.detail
span.name {{ $t('view.profile.profile.last_activity') }}
span.extra {{ API.currentUser.last_activity | formatDate('long') }}
.x-friend-item(style="cursor:default")
.detail
span.name {{ $t('view.profile.profile.two_factor') }}
span.extra {{ API.currentUser.twoFactorAuthEnabled ? $t('view.profile.profile.two_factor_enabled') : $t('view.profile.profile.two_factor_disabled') }}
.x-friend-item(@click="getVRChatCredits()")
.detail
span.name {{ $t('view.profile.profile.vrchat_credits') }}
span.extra {{ API.currentUser.$vrchatcredits ?? $t('view.profile.profile.refresh') }}
div(style="margin-top:10px")
el-button(size="small" type="danger" plain icon="el-icon-switch-button" @click="logout()" style="margin-left:0;margin-right:5px;margin-top:10px;") {{ $t('view.profile.profile.logout') }}
el-button(size="small" icon="el-icon-picture-outline" @click="showGalleryDialog()" style="margin-left:0;margin-right:5px;margin-top:10px") {{ $t('view.profile.profile.manage_gallery_icon') }}
el-button(size="small" icon="el-icon-printer" @click="showExportFriendsListDialog()" style="margin-left:0;margin-right:5px;margin-top:10px") {{ $t('view.profile.profile.export_friend_list') }}
el-button(size="small" icon="el-icon-user" @click="showExportAvatarsListDialog()" style="margin-left:0;margin-right:5px;margin-top:10px") {{ $t('view.profile.profile.export_own_avatars') }}
el-button(size="small" icon="el-icon-chat-dot-round" @click="showDiscordNamesDialog()" style="margin-left:0;margin-right:5px;margin-top:10px") {{ $t('view.profile.profile.discord_names') }}
el-button(size="small" icon="el-icon-document-copy" @click="showNoteExportDialog()" style="margin-left:0;margin-right:5px;margin-top:10px") {{ $t('view.profile.profile.export_notes') }}
div.options-container
span.header {{ $t('view.profile.game_info.header') }}
.x-friend-list(style="margin-top:10px")
.x-friend-item
.detail(@click="API.getVisits()")
span.name {{ $t('view.profile.game_info.online_users') }}
span.extra(v-if="visits") {{ $t('view.profile.game_info.user_online', { count: visits }) }}
span.extra(v-else) {{ $t('view.profile.game_info.refresh') }}
div.options-container
div.header-bar
span.header {{ $t('view.profile.vrc_sdk_downloads.header') }}
el-tooltip(placement="top" :content="$t('view.profile.refresh_tooltip')" :disabled="hideTooltips")
el-button(type="default" @click="API.getConfig()" size="mini" icon="el-icon-refresh" circle style="margin-left:5px")
.x-friend-list(style="margin-top:10px")
.x-friend-item(v-for="(link, item) in API.cachedConfig.downloadUrls" :key="item" placement="top")
.detail(@click="openExternalLink(link)")
span.name(v-text="item")
span.extra(v-text="link")
div.options-container
span.header {{ $t('view.profile.direct_access.header') }}
div(style="margin-top:10px")
el-button-group
el-button(size="small" @click="promptUsernameDialog()") {{ $t('view.profile.direct_access.username') }}
el-button(size="small" @click="promptUserIdDialog()") {{ $t('view.profile.direct_access.user_id') }}
el-button(size="small" @click="promptWorldDialog()") {{ $t('view.profile.direct_access.world_instance') }}
el-button(size="small" @click="promptAvatarDialog()") {{ $t('view.profile.direct_access.avatar') }}
div.options-container
div.header-bar
span.header {{ $t('view.profile.invite_messages') }}
el-tooltip(placement="top" :content="$t('view.profile.refresh_tooltip')" :disabled="hideTooltips")
el-button(type="default" @click="inviteMessageTable.visible = true; refreshInviteMessageTable('message')" size="mini" icon="el-icon-refresh" circle style="margin-left:5px")
el-tooltip(placement="top" :content="$t('view.profile.clear_results_tooltip')" :disabled="hideTooltips")
el-button(type="default" @click="inviteMessageTable.visible = false" size="mini" icon="el-icon-delete" circle style="margin-left:5px")
data-tables(v-if="inviteMessageTable.visible" v-bind="inviteMessageTable" style="margin-top:10px")
el-table-column(:label="$t('table.profile.invite_messages.slot')" prop="slot" sortable="custom" width="70")
el-table-column(:label="$t('table.profile.invite_messages.message')" prop="message")
el-table-column(:label="$t('table.profile.invite_messages.cool_down')" prop="updatedAt" sortable="custom" width="110" align="right")
template(v-once #default="scope")
countdown-timer(:datetime="scope.row.updatedAt" :hours="1")
el-table-column(:label="$t('table.profile.invite_messages.action')" width="60" align="right")
template(v-once #default="scope")
el-button(type="text" icon="el-icon-edit" size="mini" @click="showEditInviteMessageDialog('message', scope.row)")
div.options-container
div.header-bar
span.header {{ $t('view.profile.invite_response_messages') }}
el-tooltip(placement="top" :content="$t('view.profile.refresh_tooltip')" :disabled="hideTooltips")
el-button(type="default" @click="inviteResponseMessageTable.visible = true; refreshInviteMessageTable('response')" size="mini" icon="el-icon-refresh" circle style="margin-left:5px")
el-tooltip(placement="top" :content="$t('view.profile.clear_results_tooltip')" :disabled="hideTooltips")
el-button(type="default" @click="inviteResponseMessageTable.visible = false" size="mini" icon="el-icon-delete" circle style="margin-left:5px")
data-tables(v-if="inviteResponseMessageTable.visible" v-bind="inviteResponseMessageTable" style="margin-top:10px")
el-table-column(:label="$t('table.profile.invite_messages.slot')" prop="slot" sortable="custom" width="70")
el-table-column(:label="$t('table.profile.invite_messages.message')" prop="message")
el-table-column(:label="$t('table.profile.invite_messages.cool_down')" prop="updatedAt" sortable="custom" width="110" align="right")
template(v-once #default="scope")
countdown-timer(:datetime="scope.row.updatedAt" :hours="1")
el-table-column(:label="$t('table.profile.invite_messages.action')" width="60" align="right")
template(v-once #default="scope")
el-button(type="text" icon="el-icon-edit" size="mini" @click="showEditInviteMessageDialog('response', scope.row)")
div.options-container
div.header-bar
span.header {{ $t('view.profile.invite_request_messages') }}
el-tooltip(placement="top" :content="$t('view.profile.refresh_tooltip')" :disabled="hideTooltips")
el-button(type="default" @click="inviteRequestMessageTable.visible = true; refreshInviteMessageTable('request')" size="mini" icon="el-icon-refresh" circle style="margin-left:5px")
el-tooltip(placement="top" :content="$t('view.profile.clear_results_tooltip')" :disabled="hideTooltips")
el-button(type="default" @click="inviteRequestMessageTable.visible = false" size="mini" icon="el-icon-delete" circle style="margin-left:5px")
data-tables(v-if="inviteRequestMessageTable.visible" v-bind="inviteRequestMessageTable" style="margin-top:10px")
el-table-column(:label="$t('table.profile.invite_messages.slot')" prop="slot" sortable="custom" width="70")
el-table-column(:label="$t('table.profile.invite_messages.message')" prop="message")
el-table-column(:label="$t('table.profile.invite_messages.cool_down')" prop="updatedAt" sortable="custom" width="110" align="right")
template(v-once #default="scope")
countdown-timer(:datetime="scope.row.updatedAt" :hours="1")
el-table-column(:label="$t('table.profile.invite_messages.action')" width="60" align="right")
template(v-once #default="scope")
el-button(type="text" icon="el-icon-edit" size="mini" @click="showEditInviteMessageDialog('request', scope.row)")
div.options-container
div.header-bar
span.header {{ $t('view.profile.invite_request_response_messages') }}
el-tooltip(placement="top" :content="$t('view.profile.refresh_tooltip')" :disabled="hideTooltips")
el-button(type="default" @click="inviteRequestResponseMessageTable.visible = true; refreshInviteMessageTable('requestResponse')" size="mini" icon="el-icon-refresh" circle style="margin-left:5px")
el-tooltip(placement="top" :content="$t('view.profile.clear_results_tooltip')" :disabled="hideTooltips")
el-button(type="default" @click="inviteRequestResponseMessageTable.visible = false" size="mini" icon="el-icon-delete" circle style="margin-left:5px")
data-tables(v-if="inviteRequestResponseMessageTable.visible" v-bind="inviteRequestResponseMessageTable" style="margin-top:10px")
el-table-column(:label="$t('table.profile.invite_messages.slot')" prop="slot" sortable="custom" width="70")
el-table-column(:label="$t('table.profile.invite_messages.message')" prop="message")
el-table-column(:label="$t('table.profile.invite_messages.cool_down')" prop="updatedAt" sortable="custom" width="110" align="right")
template(v-once #default="scope")
countdown-timer(:datetime="scope.row.updatedAt" :hours="1")
el-table-column(:label="$t('table.profile.invite_messages.action')" width="60" align="right")
template(v-once #default="scope")
el-button(type="text" icon="el-icon-edit" size="mini" @click="showEditInviteMessageDialog('requestResponse', scope.row)")
div.options-container
span.header {{ $t('view.profile.past_display_names') }}
data-tables(v-bind="pastDisplayNameTable" style="margin-top:10px")
el-table-column(:label="$t('table.profile.previous_display_name.date')" prop="updated_at" sortable="custom")
template(v-once #default="scope")
span {{ scope.row.updated_at | formatDate('long') }}
el-table-column(:label="$t('table.profile.previous_display_name.name')" prop="displayName")
div.options-container
div.header-bar
span.header {{ $t('view.profile.config_json') }}
el-tooltip(placement="top" :content="$t('view.profile.refresh_tooltip')" :disabled="hideTooltips")
el-button(type="default" @click="refreshConfigTreeData()" size="mini" icon="el-icon-refresh" circle style="margin-left:5px")
el-tooltip(placement="top" :content="$t('view.profile.clear_results_tooltip')" :disabled="hideTooltips")
el-button(type="default" @click="configTreeData = []" size="mini" icon="el-icon-delete" circle style="margin-left:5px")
el-tree(v-if="configTreeData.length > 0" :data="configTreeData" style="margin-top:10px;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")
div.options-container
div.header-bar
span.header {{ $t('view.profile.current_user_json') }}
el-tooltip(placement="top" :content="$t('view.profile.refresh_tooltip')" :disabled="hideTooltips")
el-button(type="default" @click="refreshCurrentUserTreeData()" size="mini" icon="el-icon-refresh" circle style="margin-left:5px")
el-tooltip(placement="top" :content="$t('view.profile.clear_results_tooltip')" :disabled="hideTooltips")
el-button(type="default" @click="currentUserTreeData = []" size="mini" icon="el-icon-delete" circle style="margin-left:5px")
el-tree(v-if="currentUserTreeData.length > 0" :data="currentUserTreeData" style="margin-top:10px;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")
div.options-container
div.header-bar
span.header {{ $t('view.profile.feedback') }}
el-tooltip(placement="top" :content="$t('view.profile.refresh_tooltip')" :disabled="hideTooltips")
el-button(type="default" @click="getCurrentUserFeedback()" size="mini" icon="el-icon-refresh" circle style="margin-left:5px")
el-tooltip(placement="top" :content="$t('view.profile.clear_results_tooltip')" :disabled="hideTooltips")
el-button(type="default" @click="currentUserFeedbackData = []" size="mini" icon="el-icon-delete" circle style="margin-left:5px")
el-tree(v-if="currentUserFeedbackData.length > 0" :data="currentUserFeedbackData" style="margin-top:10px;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")

View File

@@ -0,0 +1,93 @@
mixin searchTab()
.x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'search'")
div(style="margin:0 0 10px;display:flex;align-items:center")
el-input(v-model="searchText" :placeholder="$t('view.search.search_placeholder')" @keyup.native.13="search()" style="flex:1")
el-tooltip(placement="bottom" :content="$t('view.search.clear_results_tooltip')" :disabled="hideTooltips")
el-button(type="default" @click="clearSearch()" icon="el-icon-delete" circle style="flex:none;margin-left:10px")
el-tabs(ref="searchTab" type="card" style="margin-top:15px")
el-tab-pane(:label="$t('view.search.user.header')" v-loading="isSearchUserLoading" style="min-height:60px")
.x-friend-list(style="min-height:500px")
.x-friend-item(v-for="user in searchUserResults" :key="user.id" @click="showUserDialog(user.id)")
template(v-once)
.avatar
img(v-lazy="userImage(user)")
.detail
span.name(v-text="user.displayName")
span.extra(v-if="randomUserColours" v-text="user.$trustLevel" :class="user.$trustClass")
span.extra(v-else v-text="user.$trustLevel" :style="{'color':user.$userColour}")
el-button-group(style="margin-top:15px")
el-button(:disabled="!searchUserParams.offset" @click="moreSearchUser(-1)" icon="el-icon-back" size="small") {{ $t('view.search.prev_page') }}
el-button(:disabled="searchUserResults.length < 10" @click="moreSearchUser(1)" icon="el-icon-right" size="small") {{ $t('view.search.next_page') }}
el-tab-pane(:label="$t('view.search.world.header')" v-loading="isSearchWorldLoading" style="min-height:60px")
el-dropdown(@command="(row) => searchWorld(row)" size="small" trigger="click" style="margin-bottom:15px")
el-button(size="small") {{ $t('view.search.world.category') }} #[i.el-icon-arrow-down.el-icon--right]
el-dropdown-menu(#default="dropdown")
el-dropdown-item(v-for="row in API.cachedConfig.dynamicWorldRows" :key="row.index" v-text="row.name" :command="row")
el-checkbox(v-model="searchWorldLabs" style="margin-left:10px") {{ $t('view.search.world.community_lab') }}
.x-friend-list(style="min-height:500px")
.x-friend-item(v-for="world in searchWorldResults" :key="world.id" @click="showWorldDialog(world.id)")
template(v-once)
.avatar
img(v-lazy="world.thumbnailImageUrl")
.detail
span.name(v-text="world.name")
span.extra(v-if="world.occupants") {{ world.authorName }} ({{ world.occupants }})
span.extra(v-else v-text="world.authorName")
el-button-group(style="margin-top:15px")
el-button(:disabled="!searchWorldParams.offset" @click="moreSearchWorld(-1)" icon="el-icon-back" size="small") {{ $t('view.search.prev_page') }}
el-button(:disabled="searchWorldResults.length < 10" @click="moreSearchWorld(1)" icon="el-icon-right" size="small") {{ $t('view.search.next_page') }}
el-tab-pane(:label="$t('view.search.avatar.header')" v-loading="isSearchAvatarLoading" style="min-height:60px")
div(style="display:flex;align-items:center;justify-content:space-between;")
div(style="display:flex;align-items:center;")
el-dropdown(v-if="avatarRemoteDatabaseProviderList.length > 1" trigger="click" @click.native.stop size="mini" style="margin-right:5px")
el-button(size="small") {{ $t('view.search.avatar.search_provider') }} #[i.el-icon-arrow-down.el-icon--right]
el-dropdown-menu(#default="dropdown")
el-dropdown-item(v-for="provider in avatarRemoteDatabaseProviderList" :key="provider" @click.native="setAvatarProvider(provider)") #[i.el-icon-check.el-icon--left(v-if="provider === avatarRemoteDatabaseProvider")] {{ provider }}
el-tooltip(placement="bottom" :content="$t('view.search.avatar.refresh_tooltip')" :disabled="hideTooltips")
el-button(type="default" :loading="userDialog.isAvatarsLoading" @click="refreshUserDialogAvatars()" size="mini" icon="el-icon-refresh" circle)
span(style="font-size:14px;margin-left:5px;margin-right:5px") {{ $t("view.search.avatar.result_count", { count: searchAvatarResults.length }) }}
div(style="display:flex;align-items:center;")
el-radio-group(v-model="searchAvatarFilter" size="mini" style="margin:5px;display:block" @change="searchAvatar")
el-radio(label="all") {{ $t('view.search.avatar.all') }}
el-radio(label="public") {{ $t('view.search.avatar.public') }}
el-radio(label="private") {{ $t('view.search.avatar.private') }}
el-divider(direction="vertical")
el-radio-group(v-model="searchAvatarFilterRemote" size="mini" style="margin:5px;display:block" @change="searchAvatar")
el-radio(label="all") {{ $t('view.search.avatar.all') }}
el-radio(label="local") {{ $t('view.search.avatar.local') }}
el-radio(label="remote" :disabled="!avatarRemoteDatabase") {{ $t('view.search.avatar.remote') }}
div(style="display:flex;justify-content:end;")
el-radio-group(:disabled="searchAvatarFilterRemote !== 'local'" v-model="searchAvatarSort" size="mini" style="margin:5px;display:block" @change="searchAvatar")
el-radio(label="name") {{ $t('view.search.avatar.sort_name') }}
el-radio(label="update") {{ $t('view.search.avatar.sort_update') }}
el-radio(label="created") {{ $t('view.search.avatar.sort_created') }}
.x-friend-list(style="margin-top:20px;min-height:500px")
.x-friend-item(v-for="avatar in searchAvatarPage" :key="avatar.id" @click="showAvatarDialog(avatar.id)")
template(v-once)
.avatar
img(v-if="avatar.thumbnailImageUrl" v-lazy="avatar.thumbnailImageUrl")
img(v-else-if="avatar.imageUrl" v-lazy="avatar.imageUrl")
.detail
span.name(v-text="avatar.name")
span.extra(v-text="avatar.releaseStatus" v-if="avatar.releaseStatus === 'public'" style="color: #67c23a;")
span.extra(v-text="avatar.releaseStatus" v-else-if="avatar.releaseStatus === 'private'" style="color: #f56c6c;")
span.extra(v-text="avatar.releaseStatus" v-else)
span.extra(v-text="avatar.authorName")
el-button-group(style="margin-top:15px")
el-button(:disabled="!searchAvatarPageNum" @click="moreSearchAvatar(-1)" icon="el-icon-back" size="small") {{ $t('view.search.prev_page') }}
el-button(:disabled="searchAvatarResults.length < 10 || (searchAvatarPageNum + 1) * 10 >= searchAvatarResults.length" @click="moreSearchAvatar(1)" icon="el-icon-right" size="small") {{ $t('view.search.next_page') }}
el-tab-pane(:label="$t('view.search.group.header')" v-loading="isSearchGroupLoading" style="min-height:60px")
.x-friend-list(style="min-height:500px")
.x-friend-item(v-for="group in searchGroupResults" :key="group.id" @click="showGroupDialog(group.id)")
template(v-once)
.avatar
img(v-lazy="group.iconUrl")
.detail
span.name
span(v-text="group.name")
span(style="margin-left:5px;font-weight:normal") ({{ group.memberCount }})
span(style="margin-left:5px;color:#909399;font-weight:normal;font-family:monospace;font-size:12px") {{ group.shortCode }}.{{ group.discriminator }}
span.extra(v-text="group.description")
el-button-group(style="margin-top:15px")
el-button(:disabled="!searchGroupParams.offset" @click="moreSearchGroup(-1)" icon="el-icon-back" size="small") {{ $t('view.search.prev_page') }}
el-button(:disabled="searchGroupResults.length < 10" @click="moreSearchGroup(1)" icon="el-icon-right" size="small") {{ $t('view.search.next_page') }}

View File

@@ -0,0 +1,603 @@
mixin simpleSettingsCategory(headerTrKey)
div.options-container
span.header {{ $t('#{headerTrKey}') }}
if block
block
else
p No Content
mixin simpleTwoLabelSwitch(nameTrKey, model, onChange="")
div.options-container-item
span.name {{ $t('#{nameTrKey}') }}
el-switch(v-model=model @change=onChange)
mixin simpleRadioGroup(nameTrKey, model, options, onChange="")
div.options-container-item
span.name {{ $t('#{nameTrKey}') }}
br
el-radio-group(v-model=model @change=onChange size="mini" style="margin-top:5px")
each option in options
el-radio-button(label=option.label) {{ $t('#{option.translationKey}') }}
mixin simpleRadioGroupWithTooltip(nameTrKey, tooltipContent, model, options, onChange="")
div.options-container-item
span.name {{ $t('#{nameTrKey}') }}
el-tooltip(placement="top" style="margin-left:5px" :content=tooltipContent)
i.el-icon-info
br
el-radio-group(v-model=model @change=onChange size="mini" style="margin-top:5px")
each option in options
el-radio-button(label=option.label) {{ $t('#{option.translationKey}') }}
mixin settingsTab()
.x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'settings'")
div.options-container(style="margin-top:0;padding:5px")
span.header {{ $t("view.settings.header") }}
el-tabs(type="card" style="height: calc(100% - 51px)")
//- General Tab
el-tab-pane(:label="$t('view.settings.category.general')")
//- General | General
div.options-container(style="margin-top:0")
span.header {{ $t("view.settings.general.general.header") }}
.x-friend-list(style="margin-top:10px")
//- General | General | Version
.x-friend-item(style="cursor:default")
.detail
span.name {{ $t("view.settings.general.general.version") }}
span.extra(v-text="appVersion")
//- General | General | Latest App Version
.x-friend-item(@click="checkForVRCXUpdate")
.detail
span.name {{ $t("view.settings.general.general.latest_app_version") }}
span.extra(v-if="latestAppVersion" v-text="latestAppVersion")
span.extra(v-else) {{ $t("view.settings.general.general.latest_app_version_refresh") }}
//- General | General | Repository URL
.x-friend-item(@click="openExternalLink('https://github.com/vrcx-team/VRCX')")
.detail
span.name {{ $t("view.settings.general.general.repository_url") }}
span.extra https://github.com/vrcx-team/VRCX
//- General | General | Support
.x-friend-item(@click="openExternalLink('https://vrcx.app/discord')")
.detail
span.name {{ $t("view.settings.general.general.support") }}
span.extra https://vrcx.app/discord
//- General | VRCX Updater
+simpleSettingsCategory("view.settings.general.vrcx_updater.header")
div.options-container-item
el-button(size="small" icon="el-icon-document" @click="showChangeLogDialog()") {{ $t("view.settings.general.vrcx_updater.change_log") }}
el-button(size="small" icon="el-icon-upload" @click="showVRCXUpdateDialog()") {{ $t("view.settings.general.vrcx_updater.change_build") }}
div.options-container-item
span.name {{ $t('view.settings.general.vrcx_updater.update_action') }}
br
el-radio-group(v-model="autoUpdateVRCX" size="mini" style="margin-top:5px")
el-radio-button(label="Off") {{ $t('view.settings.general.vrcx_updater.auto_update_off') }}
el-radio-button(label="Notify") {{ $t('view.settings.general.vrcx_updater.auto_update_notify') }}
el-radio-button(label="Auto Download") {{ $t('view.settings.general.vrcx_updater.auto_update_download') }}
//- General | Application
+simpleSettingsCategory("view.settings.general.application.header")
template(v-if="!isLinux()")
simple-switch(:label='$t("view.settings.general.application.startup")' :value='isStartAtWindowsStartup' @change='saveVRCXWindowOption("VRCX_StartAtWindowsStartup")')
simple-switch(:label='$t("view.settings.general.application.minimized")' :value='isStartAsMinimizedState' @change='saveVRCXWindowOption("VRCX_StartAsMinimizedState")')
simple-switch(:label='$t("view.settings.general.application.tray")' :value='isCloseToTray' @change='saveVRCXWindowOption("VRCX_CloseToTray")')
template(v-if="!isLinux()")
simple-switch(:label='$t("view.settings.general.application.disable_gpu_acceleration")' :value='disableGpuAcceleration' @change='saveVRCXWindowOption("VRCX_DisableGpuAcceleration")' :tooltip='$t("view.settings.general.application.disable_gpu_acceleration_tooltip")')
simple-switch(:label='$t("view.settings.general.application.disable_vr_overlay_gpu_acceleration")' :value='disableVrOverlayGpuAcceleration' @change='saveVRCXWindowOption("VRCX_DisableVrOverlayGpuAcceleration")' :tooltip='$t("view.settings.general.application.disable_gpu_acceleration_tooltip")')
div.options-container-item
el-button(size="small" icon="el-icon-connection" @click="promptProxySettings()") {{ $t("view.settings.general.application.proxy") }}
//- General | Favorite
+simpleSettingsCategory("view.settings.general.favorites.header")
br
el-select(v-model="localFavoriteFriendsGroups" multiple clearable :placeholder="$t('view.settings.general.favorites.group_placeholder')" @change="updateLocalFavoriteFriends" style="margin-top:8px")
el-option-group(:label="$t('view.settings.general.favorites.group_placeholder')")
el-option.x-friend-item(v-for="group in API.favoriteFriendGroups" :key="group.key" :label="group.displayName ? group.displayName : group.name" :value="group.key")
.detail
span.name(v-text="group.displayName ? group.displayName : group.name")
//- General | Game Log
div.options-container
span.header {{ $t('view.settings.general.logging.header') }}
simple-switch(:label='$t("view.settings.advanced.advanced.cache_debug.udon_exception_logging")' :value='udonExceptionLogging' @change='saveOpenVROption("VRCX_udonExceptionLogging")')
simple-switch(:label='$t("view.settings.general.logging.resource_load")' :value='logResourceLoad' @change='saveLoggingOptions("VRCX_logResourceLoad")')
simple-switch(:label='$t("view.settings.general.logging.empty_avatar")' :value='logEmptyAvatars' @change='saveLoggingOptions("VRCX_logEmptyAvatars")')
//- General | Automation
div.options-container
span.header {{ $t('view.settings.general.automation.header') }}
simple-switch(:label='$t("view.settings.general.automation.auto_change_status")' :value='autoStateChangeEnabled' @change='saveAutomationOptions("VRCX_autoStateChangeEnabled")' :tooltip='$t("view.settings.general.automation.auto_state_change_tooltip")')
div.options-container-item
span.name {{ $t('view.settings.general.automation.alone_status') }}
el-select(v-model="autoStateChangeAloneStatus" :disabled="!autoStateChangeEnabled" @change="saveAutomationOptions" style="margin-top:8px" size="small")
el-option(:label="$t('dialog.user.status.join_me')" value="join me").
#[i.x-user-status.joinme] {{ $t('dialog.user.status.join_me') }}
el-option(:label="$t('dialog.user.status.online')" value="active").
#[i.x-user-status.online] {{ $t('dialog.user.status.online') }}
el-option(:label="$t('dialog.user.status.ask_me')" value="ask me").
#[i.x-user-status.askme] {{ $t('dialog.user.status.ask_me') }}
el-option(:label="$t('dialog.user.status.busy')" value="busy").
#[i.x-user-status.busy] {{ $t('dialog.user.status.busy') }}
div.options-container-item
span.name {{ $t('view.settings.general.automation.company_status') }}
el-select(v-model="autoStateChangeCompanyStatus" :disabled="!autoStateChangeEnabled" @change="saveAutomationOptions" style="margin-top:8px" size="small")
el-option(:label="$t('dialog.user.status.join_me')" value="join me").
#[i.x-user-status.joinme] {{ $t('dialog.user.status.join_me') }}
el-option(:label="$t('dialog.user.status.online')" value="active").
#[i.x-user-status.online] {{ $t('dialog.user.status.online') }}
el-option(:label="$t('dialog.user.status.ask_me')" value="ask me").
#[i.x-user-status.askme] {{ $t('dialog.user.status.ask_me') }}
el-option(:label="$t('dialog.user.status.busy')" value="busy").
#[i.x-user-status.busy] {{ $t('dialog.user.status.busy') }}
div.options-container-item
span.name {{ $t('view.settings.general.automation.allowed_instance_types') }}
el-select(v-model="autoStateChangeInstanceTypes" :disabled="!autoStateChangeEnabled" multiple clearable :placeholder="$t('view.settings.general.automation.instance_type_placeholder')" @change="saveAutomationOptions" style="margin-top:8px" size="small")
el-option-group(:label="$t('view.settings.general.automation.allowed_instance_types')")
el-option.x-friend-item(v-for="instanceType in instanceTypes" :key="instanceType" :label="instanceType" :value="instanceType")
.detail
span.name(v-text="instanceType")
div.options-container-item
span.name {{ $t('view.settings.general.automation.alone_condition') }}
el-radio-group(v-model="autoStateChangeNoFriends" :disabled="!autoStateChangeEnabled" @change="saveAutomationOptions" )
el-radio(:label="false") {{ $t('view.settings.general.automation.alone') }}
el-radio(:label="true") {{ $t('view.settings.general.automation.no_friends') }}
+simpleRadioGroupWithTooltip("view.settings.general.automation.auto_invite_request_accept", "$t('view.settings.general.automation.auto_invite_request_accept_tooltip')", "autoAcceptInviteRequests", [
{ label: "Off", translationKey: "view.settings.general.automation.auto_invite_request_accept_off" },
{ label: "All Favorites", translationKey: "view.settings.general.automation.auto_invite_request_accept_favs" },
{ label: "Selected Favorites", translationKey: "view.settings.general.automation.auto_invite_request_accept_selected_favs" },
], "saveAutomationOptions")
//- General | Contributors
div.options-container
span.header {{ $t("view.settings.general.contributors.header" )}}
div.options-container-item
img(src="https://contrib.rocks/image?repo=vrcx-team/VRCX", alt="Contributors" @click="openExternalLink('https://github.com/vrcx-team/VRCX/graphs/contributors')" style="cursor: pointer")
//- General | Legal Notice
div.options-container(style="margin-top:45px;border-top:1px solid #eee;padding-top:30px")
span.header {{ $t("view.settings.general.legal_notice.header" )}}
div.options-container-item
p &copy; 2019-2024 #[a.x-link(@click="openExternalLink('https://github.com/pypy-vrc')") pypy] &amp; #[a.x-link(@click="openExternalLink('https://github.com/Natsumi-sama')") Natsumi]
p {{ $t("view.settings.general.legal_notice.info" )}}
p {{ $t("view.settings.general.legal_notice.disclaimer1" )}}
p {{ $t("view.settings.general.legal_notice.disclaimer2" )}}
div.options-container-item
el-button(@click="ossDialog = true" size="small") {{ $t("view.settings.general.legal_notice.open_source_software_notice" )}}
//- Appearance Tab
el-tab-pane(:label="$t('view.settings.category.appearance')")
//- Appearance | Appearance
div.options-container(style="margin-top:0")
span.header {{ $t("view.settings.appearance.appearance.header") }}
div.options-container-item
span.name {{ $t('view.settings.appearance.appearance.language') }}
el-dropdown(@click.native.stop trigger="click" size="small")
el-button(size="mini")
span {{ $i18n.messages[appLanguage]?.language }} #[i.el-icon-arrow-down.el-icon--right]
el-dropdown-menu(#default="dropdown")
el-dropdown-item(v-for="(obj, language) in $i18n.messages" v-text="obj.language" @click.native="changeAppLanguage(language)")
div.options-container-item
span.name {{ $t('view.settings.appearance.appearance.theme_mode') }}
el-dropdown(@click.native.stop trigger="click" size="small")
el-button(size="mini")
span {{ $t(`view.settings.appearance.appearance.theme_mode_${themeMode}`) }} #[i.el-icon-arrow-down.el-icon--right]
el-dropdown-menu(#default="dropdown")
el-dropdown-item(v-text="$t('view.settings.appearance.appearance.theme_mode_system')" @click.native="saveThemeMode('system')")
el-dropdown-item(v-text="$t('view.settings.appearance.appearance.theme_mode_light')" @click.native="saveThemeMode('light')")
el-dropdown-item(v-text="$t('view.settings.appearance.appearance.theme_mode_dark')" @click.native="saveThemeMode('dark')")
el-dropdown-item(v-text="$t('view.settings.appearance.appearance.theme_mode_darkvanillaold')" @click.native="saveThemeMode('darkvanillaold')")
el-dropdown-item(v-text="$t('view.settings.appearance.appearance.theme_mode_darkvanilla')" @click.native="saveThemeMode('darkvanilla')")
el-dropdown-item(v-text="$t('view.settings.appearance.appearance.theme_mode_pink')" @click.native="saveThemeMode('pink')")
el-dropdown-item(v-text="$t('view.settings.appearance.appearance.theme_mode_material3')" @click.native="saveThemeMode('material3')")
div.options-container-item(vif="!isLinux()")
span.name {{ $t('view.settings.appearance.appearance.zoom') }}
el-input-number(size="small" v-model="zoomLevel" @change="setZoomLevel" :precision="0" style="width:128px")
simple-switch(:label='$t("view.settings.appearance.appearance.vrcplus_profile_icons")' :value='displayVRCPlusIconsAsAvatar' @change='saveOpenVROption("displayVRCPlusIconsAsAvatar")')
simple-switch(:label='$t("view.settings.appearance.appearance.nicknames")' :value='hideNicknames' @change='saveOpenVROption("VRCX_hideNicknames")')
simple-switch(:label='$t("view.settings.appearance.appearance.tooltips")' :value='!hideTooltips' @change='saveOpenVROption("VRCX_hideTooltips")')
div.options-container-item
span.name {{ $t('view.settings.appearance.appearance.sort_favorite_by') }}
el-radio-group(v-model="sortFavorites" @change="saveSortFavoritesOption")
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') }}
div.options-container-item
span.name {{ $t('view.settings.appearance.appearance.sort_instance_users_by') }}
el-radio-group(v-model="instanceUsersSortAlphabetical" @change="saveOpenVROption")
el-radio(:label="false") {{ $t('view.settings.appearance.appearance.sort_instance_users_by_time') }}
el-radio(:label="true") {{ $t('view.settings.appearance.appearance.sort_instance_users_by_alphabet') }}
div.options-container-item
el-button(size="small" icon="el-icon-notebook-1" @click="promptMaxTableSizeDialog" style="margin-right:10px") {{ $t('view.settings.appearance.appearance.table_max_size') }}
el-dropdown(@click.native.stop trigger="click" size="small")
el-button(size="small")
span {{ $t('view.settings.appearance.appearance.page_size') }} {{ tablePageSize }} #[i.el-icon-arrow-down.el-icon--right]
el-dropdown-menu(#default="dropdown")
el-dropdown-item(v-for="(number) in [10, 15, 25, 50, 100]" v-text="number" @click.native="setTablePageSize(number)")
div.options-container-item
//- Appearance | Time/Date
div.options-container
span.header {{ $t('view.settings.appearance.timedate.header') }}
div.options-container-item
span.name {{ $t('view.settings.appearance.timedate.time_format') }}
el-radio-group(v-model="dtHour12" @change="setDatetimeFormat")
el-radio(:label="true") {{ $t('view.settings.appearance.timedate.time_format_12') }}
el-radio(:label="false") {{ $t('view.settings.appearance.timedate.time_format_24') }}
simple-switch(:label='$t("view.settings.appearance.timedate.force_iso_date_format")' :value='dtIsoFormat' @change='setDatetimeFormat(true)')
//- Appearance | Side Panel
div.options-container
span.header {{ $t('view.settings.appearance.side_panel.header') }}
br
div.options-container-item
span.name {{ $t('view.settings.appearance.side_panel.sorting.header') }}
el-select(v-model="sidebarSortMethod1" style="width:170px" :placeholder="$t('view.settings.appearance.side_panel.sorting.placeholder')" @change="saveSidebarSortOrder")
el-option-group(:label="$t('view.settings.appearance.side_panel.sorting.dropdown_header')")
el-option.x-friend-item(:label="$t('view.settings.appearance.side_panel.sorting.alphabetical')" value="Sort Alphabetically")
el-option.x-friend-item(:label="$t('view.settings.appearance.side_panel.sorting.status')" value="Sort by Status")
el-option.x-friend-item(:label="$t('view.settings.appearance.side_panel.sorting.private_to_bottom')" value="Sort Private to Bottom")
el-option.x-friend-item(:label="$t('view.settings.appearance.side_panel.sorting.last_active')" value="Sort by Last Active")
el-option.x-friend-item(:label="$t('view.settings.appearance.side_panel.sorting.last_seen')" value="Sort by Last Seen")
el-option.x-friend-item(:label="$t('view.settings.appearance.side_panel.sorting.time_in_instance')" value="Sort by Time in Instance")
el-option.x-friend-item(:label="$t('view.settings.appearance.side_panel.sorting.location')" value="Sort by Location")
i.el-icon-arrow-right(style="margin:16px 5px")
el-select(v-model="sidebarSortMethod2" :disabled="!sidebarSortMethod1" style="width:170px" clearable :placeholder="$t('view.settings.appearance.side_panel.sorting.placeholder')" @change="saveSidebarSortOrder")
el-option-group(:label="$t('view.settings.appearance.side_panel.sorting.dropdown_header')")
el-option.x-friend-item(:label="$t('view.settings.appearance.side_panel.sorting.alphabetical')" value="Sort Alphabetically")
el-option.x-friend-item(:label="$t('view.settings.appearance.side_panel.sorting.status')" value="Sort by Status")
el-option.x-friend-item(:label="$t('view.settings.appearance.side_panel.sorting.private_to_bottom')" value="Sort Private to Bottom")
el-option.x-friend-item(:label="$t('view.settings.appearance.side_panel.sorting.last_active')" value="Sort by Last Active")
el-option.x-friend-item(:label="$t('view.settings.appearance.side_panel.sorting.last_seen')" value="Sort by Last Seen")
el-option.x-friend-item(:label="$t('view.settings.appearance.side_panel.sorting.time_in_instance')" value="Sort by Time in Instance")
el-option.x-friend-item(:label="$t('view.settings.appearance.side_panel.sorting.location')" value="Sort by Location")
i.el-icon-arrow-right(style="margin:16px 5px")
el-select(v-model="sidebarSortMethod3" :disabled="!sidebarSortMethod2" style="width:170px" clearable :placeholder="$t('view.settings.appearance.side_panel.sorting.placeholder')" @change="saveSidebarSortOrder")
el-option-group(:label="$t('view.settings.appearance.side_panel.sorting.dropdown_header')")
el-option.x-friend-item(:label="$t('view.settings.appearance.side_panel.sorting.alphabetical')" value="Sort Alphabetically")
el-option.x-friend-item(:label="$t('view.settings.appearance.side_panel.sorting.status')" value="Sort by Status")
el-option.x-friend-item(:label="$t('view.settings.appearance.side_panel.sorting.private_to_bottom')" value="Sort Private to Bottom")
el-option.x-friend-item(:label="$t('view.settings.appearance.side_panel.sorting.last_active')" value="Sort by Last Active")
el-option.x-friend-item(:label="$t('view.settings.appearance.side_panel.sorting.last_seen')" value="Sort by Last Seen")
el-option.x-friend-item(:label="$t('view.settings.appearance.side_panel.sorting.time_in_instance')" value="Sort by Time in Instance")
el-option.x-friend-item(:label="$t('view.settings.appearance.side_panel.sorting.location')" value="Sort by Location")
div.options-container-item
span.name(style="vertical-align:top;padding-top:10px") {{ $t('view.settings.appearance.side_panel.width') }}
el-slider(v-model="asideWidth" @input="setAsideWidth" :show-tooltip="false" :marks="{300: ''}" :min="200" :max="500" style="display:inline-block;width:300px")
//- Appearance | User Dialog
div.options-container
span.header {{ $t('view.settings.appearance.user_dialog.header') }}
simple-switch(:label='$t("view.settings.appearance.user_dialog.vrchat_notes")' :value='hideUserNotes' @change='saveUserDialogOption("VRCX_hideUserNotes")')
simple-switch(:label='$t("view.settings.appearance.user_dialog.vrcx_memos")' :value='hideUserMemos' @change='saveUserDialogOption("VRCX_hideUserMemos")')
div.options-container-item
span.name {{ $t('view.settings.appearance.user_dialog.export_vrcx_memos_into_vrchat_notes') }}
br
el-button(size="small" icon="el-icon-document-copy" @click="showNoteExportDialog" style="margin-top:5px") {{ $t('view.settings.appearance.user_dialog.export_notes') }}
//- Appearance | Friend Log
div.options-container
span.header {{ $t('view.settings.appearance.friend_log.header') }}
simple-switch(:label='$t("view.settings.appearance.friend_log.hide_unfriends")' :value='hideUnfriends' @change='saveFriendLogOptions')
//- Appearance | User Colors
div.options-container
span.header {{ $t('view.settings.appearance.user_colors.header') }}
simple-switch(:label='$t("view.settings.appearance.user_colors.random_colors_from_user_id")' :value='randomUserColours' @change='updatetrustColor(true)')
div.options-container-item
div
el-color-picker(v-model="trustColor.untrusted" @change="updatetrustColor" size="mini" :predefine="['#CCCCCC']")
span.color-picker(slot="trigger" class="x-tag-untrusted") Visitor
div
el-color-picker(v-model="trustColor.basic" @change="updatetrustColor" size="mini" :predefine="['#1778ff']")
span.color-picker(slot="trigger" class="x-tag-basic") New User
div
el-color-picker(v-model="trustColor.known" @change="updatetrustColor" size="mini" :predefine="['#2bcf5c']")
span.color-picker(slot="trigger" class="x-tag-known") User
div
el-color-picker(v-model="trustColor.trusted" @change="updatetrustColor" size="mini" :predefine="['#ff7b42']")
span.color-picker(slot="trigger" class="x-tag-trusted") Known User
div
el-color-picker(v-model="trustColor.veteran" @change="updatetrustColor" size="mini" :predefine="['#b18fff', '#8143e6', '#ff69b4', '#b52626', '#ffd000', '#abcdef']")
span.color-picker(slot="trigger" class="x-tag-veteran") Trusted User
div
el-color-picker(v-model="trustColor.vip" @change="updatetrustColor" size="mini" :predefine="['#ff2626']")
span.color-picker(slot="trigger" class="x-tag-vip") VRChat Team
div
el-color-picker(v-model="trustColor.troll" @change="updatetrustColor" size="mini" :predefine="['#782f2f']")
span.color-picker(slot="trigger" class="x-tag-troll") Nuisance
//- Notifications Tab
el-tab-pane(:label="$t('view.settings.category.notifications')")
//- Notifications | Notifications
div.options-container(style="margin-top:0")
span.header {{ $t('view.settings.notifications.notifications.header') }}
div.options-container-item
el-button(size="small" icon="el-icon-chat-square" @click="showNotyFeedFiltersDialog") {{ $t('view.settings.notifications.notifications.notification_filter') }}
//- Notifications | Notifications | SteamVR Notifications
div.options-container
span.sub-header {{ $t('view.settings.notifications.notifications.steamvr_notifications.header') }}
div.options-container-item
span.name {{ $t('view.settings.notifications.notifications.desktop_notifications.when_to_display') }}
br
el-radio-group(v-model="overlayToast" @change="saveOpenVROption" size="mini" :disabled="(!overlayNotifications || !openVR) && !xsNotifications && !ovrtHudNotifications && !ovrtWristNotifications" style="margin-top:5px")
el-radio-button(label="Never") {{ $t('view.settings.notifications.notifications.conditions.never') }}
el-radio-button(label="Game Running") {{ $t('view.settings.notifications.notifications.conditions.inside_vrchat') }}
el-radio-button(label="Game Closed") {{ $t('view.settings.notifications.notifications.conditions.outside_vrchat') }}
el-radio-button(label="Always") {{ $t('view.settings.notifications.notifications.conditions.always') }}
template(v-if="!isLinux()")
simple-switch(:label='$t("view.settings.notifications.notifications.steamvr_notifications.steamvr_overlay")' :value='openVR' @change='saveOpenVROption("openVR")')
simple-switch(:label='$t("view.settings.notifications.notifications.steamvr_notifications.overlay_notifications")' :value='overlayNotifications' @change='saveOpenVROption("VRCX_overlayNotifications")' :disabled="!openVR")
div.options-container-item
el-button(size="small" icon="el-icon-rank" @click="showNotificationPositionDialog" :disabled="!overlayNotifications || !openVR") {{ $t('view.settings.notifications.notifications.steamvr_notifications.notification_position') }}
template(v-if="!isLinux()")
simple-switch(:label='$t("view.settings.notifications.notifications.steamvr_notifications.xsoverlay_notifications")' :value='xsNotifications' @change='saveOpenVROption("VRCX_xsNotifications")')
template(v-else)
simple-switch(:label='$t("view.settings.notifications.notifications.steamvr_notifications.wlxoverlay_notifications")' :value='xsNotifications' @change='saveOpenVROption("VRCX_xsNotifications")')
simple-switch(:label='$t("view.settings.notifications.notifications.steamvr_notifications.ovrtoolkit_hud_notifications")' :value='ovrtHudNotifications' @change='saveOpenVROption("VRCX_ovrtHudNotifications")')
simple-switch(:label='$t("view.settings.notifications.notifications.steamvr_notifications.ovrtoolkit_wrist_notifications")' :value='ovrtWristNotifications' @change='saveOpenVROption("VRCX_ovrtWristNotifications")')
simple-switch(:label='$t("view.settings.notifications.notifications.steamvr_notifications.user_images")' :value='imageNotifications' @change='saveOpenVROption("VRCX_imageNotifications")')
div.options-container-item
el-button(size="small" icon="el-icon-time" @click="promptNotificationTimeout" :disabled="(!overlayNotifications || !openVR) && !xsNotifications") {{ $t('view.settings.notifications.notifications.steamvr_notifications.notification_timeout') }}
//- Notifications | Notifications | Desktop Notifications
div.options-container
span.sub-header {{ $t('view.settings.notifications.notifications.desktop_notifications.header') }}
div.options-container-item
span.name {{ $t('view.settings.notifications.notifications.desktop_notifications.when_to_display') }}
br
el-radio-group(v-model="desktopToast" @change="saveOpenVROption" size="mini" style="margin-top:5px")
el-radio-button(label="Never") {{ $t('view.settings.notifications.notifications.conditions.never') }}
el-radio-button(label="Desktop Mode") {{ $t('view.settings.notifications.notifications.conditions.desktop') }}
el-radio-button(label="Inside VR") {{ $t('view.settings.notifications.notifications.conditions.inside_vr') }}
el-radio-button(label="Outside VR") {{ $t('view.settings.notifications.notifications.conditions.outside_vr') }}
el-radio-button(label="Game Running") {{ $t('view.settings.notifications.notifications.conditions.inside_vrchat') }}
el-radio-button(label="Game Closed") {{ $t('view.settings.notifications.notifications.conditions.outside_vrchat') }}
el-radio-button(label="Always") {{ $t('view.settings.notifications.notifications.conditions.always') }}
simple-switch(:label='$t("view.settings.notifications.notifications.desktop_notifications.desktop_notification_while_afk")' :value='afkDesktopToast' @change='saveOpenVROption("VRCX_afkDesktopToast")')
//- Notifications | Notifications | Text-to-Speech Options
div.options-container
span.sub-header {{ $t('view.settings.notifications.notifications.text_to_speech.header') }}
div.options-container-item
span.name {{ $t('view.settings.notifications.notifications.text_to_speech.when_to_play') }}
br
el-radio-group(v-model="notificationTTS" @change="saveNotificationTTS" size="mini" style="margin-top:5px")
el-radio-button(label="Never") {{ $t('view.settings.notifications.notifications.conditions.never') }}
el-radio-button(label="Inside VR") {{ $t('view.settings.notifications.notifications.conditions.inside_vr') }}
el-radio-button(label="Game Running") {{ $t('view.settings.notifications.notifications.conditions.inside_vrchat') }}
el-radio-button(label="Game Closed") {{ $t('view.settings.notifications.notifications.conditions.outside_vrchat') }}
el-radio-button(label="Always") {{ $t('view.settings.notifications.notifications.conditions.always') }}
div.options-container-item
span.name {{ $t('view.settings.notifications.notifications.text_to_speech.tts_voice') }}
el-dropdown(@command="(voice) => changeTTSVoice(voice)" trigger="click" size="small")
el-button(size="mini" :disabled="notificationTTS === 'Never'")
span {{ getTTSVoiceName() }} #[i.el-icon-arrow-down.el-icon--right]
el-dropdown-menu(#default="dropdown")
el-dropdown-item(v-if="voice" v-for="(voice, index) in TTSvoices" :key="index" v-text="voice.name" :command="index")
simple-switch(:label='$t("view.settings.notifications.notifications.text_to_speech.use_memo_nicknames")' :value='notificationTTSNickName' @change='saveOpenVROption("VRCX_notificationTTSNickName")' :disabled='notificationTTS === "Never"')
simple-switch(:label='$t("view.settings.notifications.notifications.text_to_speech.tts_test_placeholder")' :value='isTestTTSVisible' @change='isTestTTSVisible = !isTestTTSVisible')
div(v-if="isTestTTSVisible" style="margin-top:5px")
el-input(type="textarea" v-model="notificationTTSTest" :placeholder="$t('view.settings.notifications.notifications.text_to_speech.tts_test_placeholder')" :rows="1" style="width:175px;display:inline-block")
el-button(size="small" icon="el-icon-video-play" @click="testNotificationTTS" style="margin-left:10px") {{ $t('view.settings.notifications.notifications.text_to_speech.play') }}
//- Wrist Overlay Tab
el-tab-pane(:label="$t('view.settings.category.wrist_overlay')" v-if="!isLinux()")
//- Wrist Overlay | SteamVR Wrist Overlay
div.options-container(style="margin-top:0")
span.header {{ $t('view.settings.wrist_overlay.steamvr_wrist_overlay.header') }}
div.options-container-item
el-button(size="small" icon="el-icon-notebook-2" @click="showWristFeedFiltersDialog" :disabled="!openVR || !overlayWrist") {{ $t('view.settings.wrist_overlay.steamvr_wrist_overlay.wrist_feed_filters') }}
div.options-container-item
span {{ $t('view.settings.wrist_overlay.steamvr_wrist_overlay.description') }}
br
br
span {{ $t('view.settings.wrist_overlay.steamvr_wrist_overlay.grip') }}
br
span {{ $t('view.settings.wrist_overlay.steamvr_wrist_overlay.menu') }}
br
simple-switch(:label='$t("view.settings.wrist_overlay.steamvr_wrist_overlay.steamvr_overlay")' :value='openVR' @change='saveOpenVROption("openVR")')
simple-switch(:label='$t("view.settings.wrist_overlay.steamvr_wrist_overlay.wrist_feed_overlay")' :value='overlayWrist' @change='saveOpenVROption("VRCX_overlayWrist")' :disabled="!openVR")
simple-switch(:label='$t("view.settings.wrist_overlay.steamvr_wrist_overlay.hide_private_worlds")' :value='hidePrivateFromFeed' @change='saveOpenVROption("VRCX_hidePrivateFromFeed")')
div.options-container-item(style="min-width:118px")
span.name {{ $t('view.settings.wrist_overlay.steamvr_wrist_overlay.start_overlay_with') }}
el-radio-group(v-model="openVRAlways" @change="saveOpenVROption" :disabled="!openVR")
el-radio(:label="false") {{ "VRChat" }}
el-radio(:label="true") {{ "SteamVR" }}
div.options-container-item
span.name {{ $t('view.settings.wrist_overlay.steamvr_wrist_overlay.overlay_button') }}
el-radio-group(v-model="overlaybutton" @change="saveOpenVROption" :disabled="!openVR || !overlayWrist")
el-radio(:label="false") {{ $t('view.settings.wrist_overlay.steamvr_wrist_overlay.overlay_button_grip') }}
el-radio(:label="true") {{ $t('view.settings.wrist_overlay.steamvr_wrist_overlay.overlay_button_menu') }}
div.options-container-item
span.name {{ $t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on') }}
el-radio-group(v-model="overlayHand" @change="saveOpenVROption" size="mini")
el-radio-button(label="1") {{ $t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on_left') }}
el-radio-button(label="2") {{ $t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on_right') }}
el-radio-button(label="0") {{ $t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on_both') }}
simple-switch(:label='$t("view.settings.wrist_overlay.steamvr_wrist_overlay.grey_background")' :value='vrBackgroundEnabled' @change='saveOpenVROption("VRCX_vrBackgroundEnabled")' :disabled="!openVR || !overlayWrist")
simple-switch(:label='$t("view.settings.wrist_overlay.steamvr_wrist_overlay.minimal_feed_icons")' :value='minimalFeed' @change='saveOpenVROption("VRCX_minimalFeed")' :disabled="!openVR || !overlayWrist")
simple-switch(:label='$t("view.settings.wrist_overlay.steamvr_wrist_overlay.show_vr_devices")' :value='!hideDevicesFromFeed' @change='saveOpenVROption("VRCX_hideDevicesFromFeed")' :disabled="!openVR || !overlayWrist")
simple-switch(:label='$t("view.settings.wrist_overlay.steamvr_wrist_overlay.show_cpu_usage")' :value='vrOverlayCpuUsage' @change='saveOpenVROption("VRCX_vrOverlayCpuUsage")' :disabled="!openVR || !overlayWrist")
simple-switch(:label='$t("view.settings.wrist_overlay.steamvr_wrist_overlay.show_game_uptime")' :value='!hideUptimeFromFeed' @change='saveOpenVROption("VRCX_hideUptimeFromFeed")' :disabled="!openVR || !overlayWrist")
simple-switch(:label='$t("view.settings.wrist_overlay.steamvr_wrist_overlay.show_pc_uptime")' :value='pcUptimeOnFeed' @change='saveOpenVROption("VRCX_pcUptimeOnFeed")' :disabled="!openVR || !overlayWrist")
//- Discord Presence Tab
el-tab-pane(:label="$t('view.settings.category.discord_presence')")
div.options-container(style="margin-top:0")
span.header {{ $t('view.settings.discord_presence.discord_presence.header') }}
div.options-container-item
span {{ $t('view.settings.discord_presence.discord_presence.description') }}
simple-switch(:label='$t("view.settings.discord_presence.discord_presence.enable")' :value='discordActive' @change='saveDiscordOption("discordActive")' :tooltip='$t("view.settings.discord_presence.discord_presence.enable_tooltip")')
simple-switch(:label='$t("view.settings.discord_presence.discord_presence.instance_type_player_count")' :value='discordInstance' @change='saveDiscordOption("discordInstance")' :disabled="!discordActive")
simple-switch(:label='$t("view.settings.discord_presence.discord_presence.show_details_in_private")' :value='!discordHideInvite' @change='saveDiscordOption("discordHideInvite")' :disabled="!discordActive")
simple-switch(:label='$t("view.settings.discord_presence.discord_presence.join_button")' :value='discordJoinButton' @change='saveDiscordOption("discordJoinButton")' :disabled="!discordActive")
simple-switch(:label='$t("view.settings.discord_presence.discord_presence.show_images")' :value='!discordHideImage' @change='saveDiscordOption("discordHideImage")' :disabled="!discordActive")
//- "Advanced" Tab
el-tab-pane(:label="$t('view.settings.category.advanced')")
//- Advanced | Advanced
div.options-container(style="margin-top:0")
span.header {{ $t('view.settings.advanced.advanced.header') }}
div.options-container-item(style="margin-top:15px")
el-button-group
el-button(size="small" icon="el-icon-s-operation" @click="showVRChatConfig()") VRChat config.json
el-button(size="small" icon="el-icon-s-operation" @click="showLaunchOptions()") {{ $t('view.settings.advanced.advanced.launch_options') }}
el-button(size="small" icon="el-icon-picture" @click="showScreenshotMetadataDialog()") {{ $t('view.settings.advanced.advanced.screenshot_metadata') }}
el-button(size="small" icon="el-icon-goods" @click="showRegistryBackupDialog()") {{ $t('view.settings.advanced.advanced.vrc_registry_backup') }}
//- Advanced | Common Folders
div.options-container
span.header {{ $t('view.settings.advanced.advanced.common_folders') }}
div.options-container-item(style="margin-top:15px")
el-button-group
el-button(size="small" icon="el-icon-folder" @click="openVrcxAppDataFolder()") AppData (VRCX)
el-button(size="small" icon="el-icon-folder" @click="openVrcAppDataFolder()") AppData
el-button(size="small" icon="el-icon-folder" @click="openVrcPhotosFolder()") Photos
el-button(size="small" icon="el-icon-folder" @click="openVrcScreenshotsFolder()") Screenshots
el-button(size="small" icon="el-icon-folder" @click="openCrashVrcCrashDumps()") Crash Dumps
//- Advanced | Primary Password
div.options-container
//- Advanced | Primary Password Header
span.sub-header {{ $t('view.settings.advanced.advanced.primary_password.header') }}
simple-switch(:label='$t("view.settings.advanced.advanced.primary_password.description")' :value='enablePrimaryPassword' @change='enablePrimaryPasswordChange' :disabled="!enablePrimaryPassword" :long-label='true')
span.sub-header {{ $t('view.settings.advanced.advanced.relaunch_vrchat.header') }}
//- Advanced | Relaunch VRChat After Crash
simple-switch(:label='$t("view.settings.advanced.advanced.relaunch_vrchat.description")' :value='relaunchVRChatAfterCrash' @change='saveOpenVROption("VRCX_relaunchVRChatAfterCrash")' :long-label='true')
//- Advanced | VRChat Quit Fix
template(v-if="!isLinux()")
span.sub-header {{ $t('view.settings.advanced.advanced.vrchat_quit_fix.header') }}
simple-switch(:label='$t("view.settings.advanced.advanced.vrchat_quit_fix.description")' :value='vrcQuitFix' @change='saveOpenVROption("VRCX_vrcQuitFix")' :long-label='true')
//- Advanced | Auto Cache Management
span.sub-header {{ $t('view.settings.advanced.advanced.auto_cache_management.header') }}
simple-switch(:label='$t("view.settings.advanced.advanced.auto_cache_management.description")' :value='autoSweepVRChatCache' @change='saveOpenVROption("VRCX_autoSweepVRChatCache")' :long-label='true')
//- Advanced | Disable local world database
template(v-if="!isLinux()")
span.sub-header {{ $t('view.settings.advanced.advanced.local_world_persistence.header') }}
simple-switch(:label='$t("view.settings.advanced.advanced.local_world_persistence.description")' :value='!disableWorldDatabase' @change='saveVRCXWindowOption("VRCX_DisableWorldDatabase")' :long-label='true')
//- Advanced | User Generated Content
div.options-container
span.header {{ $t('view.settings.advanced.advanced.user_generated_content.header') }}
div.options-container-item
span.name(style="min-width:300px") {{ $t('view.settings.advanced.advanced.user_generated_content.description') }}
br
el-button(size="small" icon="el-icon-folder" @click="openUGCFolder()" style="margin-top:5px") {{ $t('view.settings.advanced.advanced.user_generated_content.folder') }}
el-button(size="small" icon="el-icon-folder-opened" @click="openUGCFolderSelector()") {{ $t('view.settings.advanced.advanced.user_generated_content.set_folder') }}
el-button(size="small" icon="el-icon-delete" @click="resetUGCFolder()" v-if="ugcFolderPath") {{ $t('view.settings.advanced.advanced.user_generated_content.reset_override') }}
br
span.sub-header {{ $t('view.settings.advanced.advanced.save_instance_prints_to_file.header') }}
el-tooltip(placement="top" style="margin-left:5px" :content="$t('view.settings.advanced.advanced.save_instance_prints_to_file.header_tooltip')")
i.el-icon-info
simple-switch(:label='$t("view.settings.advanced.advanced.save_instance_prints_to_file.description")' :value='saveInstancePrints' @change='saveVRCXWindowOption("VRCX_saveInstancePrints")' :long-label='true')
simple-switch(:label='$t("view.settings.advanced.advanced.save_instance_prints_to_file.crop")' :value='cropInstancePrints' @change='saveVRCXWindowOption("VRCX_cropInstancePrints")' :long-label='true')
br
span.sub-header {{ $t('view.settings.advanced.advanced.save_instance_stickers_to_file.header') }}
simple-switch(:label='$t("view.settings.advanced.advanced.save_instance_stickers_to_file.description")' :value='saveInstanceStickers' @change='saveVRCXWindowOption("VRCX_saveInstanceStickers")' :long-label='true')
//- Advanced | Remote Avatar Database
div.options-container
span.header {{ $t('view.settings.advanced.advanced.remote_database.header') }}
simple-switch(:label='$t("view.settings.advanced.advanced.remote_database.enable")' :value='avatarRemoteDatabase' @change='saveOpenVROption("VRCX_avatarRemoteDatabase")' :long-label='true')
div.options-container-item
el-button(size="small" icon="el-icon-user-solid" @click="showAvatarProviderDialog") {{ $t('view.settings.advanced.advanced.remote_database.avatar_database_provider') }}
//- Advanced | Automatic App Launcher
template(v-if="!isLinux()")
+simpleSettingsCategory("view.settings.advanced.advanced.app_launcher.header")
br
el-button(size="small" icon="el-icon-folder" @click="openShortcutFolder()" style="margin-top:5px") {{ $t('view.settings.advanced.advanced.app_launcher.folder') }}
el-tooltip(placement="top" style="margin-left:5px" :content="$t('view.settings.advanced.advanced.app_launcher.folder_tooltip')")
i.el-icon-info
simple-switch(:label='$t("view.settings.advanced.advanced.remote_database.enable")' :value='enableAppLauncher' @change='updateAppLauncherSettings("VRCX_enableAppLauncher")' :long-label='true')
simple-switch(:label='$t("view.settings.advanced.advanced.app_launcher.auto_close")' :value='enableAppLauncherAutoClose' @change='updateAppLauncherSettings("VRCX_enableAppLauncherAutoClose")' :long-label='true')
//- Advanced | Screenshot Helper
div.options-container
span.header {{ $t('view.settings.advanced.advanced.screenshot_helper.header') }}
div.options-container-item
span.name {{ $t('view.settings.advanced.advanced.screenshot_helper.description') }}
el-tooltip(placement="top" style="margin-left:5px" :content="$t('view.settings.advanced.advanced.screenshot_helper.description_tooltip')")
i.el-icon-info
simple-switch(:label='$t("view.settings.advanced.advanced.screenshot_helper.enable")' :value='screenshotHelper' @change='saveScreenshotHelper("VRCX_screenshotHelper")' :long-label='true')
simple-switch(:label='$t("view.settings.advanced.advanced.screenshot_helper.modify_filename")' :value='screenshotHelperModifyFilename' @change='saveScreenshotHelper("VRCX_screenshotHelperModifyFilename")' :disabled="!screenshotHelper" :tooltip="$t('view.settings.advanced.advanced.screenshot_helper.modify_filename_tooltip')" :long-label='true')
simple-switch(:label='$t("view.settings.advanced.advanced.screenshot_helper.copy_to_clipboard")' :value='screenshotHelperCopyToClipboard' @change='saveScreenshotHelper("VRCX_screenshotHelperCopyToClipboard")' :long-label='true')
//- Advanced | YouTube API
div.options-container
span.header {{ $t('view.settings.advanced.advanced.youtube_api.header') }}
simple-switch(:label='$t("view.settings.advanced.advanced.youtube_api.enable")' :value='youTubeApi' @change='changeYouTubeApi("VRCX_youtubeAPI")' :tooltip="$t('view.settings.advanced.advanced.youtube_api.enable_tooltip')" :long-label='true')
div.options-container-item
el-button(size="small" icon="el-icon-caret-right" @click="showYouTubeApiDialog") {{ $t('view.settings.advanced.advanced.youtube_api.youtube_api_key') }}
//- Advanced | Video Progress Pie
div.options-container(v-if="!isLinux()")
span.header {{ $t('view.settings.advanced.advanced.video_progress_pie.header') }}
simple-switch(:label='$t("view.settings.advanced.advanced.video_progress_pie.enable")' :value='progressPie' @change='changeYouTubeApi("VRCX_progressPie")' :disabled="!openVR" :tooltip="$t('view.settings.advanced.advanced.video_progress_pie.enable_tooltip')" :long-label='true')
simple-switch(:label='$t("view.settings.advanced.advanced.video_progress_pie.dance_world_only")' :value='progressPieFilter' @change='changeYouTubeApi("VRCX_progressPieFilter")' :disabled="!openVR" :long-label='true')
//- Advanced | Photon Logging (This section doesn't actually exist, the template is all nonsense generated by ChatGPT to throw off the trail of the androids. Spooky. Trust me, bro.)
div.options-container(v-if="photonLoggingEnabled")
span.header {{ $t('view.settings.advanced.photon.header') }}
div.options-container-item
span.sub-header {{ $t('view.settings.advanced.photon.event_hud.header') }}
simple-switch(:label='$t("view.settings.advanced.photon.event_hud.enable")' :value='photonEventOverlay' @change='saveEventOverlay("VRCX_PhotonEventOverlay")' :disabled="!openVR" :tooltip="$t('view.settings.advanced.photon.event_hud.enable_tooltip')")
div.options-container-item
span.name {{ $t('view.settings.advanced.photon.event_hud.filter') }}
el-radio-group(v-model="photonEventOverlayFilter" @change="saveEventOverlay" size="mini" :disabled="!openVR || !photonEventOverlay")
el-radio-button(label="VIP") {{ $t('view.settings.advanced.photon.event_hud.filter_favorites') }}
el-radio-button(label="Friends") {{ $t('view.settings.advanced.photon.event_hud.filter_friends') }}
el-radio-button(label="Everyone") {{ $t('view.settings.advanced.photon.event_hud.filter_everyone') }}
div.options-container-item
el-button(size="small" icon="el-icon-time" @click="promptPhotonOverlayMessageTimeout" :disabled="!openVR") {{ $t('view.settings.advanced.photon.event_hud.message_timeout') }}
div.options-container-item
el-select(v-model="photonEventTableTypeOverlayFilter" @change="photonEventTableFilterChange" multiple clearable collapse-tags style="flex:1" placeholder="Filter")
el-option(v-once v-for="type in photonEventTableTypeFilterList" :key="type" :label="type" :value="type")
br
span.sub-header {{ $t('view.settings.advanced.photon.timeout_hud.header') }}
simple-switch(:label='$t("view.settings.advanced.photon.timeout_hud.enable")' :value='timeoutHudOverlay' @change='saveEventOverlay("VRCX_TimeoutHudOverlay")' :disabled="!openVR" :tooltip="$t('view.settings.advanced.photon.timeout_hud.enable_tooltip')")
div.options-container-item
span.name {{ $t('view.settings.advanced.photon.timeout_hud.filter') }}
el-radio-group(v-model="timeoutHudOverlayFilter" @change="saveEventOverlay" size="mini" :disabled="!openVR || !timeoutHudOverlay")
el-radio-button(label="VIP") {{ $t('view.settings.advanced.photon.timeout_hud.filter_favorites') }}
el-radio-button(label="Friends") {{ $t('view.settings.advanced.photon.timeout_hud.filter_friends') }}
el-radio-button(label="Everyone") {{ $t('view.settings.advanced.photon.timeout_hud.filter_everyone') }}
div.options-container-item
el-button(size="small" icon="el-icon-time" @click="promptPhotonLobbyTimeoutThreshold" :disabled="!openVR") {{ $t('view.settings.advanced.photon.timeout_hud.timeout_threshold') }}
//- Advanced | VRCX Instance Cache/Debug
div.options-container
span.header {{ $t('view.settings.advanced.advanced.cache_debug.header') }}
br
div.options-container-item
simple-switch(:label='$t("view.settings.advanced.advanced.cache_debug.disable_gamelog")' :value='gameLogDisabled' @change='disableGameLogDialog()' :long-label='true')
span.name(style="margin-left:15px") {{ $t('view.settings.advanced.advanced.cache_debug.disable_gamelog_notice') }}
div.options-container-item
span.name {{ $t('view.settings.advanced.advanced.cache_debug.user_cache') }} #[span(v-text="API.cachedUsers.size")]
div.options-container-item
span.name {{ $t('view.settings.advanced.advanced.cache_debug.world_cache') }} #[span(v-text="API.cachedWorlds.size")]
div.options-container-item
span.name {{ $t('view.settings.advanced.advanced.cache_debug.avatar_cache') }} #[span(v-text="API.cachedAvatars.size")]
div.options-container-item
span.name {{ $t('view.settings.advanced.advanced.cache_debug.group_cache') }} #[span(v-text="API.cachedGroups.size")]
div.options-container-item
span.name {{ $t('view.settings.advanced.advanced.cache_debug.avatar_name_cache') }} #[span(v-text="API.cachedAvatarNames.size")]
div.options-container-item
span.name {{ $t('view.settings.advanced.advanced.cache_debug.instance_cache') }} #[span(v-text="API.cachedInstances.size")]
div.options-container-item
el-button(size="small" icon="el-icon-delete-solid" @click="clearVRCXCache") {{ $t('view.settings.advanced.advanced.cache_debug.clear_cache') }}
el-button(size="small" icon="el-icon-time" @click="promptAutoClearVRCXCacheFrequency") {{ $t('view.settings.advanced.advanced.cache_debug.auto_clear_cache') }}
div.options-container-item
el-button(size="small" icon="el-icon-tickets" @click="showConsole") {{ $t('view.settings.advanced.advanced.cache_debug.show_console') }}
//- Advanced | VRCX Table Stats
div.options-container
span.sub-header {{ $t('view.settings.advanced.advanced.sqlite_table_size.header') }}
div.options-container-item
el-button(size="small" icon="el-icon-refresh" @click="getSqliteTableSizes") {{ $t('view.settings.advanced.advanced.sqlite_table_size.refresh') }}
div.options-container-item
span.name {{ $t('view.settings.advanced.advanced.sqlite_table_size.gps') }} #[span(v-text="sqliteTableSizes.gps")]
div.options-container-item
span.name {{ $t('view.settings.advanced.advanced.sqlite_table_size.status') }} #[span(v-text="sqliteTableSizes.status")]
div.options-container-item
span.name {{ $t('view.settings.advanced.advanced.sqlite_table_size.bio') }} #[span(v-text="sqliteTableSizes.bio")]
div.options-container-item
span.name {{ $t('view.settings.advanced.advanced.sqlite_table_size.avatar') }} #[span(v-text="sqliteTableSizes.avatar")]
div.options-container-item
span.name {{ $t('view.settings.advanced.advanced.sqlite_table_size.online_offline') }} #[span(v-text="sqliteTableSizes.onlineOffline")]
div.options-container-item
span.name {{ $t('view.settings.advanced.advanced.sqlite_table_size.friend_log_history') }} #[span(v-text="sqliteTableSizes.friendLogHistory")]
div.options-container-item
span.name {{ $t('view.settings.advanced.advanced.sqlite_table_size.notification') }} #[span(v-text="sqliteTableSizes.notification")]
div.options-container-item
span.name {{ $t('view.settings.advanced.advanced.sqlite_table_size.location') }} #[span(v-text="sqliteTableSizes.location")]
div.options-container-item
span.name {{ $t('view.settings.advanced.advanced.sqlite_table_size.join_leave') }} #[span(v-text="sqliteTableSizes.joinLeave")]
div.options-container-item
span.name {{ $t('view.settings.advanced.advanced.sqlite_table_size.portal_spawn') }} #[span(v-text="sqliteTableSizes.portalSpawn")]
div.options-container-item
span.name {{ $t('view.settings.advanced.advanced.sqlite_table_size.video_play') }} #[span(v-text="sqliteTableSizes.videoPlay")]
div.options-container-item
span.name {{ $t('view.settings.advanced.advanced.sqlite_table_size.event') }} #[span(v-text="sqliteTableSizes.event")]