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>
6431
src/animated-emoji.scss
Normal file
23145
src/app.js
Normal file
954
src/app.scss
Normal file
@@ -0,0 +1,954 @@
|
||||
@charset "utf-8";
|
||||
|
||||
//
|
||||
// Copyright(c) 2019-2021 pypy and individual contributors.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This work is licensed under the terms of the MIT license.
|
||||
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||
//
|
||||
|
||||
@import '~normalize.css/normalize.css';
|
||||
@import '~animate.css/animate.min.css';
|
||||
@import '~noty/lib/noty.css';
|
||||
@import '~element-ui/lib/theme-chalk/index.css';
|
||||
|
||||
.color-palettes {
|
||||
background: #409eff;
|
||||
background: #67c23a;
|
||||
background: #e6a23c;
|
||||
background: #f56c6c;
|
||||
background: #909399;
|
||||
background: #fd9200;
|
||||
background: #e6e6e6;
|
||||
background: #c0c4cc;
|
||||
}
|
||||
|
||||
.noty_layout {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.noty_theme__mint.noty_bar {
|
||||
position: relative;
|
||||
margin: 4px 0;
|
||||
overflow: hidden;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.noty_theme__mint.noty_bar .noty_body {
|
||||
padding: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.noty_theme__mint.noty_bar .noty_buttons {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.noty_theme__mint.noty_type__alert,
|
||||
.noty_theme__mint.noty_type__notification {
|
||||
color: #2f2f2f;
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #d1d1d1;
|
||||
}
|
||||
|
||||
.noty_theme__mint.noty_type__warning {
|
||||
color: #fff;
|
||||
background-color: #ffae42;
|
||||
border-bottom: 1px solid #e89f3c;
|
||||
}
|
||||
|
||||
.noty_theme__mint.noty_type__error {
|
||||
color: #fff;
|
||||
background-color: #de636f;
|
||||
border-bottom: 1px solid #ca5a65;
|
||||
}
|
||||
|
||||
.noty_theme__mint.noty_type__info,
|
||||
.noty_theme__mint.noty_type__information {
|
||||
color: #fff;
|
||||
background-color: #7f7eff;
|
||||
border-bottom: 1px solid #7473e8;
|
||||
}
|
||||
|
||||
.noty_theme__mint.noty_type__success {
|
||||
color: #fff;
|
||||
background-color: #afc765;
|
||||
border-bottom: 1px solid #a0b55c;
|
||||
}
|
||||
|
||||
.el-table + .pagination-bar {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.el-table--mini .el-table__expanded-cell[class*='cell'] {
|
||||
padding: 20px 50px;
|
||||
}
|
||||
|
||||
.el-table--mini .el-table__cell {
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
.el-table .cell {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 1;
|
||||
}
|
||||
|
||||
.el-table th.is-sortable .cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.el-table .caret-wrapper {
|
||||
margin-top: 4.5px;
|
||||
}
|
||||
|
||||
.notification-table .el-table .cell {
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
|
||||
.el-table__row:hover .el-table__cell .cell {
|
||||
-webkit-line-clamp: unset;
|
||||
}
|
||||
|
||||
.el-dialog__body {
|
||||
padding: 20px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.el-dialog__footer > .el-button + .el-button {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
body,
|
||||
input,
|
||||
textarea,
|
||||
select,
|
||||
button {
|
||||
font-family: 'Noto Sans JP', 'Noto Sans KR', 'Noto Sans TC', 'Noto Sans SC',
|
||||
'Meiryo UI', 'Malgun Gothic', 'Segoe UI', sans-serif;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.x-link {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.x-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.x-ellipsis {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.x-app {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden auto;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.x-container {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
overflow: hidden auto;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.x-login-container {
|
||||
position: absolute;
|
||||
// modal 시작이 2000이라서
|
||||
z-index: 1999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.x-login {
|
||||
display: grid;
|
||||
grid-template-rows: repeat(2, auto);
|
||||
align-items: center;
|
||||
max-width: clamp(600px, 60svw, 800px);
|
||||
}
|
||||
|
||||
.x-login-form-container {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
height: 380px;
|
||||
}
|
||||
|
||||
.x-login-form-container:has(> div:nth-child(3)) {
|
||||
grid-template-columns: 1fr 1px 1fr;
|
||||
}
|
||||
|
||||
.x-login-form-container > div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
padding: 16px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.x-scroll-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
hr.x-vertical-divider {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
background: rgb(255 255 255 / 16%);
|
||||
}
|
||||
|
||||
.x-saved-account-list {
|
||||
display: grid;
|
||||
|
||||
> .x-friend-item {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.x-legal-notice-container {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.x-menu-container {
|
||||
flex: none;
|
||||
overflow: hidden auto;
|
||||
background: #f8f8f8;
|
||||
}
|
||||
|
||||
.x-menu-container > .el-menu {
|
||||
background: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.el-menu-item i {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.el-menu-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -4px;
|
||||
top: 50%;
|
||||
height: 36px;
|
||||
width: 3px;
|
||||
translate: 0 -50%;
|
||||
border-radius: 4px;
|
||||
background: #303133;
|
||||
transition:
|
||||
background-color 0.4s,
|
||||
left 0.2s;
|
||||
}
|
||||
|
||||
.el-menu-item.is-active::before {
|
||||
left: 2px;
|
||||
transition: left 0.2s cubic-bezier(0.175, 0.885, 0.32, 2.552);
|
||||
}
|
||||
|
||||
.el-menu-item.notify::after {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
content: '';
|
||||
background: #303133;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.pending-update {
|
||||
margin: 7px;
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.x-aside-container {
|
||||
display: flex;
|
||||
flex: none;
|
||||
flex-direction: column;
|
||||
background: #f8f8f8;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.el-popper.x-quick-search {
|
||||
width: 225px;
|
||||
min-width: 0 !important;
|
||||
}
|
||||
|
||||
.el-popper.x-quick-search .el-select-dropdown__item {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
padding: 0 10px;
|
||||
font-size: 12px;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.x-friend-list {
|
||||
padding: 0 10px;
|
||||
overflow: hidden auto;
|
||||
}
|
||||
|
||||
.x-friend-group > .el-icon-arrow-right {
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.x-friend-group > .el-icon-arrow-right.rotate {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.x-aside-container > .x-friend-list {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.x-dialog .x-friend-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
max-height: 300px;
|
||||
}
|
||||
|
||||
.x-friend-list > .x-friend-group {
|
||||
padding: 20px 0 5px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.x-friend-item {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 5px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.x-friend-item:hover {
|
||||
background: #f0f0f0;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.x-friend-item-border:hover {
|
||||
border-top-left-radius: 25px;
|
||||
border-top-right-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
border-bottom-left-radius: 25px;
|
||||
}
|
||||
|
||||
.x-aside-container > .x-friend-list > .x-friend-item:hover {
|
||||
background: #fff;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.el-select-dropdown__item .x-friend-item:hover {
|
||||
background: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.x-dialog .x-friend-item {
|
||||
width: 175px;
|
||||
}
|
||||
|
||||
.x-friend-item > .avatar {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
flex: none;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.x-friend-item > img.avatar,
|
||||
img.friends-list-avatar {
|
||||
width: unset;
|
||||
height: 22.5px;
|
||||
margin-right: 0;
|
||||
margin-left: 5px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.x-friend-item > .avatar > img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.x-friend-item > .avatar.active > img {
|
||||
filter: grayscale(1);
|
||||
}
|
||||
|
||||
.x-friend-item:hover > .avatar.offline > img,
|
||||
.x-friend-item:hover > .avatar.active > img {
|
||||
filter: none;
|
||||
}
|
||||
|
||||
.x-user-badge-hidden {
|
||||
filter: grayscale(1);
|
||||
}
|
||||
|
||||
.x-user-badge:hover {
|
||||
filter: none;
|
||||
}
|
||||
|
||||
.x-friend-item > .avatar.online.mobile > img,
|
||||
.x-friend-item > .avatar.joinme.mobile > img,
|
||||
.x-friend-item > .avatar.askme.mobile > img,
|
||||
.x-friend-item > .avatar.busy.mobile > img {
|
||||
mask-image: url(masks/usercutoutmobile.svg);
|
||||
}
|
||||
|
||||
.x-friend-item > .avatar.online.mobile::after,
|
||||
.x-friend-item > .avatar.joinme.mobile::after,
|
||||
.x-friend-item > .avatar.askme.mobile::after,
|
||||
.x-friend-item > .avatar.busy.mobile::after {
|
||||
position: absolute;
|
||||
right: -2px;
|
||||
bottom: 0px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
content: '';
|
||||
border-radius: 0px;
|
||||
mask-image: url(masks/phone.svg);
|
||||
}
|
||||
|
||||
.x-friend-item > .avatar.active > img,
|
||||
.x-friend-item > .avatar.online > img,
|
||||
.x-friend-item > .avatar.joinme > img,
|
||||
.x-friend-item > .avatar.askme > img,
|
||||
.x-friend-item > .avatar.busy > img,
|
||||
.x-friend-item > .avatar.offline > img {
|
||||
mask-image: url(masks/usercutout.svg);
|
||||
}
|
||||
|
||||
.x-friend-item > .avatar.active::after,
|
||||
.x-friend-item > .avatar.online::after,
|
||||
.x-friend-item > .avatar.joinme::after,
|
||||
.x-friend-item > .avatar.askme::after,
|
||||
.x-friend-item > .avatar.busy::after,
|
||||
.x-friend-item > .avatar.offline::after {
|
||||
position: absolute;
|
||||
right: 1px;
|
||||
bottom: 1px;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
content: '';
|
||||
background: #909399;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.x-friend-item > .avatar.active::after {
|
||||
background: #f4e05e;
|
||||
}
|
||||
|
||||
.x-friend-item > .avatar.online::after {
|
||||
background: #67c23a;
|
||||
}
|
||||
|
||||
.x-friend-item > .avatar.joinme::after {
|
||||
background: #409eff;
|
||||
mask-image: url(masks/joinme.svg);
|
||||
}
|
||||
|
||||
.x-friend-item > .avatar.askme::after {
|
||||
background: #ff9500;
|
||||
mask-image: url(masks/askme.svg);
|
||||
}
|
||||
|
||||
.x-friend-item > .avatar.busy::after {
|
||||
background: #ff2c2c;
|
||||
mask-image: url(masks/busy.svg);
|
||||
}
|
||||
|
||||
.x-friend-item > .avatar.offline::after {
|
||||
background: #909399;
|
||||
}
|
||||
|
||||
.x-friend-item.offline > .avatar::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.x-friend-item > .detail {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.x-friend-item > .detail > .name,
|
||||
.x-friend-item > .detail > .extra {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.x-friend-item > .detail > .name {
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.x-friend-item > .detail > .extra {
|
||||
font-weight: normal;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.x-friend-item > .vrcplus-icon {
|
||||
border: 4px solid #dcdfe6;
|
||||
border-radius: 20px;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.x-friend-item > .current-vrcplus-icon {
|
||||
border: 4px solid #67c23a;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.x-friend-item > .vrcplus-icon > img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 15px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.x-change-image-item {
|
||||
display: inline-block;
|
||||
padding: 4px 4px 0 4px;
|
||||
}
|
||||
|
||||
.x-change-image-item:hover {
|
||||
background: #f0f0f0;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.x-change-image-item > img,
|
||||
.x-change-image-item > .el-popover__reference-wrapper > img {
|
||||
width: 240px;
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
.current-image {
|
||||
border: 2px solid #67c23a;
|
||||
padding: 2px 2px 0 2px;
|
||||
}
|
||||
|
||||
.x-dialog > .el-dialog {
|
||||
max-width: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.x-user-dialog > .el-dialog > .el-dialog__header,
|
||||
.x-world-dialog > .el-dialog > .el-dialog__header,
|
||||
.x-avatar-dialog > .el-dialog > .el-dialog__header,
|
||||
.x-group-dialog > .el-dialog > .el-dialog__header {
|
||||
display: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.x-user-dialog > .el-dialog > .el-dialog__body,
|
||||
.x-world-dialog > .el-dialog > .el-dialog__body,
|
||||
.x-avatar-dialog > .el-dialog > .el-dialog__body,
|
||||
.x-group-dialog > .el-dialog > .el-dialog__body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.el-popper.hex {
|
||||
min-width: auto;
|
||||
padding: 10px;
|
||||
font-family: monospace;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
i.x-user-status,
|
||||
i.x-status-icon {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: #808080;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
i.x-user-status.active {
|
||||
background: #f4e05e;
|
||||
}
|
||||
|
||||
i.x-user-status.online {
|
||||
background: #67c23a;
|
||||
}
|
||||
|
||||
i.x-user-status.joinme {
|
||||
background: #409eff;
|
||||
mask-image: url(masks/joinme.svg);
|
||||
}
|
||||
|
||||
i.x-user-status.askme {
|
||||
background: #ff9500;
|
||||
mask-image: url(masks/askme.svg);
|
||||
}
|
||||
|
||||
i.x-user-status.busy {
|
||||
background: #ff2c2c;
|
||||
mask-image: url(masks/busy.svg);
|
||||
}
|
||||
|
||||
i.x-status-icon.green {
|
||||
background: #67c23a;
|
||||
}
|
||||
|
||||
i.x-status-icon.blue {
|
||||
background: #409eff;
|
||||
}
|
||||
|
||||
i.x-status-icon.orange {
|
||||
background: #ff9500;
|
||||
}
|
||||
|
||||
i.x-status-icon.red {
|
||||
background: #ff2c2c;
|
||||
}
|
||||
|
||||
.x-tag-friend {
|
||||
color: rgb(255, 208, 0) !important;
|
||||
border-color: rgb(255, 208, 0) !important;
|
||||
}
|
||||
|
||||
.x-tag-vrcplus {
|
||||
color: rgb(255, 208, 0) !important;
|
||||
border-color: rgb(255, 208, 0) !important;
|
||||
}
|
||||
|
||||
.x-tag-platform-pc {
|
||||
color: #409eff !important;
|
||||
border-color: #409eff !important;
|
||||
}
|
||||
|
||||
.x-tag-platform-quest {
|
||||
color: #67c23a !important;
|
||||
border-color: #67c23a !important;
|
||||
}
|
||||
|
||||
.x-tag-platform-ios {
|
||||
color: #c7c7ce !important;
|
||||
border-color: #c7c7ce !important;
|
||||
}
|
||||
|
||||
.x-tag-platform-other {
|
||||
color: #ff4177 !important;
|
||||
border-color: #ff4177 !important;
|
||||
}
|
||||
|
||||
.x-tag-age-verification {
|
||||
color: #ff4177 !important;
|
||||
border-color: #ff4177 !important;
|
||||
}
|
||||
|
||||
.x-grey {
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.el-tree-node {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.el-tree-node__content {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.el-progress-bar {
|
||||
padding-right: 80px;
|
||||
margin-right: -85px;
|
||||
}
|
||||
|
||||
.el-progress__text {
|
||||
color: #c8c8c8;
|
||||
}
|
||||
|
||||
.x-user-dialog .el-textarea__inner {
|
||||
padding: 0;
|
||||
background: none;
|
||||
border: 0;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.options-container {
|
||||
margin-top: 30px;
|
||||
padding: 0px 10px 10px 10px;
|
||||
}
|
||||
|
||||
.options-container .header-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.options-container .header {
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.options-container .sub-header {
|
||||
font-weight: bold;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.options-container-item {
|
||||
font-size: 12px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.options-container-item .name {
|
||||
display: inline-block;
|
||||
width: 235px;
|
||||
}
|
||||
|
||||
.toggle-switch {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.toggle-list {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.toggle-list .toggle-item {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.toggle-list .toggle-name {
|
||||
display: inline-block;
|
||||
min-width: 190px;
|
||||
padding-right: 10px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.color-picker {
|
||||
font-size: 18px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.el-color-picker__trigger {
|
||||
border: unset;
|
||||
}
|
||||
|
||||
.el-color-picker__color {
|
||||
border: 0.5px solid #999;
|
||||
}
|
||||
|
||||
.el-button--success {
|
||||
background-color: #67c23a !important;
|
||||
border-color: #67c23a !important;
|
||||
}
|
||||
|
||||
.x-dialog .el-button--danger {
|
||||
background-color: #f56c6c !important;
|
||||
border-color: #f56c6c !important;
|
||||
}
|
||||
|
||||
.el-button--warning {
|
||||
background-color: #e6a23c !important;
|
||||
border-color: #e6a23c !important;
|
||||
}
|
||||
|
||||
.avatar-info {
|
||||
cursor: pointer;
|
||||
width: fit-content;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.avatar-info-own {
|
||||
display: inline-block;
|
||||
color: #e6a23c;
|
||||
}
|
||||
|
||||
.avatar-info-public {
|
||||
display: inline-block;
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.avatar-info-unknown {
|
||||
display: inline-block;
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.el-form-item {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.photon-event-table .el-table--mini .el-table__cell,
|
||||
.current-instance-table .el-table--mini .el-table__cell {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.photon-event-table {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.current-instance-table img.friends-list-avatar {
|
||||
width: unset;
|
||||
height: 16px;
|
||||
margin-right: 0;
|
||||
margin-left: 3px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.el-pagination .el-select .el-input .el-input__inner,
|
||||
.el-input--mini .el-input__icon {
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.el-pagination .btn-next {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.el-dialog,
|
||||
.el-message-box {
|
||||
border-radius: 28px;
|
||||
}
|
||||
|
||||
.el-tabs__nav-wrap::after {
|
||||
background-color: #e4e7ed14;
|
||||
}
|
||||
|
||||
.dialog-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.group-banner-image {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.changelog-dialog img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.vrc-instance-queue-message {
|
||||
padding: 3px;
|
||||
top: 0 !important;
|
||||
}
|
||||
|
||||
.vrc-instance-queue-message .el-message__content {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.el-tab-pane {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.el-tabs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.el-tabs__content {
|
||||
flex: 1;
|
||||
max-height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.x-aside-container .el-tabs,
|
||||
.x-aside-container .el-tabs__nav-wrap,
|
||||
.x-aside-container .el-tabs__item {
|
||||
padding: 0 !important;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.el-tabs__header {
|
||||
padding: 0 1px;
|
||||
}
|
||||
|
||||
.zero-margin-tabs .el-tabs__header {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.x-friend-item .el-checkbox__inner,
|
||||
.el-table__row .el-checkbox__inner {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.x-friend-item .el-checkbox__inner::after,
|
||||
.el-table__row .el-checkbox__inner::after {
|
||||
width: 8px;
|
||||
height: 14px;
|
||||
left: 8px;
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
.max-height-el-select .el-select-dropdown__wrap {
|
||||
max-height: 83vh;
|
||||
}
|
||||
|
||||
.el-pagination .el-input .el-input__icon {
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
// User dialog memo: tag line-height
|
||||
.el-dialog__body .el-tag--mini {
|
||||
line-height: 17px;
|
||||
}
|
||||
|
||||
// feed table detail time tag line-height
|
||||
.el-table__expanded-cell .el-tag--mini {
|
||||
line-height: 17px;
|
||||
}
|
||||
|
||||
// User dialog memo: input count background color
|
||||
.x-friend-item:hover .el-input__count {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
// Align the left page with the right friend bar
|
||||
.x-app > .x-container {
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.el-collapse-item .el-tag--mini {
|
||||
line-height: 17px;
|
||||
}
|
||||
|
||||
.x-text-removed {
|
||||
text-decoration: line-through;
|
||||
color: #ff0000;
|
||||
background-color: rgba(255, 0, 0, 0.2);
|
||||
padding: 2px 2px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.x-text-added {
|
||||
color: rgb(76, 255, 80);
|
||||
background-color: rgba(76, 255, 80, 0.2);
|
||||
padding: 2px 2px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
BIN
src/blank.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
37
src/classes/API/config.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import { baseClass, $app, API, $t, $utils } from '../baseClass.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
init() {
|
||||
API.getConfig = function () {
|
||||
return this.call('config', {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
var args = {
|
||||
json
|
||||
};
|
||||
this.$emit('CONFIG', args);
|
||||
return args;
|
||||
});
|
||||
};
|
||||
|
||||
API.$on('CONFIG', function (args) {
|
||||
args.ref = this.applyConfig(args.json);
|
||||
});
|
||||
|
||||
API.applyConfig = function (json) {
|
||||
var ref = {
|
||||
...json
|
||||
};
|
||||
this.cachedConfig = ref;
|
||||
return ref;
|
||||
};
|
||||
}
|
||||
|
||||
_data = {};
|
||||
|
||||
_methods = {};
|
||||
}
|
||||
16
src/classes/_classTemplate.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import * as workerTimers from 'worker-timers';
|
||||
import configRepository from '../repository/config.js';
|
||||
import database from '../repository/database.js';
|
||||
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
init() {}
|
||||
|
||||
_data = {};
|
||||
|
||||
_methods = {};
|
||||
}
|
||||
53
src/classes/apiInit.js
Normal file
@@ -0,0 +1,53 @@
|
||||
import { baseClass, $app, API, $t } from './baseClass.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app) {
|
||||
super(_app);
|
||||
}
|
||||
|
||||
eventHandlers = new Map();
|
||||
|
||||
$emit = function (name, ...args) {
|
||||
if ($app.debug) {
|
||||
console.log(name, ...args);
|
||||
}
|
||||
var handlers = this.eventHandlers.get(name);
|
||||
if (typeof handlers === 'undefined') {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
for (var handler of handlers) {
|
||||
handler.apply(this, args);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
$on = function (name, handler) {
|
||||
var handlers = this.eventHandlers.get(name);
|
||||
if (typeof handlers === 'undefined') {
|
||||
handlers = [];
|
||||
this.eventHandlers.set(name, handlers);
|
||||
}
|
||||
handlers.push(handler);
|
||||
};
|
||||
|
||||
$off = function (name, handler) {
|
||||
var handlers = this.eventHandlers.get(name);
|
||||
if (typeof handlers === 'undefined') {
|
||||
return;
|
||||
}
|
||||
var { length } = handlers;
|
||||
for (var i = 0; i < length; ++i) {
|
||||
if (handlers[i] === handler) {
|
||||
if (length > 1) {
|
||||
handlers.splice(i, 1);
|
||||
} else {
|
||||
this.eventHandlers.delete(name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
419
src/classes/apiLogin.js
Normal file
@@ -0,0 +1,419 @@
|
||||
import Noty from 'noty';
|
||||
import security from '../security.js';
|
||||
import configRepository from '../repository/config.js';
|
||||
import { baseClass, $app, API, $t } from './baseClass.js';
|
||||
/* eslint-disable no-unused-vars */
|
||||
let webApiService = {};
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t, _webApiService) {
|
||||
super(_app, _API, _t);
|
||||
webApiService = _webApiService;
|
||||
}
|
||||
|
||||
async init() {
|
||||
API.isLoggedIn = false;
|
||||
API.attemptingAutoLogin = false;
|
||||
|
||||
/**
|
||||
* @param {{ username: string, password: string }} params credential to login
|
||||
* @returns {Promise<{origin: boolean, json: any, params}>}
|
||||
*/
|
||||
API.login = function (params) {
|
||||
var { username, password, saveCredentials, cipher } = params;
|
||||
username = encodeURIComponent(username);
|
||||
password = encodeURIComponent(password);
|
||||
var auth = btoa(`${username}:${password}`);
|
||||
if (saveCredentials) {
|
||||
delete params.saveCredentials;
|
||||
if (cipher) {
|
||||
params.password = cipher;
|
||||
delete params.cipher;
|
||||
}
|
||||
$app.saveCredentials = params;
|
||||
}
|
||||
return this.call('auth/user', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Basic ${auth}`
|
||||
}
|
||||
}).then((json) => {
|
||||
var args = {
|
||||
json,
|
||||
params,
|
||||
origin: true
|
||||
};
|
||||
if (
|
||||
json.requiresTwoFactorAuth &&
|
||||
json.requiresTwoFactorAuth.includes('emailOtp')
|
||||
) {
|
||||
this.$emit('USER:EMAILOTP', args);
|
||||
} else if (json.requiresTwoFactorAuth) {
|
||||
this.$emit('USER:2FA', args);
|
||||
} else {
|
||||
this.$emit('USER:CURRENT', args);
|
||||
}
|
||||
return args;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {{ code: string }} params One-time password
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
API.verifyOTP = function (params) {
|
||||
return this.call('auth/twofactorauth/otp/verify', {
|
||||
method: 'POST',
|
||||
params
|
||||
}).then((json) => {
|
||||
var args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
this.$emit('OTP', args);
|
||||
return args;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {{ code: string }} params One-time token
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
API.verifyTOTP = function (params) {
|
||||
return this.call('auth/twofactorauth/totp/verify', {
|
||||
method: 'POST',
|
||||
params
|
||||
}).then((json) => {
|
||||
var args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
this.$emit('TOTP', args);
|
||||
return args;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {{ code: string }} params One-time token
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
API.verifyEmailOTP = function (params) {
|
||||
return this.call('auth/twofactorauth/emailotp/verify', {
|
||||
method: 'POST',
|
||||
params
|
||||
}).then((json) => {
|
||||
var args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
this.$emit('EMAILOTP', args);
|
||||
return args;
|
||||
});
|
||||
};
|
||||
|
||||
API.$on('AUTOLOGIN', function () {
|
||||
if (this.attemptingAutoLogin) {
|
||||
return;
|
||||
}
|
||||
this.attemptingAutoLogin = true;
|
||||
var user =
|
||||
$app.loginForm.savedCredentials[
|
||||
$app.loginForm.lastUserLoggedIn
|
||||
];
|
||||
if (typeof user === 'undefined') {
|
||||
this.attemptingAutoLogin = false;
|
||||
return;
|
||||
}
|
||||
if ($app.enablePrimaryPassword) {
|
||||
this.logout();
|
||||
return;
|
||||
}
|
||||
$app.relogin(user)
|
||||
.then(() => {
|
||||
if (this.errorNoty) {
|
||||
this.errorNoty.close();
|
||||
}
|
||||
this.errorNoty = new Noty({
|
||||
type: 'success',
|
||||
text: 'Automatically logged in.'
|
||||
}).show();
|
||||
console.log('Automatically logged in.');
|
||||
})
|
||||
.catch((err) => {
|
||||
if (this.errorNoty) {
|
||||
this.errorNoty.close();
|
||||
}
|
||||
this.errorNoty = new Noty({
|
||||
type: 'error',
|
||||
text: 'Failed to login automatically.'
|
||||
}).show();
|
||||
console.error('Failed to login automatically.', err);
|
||||
})
|
||||
.finally(() => {
|
||||
if (!navigator.onLine) {
|
||||
this.errorNoty = new Noty({
|
||||
type: 'error',
|
||||
text: `You're offline.`
|
||||
}).show();
|
||||
console.error(`You're offline.`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
API.$on('USER:CURRENT', function () {
|
||||
this.attemptingAutoLogin = false;
|
||||
});
|
||||
|
||||
API.$on('LOGOUT', function () {
|
||||
this.attemptingAutoLogin = false;
|
||||
});
|
||||
|
||||
API.logout = function () {
|
||||
this.$emit('LOGOUT');
|
||||
// return this.call('logout', {
|
||||
// method: 'PUT'
|
||||
// }).finally(() => {
|
||||
// this.$emit('LOGOUT');
|
||||
// });
|
||||
};
|
||||
}
|
||||
|
||||
_data = {
|
||||
loginForm: {
|
||||
loading: true,
|
||||
username: '',
|
||||
password: '',
|
||||
endpoint: '',
|
||||
websocket: '',
|
||||
saveCredentials: false,
|
||||
savedCredentials: {},
|
||||
lastUserLoggedIn: '',
|
||||
rules: {
|
||||
username: [
|
||||
{
|
||||
required: true,
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
password: [
|
||||
{
|
||||
required: true,
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_methods = {
|
||||
async relogin(user) {
|
||||
var { loginParmas } = user;
|
||||
if (user.cookies) {
|
||||
await webApiService.setCookies(user.cookies);
|
||||
}
|
||||
this.loginForm.lastUserLoggedIn = user.user.id; // for resend email 2fa
|
||||
if (loginParmas.endpoint) {
|
||||
API.endpointDomain = loginParmas.endpoint;
|
||||
API.websocketDomain = loginParmas.websocket;
|
||||
} else {
|
||||
API.endpointDomain = API.endpointDomainVrchat;
|
||||
API.websocketDomain = API.websocketDomainVrchat;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.enablePrimaryPassword) {
|
||||
this.checkPrimaryPassword(loginParmas)
|
||||
.then((pwd) => {
|
||||
this.loginForm.loading = true;
|
||||
return API.getConfig()
|
||||
.catch((err) => {
|
||||
this.loginForm.loading = false;
|
||||
reject(err);
|
||||
})
|
||||
.then(() => {
|
||||
API.login({
|
||||
username: loginParmas.username,
|
||||
password: pwd,
|
||||
cipher: loginParmas.password,
|
||||
endpoint: loginParmas.endpoint,
|
||||
websocket: loginParmas.websocket
|
||||
})
|
||||
.catch((err2) => {
|
||||
this.loginForm.loading = false;
|
||||
// API.logout();
|
||||
reject(err2);
|
||||
})
|
||||
.then(() => {
|
||||
this.loginForm.loading = false;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch((_) => {
|
||||
this.$message({
|
||||
message: 'Incorrect primary password',
|
||||
type: 'error'
|
||||
});
|
||||
reject(_);
|
||||
});
|
||||
} else {
|
||||
API.getConfig()
|
||||
.catch((err) => {
|
||||
this.loginForm.loading = false;
|
||||
reject(err);
|
||||
})
|
||||
.then(() => {
|
||||
API.login({
|
||||
username: loginParmas.username,
|
||||
password: loginParmas.password,
|
||||
endpoint: loginParmas.endpoint,
|
||||
websocket: loginParmas.websocket
|
||||
})
|
||||
.catch((err2) => {
|
||||
this.loginForm.loading = false;
|
||||
API.logout();
|
||||
reject(err2);
|
||||
})
|
||||
.then(() => {
|
||||
this.loginForm.loading = false;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async deleteSavedLogin(userId) {
|
||||
var savedCredentials = JSON.parse(
|
||||
await configRepository.getString('savedCredentials')
|
||||
);
|
||||
delete savedCredentials[userId];
|
||||
// Disable primary password when no account is available.
|
||||
if (Object.keys(savedCredentials).length === 0) {
|
||||
this.enablePrimaryPassword = false;
|
||||
await configRepository.setBool('enablePrimaryPassword', false);
|
||||
}
|
||||
this.loginForm.savedCredentials = savedCredentials;
|
||||
var jsonCredentials = JSON.stringify(savedCredentials);
|
||||
await configRepository.setString(
|
||||
'savedCredentials',
|
||||
jsonCredentials
|
||||
);
|
||||
new Noty({
|
||||
type: 'success',
|
||||
text: 'Account removed.'
|
||||
}).show();
|
||||
},
|
||||
|
||||
async login() {
|
||||
await webApiService.clearCookies();
|
||||
this.$refs.loginForm.validate((valid) => {
|
||||
if (valid && !this.loginForm.loading) {
|
||||
this.loginForm.loading = true;
|
||||
if (this.loginForm.endpoint) {
|
||||
API.endpointDomain = this.loginForm.endpoint;
|
||||
API.websocketDomain = this.loginForm.websocket;
|
||||
} else {
|
||||
API.endpointDomain = API.endpointDomainVrchat;
|
||||
API.websocketDomain = API.websocketDomainVrchat;
|
||||
}
|
||||
API.getConfig()
|
||||
.catch((err) => {
|
||||
this.loginForm.loading = false;
|
||||
throw err;
|
||||
})
|
||||
.then((args) => {
|
||||
if (
|
||||
this.loginForm.saveCredentials &&
|
||||
this.enablePrimaryPassword
|
||||
) {
|
||||
$app.$prompt(
|
||||
$t('prompt.primary_password.description'),
|
||||
$t('prompt.primary_password.header'),
|
||||
{
|
||||
inputType: 'password',
|
||||
inputPattern: /[\s\S]{1,32}/
|
||||
}
|
||||
)
|
||||
.then(({ value }) => {
|
||||
let saveCredential =
|
||||
this.loginForm.savedCredentials[
|
||||
Object.keys(
|
||||
this.loginForm
|
||||
.savedCredentials
|
||||
)[0]
|
||||
];
|
||||
security
|
||||
.decrypt(
|
||||
saveCredential.loginParmas
|
||||
.password,
|
||||
value
|
||||
)
|
||||
.then(() => {
|
||||
security
|
||||
.encrypt(
|
||||
this.loginForm.password,
|
||||
value
|
||||
)
|
||||
.then((pwd) => {
|
||||
API.login({
|
||||
username:
|
||||
this.loginForm
|
||||
.username,
|
||||
password:
|
||||
this.loginForm
|
||||
.password,
|
||||
endpoint:
|
||||
this.loginForm
|
||||
.endpoint,
|
||||
websocket:
|
||||
this.loginForm
|
||||
.websocket,
|
||||
saveCredentials:
|
||||
this.loginForm
|
||||
.saveCredentials,
|
||||
cipher: pwd
|
||||
}).then(() => {
|
||||
this.$refs.loginForm.resetFields();
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
this.loginForm.loading = false;
|
||||
});
|
||||
return args;
|
||||
}
|
||||
API.login({
|
||||
username: this.loginForm.username,
|
||||
password: this.loginForm.password,
|
||||
endpoint: this.loginForm.endpoint,
|
||||
websocket: this.loginForm.websocket,
|
||||
saveCredentials: this.loginForm.saveCredentials
|
||||
})
|
||||
.then(() => {
|
||||
this.$refs.loginForm.resetFields();
|
||||
})
|
||||
.finally(() => {
|
||||
this.loginForm.loading = false;
|
||||
});
|
||||
return args;
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
logout() {
|
||||
this.$confirm('Continue? Logout', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info',
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
API.logout();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
385
src/classes/apiRequestHandler.js
Normal file
@@ -0,0 +1,385 @@
|
||||
import Noty from 'noty';
|
||||
import { baseClass, $app, API, $t } from './baseClass.js';
|
||||
/* eslint-disable no-unused-vars */
|
||||
let webApiService = {};
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t, _webApiService) {
|
||||
super(_app, _API, _t);
|
||||
webApiService = _webApiService;
|
||||
}
|
||||
|
||||
init() {
|
||||
API.cachedConfig = {};
|
||||
API.pendingGetRequests = new Map();
|
||||
API.failedGetRequests = new Map();
|
||||
API.endpointDomainVrchat = 'https://api.vrchat.cloud/api/1';
|
||||
API.websocketDomainVrchat = 'wss://pipeline.vrchat.cloud';
|
||||
API.endpointDomain = 'https://api.vrchat.cloud/api/1';
|
||||
API.websocketDomain = 'wss://pipeline.vrchat.cloud';
|
||||
|
||||
API.call = function (endpoint, options) {
|
||||
var init = {
|
||||
url: `${API.endpointDomain}/${endpoint}`,
|
||||
method: 'GET',
|
||||
...options
|
||||
};
|
||||
var { params } = init;
|
||||
if (init.method === 'GET') {
|
||||
// don't retry recent 404/403
|
||||
if (this.failedGetRequests.has(endpoint)) {
|
||||
var lastRun = this.failedGetRequests.get(endpoint);
|
||||
if (lastRun >= Date.now() - 900000) {
|
||||
// 15mins
|
||||
throw new Error(
|
||||
`${$t('api.error.message.403_404_bailing_request')}, ${endpoint}`
|
||||
);
|
||||
}
|
||||
this.failedGetRequests.delete(endpoint);
|
||||
}
|
||||
// transform body to url
|
||||
if (params === Object(params)) {
|
||||
var url = new URL(init.url);
|
||||
var { searchParams } = url;
|
||||
for (var key in params) {
|
||||
searchParams.set(key, params[key]);
|
||||
}
|
||||
init.url = url.toString();
|
||||
}
|
||||
// merge requests
|
||||
var req = this.pendingGetRequests.get(init.url);
|
||||
if (typeof req !== 'undefined') {
|
||||
if (req.time >= Date.now() - 10000) {
|
||||
// 10s
|
||||
return req.req;
|
||||
}
|
||||
this.pendingGetRequests.delete(init.url);
|
||||
}
|
||||
} else if (
|
||||
init.uploadImage ||
|
||||
init.uploadFilePUT ||
|
||||
init.uploadImageLegacy
|
||||
) {
|
||||
// nothing
|
||||
} else {
|
||||
init.headers = {
|
||||
'Content-Type': 'application/json;charset=utf-8',
|
||||
...init.headers
|
||||
};
|
||||
init.body =
|
||||
params === Object(params) ? JSON.stringify(params) : '{}';
|
||||
}
|
||||
var req = webApiService
|
||||
.execute(init)
|
||||
.catch((err) => {
|
||||
this.$throw(0, err, endpoint);
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.data) {
|
||||
return response;
|
||||
}
|
||||
try {
|
||||
response.data = JSON.parse(response.data);
|
||||
if ($app.debugWebRequests) {
|
||||
console.log(init, response.data);
|
||||
}
|
||||
return response;
|
||||
} catch (e) {}
|
||||
if (response.status === 200) {
|
||||
this.$throw(
|
||||
0,
|
||||
$t('api.error.message.invalid_json_response'),
|
||||
endpoint
|
||||
);
|
||||
}
|
||||
if (
|
||||
response.status === 429 &&
|
||||
init.url.endsWith('/instances/groups')
|
||||
) {
|
||||
$app.nextGroupInstanceRefresh = 120; // 1min
|
||||
throw new Error(
|
||||
`${response.status}: rate limited ${endpoint}`
|
||||
);
|
||||
}
|
||||
if (response.status === 504 || response.status === 502) {
|
||||
// ignore expected API errors
|
||||
throw new Error(
|
||||
`${response.status}: ${response.data} ${endpoint}`
|
||||
);
|
||||
}
|
||||
this.$throw(response.status, endpoint);
|
||||
return {};
|
||||
})
|
||||
.then(({ data, status }) => {
|
||||
if (status === 200) {
|
||||
if (!data) {
|
||||
return data;
|
||||
}
|
||||
var text = '';
|
||||
if (data.success === Object(data.success)) {
|
||||
text = data.success.message;
|
||||
} else if (data.OK === String(data.OK)) {
|
||||
text = data.OK;
|
||||
}
|
||||
if (text) {
|
||||
new Noty({
|
||||
type: 'success',
|
||||
text: $app.escapeTag(text)
|
||||
}).show();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
if (
|
||||
status === 401 &&
|
||||
data.error.message === '"Missing Credentials"'
|
||||
) {
|
||||
this.$emit('AUTOLOGIN');
|
||||
throw new Error(
|
||||
`401 ${$t('api.error.message.missing_credentials')}`
|
||||
);
|
||||
}
|
||||
if (
|
||||
status === 401 &&
|
||||
data.error.message === '"Unauthorized"' &&
|
||||
endpoint !== 'auth/user'
|
||||
) {
|
||||
// trigger 2FA dialog
|
||||
if (!$app.twoFactorAuthDialogVisible) {
|
||||
$app.API.getCurrentUser();
|
||||
}
|
||||
throw new Error(`401 ${$t('api.status_code.401')}`);
|
||||
}
|
||||
if (status === 403 && endpoint === 'config') {
|
||||
$app.$alert(
|
||||
$t('api.error.message.vpn_in_use'),
|
||||
`403 ${$t('api.error.message.login_error')}`
|
||||
);
|
||||
this.logout();
|
||||
throw new Error(`403 ${endpoint}`);
|
||||
}
|
||||
if (
|
||||
init.method === 'GET' &&
|
||||
status === 404 &&
|
||||
endpoint.startsWith('avatars/')
|
||||
) {
|
||||
$app.$message({
|
||||
message: $t(
|
||||
'message.api_handler.avatar_private_or_deleted'
|
||||
),
|
||||
type: 'error'
|
||||
});
|
||||
$app.avatarDialog.visible = false;
|
||||
throw new Error(
|
||||
`404: ${data.error.message} ${endpoint}`
|
||||
);
|
||||
}
|
||||
if (
|
||||
status === 404 &&
|
||||
endpoint.endsWith('/persist/exists')
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
init.method === 'GET' &&
|
||||
(status === 404 || status === 403) &&
|
||||
!endpoint.startsWith('auth/user')
|
||||
) {
|
||||
this.failedGetRequests.set(endpoint, Date.now());
|
||||
}
|
||||
if (
|
||||
init.method === 'GET' &&
|
||||
status === 404 &&
|
||||
endpoint.startsWith('users/') &&
|
||||
endpoint.split('/').length - 1 === 1
|
||||
) {
|
||||
throw new Error(
|
||||
`404: ${data.error.message} ${endpoint}`
|
||||
);
|
||||
}
|
||||
if (
|
||||
status === 404 &&
|
||||
endpoint.startsWith('invite/') &&
|
||||
init.inviteId
|
||||
) {
|
||||
this.expireNotification(init.inviteId);
|
||||
}
|
||||
if (
|
||||
status === 403 &&
|
||||
endpoint.startsWith('invite/myself/to/')
|
||||
) {
|
||||
throw new Error(
|
||||
`403: ${data.error.message} ${endpoint}`
|
||||
);
|
||||
}
|
||||
if (data && data.error === Object(data.error)) {
|
||||
this.$throw(
|
||||
data.error.status_code || status,
|
||||
data.error.message,
|
||||
endpoint
|
||||
);
|
||||
} else if (data && typeof data.error === 'string') {
|
||||
this.$throw(
|
||||
data.status_code || status,
|
||||
data.error,
|
||||
endpoint
|
||||
);
|
||||
}
|
||||
this.$throw(status, data, endpoint);
|
||||
return data;
|
||||
});
|
||||
if (init.method === 'GET') {
|
||||
req.finally(() => {
|
||||
this.pendingGetRequests.delete(init.url);
|
||||
});
|
||||
this.pendingGetRequests.set(init.url, {
|
||||
req,
|
||||
time: Date.now()
|
||||
});
|
||||
}
|
||||
return req;
|
||||
};
|
||||
|
||||
// FIXME : extra를 없애줘
|
||||
API.$throw = function (code, error, endpoint) {
|
||||
var text = [];
|
||||
if (code > 0) {
|
||||
const status = this.statusCodes[code];
|
||||
if (typeof status === 'undefined') {
|
||||
text.push(`${code}`);
|
||||
} else {
|
||||
const codeText = $t(`api.status_code.${code}`);
|
||||
text.push(`${code} ${codeText}`);
|
||||
}
|
||||
}
|
||||
if (typeof error !== 'undefined') {
|
||||
text.push(
|
||||
`${$t('api.error.message.error_message')}: ${typeof error === 'string' ? error : JSON.stringify(error)}`
|
||||
);
|
||||
}
|
||||
if (typeof endpoint !== 'undefined') {
|
||||
text.push(
|
||||
`${$t('api.error.message.endpoint')}: "${typeof endpoint === 'string' ? endpoint : JSON.stringify(endpoint)}"`
|
||||
);
|
||||
}
|
||||
text = text.map((s) => $app.escapeTag(s)).join('<br>');
|
||||
if (text.length) {
|
||||
if (this.errorNoty) {
|
||||
this.errorNoty.close();
|
||||
}
|
||||
this.errorNoty = new Noty({
|
||||
type: 'error',
|
||||
text
|
||||
}).show();
|
||||
}
|
||||
throw new Error(text);
|
||||
};
|
||||
|
||||
API.$bulk = function (options, args) {
|
||||
if ('handle' in options) {
|
||||
options.handle.call(this, args, options);
|
||||
}
|
||||
if (
|
||||
args.json.length > 0 &&
|
||||
((options.params.offset += args.json.length),
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
options.N > 0
|
||||
? options.N > options.params.offset
|
||||
: options.N < 0
|
||||
? args.json.length
|
||||
: options.params.n === args.json.length)
|
||||
) {
|
||||
this.bulk(options);
|
||||
} else if ('done' in options) {
|
||||
options.done.call(this, true, options);
|
||||
}
|
||||
return args;
|
||||
};
|
||||
|
||||
API.bulk = function (options) {
|
||||
this[options.fn](options.params)
|
||||
.catch((err) => {
|
||||
if ('done' in options) {
|
||||
options.done.call(this, false, options);
|
||||
}
|
||||
throw err;
|
||||
})
|
||||
.then((args) => this.$bulk(options, args));
|
||||
};
|
||||
|
||||
API.statusCodes = {
|
||||
100: 'Continue',
|
||||
101: 'Switching Protocols',
|
||||
102: 'Processing',
|
||||
103: 'Early Hints',
|
||||
200: 'OK',
|
||||
201: 'Created',
|
||||
202: 'Accepted',
|
||||
203: 'Non-Authoritative Information',
|
||||
204: 'No Content',
|
||||
205: 'Reset Content',
|
||||
206: 'Partial Content',
|
||||
207: 'Multi-Status',
|
||||
208: 'Already Reported',
|
||||
226: 'IM Used',
|
||||
300: 'Multiple Choices',
|
||||
301: 'Moved Permanently',
|
||||
302: 'Found',
|
||||
303: 'See Other',
|
||||
304: 'Not Modified',
|
||||
305: 'Use Proxy',
|
||||
306: 'Switch Proxy',
|
||||
307: 'Temporary Redirect',
|
||||
308: 'Permanent Redirect',
|
||||
400: 'Bad Request',
|
||||
401: 'Unauthorized',
|
||||
402: 'Payment Required',
|
||||
403: 'Forbidden',
|
||||
404: 'Not Found',
|
||||
405: 'Method Not Allowed',
|
||||
406: 'Not Acceptable',
|
||||
407: 'Proxy Authentication Required',
|
||||
408: 'Request Timeout',
|
||||
409: 'Conflict',
|
||||
410: 'Gone',
|
||||
411: 'Length Required',
|
||||
412: 'Precondition Failed',
|
||||
413: 'Payload Too Large',
|
||||
414: 'URI Too Long',
|
||||
415: 'Unsupported Media Type',
|
||||
416: 'Range Not Satisfiable',
|
||||
417: 'Expectation Failed',
|
||||
418: "I'm a teapot",
|
||||
421: 'Misdirected Request',
|
||||
422: 'Unprocessable Entity',
|
||||
423: 'Locked',
|
||||
424: 'Failed Dependency',
|
||||
425: 'Too Early',
|
||||
426: 'Upgrade Required',
|
||||
428: 'Precondition Required',
|
||||
429: 'Too Many Requests',
|
||||
431: 'Request Header Fields Too Large',
|
||||
451: 'Unavailable For Legal Reasons',
|
||||
500: 'Internal Server Error',
|
||||
501: 'Not Implemented',
|
||||
502: 'Bad Gateway',
|
||||
503: 'Service Unavailable',
|
||||
504: 'Gateway Timeout',
|
||||
505: 'HTTP Version Not Supported',
|
||||
506: 'Variant Also Negotiates',
|
||||
507: 'Insufficient Storage',
|
||||
508: 'Loop Detected',
|
||||
510: 'Not Extended',
|
||||
511: 'Network Authentication Required',
|
||||
// CloudFlare Error
|
||||
520: 'Web server returns an unknown error',
|
||||
521: 'Web server is down',
|
||||
522: 'Connection timed out',
|
||||
523: 'Origin is unreachable',
|
||||
524: 'A timeout occurred',
|
||||
525: 'SSL handshake failed',
|
||||
526: 'Invalid SSL certificate',
|
||||
527: 'Railgun Listener to origin error'
|
||||
};
|
||||
}
|
||||
}
|
||||
28
src/classes/baseClass.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import $utils from './utils';
|
||||
/* eslint-disable no-unused-vars */
|
||||
let $app = {};
|
||||
let API = {};
|
||||
let $t = {};
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
class baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
$app = _app;
|
||||
API = _API;
|
||||
$t = _t;
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
updateRef(_app) {
|
||||
$app = _app;
|
||||
}
|
||||
|
||||
init() {}
|
||||
|
||||
_data = {};
|
||||
|
||||
_methods = {};
|
||||
}
|
||||
|
||||
export { baseClass, $app, API, $t, $utils };
|
||||
103
src/classes/booping.js
Normal file
@@ -0,0 +1,103 @@
|
||||
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
init() {
|
||||
/**
|
||||
* @params {{
|
||||
userId: string,
|
||||
emojiId: string
|
||||
}} params
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
API.sendBoop = function (params) {
|
||||
return this.call(`users/${params.userId}/boop`, {
|
||||
method: 'POST',
|
||||
params
|
||||
}).then((json) => {
|
||||
var args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
this.$emit('BOOP:SEND', args);
|
||||
return args;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
_data = {
|
||||
sendBoopDialog: {
|
||||
visible: false,
|
||||
userId: '',
|
||||
fileId: ''
|
||||
}
|
||||
};
|
||||
|
||||
_methods = {
|
||||
sendBoop() {
|
||||
var D = this.sendBoopDialog;
|
||||
this.dismissBoop(D.userId);
|
||||
var params = {
|
||||
userId: D.userId
|
||||
};
|
||||
if (D.fileId) {
|
||||
params.emojiId = D.fileId;
|
||||
}
|
||||
API.sendBoop(params);
|
||||
D.visible = false;
|
||||
},
|
||||
|
||||
dismissBoop(userId) {
|
||||
// JANK: This is a hack to remove boop notifications when responding
|
||||
var array = this.notificationTable.data;
|
||||
for (var i = array.length - 1; i >= 0; i--) {
|
||||
var ref = array[i];
|
||||
if (
|
||||
ref.type !== 'boop' ||
|
||||
ref.$isExpired ||
|
||||
ref.senderUserId !== userId
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
API.sendNotificationResponse({
|
||||
notificationId: ref.id,
|
||||
responseType: 'delete',
|
||||
responseData: ''
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
showSendBoopDialog(userId) {
|
||||
this.$nextTick(() =>
|
||||
$app.adjustDialogZ(this.$refs.sendBoopDialog.$el)
|
||||
);
|
||||
var D = this.sendBoopDialog;
|
||||
D.userId = userId;
|
||||
D.visible = true;
|
||||
if (this.emojiTable.length === 0 && API.currentUser.$isVRCPlus) {
|
||||
this.refreshEmojiTable();
|
||||
}
|
||||
},
|
||||
|
||||
getEmojiValue(emojiName) {
|
||||
if (!emojiName) {
|
||||
return '';
|
||||
}
|
||||
return `vrchat_${emojiName.replace(/ /g, '_').toLowerCase()}`;
|
||||
},
|
||||
|
||||
getEmojiName(emojiValue) {
|
||||
// uppercase first letter of each word
|
||||
if (!emojiValue) {
|
||||
return '';
|
||||
}
|
||||
return emojiValue
|
||||
.replace('vrchat_', '')
|
||||
.replace(/_/g, ' ')
|
||||
.replace(/\b\w/g, (l) => l.toUpperCase());
|
||||
}
|
||||
};
|
||||
}
|
||||
341
src/classes/currentUser.js
Normal file
@@ -0,0 +1,341 @@
|
||||
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
init() {
|
||||
API.currentUser = {
|
||||
$userColour: ''
|
||||
};
|
||||
|
||||
API.getCurrentUser = function () {
|
||||
return this.call('auth/user', {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
var args = {
|
||||
json,
|
||||
fromGetCurrentUser: true
|
||||
};
|
||||
if (
|
||||
json.requiresTwoFactorAuth &&
|
||||
json.requiresTwoFactorAuth.includes('emailOtp')
|
||||
) {
|
||||
this.$emit('USER:EMAILOTP', args);
|
||||
} else if (json.requiresTwoFactorAuth) {
|
||||
this.$emit('USER:2FA', args);
|
||||
} else {
|
||||
if ($app.debugCurrentUserDiff) {
|
||||
var ref = args.json;
|
||||
var $ref = this.currentUser;
|
||||
var props = {};
|
||||
for (var prop in $ref) {
|
||||
if ($ref[prop] !== Object($ref[prop])) {
|
||||
props[prop] = true;
|
||||
}
|
||||
}
|
||||
for (var prop in ref) {
|
||||
if (
|
||||
Array.isArray(ref[prop]) &&
|
||||
Array.isArray($ref[prop])
|
||||
) {
|
||||
if (!$app.arraysMatch(ref[prop], $ref[prop])) {
|
||||
props[prop] = true;
|
||||
}
|
||||
} else if (ref[prop] !== Object(ref[prop])) {
|
||||
props[prop] = true;
|
||||
}
|
||||
}
|
||||
var has = false;
|
||||
for (var prop in props) {
|
||||
var asis = $ref[prop];
|
||||
var tobe = ref[prop];
|
||||
if (asis === tobe) {
|
||||
delete props[prop];
|
||||
} else {
|
||||
if (
|
||||
prop.startsWith('$') ||
|
||||
prop === 'offlineFriends' ||
|
||||
prop === 'onlineFriends' ||
|
||||
prop === 'activeFriends'
|
||||
) {
|
||||
delete props[prop];
|
||||
continue;
|
||||
}
|
||||
props[prop] = [tobe, asis];
|
||||
has = true;
|
||||
}
|
||||
}
|
||||
if (has) {
|
||||
console.log('API.getCurrentUser diff', props);
|
||||
}
|
||||
}
|
||||
$app.nextCurrentUserRefresh = 420; // 7mins
|
||||
this.$emit('USER:CURRENT', args);
|
||||
}
|
||||
return args;
|
||||
});
|
||||
};
|
||||
|
||||
API.$on('USER:CURRENT', function (args) {
|
||||
var { json } = args;
|
||||
args.ref = this.applyCurrentUser(json);
|
||||
|
||||
// when isGameRunning use gameLog instead of API
|
||||
var $location = $app.parseLocation($app.lastLocation.location);
|
||||
var $travelingLocation = $app.parseLocation(
|
||||
$app.lastLocationDestination
|
||||
);
|
||||
var location = $app.lastLocation.location;
|
||||
var instanceId = $location.instanceId;
|
||||
var worldId = $location.worldId;
|
||||
var travelingToLocation = $app.lastLocationDestination;
|
||||
var travelingToWorld = $travelingLocation.worldId;
|
||||
var travelingToInstance = $travelingLocation.instanceId;
|
||||
if (!$app.isGameRunning && json.presence) {
|
||||
if ($app.isRealInstance(json.presence.world)) {
|
||||
location = `${json.presence.world}:${json.presence.instance}`;
|
||||
travelingToLocation = `${json.presence.travelingToWorld}:${json.presence.travelingToInstance}`;
|
||||
} else {
|
||||
location = json.presence.world;
|
||||
travelingToLocation = json.presence.travelingToWorld;
|
||||
}
|
||||
instanceId = json.presence.instance;
|
||||
worldId = json.presence.world;
|
||||
travelingToInstance = json.presence.travelingToInstance;
|
||||
travelingToWorld = json.presence.travelingToWorld;
|
||||
}
|
||||
|
||||
this.applyUser({
|
||||
allowAvatarCopying: json.allowAvatarCopying,
|
||||
badges: json.badges,
|
||||
bio: json.bio,
|
||||
bioLinks: json.bioLinks,
|
||||
currentAvatarImageUrl: json.currentAvatarImageUrl,
|
||||
currentAvatarTags: json.currentAvatarTags,
|
||||
currentAvatarThumbnailImageUrl:
|
||||
json.currentAvatarThumbnailImageUrl,
|
||||
date_joined: json.date_joined,
|
||||
developerType: json.developerType,
|
||||
displayName: json.displayName,
|
||||
friendKey: json.friendKey,
|
||||
// json.friendRequestStatus - missing from currentUser
|
||||
id: json.id,
|
||||
// instanceId - missing from currentUser
|
||||
isFriend: json.isFriend,
|
||||
last_activity: json.last_activity,
|
||||
last_login: json.last_login,
|
||||
last_mobile: json.last_mobile,
|
||||
last_platform: json.last_platform,
|
||||
// location - missing from currentUser
|
||||
// platform - missing from currentUser
|
||||
// note - missing from currentUser
|
||||
profilePicOverride: json.profilePicOverride,
|
||||
// profilePicOverrideThumbnail - missing from currentUser
|
||||
pronouns: json.pronouns,
|
||||
state: json.state,
|
||||
status: json.status,
|
||||
statusDescription: json.statusDescription,
|
||||
tags: json.tags,
|
||||
// travelingToInstance - missing from currentUser
|
||||
// travelingToLocation - missing from currentUser
|
||||
// travelingToWorld - missing from currentUser
|
||||
userIcon: json.userIcon,
|
||||
// worldId - missing from currentUser
|
||||
fallbackAvatar: json.fallbackAvatar,
|
||||
|
||||
// Location from gameLog/presence
|
||||
location,
|
||||
instanceId,
|
||||
worldId,
|
||||
travelingToLocation,
|
||||
travelingToInstance,
|
||||
travelingToWorld,
|
||||
|
||||
// set VRCX online/offline timers
|
||||
$online_for: this.currentUser.$online_for,
|
||||
$offline_for: this.currentUser.$offline_for,
|
||||
$location_at: this.currentUser.$location_at,
|
||||
$travelingToTime: this.currentUser.$travelingToTime
|
||||
});
|
||||
});
|
||||
|
||||
API.applyCurrentUser = function (json) {
|
||||
var ref = this.currentUser;
|
||||
if (this.isLoggedIn) {
|
||||
if (json.currentAvatar !== ref.currentAvatar) {
|
||||
$app.addAvatarToHistory(json.currentAvatar);
|
||||
}
|
||||
Object.assign(ref, json);
|
||||
if (ref.homeLocation !== ref.$homeLocation.tag) {
|
||||
ref.$homeLocation = $app.parseLocation(ref.homeLocation);
|
||||
// apply home location name to user dialog
|
||||
if (
|
||||
$app.userDialog.visible &&
|
||||
$app.userDialog.id === ref.id
|
||||
) {
|
||||
$app.getWorldName(API.currentUser.homeLocation).then(
|
||||
(worldName) => {
|
||||
$app.userDialog.$homeLocationName = worldName;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
ref.$isVRCPlus = ref.tags.includes('system_supporter');
|
||||
this.applyUserTrustLevel(ref);
|
||||
this.applyUserLanguage(ref);
|
||||
this.applyPresenceLocation(ref);
|
||||
this.applyQueuedInstance(ref.queuedInstance);
|
||||
this.applyPresenceGroups(ref);
|
||||
} else {
|
||||
ref = {
|
||||
acceptedPrivacyVersion: 0,
|
||||
acceptedTOSVersion: 0,
|
||||
accountDeletionDate: null,
|
||||
accountDeletionLog: null,
|
||||
activeFriends: [],
|
||||
ageVerificationStatus: '',
|
||||
ageVerified: false,
|
||||
allowAvatarCopying: false,
|
||||
badges: [],
|
||||
bio: '',
|
||||
bioLinks: [],
|
||||
currentAvatar: '',
|
||||
currentAvatarAssetUrl: '',
|
||||
currentAvatarImageUrl: '',
|
||||
currentAvatarTags: [],
|
||||
currentAvatarThumbnailImageUrl: '',
|
||||
date_joined: '',
|
||||
developerType: '',
|
||||
displayName: '',
|
||||
emailVerified: false,
|
||||
fallbackAvatar: '',
|
||||
friendGroupNames: [],
|
||||
friendKey: '',
|
||||
friends: [],
|
||||
googleId: '',
|
||||
hasBirthday: false,
|
||||
hasEmail: false,
|
||||
hasLoggedInFromClient: false,
|
||||
hasPendingEmail: false,
|
||||
hideContentFilterSettings: false,
|
||||
homeLocation: '',
|
||||
id: '',
|
||||
isBoopingEnabled: false,
|
||||
isFriend: false,
|
||||
last_activity: '',
|
||||
last_login: '',
|
||||
last_mobile: null,
|
||||
last_platform: '',
|
||||
obfuscatedEmail: '',
|
||||
obfuscatedPendingEmail: '',
|
||||
oculusId: '',
|
||||
offlineFriends: [],
|
||||
onlineFriends: [],
|
||||
pastDisplayNames: [],
|
||||
picoId: '',
|
||||
presence: {
|
||||
avatarThumbnail: '',
|
||||
currentAvatarTags: '',
|
||||
displayName: '',
|
||||
groups: [],
|
||||
id: '',
|
||||
instance: '',
|
||||
instanceType: '',
|
||||
platform: '',
|
||||
profilePicOverride: '',
|
||||
status: '',
|
||||
travelingToInstance: '',
|
||||
travelingToWorld: '',
|
||||
userIcon: '',
|
||||
world: '',
|
||||
...json.presence
|
||||
},
|
||||
profilePicOverride: '',
|
||||
pronouns: '',
|
||||
queuedInstance: '',
|
||||
state: '',
|
||||
status: '',
|
||||
statusDescription: '',
|
||||
statusFirstTime: false,
|
||||
statusHistory: [],
|
||||
steamDetails: {},
|
||||
steamId: '',
|
||||
tags: [],
|
||||
twoFactorAuthEnabled: false,
|
||||
twoFactorAuthEnabledDate: null,
|
||||
unsubscribe: false,
|
||||
updated_at: '',
|
||||
userIcon: '',
|
||||
userLanguage: '',
|
||||
userLanguageCode: '',
|
||||
username: '',
|
||||
viveId: '',
|
||||
// VRCX
|
||||
$online_for: Date.now(),
|
||||
$offline_for: '',
|
||||
$location_at: Date.now(),
|
||||
$travelingToTime: Date.now(),
|
||||
$homeLocation: {},
|
||||
$isVRCPlus: false,
|
||||
$isModerator: false,
|
||||
$isTroll: false,
|
||||
$isProbableTroll: false,
|
||||
$trustLevel: 'Visitor',
|
||||
$trustClass: 'x-tag-untrusted',
|
||||
$userColour: '',
|
||||
$trustSortNum: 1,
|
||||
$languages: [],
|
||||
$locationTag: '',
|
||||
$travelingToLocation: '',
|
||||
$vrchatcredits: null,
|
||||
...json
|
||||
};
|
||||
ref.$homeLocation = $app.parseLocation(ref.homeLocation);
|
||||
ref.$isVRCPlus = ref.tags.includes('system_supporter');
|
||||
this.applyUserTrustLevel(ref);
|
||||
this.applyUserLanguage(ref);
|
||||
this.applyPresenceLocation(ref);
|
||||
this.applyPresenceGroups(ref);
|
||||
this.currentUser = ref;
|
||||
this.isLoggedIn = true;
|
||||
this.$emit('LOGIN', {
|
||||
json,
|
||||
ref
|
||||
});
|
||||
}
|
||||
return ref;
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* status: 'active' | 'offline' | 'busy' | 'ask me' | 'join me',
|
||||
* statusDescription: string
|
||||
* }} SaveCurrentUserParameters
|
||||
*/
|
||||
|
||||
/**
|
||||
* Updates current user's status.
|
||||
* @param params {SaveCurrentUserParameters} new status to be set
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
API.saveCurrentUser = function (params) {
|
||||
return this.call(`users/${this.currentUser.id}`, {
|
||||
method: 'PUT',
|
||||
params
|
||||
}).then((json) => {
|
||||
var args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
this.$emit('USER:CURRENT:SAVE', args);
|
||||
return args;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
_data = {};
|
||||
|
||||
_methods = {};
|
||||
}
|
||||
285
src/classes/discordRpc.js
Normal file
@@ -0,0 +1,285 @@
|
||||
import configRepository from '../repository/config.js';
|
||||
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
_data = {
|
||||
isDiscordActive: false,
|
||||
discordActive: false,
|
||||
discordInstance: true,
|
||||
discordJoinButton: false,
|
||||
discordHideInvite: true,
|
||||
discordHideImage: false
|
||||
};
|
||||
|
||||
_methods = {
|
||||
updateDiscord() {
|
||||
var currentLocation = this.lastLocation.location;
|
||||
var timeStamp = this.lastLocation.date;
|
||||
if (this.lastLocation.location === 'traveling') {
|
||||
currentLocation = this.lastLocationDestination;
|
||||
timeStamp = this.lastLocationDestinationTime;
|
||||
}
|
||||
if (
|
||||
!this.discordActive ||
|
||||
(!this.isGameRunning && !this.gameLogDisabled) ||
|
||||
(!currentLocation && !this.lastLocation$.tag)
|
||||
) {
|
||||
this.setDiscordActive(false);
|
||||
return;
|
||||
}
|
||||
this.setDiscordActive(true);
|
||||
var L = this.lastLocation$;
|
||||
if (currentLocation !== this.lastLocation$.tag) {
|
||||
Discord.SetTimestamps(timeStamp, 0);
|
||||
L = $app.parseLocation(currentLocation);
|
||||
L.worldName = '';
|
||||
L.thumbnailImageUrl = '';
|
||||
L.worldCapacity = 0;
|
||||
L.joinUrl = '';
|
||||
L.accessName = '';
|
||||
if (L.worldId) {
|
||||
var ref = API.cachedWorlds.get(L.worldId);
|
||||
if (ref) {
|
||||
L.worldName = ref.name;
|
||||
L.thumbnailImageUrl = ref.thumbnailImageUrl;
|
||||
L.worldCapacity = ref.capacity;
|
||||
} else {
|
||||
API.getWorld({
|
||||
worldId: L.worldId
|
||||
}).then((args) => {
|
||||
L.worldName = args.ref.name;
|
||||
L.thumbnailImageUrl = args.ref.thumbnailImageUrl;
|
||||
L.worldCapacity = args.ref.capacity;
|
||||
return args;
|
||||
});
|
||||
}
|
||||
if (this.isGameNoVR) {
|
||||
var platform = 'Desktop';
|
||||
} else {
|
||||
var platform = 'VR';
|
||||
}
|
||||
var groupAccessType = '';
|
||||
if (L.groupAccessType) {
|
||||
if (L.groupAccessType === 'public') {
|
||||
groupAccessType = 'Public';
|
||||
} else if (L.groupAccessType === 'plus') {
|
||||
groupAccessType = 'Plus';
|
||||
}
|
||||
}
|
||||
switch (L.accessType) {
|
||||
case 'public':
|
||||
L.joinUrl = this.getLaunchURL(L);
|
||||
L.accessName = `Public #${L.instanceName} (${platform})`;
|
||||
break;
|
||||
case 'invite+':
|
||||
L.accessName = `Invite+ #${L.instanceName} (${platform})`;
|
||||
break;
|
||||
case 'invite':
|
||||
L.accessName = `Invite #${L.instanceName} (${platform})`;
|
||||
break;
|
||||
case 'friends':
|
||||
L.accessName = `Friends #${L.instanceName} (${platform})`;
|
||||
break;
|
||||
case 'friends+':
|
||||
L.accessName = `Friends+ #${L.instanceName} (${platform})`;
|
||||
break;
|
||||
case 'group':
|
||||
L.accessName = `Group #${L.instanceName} (${platform})`;
|
||||
this.getGroupName(L.groupId).then((groupName) => {
|
||||
if (groupName) {
|
||||
L.accessName = `Group${groupAccessType}(${groupName}) #${L.instanceName} (${platform})`;
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.lastLocation$ = L;
|
||||
}
|
||||
var hidePrivate = false;
|
||||
if (
|
||||
this.discordHideInvite &&
|
||||
(L.accessType === 'invite' ||
|
||||
L.accessType === 'invite+' ||
|
||||
L.groupAccessType === 'members')
|
||||
) {
|
||||
hidePrivate = true;
|
||||
}
|
||||
switch (API.currentUser.status) {
|
||||
case 'active':
|
||||
L.statusName = 'Online';
|
||||
L.statusImage = 'active';
|
||||
break;
|
||||
case 'join me':
|
||||
L.statusName = 'Join Me';
|
||||
L.statusImage = 'joinme';
|
||||
break;
|
||||
case 'ask me':
|
||||
L.statusName = 'Ask Me';
|
||||
L.statusImage = 'askme';
|
||||
if (this.discordHideInvite) {
|
||||
hidePrivate = true;
|
||||
}
|
||||
break;
|
||||
case 'busy':
|
||||
L.statusName = 'Do Not Disturb';
|
||||
L.statusImage = 'busy';
|
||||
hidePrivate = true;
|
||||
break;
|
||||
}
|
||||
var appId = '883308884863901717';
|
||||
var bigIcon = 'vrchat';
|
||||
var partyId = `${L.worldId}:${L.instanceName}`;
|
||||
var partySize = this.lastLocation.playerList.size;
|
||||
var partyMaxSize = L.worldCapacity;
|
||||
if (partySize > partyMaxSize) {
|
||||
partyMaxSize = partySize;
|
||||
}
|
||||
var buttonText = 'Join';
|
||||
var buttonUrl = L.joinUrl;
|
||||
if (!this.discordJoinButton) {
|
||||
buttonText = '';
|
||||
buttonUrl = '';
|
||||
}
|
||||
if (!this.discordInstance) {
|
||||
partySize = 0;
|
||||
partyMaxSize = 0;
|
||||
}
|
||||
if (hidePrivate) {
|
||||
partyId = '';
|
||||
partySize = 0;
|
||||
partyMaxSize = 0;
|
||||
buttonText = '';
|
||||
buttonUrl = '';
|
||||
} else if (this.isRpcWorld(L.tag)) {
|
||||
// custom world rpc
|
||||
if (
|
||||
L.worldId === 'wrld_f20326da-f1ac-45fc-a062-609723b097b1' ||
|
||||
L.worldId === 'wrld_10e5e467-fc65-42ed-8957-f02cace1398c' ||
|
||||
L.worldId === 'wrld_04899f23-e182-4a8d-b2c7-2c74c7c15534'
|
||||
) {
|
||||
appId = '784094509008551956';
|
||||
bigIcon = 'pypy';
|
||||
} else if (
|
||||
L.worldId === 'wrld_42377cf1-c54f-45ed-8996-5875b0573a83' ||
|
||||
L.worldId === 'wrld_dd6d2888-dbdc-47c2-bc98-3d631b2acd7c'
|
||||
) {
|
||||
appId = '846232616054030376';
|
||||
bigIcon = 'vr_dancing';
|
||||
} else if (
|
||||
L.worldId === 'wrld_52bdcdab-11cd-4325-9655-0fb120846945' ||
|
||||
L.worldId === 'wrld_2d40da63-8f1f-4011-8a9e-414eb8530acd'
|
||||
) {
|
||||
appId = '939473404808007731';
|
||||
bigIcon = 'zuwa_zuwa_dance';
|
||||
} else if (
|
||||
L.worldId === 'wrld_74970324-58e8-4239-a17b-2c59dfdf00db' ||
|
||||
L.worldId === 'wrld_db9d878f-6e76-4776-8bf2-15bcdd7fc445' ||
|
||||
L.worldId === 'wrld_435bbf25-f34f-4b8b-82c6-cd809057eb8e' ||
|
||||
L.worldId === 'wrld_f767d1c8-b249-4ecc-a56f-614e433682c8'
|
||||
) {
|
||||
appId = '968292722391785512';
|
||||
bigIcon = 'ls_media';
|
||||
} else if (
|
||||
L.worldId === 'wrld_266523e8-9161-40da-acd0-6bd82e075833'
|
||||
) {
|
||||
appId = '1095440531821170820';
|
||||
bigIcon = 'movie_and_chill';
|
||||
}
|
||||
if (this.nowPlaying.name) {
|
||||
L.worldName = this.nowPlaying.name;
|
||||
}
|
||||
if (this.nowPlaying.playing) {
|
||||
Discord.SetTimestamps(
|
||||
Date.now(),
|
||||
(this.nowPlaying.startTime -
|
||||
this.nowPlaying.offset +
|
||||
this.nowPlaying.length) *
|
||||
1000
|
||||
);
|
||||
}
|
||||
} else if (!this.discordHideImage && L.thumbnailImageUrl) {
|
||||
bigIcon = L.thumbnailImageUrl;
|
||||
}
|
||||
Discord.SetAssets(
|
||||
bigIcon, // big icon
|
||||
'Powered by VRCX', // big icon hover text
|
||||
L.statusImage, // small icon
|
||||
L.statusName, // small icon hover text
|
||||
partyId, // party id
|
||||
partySize, // party size
|
||||
partyMaxSize, // party max size
|
||||
buttonText, // button text
|
||||
buttonUrl, // button url
|
||||
appId // app id
|
||||
);
|
||||
// NOTE
|
||||
// 글자 수가 짧으면 업데이트가 안된다..
|
||||
if (L.worldName.length < 2) {
|
||||
L.worldName += '\uFFA0'.repeat(2 - L.worldName.length);
|
||||
}
|
||||
if (hidePrivate) {
|
||||
Discord.SetText('Private', '');
|
||||
Discord.SetTimestamps(0, 0);
|
||||
} else if (this.discordInstance) {
|
||||
Discord.SetText(L.worldName, L.accessName);
|
||||
} else {
|
||||
Discord.SetText(L.worldName, '');
|
||||
}
|
||||
},
|
||||
|
||||
async setDiscordActive(active) {
|
||||
if (active !== this.isDiscordActive) {
|
||||
this.isDiscordActive = await Discord.SetActive(active);
|
||||
}
|
||||
},
|
||||
|
||||
async saveDiscordOption(configLabel = '') {
|
||||
if (configLabel === 'discordActive') {
|
||||
this.discordActive = !this.discordActive;
|
||||
await configRepository.setBool(
|
||||
'discordActive',
|
||||
this.discordActive
|
||||
);
|
||||
}
|
||||
|
||||
if (configLabel === 'discordInstance') {
|
||||
this.discordInstance = !this.discordInstance;
|
||||
await configRepository.setBool(
|
||||
'discordInstance',
|
||||
this.discordInstance
|
||||
);
|
||||
}
|
||||
|
||||
if (configLabel === 'discordJoinButton') {
|
||||
this.discordJoinButton = !this.discordJoinButton;
|
||||
await configRepository.setBool(
|
||||
'discordJoinButton',
|
||||
this.discordJoinButton
|
||||
);
|
||||
}
|
||||
|
||||
if (configLabel === 'discordHideInvite') {
|
||||
this.discordHideInvite = !this.discordHideInvite;
|
||||
await configRepository.setBool(
|
||||
'discordHideInvite',
|
||||
this.discordHideInvite
|
||||
);
|
||||
}
|
||||
if (configLabel === 'discordHideImage') {
|
||||
this.discordHideImage = !this.discordHideImage;
|
||||
await configRepository.setBool(
|
||||
'discordHideImage',
|
||||
this.discordHideImage
|
||||
);
|
||||
}
|
||||
|
||||
this.lastLocation$.tag = '';
|
||||
this.nextDiscordUpdate = 3;
|
||||
this.updateDiscord();
|
||||
}
|
||||
};
|
||||
}
|
||||
179
src/classes/feed.js
Normal file
@@ -0,0 +1,179 @@
|
||||
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
|
||||
import configRepository from '../repository/config.js';
|
||||
import database from '../repository/database.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
_data = {
|
||||
feedTable: {
|
||||
data: [],
|
||||
search: '',
|
||||
vip: false,
|
||||
loading: false,
|
||||
filter: [],
|
||||
tableProps: {
|
||||
stripe: true,
|
||||
size: 'mini',
|
||||
defaultSort: {
|
||||
prop: 'created_at',
|
||||
order: 'descending'
|
||||
}
|
||||
},
|
||||
pageSize: 15,
|
||||
paginationProps: {
|
||||
small: true,
|
||||
layout: 'sizes,prev,pager,next,total',
|
||||
pageSizes: [10, 15, 25, 50, 100]
|
||||
}
|
||||
},
|
||||
|
||||
feedSessionTable: []
|
||||
};
|
||||
|
||||
_methods = {
|
||||
feedSearch(row) {
|
||||
var value = this.feedTable.search.toUpperCase();
|
||||
if (!value) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
value.startsWith('wrld_') &&
|
||||
String(row.location).toUpperCase().includes(value)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
switch (row.type) {
|
||||
case 'GPS':
|
||||
if (String(row.displayName).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
if (String(row.worldName).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case 'Online':
|
||||
if (String(row.displayName).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
if (String(row.worldName).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case 'Offline':
|
||||
if (String(row.displayName).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
if (String(row.worldName).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case 'Status':
|
||||
if (String(row.displayName).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
if (String(row.status).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
String(row.statusDescription)
|
||||
.toUpperCase()
|
||||
.includes(value)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case 'Avatar':
|
||||
if (String(row.displayName).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
if (String(row.avatarName).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case 'Bio':
|
||||
if (String(row.displayName).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
if (String(row.bio).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
if (String(row.previousBio).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
async feedTableLookup() {
|
||||
await configRepository.setString(
|
||||
'VRCX_feedTableFilters',
|
||||
JSON.stringify(this.feedTable.filter)
|
||||
);
|
||||
await configRepository.setBool(
|
||||
'VRCX_feedTableVIPFilter',
|
||||
this.feedTable.vip
|
||||
);
|
||||
this.feedTable.loading = true;
|
||||
var vipList = [];
|
||||
if (this.feedTable.vip) {
|
||||
vipList = Array.from(this.localFavoriteFriends.values());
|
||||
}
|
||||
this.feedTable.data = await database.lookupFeedDatabase(
|
||||
this.feedTable.search,
|
||||
this.feedTable.filter,
|
||||
vipList
|
||||
);
|
||||
this.feedTable.loading = false;
|
||||
},
|
||||
|
||||
addFeed(feed) {
|
||||
this.queueFeedNoty(feed);
|
||||
this.feedSessionTable.push(feed);
|
||||
this.updateSharedFeed(false);
|
||||
if (
|
||||
this.feedTable.filter.length > 0 &&
|
||||
!this.feedTable.filter.includes(feed.type)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
this.feedTable.vip &&
|
||||
!this.localFavoriteFriends.has(feed.userId)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (!this.feedSearch(feed)) {
|
||||
return;
|
||||
}
|
||||
this.feedTable.data.push(feed);
|
||||
this.sweepFeed();
|
||||
this.notifyMenu('feed');
|
||||
},
|
||||
|
||||
sweepFeed() {
|
||||
var { data } = this.feedTable;
|
||||
var j = data.length;
|
||||
if (j > this.maxTableSize) {
|
||||
data.splice(0, j - this.maxTableSize);
|
||||
}
|
||||
|
||||
var date = new Date();
|
||||
date.setDate(date.getDate() - 1); // 24 hour limit
|
||||
var limit = date.toJSON();
|
||||
var i = 0;
|
||||
var k = this.feedSessionTable.length;
|
||||
while (i < k && this.feedSessionTable[i].created_at < limit) {
|
||||
++i;
|
||||
}
|
||||
if (i === k) {
|
||||
this.feedSessionTable = [];
|
||||
} else if (i) {
|
||||
this.feedSessionTable.splice(0, i);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
1129
src/classes/gameLog.js
Normal file
1545
src/classes/gameRealtimeLogging.js
Normal file
3569
src/classes/groups.js
Normal file
162
src/classes/languages.js
Normal file
@@ -0,0 +1,162 @@
|
||||
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
init() {
|
||||
API.$on('CONFIG', function (args) {
|
||||
var languages =
|
||||
args.ref?.constants?.LANGUAGE?.SPOKEN_LANGUAGE_OPTIONS;
|
||||
if (!languages) {
|
||||
return;
|
||||
}
|
||||
$app.subsetOfLanguages = languages;
|
||||
var data = [];
|
||||
for (var key in languages) {
|
||||
var value = languages[key];
|
||||
data.push({
|
||||
key,
|
||||
value
|
||||
});
|
||||
}
|
||||
$app.languageDialog.languages = data;
|
||||
});
|
||||
|
||||
API.$on('LOGOUT', function () {
|
||||
$app.languageDialog.visible = false;
|
||||
});
|
||||
}
|
||||
|
||||
_data = {
|
||||
// vrchat to famfamfam language mappings
|
||||
languageMappings: {
|
||||
eng: 'us',
|
||||
kor: 'kr',
|
||||
rus: 'ru',
|
||||
spa: 'es',
|
||||
por: 'pt',
|
||||
zho: 'cn',
|
||||
deu: 'de',
|
||||
jpn: 'jp',
|
||||
fra: 'fr',
|
||||
swe: 'se',
|
||||
nld: 'nl',
|
||||
pol: 'pl',
|
||||
dan: 'dk',
|
||||
nor: 'no',
|
||||
ita: 'it',
|
||||
tha: 'th',
|
||||
fin: 'fi',
|
||||
hun: 'hu',
|
||||
ces: 'cz',
|
||||
tur: 'tr',
|
||||
ara: 'ae',
|
||||
ron: 'ro',
|
||||
vie: 'vn',
|
||||
ukr: 'ua',
|
||||
ase: 'us',
|
||||
bfi: 'gb',
|
||||
dse: 'nl',
|
||||
fsl: 'fr',
|
||||
jsl: 'jp',
|
||||
kvk: 'kr',
|
||||
|
||||
mlt: 'mt',
|
||||
ind: 'id',
|
||||
hrv: 'hr',
|
||||
heb: 'he',
|
||||
afr: 'af',
|
||||
ben: 'be',
|
||||
bul: 'bg',
|
||||
cmn: 'cn',
|
||||
cym: 'cy',
|
||||
ell: 'el',
|
||||
est: 'et',
|
||||
fil: 'ph',
|
||||
gla: 'gd',
|
||||
gle: 'ga',
|
||||
hin: 'hi',
|
||||
hmn: 'cn',
|
||||
hye: 'hy',
|
||||
isl: 'is',
|
||||
lav: 'lv',
|
||||
lit: 'lt',
|
||||
ltz: 'lb',
|
||||
mar: 'hi',
|
||||
mkd: 'mk',
|
||||
msa: 'my',
|
||||
sco: 'gd',
|
||||
slk: 'sk',
|
||||
slv: 'sl',
|
||||
tel: 'hi',
|
||||
mri: 'nz',
|
||||
wuu: 'cn',
|
||||
yue: 'cn',
|
||||
tws: 'cn',
|
||||
asf: 'au',
|
||||
nzs: 'nz',
|
||||
gsg: 'de',
|
||||
epo: 'eo',
|
||||
tok: 'tok'
|
||||
},
|
||||
|
||||
subsetOfLanguages: [],
|
||||
|
||||
languageDialog: {
|
||||
visible: false,
|
||||
loading: false,
|
||||
languageChoice: false,
|
||||
languageValue: '',
|
||||
languages: []
|
||||
}
|
||||
};
|
||||
|
||||
_methods = {
|
||||
languageClass(language) {
|
||||
var style = {};
|
||||
var mapping = this.languageMappings[language];
|
||||
if (typeof mapping !== 'undefined') {
|
||||
style[mapping] = true;
|
||||
} else {
|
||||
style.unknown = true;
|
||||
}
|
||||
return style;
|
||||
},
|
||||
|
||||
addUserLanguage(language) {
|
||||
if (language !== String(language)) {
|
||||
return;
|
||||
}
|
||||
var D = this.languageDialog;
|
||||
D.loading = true;
|
||||
API.addUserTags({
|
||||
tags: [`language_${language}`]
|
||||
}).finally(function () {
|
||||
D.loading = false;
|
||||
});
|
||||
},
|
||||
|
||||
removeUserLanguage(language) {
|
||||
if (language !== String(language)) {
|
||||
return;
|
||||
}
|
||||
var D = this.languageDialog;
|
||||
D.loading = true;
|
||||
API.removeUserTags({
|
||||
tags: [`language_${language}`]
|
||||
}).finally(function () {
|
||||
D.loading = false;
|
||||
});
|
||||
},
|
||||
|
||||
showLanguageDialog() {
|
||||
this.$nextTick(() =>
|
||||
$app.adjustDialogZ(this.$refs.languageDialog.$el)
|
||||
);
|
||||
var D = this.languageDialog;
|
||||
D.visible = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
147
src/classes/memos.js
Normal file
@@ -0,0 +1,147 @@
|
||||
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
|
||||
import database from '../repository/database.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
init() {}
|
||||
|
||||
_data = {
|
||||
hideUserMemos: false
|
||||
};
|
||||
|
||||
_methods = {
|
||||
async migrateMemos() {
|
||||
var json = JSON.parse(await VRCXStorage.GetAll());
|
||||
database.begin();
|
||||
for (var line in json) {
|
||||
if (line.substring(0, 8) === 'memo_usr') {
|
||||
var userId = line.substring(5);
|
||||
var memo = json[line];
|
||||
if (memo) {
|
||||
await this.saveUserMemo(userId, memo);
|
||||
VRCXStorage.Remove(`memo_${userId}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
database.commit();
|
||||
},
|
||||
|
||||
onUserMemoChange() {
|
||||
var D = this.userDialog;
|
||||
this.saveUserMemo(D.id, D.memo);
|
||||
},
|
||||
|
||||
async getUserMemo(userId) {
|
||||
try {
|
||||
return await database.getUserMemo(userId);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return {
|
||||
userId: '',
|
||||
editedAt: '',
|
||||
memo: ''
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
saveUserMemo(id, memo) {
|
||||
if (memo) {
|
||||
database.setUserMemo({
|
||||
userId: id,
|
||||
editedAt: new Date().toJSON(),
|
||||
memo
|
||||
});
|
||||
} else {
|
||||
database.deleteUserMemo(id);
|
||||
}
|
||||
var ref = this.friends.get(id);
|
||||
if (ref) {
|
||||
ref.memo = String(memo || '');
|
||||
if (memo) {
|
||||
var array = memo.split('\n');
|
||||
ref.$nickName = array[0];
|
||||
} else {
|
||||
ref.$nickName = '';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async getAllUserMemos() {
|
||||
var memos = await database.getAllUserMemos();
|
||||
memos.forEach((memo) => {
|
||||
var ref = $app.friends.get(memo.userId);
|
||||
if (typeof ref !== 'undefined') {
|
||||
ref.memo = memo.memo;
|
||||
ref.$nickName = '';
|
||||
if (memo.memo) {
|
||||
var array = memo.memo.split('\n');
|
||||
ref.$nickName = array[0];
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onWorldMemoChange() {
|
||||
var D = this.worldDialog;
|
||||
this.saveWorldMemo(D.id, D.memo);
|
||||
},
|
||||
|
||||
async getWorldMemo(worldId) {
|
||||
try {
|
||||
return await database.getWorldMemo(worldId);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return {
|
||||
worldId: '',
|
||||
editedAt: '',
|
||||
memo: ''
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
saveWorldMemo(worldId, memo) {
|
||||
if (memo) {
|
||||
database.setWorldMemo({
|
||||
worldId,
|
||||
editedAt: new Date().toJSON(),
|
||||
memo
|
||||
});
|
||||
} else {
|
||||
database.deleteWorldMemo(worldId);
|
||||
}
|
||||
},
|
||||
|
||||
onAvatarMemoChange() {
|
||||
var D = this.avatarDialog;
|
||||
this.saveAvatarMemo(D.id, D.memo);
|
||||
},
|
||||
|
||||
async getAvatarMemo(avatarId) {
|
||||
try {
|
||||
return await database.getAvatarMemoDB(avatarId);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return {
|
||||
avatarId: '',
|
||||
editedAt: '',
|
||||
memo: ''
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
saveAvatarMemo(avatarId, memo) {
|
||||
if (memo) {
|
||||
database.setAvatarMemo({
|
||||
avatarId,
|
||||
editedAt: new Date().toJSON(),
|
||||
memo
|
||||
});
|
||||
} else {
|
||||
database.deleteAvatarMemo(avatarId);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
809
src/classes/prompts.js
Normal file
@@ -0,0 +1,809 @@
|
||||
import * as workerTimers from 'worker-timers';
|
||||
import configRepository from '../repository/config.js';
|
||||
import database from '../repository/database.js';
|
||||
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
_methods = {
|
||||
promptTOTP() {
|
||||
if (this.twoFactorAuthDialogVisible) {
|
||||
return;
|
||||
}
|
||||
AppApi.FlashWindow();
|
||||
this.twoFactorAuthDialogVisible = true;
|
||||
this.$prompt(
|
||||
$t('prompt.totp.description'),
|
||||
$t('prompt.totp.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
cancelButtonText: $t('prompt.totp.use_otp'),
|
||||
confirmButtonText: $t('prompt.totp.verify'),
|
||||
inputPlaceholder: $t('prompt.totp.input_placeholder'),
|
||||
inputPattern: /^[0-9]{6}$/,
|
||||
inputErrorMessage: $t('prompt.totp.input_error'),
|
||||
callback: (action, instance) => {
|
||||
if (action === 'confirm') {
|
||||
API.verifyTOTP({
|
||||
code: instance.inputValue.trim()
|
||||
})
|
||||
.catch((err) => {
|
||||
this.promptTOTP();
|
||||
throw err;
|
||||
})
|
||||
.then((args) => {
|
||||
API.getCurrentUser();
|
||||
return args;
|
||||
});
|
||||
} else if (action === 'cancel') {
|
||||
this.promptOTP();
|
||||
}
|
||||
},
|
||||
beforeClose: (action, instance, done) => {
|
||||
this.twoFactorAuthDialogVisible = false;
|
||||
done();
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptOTP() {
|
||||
if (this.twoFactorAuthDialogVisible) {
|
||||
return;
|
||||
}
|
||||
this.twoFactorAuthDialogVisible = true;
|
||||
this.$prompt(
|
||||
$t('prompt.otp.description'),
|
||||
$t('prompt.otp.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
cancelButtonText: $t('prompt.otp.use_totp'),
|
||||
confirmButtonText: $t('prompt.otp.verify'),
|
||||
inputPlaceholder: $t('prompt.otp.input_placeholder'),
|
||||
inputPattern: /^[a-z0-9]{4}-[a-z0-9]{4}$/,
|
||||
inputErrorMessage: $t('prompt.otp.input_error'),
|
||||
callback: (action, instance) => {
|
||||
if (action === 'confirm') {
|
||||
API.verifyOTP({
|
||||
code: instance.inputValue.trim()
|
||||
})
|
||||
.catch((err) => {
|
||||
this.promptOTP();
|
||||
throw err;
|
||||
})
|
||||
.then((args) => {
|
||||
API.getCurrentUser();
|
||||
return args;
|
||||
});
|
||||
} else if (action === 'cancel') {
|
||||
this.promptTOTP();
|
||||
}
|
||||
},
|
||||
beforeClose: (action, instance, done) => {
|
||||
this.twoFactorAuthDialogVisible = false;
|
||||
done();
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptEmailOTP() {
|
||||
if (this.twoFactorAuthDialogVisible) {
|
||||
return;
|
||||
}
|
||||
AppApi.FlashWindow();
|
||||
this.twoFactorAuthDialogVisible = true;
|
||||
this.$prompt(
|
||||
$t('prompt.email_otp.description'),
|
||||
$t('prompt.email_otp.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
cancelButtonText: $t('prompt.email_otp.resend'),
|
||||
confirmButtonText: $t('prompt.email_otp.verify'),
|
||||
inputPlaceholder: $t('prompt.email_otp.input_placeholder'),
|
||||
inputPattern: /^[0-9]{6}$/,
|
||||
inputErrorMessage: $t('prompt.email_otp.input_error'),
|
||||
callback: (action, instance) => {
|
||||
if (action === 'confirm') {
|
||||
API.verifyEmailOTP({
|
||||
code: instance.inputValue.trim()
|
||||
})
|
||||
.catch((err) => {
|
||||
this.promptEmailOTP();
|
||||
throw err;
|
||||
})
|
||||
.then((args) => {
|
||||
API.getCurrentUser();
|
||||
return args;
|
||||
});
|
||||
} else if (action === 'cancel') {
|
||||
this.resendEmail2fa();
|
||||
}
|
||||
},
|
||||
beforeClose: (action, instance, done) => {
|
||||
this.twoFactorAuthDialogVisible = false;
|
||||
done();
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptUserIdDialog() {
|
||||
this.$prompt(
|
||||
$t('prompt.direct_access_user_id.description'),
|
||||
$t('prompt.direct_access_user_id.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.direct_access_user_id.ok'),
|
||||
cancelButtonText: $t('prompt.direct_access_user_id.cancel'),
|
||||
inputPattern: /\S+/,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.direct_access_user_id.input_error'
|
||||
),
|
||||
callback: (action, instance) => {
|
||||
if (action === 'confirm' && instance.inputValue) {
|
||||
var testUrl = instance.inputValue.substring(0, 15);
|
||||
if (testUrl === 'https://vrchat.') {
|
||||
var userId = this.parseUserUrl(
|
||||
instance.inputValue
|
||||
);
|
||||
if (userId) {
|
||||
this.showUserDialog(userId);
|
||||
} else {
|
||||
this.$message({
|
||||
message: $t(
|
||||
'prompt.direct_access_user_id.message.error'
|
||||
),
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.showUserDialog(instance.inputValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptUsernameDialog() {
|
||||
this.$prompt(
|
||||
$t('prompt.direct_access_username.description'),
|
||||
$t('prompt.direct_access_username.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.direct_access_username.ok'),
|
||||
cancelButtonText: $t(
|
||||
'prompt.direct_access_username.cancel'
|
||||
),
|
||||
inputPattern: /\S+/,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.direct_access_username.input_error'
|
||||
),
|
||||
callback: (action, instance) => {
|
||||
if (action === 'confirm' && instance.inputValue) {
|
||||
this.lookupUser({
|
||||
displayName: instance.inputValue
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptWorldDialog() {
|
||||
this.$prompt(
|
||||
$t('prompt.direct_access_world_id.description'),
|
||||
$t('prompt.direct_access_world_id.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.direct_access_world_id.ok'),
|
||||
cancelButtonText: $t(
|
||||
'prompt.direct_access_world_id.cancel'
|
||||
),
|
||||
inputPattern: /\S+/,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.direct_access_world_id.input_error'
|
||||
),
|
||||
callback: (action, instance) => {
|
||||
if (action === 'confirm' && instance.inputValue) {
|
||||
if (!this.directAccessWorld(instance.inputValue)) {
|
||||
this.$message({
|
||||
message: $t(
|
||||
'prompt.direct_access_world_id.message.error'
|
||||
),
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptAvatarDialog() {
|
||||
this.$prompt(
|
||||
$t('prompt.direct_access_avatar_id.description'),
|
||||
$t('prompt.direct_access_avatar_id.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.direct_access_avatar_id.ok'),
|
||||
cancelButtonText: $t(
|
||||
'prompt.direct_access_avatar_id.cancel'
|
||||
),
|
||||
inputPattern: /\S+/,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.direct_access_avatar_id.input_error'
|
||||
),
|
||||
callback: (action, instance) => {
|
||||
if (action === 'confirm' && instance.inputValue) {
|
||||
var testUrl = instance.inputValue.substring(0, 15);
|
||||
if (testUrl === 'https://vrchat.') {
|
||||
var avatarId = this.parseAvatarUrl(
|
||||
instance.inputValue
|
||||
);
|
||||
if (avatarId) {
|
||||
this.showAvatarDialog(avatarId);
|
||||
} else {
|
||||
this.$message({
|
||||
message: $t(
|
||||
'prompt.direct_access_avatar_id.message.error'
|
||||
),
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.showAvatarDialog(instance.inputValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptOmniDirectDialog() {
|
||||
this.$prompt(
|
||||
$t('prompt.direct_access_omni.description'),
|
||||
$t('prompt.direct_access_omni.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.direct_access_omni.ok'),
|
||||
cancelButtonText: $t('prompt.direct_access_omni.cancel'),
|
||||
inputPattern: /\S+/,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.direct_access_omni.input_error'
|
||||
),
|
||||
callback: (action, instance) => {
|
||||
if (action === 'confirm' && instance.inputValue) {
|
||||
var input = instance.inputValue.trim();
|
||||
if (!this.directAccessParse(input)) {
|
||||
this.$message({
|
||||
message: $t(
|
||||
'prompt.direct_access_omni.message.error'
|
||||
),
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
changeFavoriteGroupName(ctx) {
|
||||
this.$prompt(
|
||||
$t('prompt.change_favorite_group_name.description'),
|
||||
$t('prompt.change_favorite_group_name.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
cancelButtonText: $t(
|
||||
'prompt.change_favorite_group_name.cancel'
|
||||
),
|
||||
confirmButtonText: $t(
|
||||
'prompt.change_favorite_group_name.change'
|
||||
),
|
||||
inputPlaceholder: $t(
|
||||
'prompt.change_favorite_group_name.input_placeholder'
|
||||
),
|
||||
inputValue: ctx.displayName,
|
||||
inputPattern: /\S+/,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.change_favorite_group_name.input_error'
|
||||
),
|
||||
callback: (action, instance) => {
|
||||
if (action === 'confirm') {
|
||||
API.saveFavoriteGroup({
|
||||
type: ctx.type,
|
||||
group: ctx.name,
|
||||
displayName: instance.inputValue
|
||||
}).then((args) => {
|
||||
this.$message({
|
||||
message: $t(
|
||||
'prompt.change_favorite_group_name.message.success'
|
||||
),
|
||||
type: 'success'
|
||||
});
|
||||
return args;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptNotificationTimeout() {
|
||||
this.$prompt(
|
||||
$t('prompt.notification_timeout.description'),
|
||||
$t('prompt.notification_timeout.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.notification_timeout.ok'),
|
||||
cancelButtonText: $t('prompt.notification_timeout.cancel'),
|
||||
inputValue: this.notificationTimeout / 1000,
|
||||
inputPattern: /\d+$/,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.notification_timeout.input_error'
|
||||
),
|
||||
callback: async (action, instance) => {
|
||||
if (
|
||||
action === 'confirm' &&
|
||||
instance.inputValue &&
|
||||
!isNaN(instance.inputValue)
|
||||
) {
|
||||
this.notificationTimeout = Math.trunc(
|
||||
Number(instance.inputValue) * 1000
|
||||
);
|
||||
await configRepository.setString(
|
||||
'VRCX_notificationTimeout',
|
||||
this.notificationTimeout
|
||||
);
|
||||
this.updateVRConfigVars();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptPhotonOverlayMessageTimeout() {
|
||||
this.$prompt(
|
||||
$t('prompt.overlay_message_timeout.description'),
|
||||
$t('prompt.overlay_message_timeout.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.overlay_message_timeout.ok'),
|
||||
cancelButtonText: $t(
|
||||
'prompt.overlay_message_timeout.cancel'
|
||||
),
|
||||
inputValue: this.photonOverlayMessageTimeout / 1000,
|
||||
inputPattern: /\d+$/,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.overlay_message_timeout.input_error'
|
||||
),
|
||||
callback: async (action, instance) => {
|
||||
if (
|
||||
action === 'confirm' &&
|
||||
instance.inputValue &&
|
||||
!isNaN(instance.inputValue)
|
||||
) {
|
||||
this.photonOverlayMessageTimeout = Math.trunc(
|
||||
Number(instance.inputValue) * 1000
|
||||
);
|
||||
await configRepository.setString(
|
||||
'VRCX_photonOverlayMessageTimeout',
|
||||
this.photonOverlayMessageTimeout
|
||||
);
|
||||
this.updateVRConfigVars();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptRenameAvatar(avatar) {
|
||||
this.$prompt(
|
||||
$t('prompt.rename_avatar.description'),
|
||||
$t('prompt.rename_avatar.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.rename_avatar.ok'),
|
||||
cancelButtonText: $t('prompt.rename_avatar.cancel'),
|
||||
inputValue: avatar.ref.name,
|
||||
inputErrorMessage: $t('prompt.rename_avatar.input_error'),
|
||||
callback: (action, instance) => {
|
||||
if (
|
||||
action === 'confirm' &&
|
||||
instance.inputValue !== avatar.ref.name
|
||||
) {
|
||||
API.saveAvatar({
|
||||
id: avatar.id,
|
||||
name: instance.inputValue
|
||||
}).then((args) => {
|
||||
this.$message({
|
||||
message: $t(
|
||||
'prompt.rename_avatar.message.success'
|
||||
),
|
||||
type: 'success'
|
||||
});
|
||||
return args;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptChangeAvatarDescription(avatar) {
|
||||
this.$prompt(
|
||||
$t('prompt.change_avatar_description.description'),
|
||||
$t('prompt.change_avatar_description.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t(
|
||||
'prompt.change_avatar_description.ok'
|
||||
),
|
||||
cancelButtonText: $t(
|
||||
'prompt.change_avatar_description.cancel'
|
||||
),
|
||||
inputValue: avatar.ref.description,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.change_avatar_description.input_error'
|
||||
),
|
||||
callback: (action, instance) => {
|
||||
if (
|
||||
action === 'confirm' &&
|
||||
instance.inputValue !== avatar.ref.description
|
||||
) {
|
||||
API.saveAvatar({
|
||||
id: avatar.id,
|
||||
description: instance.inputValue
|
||||
}).then((args) => {
|
||||
this.$message({
|
||||
message: $t(
|
||||
'prompt.change_avatar_description.message.success'
|
||||
),
|
||||
type: 'success'
|
||||
});
|
||||
return args;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptRenameWorld(world) {
|
||||
this.$prompt(
|
||||
$t('prompt.rename_world.description'),
|
||||
$t('prompt.rename_world.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.rename_world.ok'),
|
||||
cancelButtonText: $t('prompt.rename_world.cancel'),
|
||||
inputValue: world.ref.name,
|
||||
inputErrorMessage: $t('prompt.rename_world.input_error'),
|
||||
callback: (action, instance) => {
|
||||
if (
|
||||
action === 'confirm' &&
|
||||
instance.inputValue !== world.ref.name
|
||||
) {
|
||||
API.saveWorld({
|
||||
id: world.id,
|
||||
name: instance.inputValue
|
||||
}).then((args) => {
|
||||
this.$message({
|
||||
message: $t(
|
||||
'prompt.rename_world.message.success'
|
||||
),
|
||||
type: 'success'
|
||||
});
|
||||
return args;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptChangeWorldDescription(world) {
|
||||
this.$prompt(
|
||||
$t('prompt.change_world_description.description'),
|
||||
$t('prompt.change_world_description.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.change_world_description.ok'),
|
||||
cancelButtonText: $t(
|
||||
'prompt.change_world_description.cancel'
|
||||
),
|
||||
inputValue: world.ref.description,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.change_world_description.input_error'
|
||||
),
|
||||
callback: (action, instance) => {
|
||||
if (
|
||||
action === 'confirm' &&
|
||||
instance.inputValue !== world.ref.description
|
||||
) {
|
||||
API.saveWorld({
|
||||
id: world.id,
|
||||
description: instance.inputValue
|
||||
}).then((args) => {
|
||||
this.$message({
|
||||
message: $t(
|
||||
'prompt.change_world_description.message.success'
|
||||
),
|
||||
type: 'success'
|
||||
});
|
||||
return args;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptChangeWorldCapacity(world) {
|
||||
this.$prompt(
|
||||
$t('prompt.change_world_capacity.description'),
|
||||
$t('prompt.change_world_capacity.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.change_world_capacity.ok'),
|
||||
cancelButtonText: $t('prompt.change_world_capacity.cancel'),
|
||||
inputValue: world.ref.capacity,
|
||||
inputPattern: /\d+$/,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.change_world_capacity.input_error'
|
||||
),
|
||||
callback: (action, instance) => {
|
||||
if (
|
||||
action === 'confirm' &&
|
||||
instance.inputValue !== world.ref.capacity
|
||||
) {
|
||||
API.saveWorld({
|
||||
id: world.id,
|
||||
capacity: instance.inputValue
|
||||
}).then((args) => {
|
||||
this.$message({
|
||||
message: $t(
|
||||
'prompt.change_world_capacity.message.success'
|
||||
),
|
||||
type: 'success'
|
||||
});
|
||||
return args;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptChangeWorldRecommendedCapacity(world) {
|
||||
this.$prompt(
|
||||
$t('prompt.change_world_recommended_capacity.description'),
|
||||
$t('prompt.change_world_recommended_capacity.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.change_world_capacity.ok'),
|
||||
cancelButtonText: $t('prompt.change_world_capacity.cancel'),
|
||||
inputValue: world.ref.recommendedCapacity,
|
||||
inputPattern: /\d+$/,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.change_world_recommended_capacity.input_error'
|
||||
),
|
||||
callback: (action, instance) => {
|
||||
if (
|
||||
action === 'confirm' &&
|
||||
instance.inputValue !==
|
||||
world.ref.recommendedCapacity
|
||||
) {
|
||||
API.saveWorld({
|
||||
id: world.id,
|
||||
recommendedCapacity: instance.inputValue
|
||||
}).then((args) => {
|
||||
this.$message({
|
||||
message: $t(
|
||||
'prompt.change_world_recommended_capacity.message.success'
|
||||
),
|
||||
type: 'success'
|
||||
});
|
||||
return args;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptChangeWorldYouTubePreview(world) {
|
||||
this.$prompt(
|
||||
$t('prompt.change_world_preview.description'),
|
||||
$t('prompt.change_world_preview.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.change_world_preview.ok'),
|
||||
cancelButtonText: $t('prompt.change_world_preview.cancel'),
|
||||
inputValue: world.ref.previewYoutubeId,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.change_world_preview.input_error'
|
||||
),
|
||||
callback: (action, instance) => {
|
||||
if (
|
||||
action === 'confirm' &&
|
||||
instance.inputValue !== world.ref.previewYoutubeId
|
||||
) {
|
||||
if (instance.inputValue.length > 11) {
|
||||
try {
|
||||
var url = new URL(instance.inputValue);
|
||||
var id1 = url.pathname;
|
||||
var id2 = url.searchParams.get('v');
|
||||
if (id1 && id1.length === 12) {
|
||||
instance.inputValue = id1.substring(
|
||||
1,
|
||||
12
|
||||
);
|
||||
}
|
||||
if (id2 && id2.length === 11) {
|
||||
instance.inputValue = id2;
|
||||
}
|
||||
} catch {
|
||||
this.$message({
|
||||
message: $t(
|
||||
'prompt.change_world_preview.message.error'
|
||||
),
|
||||
type: 'error'
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (
|
||||
instance.inputValue !==
|
||||
world.ref.previewYoutubeId
|
||||
) {
|
||||
API.saveWorld({
|
||||
id: world.id,
|
||||
previewYoutubeId: instance.inputValue
|
||||
}).then((args) => {
|
||||
this.$message({
|
||||
message: $t(
|
||||
'prompt.change_world_preview.message.success'
|
||||
),
|
||||
type: 'success'
|
||||
});
|
||||
return args;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptMaxTableSizeDialog() {
|
||||
this.$prompt(
|
||||
$t('prompt.change_table_size.description'),
|
||||
$t('prompt.change_table_size.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.change_table_size.save'),
|
||||
cancelButtonText: $t('prompt.change_table_size.cancel'),
|
||||
inputValue: this.maxTableSize,
|
||||
inputPattern: /\d+$/,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.change_table_size.input_error'
|
||||
),
|
||||
callback: async (action, instance) => {
|
||||
if (action === 'confirm' && instance.inputValue) {
|
||||
if (instance.inputValue > 10000) {
|
||||
instance.inputValue = 10000;
|
||||
}
|
||||
this.maxTableSize = instance.inputValue;
|
||||
await configRepository.setString(
|
||||
'VRCX_maxTableSize',
|
||||
this.maxTableSize
|
||||
);
|
||||
database.setmaxTableSize(this.maxTableSize);
|
||||
this.feedTableLookup();
|
||||
this.gameLogTableLookup();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptProxySettings() {
|
||||
this.$prompt(
|
||||
$t('prompt.proxy_settings.description'),
|
||||
$t('prompt.proxy_settings.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.proxy_settings.restart'),
|
||||
cancelButtonText: $t('prompt.proxy_settings.close'),
|
||||
inputValue: this.proxyServer,
|
||||
inputPlaceholder: $t('prompt.proxy_settings.placeholder'),
|
||||
callback: async (action, instance) => {
|
||||
this.proxyServer = instance.inputValue;
|
||||
await VRCXStorage.Set(
|
||||
'VRCX_ProxyServer',
|
||||
this.proxyServer
|
||||
);
|
||||
await VRCXStorage.Flush();
|
||||
await new Promise((resolve) => {
|
||||
workerTimers.setTimeout(resolve, 100);
|
||||
});
|
||||
if (action === 'confirm') {
|
||||
var isUpgrade = false;
|
||||
this.restartVRCX(isUpgrade);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptPhotonLobbyTimeoutThreshold() {
|
||||
this.$prompt(
|
||||
$t('prompt.photon_lobby_timeout.description'),
|
||||
$t('prompt.photon_lobby_timeout.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.photon_lobby_timeout.ok'),
|
||||
cancelButtonText: $t('prompt.photon_lobby_timeout.cancel'),
|
||||
inputValue: this.photonLobbyTimeoutThreshold / 1000,
|
||||
inputPattern: /\d+$/,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.photon_lobby_timeout.input_error'
|
||||
),
|
||||
callback: async (action, instance) => {
|
||||
if (
|
||||
action === 'confirm' &&
|
||||
instance.inputValue &&
|
||||
!isNaN(instance.inputValue)
|
||||
) {
|
||||
this.photonLobbyTimeoutThreshold = Math.trunc(
|
||||
Number(instance.inputValue) * 1000
|
||||
);
|
||||
await configRepository.setString(
|
||||
'VRCX_photonLobbyTimeoutThreshold',
|
||||
this.photonLobbyTimeoutThreshold
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptAutoClearVRCXCacheFrequency() {
|
||||
this.$prompt(
|
||||
$t('prompt.auto_clear_cache.description'),
|
||||
$t('prompt.auto_clear_cache.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.auto_clear_cache.ok'),
|
||||
cancelButtonText: $t('prompt.auto_clear_cache.cancel'),
|
||||
inputValue: this.clearVRCXCacheFrequency / 3600 / 2,
|
||||
inputPattern: /\d+$/,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.auto_clear_cache.input_error'
|
||||
),
|
||||
callback: async (action, instance) => {
|
||||
if (
|
||||
action === 'confirm' &&
|
||||
instance.inputValue &&
|
||||
!isNaN(instance.inputValue)
|
||||
) {
|
||||
this.clearVRCXCacheFrequency = Math.trunc(
|
||||
Number(instance.inputValue) * 3600 * 2
|
||||
);
|
||||
await configRepository.setString(
|
||||
'VRCX_clearVRCXCacheFrequency',
|
||||
this.clearVRCXCacheFrequency
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
284
src/classes/restoreFriendOrder.js
Normal file
@@ -0,0 +1,284 @@
|
||||
import * as workerTimers from 'worker-timers';
|
||||
import configRepository from '../repository/config.js';
|
||||
import database from '../repository/database.js';
|
||||
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
init() {}
|
||||
|
||||
_data = {};
|
||||
|
||||
_methods = {
|
||||
async tryRestoreFriendNumber() {
|
||||
var lastUpdate = await configRepository.getString(
|
||||
`VRCX_lastStoreTime_${API.currentUser.id}`
|
||||
);
|
||||
if (lastUpdate == -4) {
|
||||
// this means the backup was already applied
|
||||
return;
|
||||
}
|
||||
var status = false;
|
||||
this.friendNumber = 0;
|
||||
for (var ref of this.friendLog.values()) {
|
||||
ref.friendNumber = 0;
|
||||
}
|
||||
try {
|
||||
if (lastUpdate) {
|
||||
// backup ready to try apply
|
||||
status = await this.restoreFriendNumber();
|
||||
}
|
||||
// needs to be in reverse because we don't know the starting number
|
||||
this.applyFriendLogFriendOrderInReverse();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
// if (status) {
|
||||
// this.$message({
|
||||
// message: 'Friend order restored from backup',
|
||||
// type: 'success',
|
||||
// duration: 0,
|
||||
// showClose: true
|
||||
// });
|
||||
// } else if (this.friendLogTable.data.length > 0) {
|
||||
// this.$message({
|
||||
// message:
|
||||
// 'No backup found, friend order partially restored from friendLog',
|
||||
// type: 'success',
|
||||
// duration: 0,
|
||||
// showClose: true
|
||||
// });
|
||||
// }
|
||||
await configRepository.setString(
|
||||
`VRCX_lastStoreTime_${API.currentUser.id}`,
|
||||
-4
|
||||
);
|
||||
},
|
||||
|
||||
async restoreFriendNumber() {
|
||||
var storedData = null;
|
||||
try {
|
||||
var data = await configRepository.getString(
|
||||
`VRCX_friendOrder_${API.currentUser.id}`
|
||||
);
|
||||
if (data) {
|
||||
var storedData = JSON.parse(data);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
if (!storedData || storedData.length === 0) {
|
||||
var message = 'whomp whomp, no friend order backup found';
|
||||
console.error(message);
|
||||
return false;
|
||||
}
|
||||
|
||||
var friendLogTable = this.getFriendLogFriendOrder();
|
||||
|
||||
// for storedData
|
||||
var machList = [];
|
||||
for (var i = 0; i < Object.keys(storedData).length; i++) {
|
||||
var key = Object.keys(storedData)[i];
|
||||
var value = storedData[key];
|
||||
var item = this.parseFriendOrderBackup(
|
||||
friendLogTable,
|
||||
key,
|
||||
value
|
||||
);
|
||||
machList.push(item);
|
||||
}
|
||||
machList.sort((a, b) => b.matches - a.matches);
|
||||
console.log(
|
||||
`friendLog: ${friendLogTable.length} friendOrderBackups:`,
|
||||
machList
|
||||
);
|
||||
|
||||
var bestBackup = machList[0];
|
||||
if (!bestBackup?.isValid) {
|
||||
var message = 'whomp whomp, no valid backup found';
|
||||
console.error(message);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.applyFriendOrderBackup(bestBackup.table);
|
||||
this.applyFriendLogFriendOrder();
|
||||
await configRepository.setInt(
|
||||
`VRCX_friendNumber_${API.currentUser.id}`,
|
||||
this.friendNumber
|
||||
);
|
||||
return true;
|
||||
},
|
||||
|
||||
getFriendLogFriendOrder() {
|
||||
var friendLogTable = [];
|
||||
for (var i = 0; i < this.friendLogTable.data.length; i++) {
|
||||
var ref = this.friendLogTable.data[i];
|
||||
if (ref.type !== 'Friend') {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
friendLogTable.findIndex((x) => x.id === ref.userId) !== -1
|
||||
) {
|
||||
// console.log(
|
||||
// 'ignoring duplicate friend',
|
||||
// ref.displayName,
|
||||
// ref.created_at
|
||||
// );
|
||||
continue;
|
||||
}
|
||||
friendLogTable.push({
|
||||
id: ref.userId,
|
||||
displayName: ref.displayName,
|
||||
created_at: ref.created_at
|
||||
});
|
||||
}
|
||||
var compareByCreatedAt = function (a, b) {
|
||||
var A = a.created_at;
|
||||
var B = b.created_at;
|
||||
if (A < B) {
|
||||
return -1;
|
||||
}
|
||||
if (A > B) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
friendLogTable.sort(compareByCreatedAt);
|
||||
return friendLogTable;
|
||||
},
|
||||
|
||||
applyFriendLogFriendOrder() {
|
||||
var friendLogTable = this.getFriendLogFriendOrder();
|
||||
if (this.friendNumber === 0) {
|
||||
console.log(
|
||||
'No backup applied, applying friend log in reverse'
|
||||
);
|
||||
// this means no FriendOrderBackup was applied
|
||||
// will need to apply in reverse order instead
|
||||
return;
|
||||
}
|
||||
for (var friendLog of friendLogTable) {
|
||||
var ref = this.friendLog.get(friendLog.id);
|
||||
if (!ref || ref.friendNumber) {
|
||||
continue;
|
||||
}
|
||||
ref.friendNumber = ++this.friendNumber;
|
||||
this.friendLog.set(ref.userId, ref);
|
||||
database.setFriendLogCurrent(ref);
|
||||
var friendRef = this.friends.get(friendLog.id);
|
||||
if (friendRef?.ref) {
|
||||
friendRef.ref.$friendNumber = ref.friendNumber;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
applyFriendLogFriendOrderInReverse() {
|
||||
this.friendNumber = this.friends.size + 1;
|
||||
var friendLogTable = this.getFriendLogFriendOrder();
|
||||
for (var i = friendLogTable.length - 1; i > -1; i--) {
|
||||
var friendLog = friendLogTable[i];
|
||||
var ref = this.friendLog.get(friendLog.id);
|
||||
if (!ref) {
|
||||
continue;
|
||||
}
|
||||
if (ref.friendNumber) {
|
||||
break;
|
||||
}
|
||||
ref.friendNumber = --this.friendNumber;
|
||||
this.friendLog.set(ref.userId, ref);
|
||||
database.setFriendLogCurrent(ref);
|
||||
var friendRef = this.friends.get(friendLog.id);
|
||||
if (friendRef?.ref) {
|
||||
friendRef.ref.$friendNumber = ref.friendNumber;
|
||||
}
|
||||
}
|
||||
this.friendNumber = this.friends.size;
|
||||
console.log('Applied friend order from friendLog');
|
||||
},
|
||||
|
||||
parseFriendOrderBackup(friendLogTable, created_at, backupUserIds) {
|
||||
var backupTable = [];
|
||||
for (var i = 0; i < backupUserIds.length; i++) {
|
||||
var userId = backupUserIds[i];
|
||||
var ctx = this.friends.get(userId);
|
||||
if (ctx) {
|
||||
backupTable.push({
|
||||
id: ctx.id,
|
||||
displayName: ctx.name
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// var compareTable = [];
|
||||
// compare 2 tables, find max amount of id's in same order
|
||||
var maxMatches = 0;
|
||||
var currentMatches = 0;
|
||||
var backupIndex = 0;
|
||||
for (var i = 0; i < friendLogTable.length; i++) {
|
||||
var isMatch = false;
|
||||
var ref = friendLogTable[i];
|
||||
if (backupIndex <= 0) {
|
||||
backupIndex = backupTable.findIndex((x) => x.id === ref.id);
|
||||
if (backupIndex !== -1) {
|
||||
currentMatches = 1;
|
||||
}
|
||||
} else if (backupTable[backupIndex].id === ref.id) {
|
||||
currentMatches++;
|
||||
isMatch = true;
|
||||
} else {
|
||||
var backupIndex = backupTable.findIndex(
|
||||
(x) => x.id === ref.id
|
||||
);
|
||||
if (backupIndex !== -1) {
|
||||
currentMatches = 1;
|
||||
}
|
||||
}
|
||||
if (backupIndex === backupTable.length - 1) {
|
||||
backupIndex = 0;
|
||||
} else {
|
||||
backupIndex++;
|
||||
}
|
||||
if (currentMatches > maxMatches) {
|
||||
maxMatches = currentMatches;
|
||||
}
|
||||
// compareTable.push({
|
||||
// id: ref.id,
|
||||
// displayName: ref.displayName,
|
||||
// match: isMatch
|
||||
// });
|
||||
}
|
||||
|
||||
var lerp = (a, b, alpha) => {
|
||||
return a + alpha * (b - a);
|
||||
};
|
||||
return {
|
||||
matches: parseFloat(`${maxMatches}.${created_at}`),
|
||||
table: backupUserIds,
|
||||
isValid: maxMatches > lerp(4, 10, backupTable.length / 1000) // pls no collisions
|
||||
};
|
||||
},
|
||||
|
||||
applyFriendOrderBackup(userIdOrder) {
|
||||
for (var i = 0; i < userIdOrder.length; i++) {
|
||||
var userId = userIdOrder[i];
|
||||
var ctx = this.friends.get(userId);
|
||||
var ref = ctx?.ref;
|
||||
if (!ref || ref.$friendNumber) {
|
||||
continue;
|
||||
}
|
||||
var friendLogCurrent = {
|
||||
userId,
|
||||
displayName: ref.displayName,
|
||||
trustLevel: ref.$trustLevel,
|
||||
friendNumber: i + 1
|
||||
};
|
||||
this.friendLog.set(userId, friendLogCurrent);
|
||||
database.setFriendLogCurrent(friendLogCurrent);
|
||||
this.friendNumber = i + 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
596
src/classes/sharedFeed.js
Normal file
@@ -0,0 +1,596 @@
|
||||
import * as workerTimers from 'worker-timers';
|
||||
import configRepository from '../repository/config.js';
|
||||
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
_data = {
|
||||
sharedFeed: {
|
||||
gameLog: {
|
||||
wrist: [],
|
||||
lastEntryDate: ''
|
||||
},
|
||||
feedTable: {
|
||||
wrist: [],
|
||||
lastEntryDate: ''
|
||||
},
|
||||
notificationTable: {
|
||||
wrist: [],
|
||||
lastEntryDate: ''
|
||||
},
|
||||
friendLogTable: {
|
||||
wrist: [],
|
||||
lastEntryDate: ''
|
||||
},
|
||||
moderationAgainstTable: {
|
||||
wrist: [],
|
||||
lastEntryDate: ''
|
||||
},
|
||||
pendingUpdate: false
|
||||
},
|
||||
updateSharedFeedTimer: null,
|
||||
updateSharedFeedPending: false,
|
||||
updateSharedFeedPendingForceUpdate: false
|
||||
};
|
||||
|
||||
_methods = {
|
||||
updateSharedFeed(forceUpdate) {
|
||||
if (!this.friendLogInitStatus) {
|
||||
return;
|
||||
}
|
||||
if (this.updateSharedFeedTimer) {
|
||||
if (forceUpdate) {
|
||||
this.updateSharedFeedPendingForceUpdate = true;
|
||||
}
|
||||
this.updateSharedFeedPending = true;
|
||||
} else {
|
||||
this.updateSharedExecute(forceUpdate);
|
||||
this.updateSharedFeedTimer = setTimeout(() => {
|
||||
if (this.updateSharedFeedPending) {
|
||||
this.updateSharedExecute(
|
||||
this.updateSharedFeedPendingForceUpdate
|
||||
);
|
||||
}
|
||||
this.updateSharedFeedTimer = null;
|
||||
}, 150);
|
||||
}
|
||||
},
|
||||
|
||||
updateSharedExecute(forceUpdate) {
|
||||
try {
|
||||
this.updateSharedFeedDebounce(forceUpdate);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
this.updateSharedFeedTimer = null;
|
||||
this.updateSharedFeedPending = false;
|
||||
this.updateSharedFeedPendingForceUpdate = false;
|
||||
},
|
||||
|
||||
updateSharedFeedDebounce(forceUpdate) {
|
||||
this.updateSharedFeedGameLog(forceUpdate);
|
||||
this.updateSharedFeedFeedTable(forceUpdate);
|
||||
this.updateSharedFeedNotificationTable(forceUpdate);
|
||||
this.updateSharedFeedFriendLogTable(forceUpdate);
|
||||
this.updateSharedFeedModerationAgainstTable(forceUpdate);
|
||||
var feeds = this.sharedFeed;
|
||||
if (!feeds.pendingUpdate) {
|
||||
return;
|
||||
}
|
||||
var wristFeed = [];
|
||||
wristFeed = wristFeed.concat(
|
||||
feeds.gameLog.wrist,
|
||||
feeds.feedTable.wrist,
|
||||
feeds.notificationTable.wrist,
|
||||
feeds.friendLogTable.wrist,
|
||||
feeds.moderationAgainstTable.wrist
|
||||
);
|
||||
// OnPlayerJoining/Traveling
|
||||
API.currentTravelers.forEach((ref) => {
|
||||
var isFavorite = this.localFavoriteFriends.has(ref.id);
|
||||
if (
|
||||
(this.sharedFeedFilters.wrist.OnPlayerJoining ===
|
||||
'Friends' ||
|
||||
(this.sharedFeedFilters.wrist.OnPlayerJoining ===
|
||||
'VIP' &&
|
||||
isFavorite)) &&
|
||||
!$app.lastLocation.playerList.has(ref.id)
|
||||
) {
|
||||
if (ref.$location.tag === $app.lastLocation.location) {
|
||||
var feedEntry = {
|
||||
...ref,
|
||||
isFavorite,
|
||||
isFriend: true,
|
||||
type: 'OnPlayerJoining'
|
||||
};
|
||||
wristFeed.unshift(feedEntry);
|
||||
} else {
|
||||
var worldRef = API.cachedWorlds.get(
|
||||
ref.$location.worldId
|
||||
);
|
||||
var groupName = '';
|
||||
if (ref.$location.groupId) {
|
||||
var groupRef = API.cachedGroups.get(
|
||||
ref.$location.groupId
|
||||
);
|
||||
if (typeof groupRef !== 'undefined') {
|
||||
groupName = groupRef.name;
|
||||
} else {
|
||||
// no group cache, fetch group and try again
|
||||
API.getGroup({
|
||||
groupId: ref.$location.groupId
|
||||
})
|
||||
.then((args) => {
|
||||
workerTimers.setTimeout(() => {
|
||||
// delay to allow for group cache to update
|
||||
$app.sharedFeed.pendingUpdate = true;
|
||||
$app.updateSharedFeed(false);
|
||||
}, 100);
|
||||
return args;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (typeof worldRef !== 'undefined') {
|
||||
var feedEntry = {
|
||||
created_at: ref.created_at,
|
||||
type: 'GPS',
|
||||
userId: ref.id,
|
||||
displayName: ref.displayName,
|
||||
location: ref.$location.tag,
|
||||
worldName: worldRef.name,
|
||||
groupName,
|
||||
previousLocation: '',
|
||||
isFavorite,
|
||||
time: 0,
|
||||
isFriend: true,
|
||||
isTraveling: true
|
||||
};
|
||||
wristFeed.unshift(feedEntry);
|
||||
} else {
|
||||
// no world cache, fetch world and try again
|
||||
API.getWorld({
|
||||
worldId: ref.$location.worldId
|
||||
})
|
||||
.then((args) => {
|
||||
workerTimers.setTimeout(() => {
|
||||
// delay to allow for world cache to update
|
||||
$app.sharedFeed.pendingUpdate = true;
|
||||
$app.updateSharedFeed(false);
|
||||
}, 100);
|
||||
return args;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
wristFeed.sort(function (a, b) {
|
||||
if (a.created_at < b.created_at) {
|
||||
return 1;
|
||||
}
|
||||
if (a.created_at > b.created_at) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
wristFeed.splice(16);
|
||||
AppApi.ExecuteVrFeedFunction(
|
||||
'wristFeedUpdate',
|
||||
JSON.stringify(wristFeed)
|
||||
);
|
||||
this.applyUserDialogLocation();
|
||||
this.applyWorldDialogInstances();
|
||||
this.applyGroupDialogInstances();
|
||||
feeds.pendingUpdate = false;
|
||||
},
|
||||
|
||||
updateSharedFeedGameLog(forceUpdate) {
|
||||
// Location, OnPlayerJoined, OnPlayerLeft
|
||||
var sessionTable = this.gameLogSessionTable;
|
||||
var i = sessionTable.length;
|
||||
if (i > 0) {
|
||||
if (
|
||||
sessionTable[i - 1].created_at ===
|
||||
this.sharedFeed.gameLog.lastEntryDate &&
|
||||
forceUpdate === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.sharedFeed.gameLog.lastEntryDate =
|
||||
sessionTable[i - 1].created_at;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
var bias = new Date(Date.now() - 86400000).toJSON(); // 24 hours
|
||||
var wristArr = [];
|
||||
var w = 0;
|
||||
var wristFilter = this.sharedFeedFilters.wrist;
|
||||
var currentUserLeaveTime = 0;
|
||||
var locationJoinTime = 0;
|
||||
for (var i = sessionTable.length - 1; i > -1; i--) {
|
||||
var ctx = sessionTable[i];
|
||||
if (ctx.created_at < bias) {
|
||||
break;
|
||||
}
|
||||
if (ctx.type === 'Notification') {
|
||||
continue;
|
||||
}
|
||||
// on Location change remove OnPlayerLeft
|
||||
if (ctx.type === 'LocationDestination') {
|
||||
currentUserLeaveTime = Date.parse(ctx.created_at);
|
||||
var currentUserLeaveTimeOffset =
|
||||
currentUserLeaveTime + 5 * 1000;
|
||||
for (var k = w - 1; k > -1; k--) {
|
||||
var feedItem = wristArr[k];
|
||||
if (
|
||||
(feedItem.type === 'OnPlayerLeft' ||
|
||||
feedItem.type === 'BlockedOnPlayerLeft' ||
|
||||
feedItem.type === 'MutedOnPlayerLeft') &&
|
||||
Date.parse(feedItem.created_at) >=
|
||||
currentUserLeaveTime &&
|
||||
Date.parse(feedItem.created_at) <=
|
||||
currentUserLeaveTimeOffset
|
||||
) {
|
||||
wristArr.splice(k, 1);
|
||||
w--;
|
||||
}
|
||||
}
|
||||
}
|
||||
// on Location change remove OnPlayerJoined
|
||||
if (ctx.type === 'Location') {
|
||||
locationJoinTime = Date.parse(ctx.created_at);
|
||||
var locationJoinTimeOffset = locationJoinTime + 20 * 1000;
|
||||
for (var k = w - 1; k > -1; k--) {
|
||||
var feedItem = wristArr[k];
|
||||
if (
|
||||
(feedItem.type === 'OnPlayerJoined' ||
|
||||
feedItem.type === 'BlockedOnPlayerJoined' ||
|
||||
feedItem.type === 'MutedOnPlayerJoined') &&
|
||||
Date.parse(feedItem.created_at) >=
|
||||
locationJoinTime &&
|
||||
Date.parse(feedItem.created_at) <=
|
||||
locationJoinTimeOffset
|
||||
) {
|
||||
wristArr.splice(k, 1);
|
||||
w--;
|
||||
}
|
||||
}
|
||||
}
|
||||
// remove current user
|
||||
if (
|
||||
(ctx.type === 'OnPlayerJoined' ||
|
||||
ctx.type === 'OnPlayerLeft' ||
|
||||
ctx.type === 'PortalSpawn') &&
|
||||
ctx.displayName === API.currentUser.displayName
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
var isFriend = false;
|
||||
var isFavorite = false;
|
||||
if (ctx.userId) {
|
||||
isFriend = this.friends.has(ctx.userId);
|
||||
isFavorite = this.localFavoriteFriends.has(ctx.userId);
|
||||
} else if (ctx.displayName) {
|
||||
for (var ref of API.cachedUsers.values()) {
|
||||
if (ref.displayName === ctx.displayName) {
|
||||
isFriend = this.friends.has(ref.id);
|
||||
isFavorite = this.localFavoriteFriends.has(ref.id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// add tag colour
|
||||
var tagColour = '';
|
||||
if (ctx.userId) {
|
||||
var tagRef = this.customUserTags.get(ctx.userId);
|
||||
if (typeof tagRef !== 'undefined') {
|
||||
tagColour = tagRef.colour;
|
||||
}
|
||||
}
|
||||
// BlockedOnPlayerJoined, BlockedOnPlayerLeft, MutedOnPlayerJoined, MutedOnPlayerLeft
|
||||
if (
|
||||
ctx.type === 'OnPlayerJoined' ||
|
||||
ctx.type === 'OnPlayerLeft'
|
||||
) {
|
||||
for (var ref of API.cachedPlayerModerations.values()) {
|
||||
if (
|
||||
ref.targetDisplayName !== ctx.displayName &&
|
||||
ref.sourceUserId !== ctx.userId
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ref.type === 'block') {
|
||||
var type = `Blocked${ctx.type}`;
|
||||
} else if (ref.type === 'mute') {
|
||||
var type = `Muted${ctx.type}`;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
var entry = {
|
||||
created_at: ctx.created_at,
|
||||
type,
|
||||
displayName: ref.targetDisplayName,
|
||||
userId: ref.targetUserId,
|
||||
isFriend,
|
||||
isFavorite
|
||||
};
|
||||
if (
|
||||
wristFilter[type] &&
|
||||
(wristFilter[type] === 'Everyone' ||
|
||||
(wristFilter[type] === 'Friends' && isFriend) ||
|
||||
(wristFilter[type] === 'VIP' && isFavorite))
|
||||
) {
|
||||
wristArr.unshift(entry);
|
||||
}
|
||||
this.queueGameLogNoty(entry);
|
||||
}
|
||||
}
|
||||
// when too many user joins happen at once when switching instances
|
||||
// the "w" counter maxes out and wont add any more entries
|
||||
// until the onJoins are cleared by "Location"
|
||||
// e.g. if a "VideoPlay" occurs between "OnPlayerJoined" and "Location" it wont be added
|
||||
if (
|
||||
w < 50 &&
|
||||
wristFilter[ctx.type] &&
|
||||
(wristFilter[ctx.type] === 'On' ||
|
||||
wristFilter[ctx.type] === 'Everyone' ||
|
||||
(wristFilter[ctx.type] === 'Friends' && isFriend) ||
|
||||
(wristFilter[ctx.type] === 'VIP' && isFavorite))
|
||||
) {
|
||||
wristArr.push({
|
||||
...ctx,
|
||||
tagColour,
|
||||
isFriend,
|
||||
isFavorite
|
||||
});
|
||||
++w;
|
||||
}
|
||||
}
|
||||
this.sharedFeed.gameLog.wrist = wristArr;
|
||||
this.sharedFeed.pendingUpdate = true;
|
||||
},
|
||||
|
||||
updateSharedFeedFeedTable(forceUpdate) {
|
||||
// GPS, Online, Offline, Status, Avatar
|
||||
var feedSession = this.feedSessionTable;
|
||||
var i = feedSession.length;
|
||||
if (i > 0) {
|
||||
if (
|
||||
feedSession[i - 1].created_at ===
|
||||
this.sharedFeed.feedTable.lastEntryDate &&
|
||||
forceUpdate === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.sharedFeed.feedTable.lastEntryDate =
|
||||
feedSession[i - 1].created_at;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
var bias = new Date(Date.now() - 86400000).toJSON(); // 24 hours
|
||||
var wristArr = [];
|
||||
var w = 0;
|
||||
var wristFilter = this.sharedFeedFilters.wrist;
|
||||
for (var i = feedSession.length - 1; i > -1; i--) {
|
||||
var ctx = feedSession[i];
|
||||
if (ctx.created_at < bias) {
|
||||
break;
|
||||
}
|
||||
if (ctx.type === 'Avatar') {
|
||||
continue;
|
||||
}
|
||||
// hide private worlds from feed
|
||||
if (
|
||||
this.hidePrivateFromFeed &&
|
||||
ctx.type === 'GPS' &&
|
||||
ctx.location === 'private'
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
var isFriend = this.friends.has(ctx.userId);
|
||||
var isFavorite = this.localFavoriteFriends.has(ctx.userId);
|
||||
if (
|
||||
w < 20 &&
|
||||
wristFilter[ctx.type] &&
|
||||
(wristFilter[ctx.type] === 'Friends' ||
|
||||
(wristFilter[ctx.type] === 'VIP' && isFavorite))
|
||||
) {
|
||||
wristArr.push({
|
||||
...ctx,
|
||||
isFriend,
|
||||
isFavorite
|
||||
});
|
||||
++w;
|
||||
}
|
||||
}
|
||||
this.sharedFeed.feedTable.wrist = wristArr;
|
||||
this.sharedFeed.pendingUpdate = true;
|
||||
},
|
||||
|
||||
updateSharedFeedNotificationTable(forceUpdate) {
|
||||
// invite, requestInvite, requestInviteResponse, inviteResponse, friendRequest
|
||||
var notificationTable = this.notificationTable;
|
||||
var i = notificationTable.length;
|
||||
if (i > 0) {
|
||||
if (
|
||||
notificationTable[i - 1].created_at ===
|
||||
this.sharedFeed.notificationTable.lastEntryDate &&
|
||||
forceUpdate === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.sharedFeed.notificationTable.lastEntryDate =
|
||||
notificationTable[i - 1].created_at;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
var bias = new Date(Date.now() - 86400000).toJSON(); // 24 hours
|
||||
var wristArr = [];
|
||||
var w = 0;
|
||||
var wristFilter = this.sharedFeedFilters.wrist;
|
||||
for (var i = notificationTable.length - 1; i > -1; i--) {
|
||||
var ctx = notificationTable[i];
|
||||
if (ctx.created_at < bias) {
|
||||
break;
|
||||
}
|
||||
if (ctx.senderUserId === API.currentUser.id) {
|
||||
continue;
|
||||
}
|
||||
var isFriend = this.friends.has(ctx.senderUserId);
|
||||
var isFavorite = this.localFavoriteFriends.has(
|
||||
ctx.senderUserId
|
||||
);
|
||||
if (
|
||||
w < 20 &&
|
||||
wristFilter[ctx.type] &&
|
||||
(wristFilter[ctx.type] === 'On' ||
|
||||
wristFilter[ctx.type] === 'Friends' ||
|
||||
(wristFilter[ctx.type] === 'VIP' && isFavorite))
|
||||
) {
|
||||
wristArr.push({
|
||||
...ctx,
|
||||
isFriend,
|
||||
isFavorite
|
||||
});
|
||||
++w;
|
||||
}
|
||||
}
|
||||
this.sharedFeed.notificationTable.wrist = wristArr;
|
||||
this.sharedFeed.pendingUpdate = true;
|
||||
},
|
||||
|
||||
updateSharedFeedFriendLogTable(forceUpdate) {
|
||||
// TrustLevel, Friend, FriendRequest, Unfriend, DisplayName
|
||||
var friendLog = this.friendLogTable;
|
||||
var i = friendLog.length;
|
||||
if (i > 0) {
|
||||
if (
|
||||
friendLog[i - 1].created_at ===
|
||||
this.sharedFeed.friendLogTable.lastEntryDate &&
|
||||
forceUpdate === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.sharedFeed.friendLogTable.lastEntryDate =
|
||||
friendLog[i - 1].created_at;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
var bias = new Date(Date.now() - 86400000).toJSON(); // 24 hours
|
||||
var wristArr = [];
|
||||
var w = 0;
|
||||
var wristFilter = this.sharedFeedFilters.wrist;
|
||||
for (var i = friendLog.length - 1; i > -1; i--) {
|
||||
var ctx = friendLog[i];
|
||||
if (ctx.created_at < bias) {
|
||||
break;
|
||||
}
|
||||
if (ctx.type === 'FriendRequest') {
|
||||
continue;
|
||||
}
|
||||
var isFriend = this.friends.has(ctx.userId);
|
||||
var isFavorite = this.localFavoriteFriends.has(ctx.userId);
|
||||
if (
|
||||
w < 20 &&
|
||||
wristFilter[ctx.type] &&
|
||||
(wristFilter[ctx.type] === 'On' ||
|
||||
wristFilter[ctx.type] === 'Friends' ||
|
||||
(wristFilter[ctx.type] === 'VIP' && isFavorite))
|
||||
) {
|
||||
wristArr.push({
|
||||
...ctx,
|
||||
isFriend,
|
||||
isFavorite
|
||||
});
|
||||
++w;
|
||||
}
|
||||
}
|
||||
this.sharedFeed.friendLogTable.wrist = wristArr;
|
||||
this.sharedFeed.pendingUpdate = true;
|
||||
},
|
||||
|
||||
updateSharedFeedModerationAgainstTable(forceUpdate) {
|
||||
// Unblocked, Blocked, Muted, Unmuted
|
||||
var moderationAgainst = this.moderationAgainstTable;
|
||||
var i = moderationAgainst.length;
|
||||
if (i > 0) {
|
||||
if (
|
||||
moderationAgainst[i - 1].created_at ===
|
||||
this.sharedFeed.moderationAgainstTable.lastEntryDate &&
|
||||
forceUpdate === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.sharedFeed.moderationAgainstTable.lastEntryDate =
|
||||
moderationAgainst[i - 1].created_at;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
var bias = new Date(Date.now() - 86400000).toJSON(); // 24 hours
|
||||
var wristArr = [];
|
||||
var w = 0;
|
||||
var wristFilter = this.sharedFeedFilters.wrist;
|
||||
for (var i = moderationAgainst.length - 1; i > -1; i--) {
|
||||
var ctx = moderationAgainst[i];
|
||||
if (ctx.created_at < bias) {
|
||||
break;
|
||||
}
|
||||
var isFriend = this.friends.has(ctx.userId);
|
||||
var isFavorite = this.localFavoriteFriends.has(ctx.userId);
|
||||
// add tag colour
|
||||
var tagColour = '';
|
||||
var tagRef = this.customUserTags.get(ctx.userId);
|
||||
if (typeof tagRef !== 'undefined') {
|
||||
tagColour = tagRef.colour;
|
||||
}
|
||||
if (
|
||||
w < 20 &&
|
||||
wristFilter[ctx.type] &&
|
||||
wristFilter[ctx.type] === 'On'
|
||||
) {
|
||||
wristArr.push({
|
||||
...ctx,
|
||||
isFriend,
|
||||
isFavorite,
|
||||
tagColour
|
||||
});
|
||||
++w;
|
||||
}
|
||||
}
|
||||
this.sharedFeed.moderationAgainstTable.wrist = wristArr;
|
||||
this.sharedFeed.pendingUpdate = true;
|
||||
},
|
||||
|
||||
saveSharedFeedFilters() {
|
||||
configRepository.setString(
|
||||
'sharedFeedFilters',
|
||||
JSON.stringify(this.sharedFeedFilters)
|
||||
);
|
||||
this.updateSharedFeed(true);
|
||||
},
|
||||
|
||||
async resetNotyFeedFilters() {
|
||||
this.sharedFeedFilters.noty = {
|
||||
...this.sharedFeedFiltersDefaults.noty
|
||||
};
|
||||
this.saveSharedFeedFilters();
|
||||
},
|
||||
|
||||
async resetWristFeedFilters() {
|
||||
this.sharedFeedFilters.wrist = {
|
||||
...this.sharedFeedFiltersDefaults.wrist
|
||||
};
|
||||
this.saveSharedFeedFilters();
|
||||
}
|
||||
};
|
||||
}
|
||||
618
src/classes/uiComponents.js
Normal file
@@ -0,0 +1,618 @@
|
||||
import Vue from 'vue';
|
||||
import VueMarkdown from 'vue-markdown';
|
||||
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
init() {
|
||||
Vue.component('vue-markdown', VueMarkdown);
|
||||
|
||||
Vue.component('launch', {
|
||||
template:
|
||||
'<el-button @click="confirm" size="mini" icon="el-icon-info" circle></el-button>',
|
||||
props: {
|
||||
location: String
|
||||
},
|
||||
methods: {
|
||||
parse() {
|
||||
this.$el.style.display = $app.checkCanInviteSelf(
|
||||
this.location
|
||||
)
|
||||
? ''
|
||||
: 'none';
|
||||
},
|
||||
confirm() {
|
||||
API.$emit('SHOW_LAUNCH_DIALOG', this.location);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
location() {
|
||||
this.parse();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.parse();
|
||||
}
|
||||
});
|
||||
|
||||
Vue.component('invite-yourself', {
|
||||
template:
|
||||
'<el-button @click="confirm" size="mini" icon="el-icon-message" circle></el-button>',
|
||||
props: {
|
||||
location: String,
|
||||
shortname: String
|
||||
},
|
||||
methods: {
|
||||
parse() {
|
||||
this.$el.style.display = $app.checkCanInviteSelf(
|
||||
this.location
|
||||
)
|
||||
? ''
|
||||
: 'none';
|
||||
},
|
||||
confirm() {
|
||||
$app.selfInvite(this.location, this.shortname);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
location() {
|
||||
this.parse();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.parse();
|
||||
}
|
||||
});
|
||||
|
||||
Vue.component('location', {
|
||||
template:
|
||||
"<span><span @click=\"showWorldDialog\" :class=\"{ 'x-link': link && this.location !== 'private' && this.location !== 'offline'}\">" +
|
||||
'<i v-if="isTraveling" class="el-icon el-icon-loading" style="display:inline-block;margin-right:5px"></i>' +
|
||||
'<span>{{ text }}</span></span>' +
|
||||
'<span v-if="groupName" @click="showGroupDialog" :class="{ \'x-link\': link}">({{ groupName }})</span>' +
|
||||
'<span v-if="region" class="flags" :class="region" style="display:inline-block;margin-left:5px"></span>' +
|
||||
'<i v-if="strict" class="el-icon el-icon-lock" style="display:inline-block;margin-left:5px"></i></span>',
|
||||
props: {
|
||||
location: String,
|
||||
traveling: String,
|
||||
hint: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
grouphint: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
link: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
text: this.location,
|
||||
region: this.region,
|
||||
strict: this.strict,
|
||||
isTraveling: this.isTraveling,
|
||||
groupName: this.groupName
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
parse() {
|
||||
this.isTraveling = false;
|
||||
this.groupName = '';
|
||||
var instanceId = this.location;
|
||||
if (
|
||||
typeof this.traveling !== 'undefined' &&
|
||||
this.location === 'traveling'
|
||||
) {
|
||||
instanceId = this.traveling;
|
||||
this.isTraveling = true;
|
||||
}
|
||||
this.text = instanceId;
|
||||
var L = $utils.parseLocation(instanceId);
|
||||
if (L.isOffline) {
|
||||
this.text = 'Offline';
|
||||
} else if (L.isPrivate) {
|
||||
this.text = 'Private';
|
||||
} else if (L.isTraveling) {
|
||||
this.text = 'Traveling';
|
||||
} else if (
|
||||
typeof this.hint === 'string' &&
|
||||
this.hint !== ''
|
||||
) {
|
||||
if (L.instanceId) {
|
||||
this.text = `${this.hint} #${L.instanceName} ${L.accessTypeName}`;
|
||||
} else {
|
||||
this.text = this.hint;
|
||||
}
|
||||
} else if (L.worldId) {
|
||||
var ref = API.cachedWorlds.get(L.worldId);
|
||||
if (typeof ref === 'undefined') {
|
||||
$app.getWorldName(L.worldId).then((worldName) => {
|
||||
if (L.tag === instanceId) {
|
||||
if (L.instanceId) {
|
||||
this.text = `${worldName} #${L.instanceName} ${L.accessTypeName}`;
|
||||
} else {
|
||||
this.text = worldName;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (L.instanceId) {
|
||||
this.text = `${ref.name} #${L.instanceName} ${L.accessTypeName}`;
|
||||
} else {
|
||||
this.text = ref.name;
|
||||
}
|
||||
}
|
||||
if (this.grouphint) {
|
||||
this.groupName = this.grouphint;
|
||||
} else if (L.groupId) {
|
||||
this.groupName = L.groupId;
|
||||
$app.getGroupName(instanceId).then((groupName) => {
|
||||
if (L.tag === instanceId) {
|
||||
this.groupName = groupName;
|
||||
}
|
||||
});
|
||||
}
|
||||
this.region = '';
|
||||
if (!L.isOffline && !L.isPrivate && !L.isTraveling) {
|
||||
this.region = L.region;
|
||||
if (!L.region && L.instanceId) {
|
||||
this.region = 'us';
|
||||
}
|
||||
}
|
||||
this.strict = L.strict;
|
||||
},
|
||||
showWorldDialog() {
|
||||
if (this.link) {
|
||||
var instanceId = this.location;
|
||||
if (this.traveling && this.location === 'traveling') {
|
||||
instanceId = this.traveling;
|
||||
}
|
||||
if (!instanceId && this.hint.length === 8) {
|
||||
// shortName
|
||||
API.$emit('SHOW_WORLD_DIALOG_SHORTNAME', this.hint);
|
||||
return;
|
||||
}
|
||||
API.$emit('SHOW_WORLD_DIALOG', instanceId);
|
||||
}
|
||||
},
|
||||
showGroupDialog() {
|
||||
var location = this.location;
|
||||
if (this.isTraveling) {
|
||||
location = this.traveling;
|
||||
}
|
||||
if (!location || !this.link) {
|
||||
return;
|
||||
}
|
||||
var L = $utils.parseLocation(location);
|
||||
if (!L.groupId) {
|
||||
return;
|
||||
}
|
||||
API.$emit('SHOW_GROUP_DIALOG', L.groupId);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
location() {
|
||||
this.parse();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.parse();
|
||||
}
|
||||
});
|
||||
|
||||
Vue.component('location-world', {
|
||||
template:
|
||||
'<span><span @click="showLaunchDialog" class="x-link">' +
|
||||
'<i v-if="isUnlocked" class="el-icon el-icon-unlock" style="display:inline-block;margin-right:5px"></i>' +
|
||||
'<span>#{{ instanceName }} {{ accessTypeName }}</span></span>' +
|
||||
'<span v-if="groupName" @click="showGroupDialog" class="x-link">({{ groupName }})</span>' +
|
||||
'<span class="flags" :class="region" style="display:inline-block;margin-left:5px"></span>' +
|
||||
'<i v-if="strict" class="el-icon el-icon-lock" style="display:inline-block;margin-left:5px"></i></span>',
|
||||
props: {
|
||||
locationobject: Object,
|
||||
currentuserid: String,
|
||||
worlddialogshortname: String,
|
||||
grouphint: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
location: this.location,
|
||||
instanceName: this.instanceName,
|
||||
accessTypeName: this.accessTypeName,
|
||||
region: this.region,
|
||||
shortName: this.shortName,
|
||||
isUnlocked: this.isUnlocked,
|
||||
strict: this.strict,
|
||||
groupName: this.groupName
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
parse() {
|
||||
this.location = this.locationobject.tag;
|
||||
this.instanceName = this.locationobject.instanceName;
|
||||
this.accessTypeName = this.locationobject.accessTypeName;
|
||||
this.strict = this.locationobject.strict;
|
||||
this.shortName = this.locationobject.shortName;
|
||||
|
||||
this.isUnlocked = false;
|
||||
if (
|
||||
(this.worlddialogshortname &&
|
||||
this.locationobject.shortName &&
|
||||
this.worlddialogshortname ===
|
||||
this.locationobject.shortName) ||
|
||||
this.currentuserid === this.locationobject.userId
|
||||
) {
|
||||
this.isUnlocked = true;
|
||||
}
|
||||
|
||||
this.region = this.locationobject.region;
|
||||
if (!this.region) {
|
||||
this.region = 'us';
|
||||
}
|
||||
|
||||
this.groupName = '';
|
||||
if (this.grouphint) {
|
||||
this.groupName = this.grouphint;
|
||||
} else if (this.locationobject.groupId) {
|
||||
this.groupName = this.locationobject.groupId;
|
||||
$app.getGroupName(this.locationobject.groupId).then(
|
||||
(groupName) => {
|
||||
this.groupName = groupName;
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
showLaunchDialog() {
|
||||
API.$emit(
|
||||
'SHOW_LAUNCH_DIALOG',
|
||||
this.location,
|
||||
this.shortName
|
||||
);
|
||||
},
|
||||
showGroupDialog() {
|
||||
if (!this.location) {
|
||||
return;
|
||||
}
|
||||
var L = $utils.parseLocation(this.location);
|
||||
if (!L.groupId) {
|
||||
return;
|
||||
}
|
||||
API.$emit('SHOW_GROUP_DIALOG', L.groupId);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
locationobject() {
|
||||
this.parse();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.parse();
|
||||
}
|
||||
});
|
||||
|
||||
Vue.component('last-join', {
|
||||
template:
|
||||
'<span>' +
|
||||
'<el-tooltip placement="top" style="margin-left:5px" v-if="lastJoin">' +
|
||||
'<div slot="content">' +
|
||||
'<span>{{ $t("dialog.user.info.last_join") }} <timer :epoch="lastJoin"></timer></span>' +
|
||||
'</div>' +
|
||||
'<i v-if="lastJoin" class="el-icon el-icon-location-outline" style="display:inline-block"></i>' +
|
||||
'</el-tooltip>' +
|
||||
'</span>',
|
||||
props: {
|
||||
location: String,
|
||||
currentlocation: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
lastJoin: this.lastJoin
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
parse() {
|
||||
this.lastJoin = $app.instanceJoinHistory.get(this.location);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
location() {
|
||||
this.parse();
|
||||
},
|
||||
currentlocation() {
|
||||
this.parse();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.parse();
|
||||
}
|
||||
});
|
||||
|
||||
Vue.component('instance-info', {
|
||||
template:
|
||||
'<div style="display:inline-block;margin-left:5px">' +
|
||||
'<el-tooltip v-if="isValidInstance" placement="bottom">' +
|
||||
'<div slot="content">' +
|
||||
'<template v-if="isClosed"><span>Closed At: {{ closedAt | formatDate(\'long\') }}</span></br></template>' +
|
||||
'<template v-if="canCloseInstance"><el-button :disabled="isClosed" size="mini" type="primary" @click="$app.closeInstance(location)">{{ $t("dialog.user.info.close_instance") }}</el-button></br></br></template>' +
|
||||
'<span><span style="color:#409eff">PC: </span>{{ platforms.standalonewindows }}</span></br>' +
|
||||
'<span><span style="color:#67c23a">Android: </span>{{ platforms.android }}</span></br>' +
|
||||
'<span>{{ $t("dialog.user.info.instance_game_version") }} {{ gameServerVersion }}</span></br>' +
|
||||
'<span v-if="queueEnabled">{{ $t("dialog.user.info.instance_queuing_enabled") }}</br></span>' +
|
||||
'<span v-if="userList.length">{{ $t("dialog.user.info.instance_users") }}</br></span>' +
|
||||
'<template v-for="user in userList"><span style="cursor:pointer;margin-right:5px" @click="showUserDialog(user.id)" v-text="user.displayName"></span></template>' +
|
||||
'</div>' +
|
||||
'<i class="el-icon-caret-bottom"></i>' +
|
||||
'</el-tooltip>' +
|
||||
'<span v-if="occupants" style="margin-left:5px">{{ occupants }}/{{ capacity }}</span>' +
|
||||
'<span v-if="friendcount" style="margin-left:5px">({{ friendcount }})</span>' +
|
||||
'<span v-if="isFull" style="margin-left:5px;color:lightcoral">{{ $t("dialog.user.info.instance_full") }}</span>' +
|
||||
'<span v-if="isHardClosed" style="margin-left:5px;color:lightcoral">{{ $t("dialog.user.info.instance_hard_closed") }}</span>' +
|
||||
'<span v-else-if="isClosed" style="margin-left:5px;color:lightcoral">{{ $t("dialog.user.info.instance_closed") }}</span>' +
|
||||
'<span v-if="queueSize" style="margin-left:5px">{{ $t("dialog.user.info.instance_queue") }} {{ queueSize }}</span>' +
|
||||
'<span v-if="isAgeGated" style="margin-left:5px;color:lightcoral">{{ $t("dialog.user.info.instance_age_gated") }}</span>' +
|
||||
'</div>',
|
||||
props: {
|
||||
location: String,
|
||||
instance: Object,
|
||||
friendcount: Number,
|
||||
updateelement: Number
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isValidInstance: this.isValidInstance,
|
||||
isFull: this.isFull,
|
||||
isClosed: this.isClosed,
|
||||
isHardClosed: this.isHardClosed,
|
||||
closedAt: this.closedAt,
|
||||
occupants: this.occupants,
|
||||
capacity: this.capacity,
|
||||
queueSize: this.queueSize,
|
||||
queueEnabled: this.queueEnabled,
|
||||
platforms: this.platforms,
|
||||
userList: this.userList,
|
||||
gameServerVersion: this.gameServerVersion,
|
||||
canCloseInstance: this.canCloseInstance
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
parse() {
|
||||
this.isValidInstance = false;
|
||||
this.isFull = false;
|
||||
this.isClosed = false;
|
||||
this.isHardClosed = false;
|
||||
this.closedAt = '';
|
||||
this.occupants = 0;
|
||||
this.capacity = 0;
|
||||
this.queueSize = 0;
|
||||
this.queueEnabled = false;
|
||||
this.platforms = [];
|
||||
this.userList = [];
|
||||
this.gameServerVersion = '';
|
||||
this.canCloseInstance = false;
|
||||
this.isAgeGated = false;
|
||||
if (
|
||||
!this.location ||
|
||||
!this.instance ||
|
||||
Object.keys(this.instance).length === 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.isValidInstance = true;
|
||||
this.isFull =
|
||||
typeof this.instance.hasCapacityForYou !==
|
||||
'undefined' && !this.instance.hasCapacityForYou;
|
||||
if (this.instance.closedAt) {
|
||||
this.isClosed = true;
|
||||
this.closedAt = this.instance.closedAt;
|
||||
}
|
||||
this.isHardClosed = this.instance.hardClose === true;
|
||||
this.occupants = this.instance.userCount;
|
||||
if (this.location === $app.lastLocation.location) {
|
||||
// use gameLog for occupants when in same location
|
||||
this.occupants = $app.lastLocation.playerList.size;
|
||||
}
|
||||
this.capacity = this.instance.capacity;
|
||||
this.gameServerVersion = this.instance.gameServerVersion;
|
||||
this.queueSize = this.instance.queueSize;
|
||||
if (this.instance.platforms) {
|
||||
this.platforms = this.instance.platforms;
|
||||
}
|
||||
if (this.instance.users) {
|
||||
this.userList = this.instance.users;
|
||||
}
|
||||
if (this.instance.ownerId === API.currentUser.id) {
|
||||
this.canCloseInstance = true;
|
||||
} else if (this.instance?.ownerId?.startsWith('grp_')) {
|
||||
// check group perms
|
||||
var groupId = this.instance.ownerId;
|
||||
var group = API.cachedGroups.get(groupId);
|
||||
this.canCloseInstance = $app.hasGroupPermission(
|
||||
group,
|
||||
'group-instance-moderate'
|
||||
);
|
||||
}
|
||||
this.isAgeGated = this.instance.ageGate === true;
|
||||
if (this.location && this.location.includes('~ageGate')) {
|
||||
// dumb workaround for API not returning `ageGate`
|
||||
this.isAgeGated = true;
|
||||
}
|
||||
},
|
||||
showUserDialog(userId) {
|
||||
API.$emit('SHOW_USER_DIALOG', userId);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
updateelement() {
|
||||
this.parse();
|
||||
},
|
||||
location() {
|
||||
this.parse();
|
||||
},
|
||||
friendcount() {
|
||||
this.parse();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.parse();
|
||||
}
|
||||
});
|
||||
|
||||
Vue.component('avatar-info', {
|
||||
template:
|
||||
'<div @click="confirm" class="avatar-info">' +
|
||||
'<span style="margin-right:5px">{{ avatarName }}</span>' +
|
||||
'<span style="margin-right:5px" :class="color">{{ avatarType }}</span>' +
|
||||
'<span style="color:#909399;font-family:monospace;font-size:12px;">{{ avatarTags }}</span>' +
|
||||
'</div>',
|
||||
props: {
|
||||
imageurl: String,
|
||||
userid: String,
|
||||
hintownerid: String,
|
||||
hintavatarname: String,
|
||||
avatartags: Array
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
avatarName: this.avatarName,
|
||||
avatarType: this.avatarType,
|
||||
avatarTags: this.avatarTags,
|
||||
color: this.color
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async parse() {
|
||||
this.ownerId = '';
|
||||
this.avatarName = '';
|
||||
this.avatarType = '';
|
||||
this.color = '';
|
||||
this.avatarTags = '';
|
||||
if (!this.imageurl) {
|
||||
this.avatarName = '-';
|
||||
} else if (this.hintownerid) {
|
||||
this.avatarName = this.hintavatarname;
|
||||
this.ownerId = this.hintownerid;
|
||||
} else {
|
||||
try {
|
||||
var avatarInfo = await $app.getAvatarName(
|
||||
this.imageurl
|
||||
);
|
||||
this.avatarName = avatarInfo.avatarName;
|
||||
this.ownerId = avatarInfo.ownerId;
|
||||
} catch (err) {}
|
||||
}
|
||||
if (typeof this.userid === 'undefined' || !this.ownerId) {
|
||||
this.color = '';
|
||||
this.avatarType = '';
|
||||
} else if (this.ownerId === this.userid) {
|
||||
this.color = 'avatar-info-own';
|
||||
this.avatarType = '(own)';
|
||||
} else {
|
||||
this.color = 'avatar-info-public';
|
||||
this.avatarType = '(public)';
|
||||
}
|
||||
if (typeof this.avatartags === 'object') {
|
||||
var tagString = '';
|
||||
for (var i = 0; i < this.avatartags.length; i++) {
|
||||
var tagName = this.avatartags[i].replace(
|
||||
'content_',
|
||||
''
|
||||
);
|
||||
tagString += tagName;
|
||||
if (i < this.avatartags.length - 1) {
|
||||
tagString += ', ';
|
||||
}
|
||||
}
|
||||
this.avatarTags = tagString;
|
||||
}
|
||||
},
|
||||
confirm() {
|
||||
if (!this.imageurl) {
|
||||
return;
|
||||
}
|
||||
$app.showAvatarAuthorDialog(
|
||||
this.userid,
|
||||
this.ownerId,
|
||||
this.imageurl
|
||||
);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
imageurl() {
|
||||
this.parse();
|
||||
},
|
||||
userid() {
|
||||
this.parse();
|
||||
},
|
||||
avatartags() {
|
||||
this.parse();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.parse();
|
||||
}
|
||||
});
|
||||
|
||||
Vue.component('display-name', {
|
||||
template:
|
||||
'<span @click="showUserDialog" class="x-link">{{ username }}</span>',
|
||||
props: {
|
||||
userid: String,
|
||||
location: String,
|
||||
key: Number,
|
||||
hint: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
username: this.username
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async parse() {
|
||||
this.username = this.userid;
|
||||
if (this.hint) {
|
||||
this.username = this.hint;
|
||||
} else if (this.userid) {
|
||||
var args = await API.getCachedUser({
|
||||
userId: this.userid
|
||||
});
|
||||
}
|
||||
if (
|
||||
typeof args !== 'undefined' &&
|
||||
typeof args.json !== 'undefined' &&
|
||||
typeof args.json.displayName !== 'undefined'
|
||||
) {
|
||||
this.username = args.json.displayName;
|
||||
}
|
||||
},
|
||||
showUserDialog() {
|
||||
$app.showUserDialog(this.userid);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
location() {
|
||||
this.parse();
|
||||
},
|
||||
key() {
|
||||
this.parse();
|
||||
},
|
||||
userid() {
|
||||
this.parse();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.parse();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
112
src/classes/updateLoop.js
Normal file
@@ -0,0 +1,112 @@
|
||||
import * as workerTimers from 'worker-timers';
|
||||
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
init() {
|
||||
API.$on('LOGIN', function () {
|
||||
$app.nextCurrentUserRefresh = 300;
|
||||
$app.nextFriendsRefresh = 3600;
|
||||
$app.nextGroupInstanceRefresh = 0;
|
||||
});
|
||||
}
|
||||
|
||||
_data = {
|
||||
nextCurrentUserRefresh: 300,
|
||||
nextFriendsRefresh: 3600,
|
||||
nextGroupInstanceRefresh: 0,
|
||||
nextAppUpdateCheck: 3600,
|
||||
ipcTimeout: 0,
|
||||
nextClearVRCXCacheCheck: 0,
|
||||
nextDiscordUpdate: 0,
|
||||
nextAutoStateChange: 0,
|
||||
nextGetLogCheck: 0,
|
||||
nextGameRunningCheck: 0
|
||||
};
|
||||
|
||||
_methods = {
|
||||
async updateLoop() {
|
||||
try {
|
||||
if (API.isLoggedIn === true) {
|
||||
if (--this.nextCurrentUserRefresh <= 0) {
|
||||
this.nextCurrentUserRefresh = 300; // 5min
|
||||
API.getCurrentUser();
|
||||
}
|
||||
if (--this.nextFriendsRefresh <= 0) {
|
||||
this.nextFriendsRefresh = 3600; // 1hour
|
||||
this.refreshFriendsList();
|
||||
this.updateStoredUser(API.currentUser);
|
||||
if (this.isGameRunning) {
|
||||
API.refreshPlayerModerations();
|
||||
}
|
||||
}
|
||||
if (--this.nextGroupInstanceRefresh <= 0) {
|
||||
if (this.friendLogInitStatus) {
|
||||
this.nextGroupInstanceRefresh = 300; // 5min
|
||||
API.getUsersGroupInstances();
|
||||
}
|
||||
AppApi.CheckGameRunning();
|
||||
}
|
||||
if (--this.nextAppUpdateCheck <= 0) {
|
||||
this.nextAppUpdateCheck = 3600; // 1hour
|
||||
if (this.autoUpdateVRCX !== 'Off') {
|
||||
this.checkForVRCXUpdate();
|
||||
}
|
||||
}
|
||||
if (--this.ipcTimeout <= 0) {
|
||||
this.ipcEnabled = false;
|
||||
}
|
||||
if (
|
||||
--this.nextClearVRCXCacheCheck <= 0 &&
|
||||
this.clearVRCXCacheFrequency > 0
|
||||
) {
|
||||
this.nextClearVRCXCacheCheck =
|
||||
this.clearVRCXCacheFrequency / 2;
|
||||
this.clearVRCXCache();
|
||||
}
|
||||
if (--this.nextDiscordUpdate <= 0) {
|
||||
this.nextDiscordUpdate = 3;
|
||||
if (this.discordActive) {
|
||||
this.updateDiscord();
|
||||
}
|
||||
}
|
||||
if (--this.nextAutoStateChange <= 0) {
|
||||
this.nextAutoStateChange = 3;
|
||||
this.updateAutoStateChange();
|
||||
}
|
||||
if (
|
||||
(this.isRunningUnderWine || LINUX) &&
|
||||
--this.nextGetLogCheck <= 0
|
||||
) {
|
||||
this.nextGetLogCheck = 0.5;
|
||||
const logLines = await LogWatcher.GetLogLines();
|
||||
if (logLines) {
|
||||
logLines.forEach((logLine) => {
|
||||
$app.addGameLogEvent(logLine);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (
|
||||
(this.isRunningUnderWine || LINUX) &&
|
||||
--this.nextGameRunningCheck <= 0
|
||||
) {
|
||||
if (LINUX) {
|
||||
this.nextGameRunningCheck = 1;
|
||||
$app.updateIsGameRunning(await AppApi.IsGameRunning(), await AppApi.IsSteamVRRunning(), false);
|
||||
} else {
|
||||
this.nextGameRunningCheck = 3;
|
||||
AppApi.CheckGameRunning();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
API.isRefreshFriendsLoading = false;
|
||||
console.error(err);
|
||||
}
|
||||
workerTimers.setTimeout(() => this.updateLoop(), 1000);
|
||||
}
|
||||
};
|
||||
}
|
||||
303
src/classes/utils.js
Normal file
@@ -0,0 +1,303 @@
|
||||
export default {
|
||||
removeFromArray(array, item) {
|
||||
var { length } = array;
|
||||
for (var i = 0; i < length; ++i) {
|
||||
if (array[i] === item) {
|
||||
array.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
arraysMatch(a, b) {
|
||||
if (!Array.isArray(a) || !Array.isArray(b)) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
a.length === b.length &&
|
||||
a.every(
|
||||
(element, index) =>
|
||||
JSON.stringify(element) === JSON.stringify(b[index])
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
escapeTag(tag) {
|
||||
var s = String(tag);
|
||||
return s.replace(/["&'<>]/g, (c) => `&#${c.charCodeAt(0)};`);
|
||||
},
|
||||
|
||||
escapeTagRecursive(obj) {
|
||||
if (typeof obj === 'string') {
|
||||
return this.escapeTag(obj);
|
||||
}
|
||||
if (typeof obj === 'object') {
|
||||
for (var key in obj) {
|
||||
obj[key] = this.escapeTagRecursive(obj[key]);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
|
||||
timeToText(sec) {
|
||||
var n = Number(sec);
|
||||
if (isNaN(n)) {
|
||||
return this.escapeTag(sec);
|
||||
}
|
||||
n = Math.floor(n / 1000);
|
||||
var arr = [];
|
||||
if (n < 0) {
|
||||
n = -n;
|
||||
}
|
||||
if (n >= 86400) {
|
||||
arr.push(`${Math.floor(n / 86400)}d`);
|
||||
n %= 86400;
|
||||
}
|
||||
if (n >= 3600) {
|
||||
arr.push(`${Math.floor(n / 3600)}h`);
|
||||
n %= 3600;
|
||||
}
|
||||
if (n >= 60) {
|
||||
arr.push(`${Math.floor(n / 60)}m`);
|
||||
n %= 60;
|
||||
}
|
||||
if (arr.length === 0 && n < 60) {
|
||||
arr.push(`${n}s`);
|
||||
}
|
||||
return arr.join(' ');
|
||||
},
|
||||
|
||||
textToHex(text) {
|
||||
var s = String(text);
|
||||
return s
|
||||
.split('')
|
||||
.map((c) => c.charCodeAt(0).toString(16))
|
||||
.join(' ');
|
||||
},
|
||||
|
||||
commaNumber(num) {
|
||||
if (!num) {
|
||||
return '0';
|
||||
}
|
||||
var s = String(Number(num));
|
||||
return s.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
|
||||
},
|
||||
|
||||
parseLocation(tag) {
|
||||
var _tag = String(tag || '');
|
||||
var ctx = {
|
||||
tag: _tag,
|
||||
isOffline: false,
|
||||
isPrivate: false,
|
||||
isTraveling: false,
|
||||
worldId: '',
|
||||
instanceId: '',
|
||||
instanceName: '',
|
||||
accessType: '',
|
||||
accessTypeName: '',
|
||||
region: '',
|
||||
shortName: '',
|
||||
userId: null,
|
||||
hiddenId: null,
|
||||
privateId: null,
|
||||
friendsId: null,
|
||||
groupId: null,
|
||||
groupAccessType: null,
|
||||
canRequestInvite: false,
|
||||
strict: false,
|
||||
ageGate: false
|
||||
};
|
||||
if (_tag === 'offline' || _tag === 'offline:offline') {
|
||||
ctx.isOffline = true;
|
||||
} else if (_tag === 'private' || _tag === 'private:private') {
|
||||
ctx.isPrivate = true;
|
||||
} else if (_tag === 'traveling' || _tag === 'traveling:traveling') {
|
||||
ctx.isTraveling = true;
|
||||
} else if (_tag.startsWith('local') === false) {
|
||||
var sep = _tag.indexOf(':');
|
||||
// technically not part of instance id, but might be there when coping id from url so why not support it
|
||||
var shortNameQualifier = '&shortName=';
|
||||
var shortNameIndex = _tag.indexOf(shortNameQualifier);
|
||||
if (shortNameIndex >= 0) {
|
||||
ctx.shortName = _tag.substr(
|
||||
shortNameIndex + shortNameQualifier.length
|
||||
);
|
||||
_tag = _tag.substr(0, shortNameIndex);
|
||||
}
|
||||
if (sep >= 0) {
|
||||
ctx.worldId = _tag.substr(0, sep);
|
||||
ctx.instanceId = _tag.substr(sep + 1);
|
||||
ctx.instanceId.split('~').forEach((s, i) => {
|
||||
if (i) {
|
||||
var A = s.indexOf('(');
|
||||
var Z = A >= 0 ? s.lastIndexOf(')') : -1;
|
||||
var key = Z >= 0 ? s.substr(0, A) : s;
|
||||
var value = A < Z ? s.substr(A + 1, Z - A - 1) : '';
|
||||
if (key === 'hidden') {
|
||||
ctx.hiddenId = value;
|
||||
} else if (key === 'private') {
|
||||
ctx.privateId = value;
|
||||
} else if (key === 'friends') {
|
||||
ctx.friendsId = value;
|
||||
} else if (key === 'canRequestInvite') {
|
||||
ctx.canRequestInvite = true;
|
||||
} else if (key === 'region') {
|
||||
ctx.region = value;
|
||||
} else if (key === 'group') {
|
||||
ctx.groupId = value;
|
||||
} else if (key === 'groupAccessType') {
|
||||
ctx.groupAccessType = value;
|
||||
} else if (key === 'strict') {
|
||||
ctx.strict = true;
|
||||
} else if (key === 'ageGate') {
|
||||
ctx.ageGate = true;
|
||||
}
|
||||
} else {
|
||||
ctx.instanceName = s;
|
||||
}
|
||||
});
|
||||
ctx.accessType = 'public';
|
||||
if (ctx.privateId !== null) {
|
||||
if (ctx.canRequestInvite) {
|
||||
// InvitePlus
|
||||
ctx.accessType = 'invite+';
|
||||
} else {
|
||||
// InviteOnly
|
||||
ctx.accessType = 'invite';
|
||||
}
|
||||
ctx.userId = ctx.privateId;
|
||||
} else if (ctx.friendsId !== null) {
|
||||
// FriendsOnly
|
||||
ctx.accessType = 'friends';
|
||||
ctx.userId = ctx.friendsId;
|
||||
} else if (ctx.hiddenId !== null) {
|
||||
// FriendsOfGuests
|
||||
ctx.accessType = 'friends+';
|
||||
ctx.userId = ctx.hiddenId;
|
||||
} else if (ctx.groupId !== null) {
|
||||
// Group
|
||||
ctx.accessType = 'group';
|
||||
}
|
||||
ctx.accessTypeName = ctx.accessType;
|
||||
if (ctx.groupAccessType !== null) {
|
||||
if (ctx.groupAccessType === 'public') {
|
||||
ctx.accessTypeName = 'groupPublic';
|
||||
} else if (ctx.groupAccessType === 'plus') {
|
||||
ctx.accessTypeName = 'groupPlus';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ctx.worldId = _tag;
|
||||
}
|
||||
}
|
||||
return ctx;
|
||||
},
|
||||
|
||||
displayLocation(location, worldName, groupName) {
|
||||
var text = worldName;
|
||||
var L = this.parseLocation(location);
|
||||
if (L.isOffline) {
|
||||
text = 'Offline';
|
||||
} else if (L.isPrivate) {
|
||||
text = 'Private';
|
||||
} else if (L.isTraveling) {
|
||||
text = 'Traveling';
|
||||
} else if (L.worldId) {
|
||||
if (groupName) {
|
||||
text = `${worldName} ${L.accessTypeName}(${groupName})`;
|
||||
} else if (L.instanceId) {
|
||||
text = `${worldName} ${L.accessTypeName}`;
|
||||
}
|
||||
}
|
||||
return text;
|
||||
},
|
||||
|
||||
extractFileId(s) {
|
||||
var match = String(s).match(/file_[0-9A-Za-z-]+/);
|
||||
return match ? match[0] : '';
|
||||
},
|
||||
|
||||
extractFileVersion(s) {
|
||||
var match = /(?:\/file_[0-9A-Za-z-]+\/)([0-9]+)/gi.exec(s);
|
||||
return match ? match[1] : '';
|
||||
},
|
||||
|
||||
extractVariantVersion(url) {
|
||||
if (!url) {
|
||||
return '0';
|
||||
}
|
||||
try {
|
||||
const params = new URLSearchParams(new URL(url).search);
|
||||
const version = params.get('v');
|
||||
if (version) {
|
||||
return version;
|
||||
}
|
||||
return '0';
|
||||
} catch {
|
||||
return '0';
|
||||
}
|
||||
},
|
||||
|
||||
buildTreeData(json) {
|
||||
var node = [];
|
||||
for (var key in json) {
|
||||
if (key[0] === '$') {
|
||||
continue;
|
||||
}
|
||||
var value = json[key];
|
||||
if (Array.isArray(value) && value.length === 0) {
|
||||
node.push({
|
||||
key,
|
||||
value: '[]'
|
||||
});
|
||||
} else if (
|
||||
value === Object(value) &&
|
||||
Object.keys(value).length === 0
|
||||
) {
|
||||
node.push({
|
||||
key,
|
||||
value: '{}'
|
||||
});
|
||||
} else if (Array.isArray(value)) {
|
||||
node.push({
|
||||
children: value.map((val, idx) => {
|
||||
if (val === Object(val)) {
|
||||
return {
|
||||
children: this.buildTreeData(val),
|
||||
key: idx
|
||||
};
|
||||
}
|
||||
return {
|
||||
key: idx,
|
||||
value: val
|
||||
};
|
||||
}),
|
||||
key
|
||||
});
|
||||
} else if (value === Object(value)) {
|
||||
node.push({
|
||||
children: this.buildTreeData(value),
|
||||
key
|
||||
});
|
||||
} else {
|
||||
node.push({
|
||||
key,
|
||||
value: String(value)
|
||||
});
|
||||
}
|
||||
}
|
||||
node.sort(function (a, b) {
|
||||
var A = String(a.key).toUpperCase();
|
||||
var B = String(b.key).toUpperCase();
|
||||
if (A < B) {
|
||||
return -1;
|
||||
}
|
||||
if (A > B) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
return node;
|
||||
}
|
||||
};
|
||||
322
src/classes/vrcRegistry.js
Normal file
@@ -0,0 +1,322 @@
|
||||
import configRepository from '../repository/config.js';
|
||||
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
init() {}
|
||||
|
||||
_data = {
|
||||
registryBackupDialog: {
|
||||
visible: false
|
||||
},
|
||||
|
||||
registryBackupTable: {
|
||||
data: [],
|
||||
tableProps: {
|
||||
stripe: true,
|
||||
size: 'mini',
|
||||
defaultSort: {
|
||||
prop: 'date',
|
||||
order: 'descending'
|
||||
}
|
||||
},
|
||||
layout: 'table'
|
||||
}
|
||||
};
|
||||
|
||||
_methods = {
|
||||
showRegistryBackupDialog() {
|
||||
this.$nextTick(() =>
|
||||
$app.adjustDialogZ(this.$refs.registryBackupDialog.$el)
|
||||
);
|
||||
var D = this.registryBackupDialog;
|
||||
D.visible = true;
|
||||
this.updateRegistryBackupDialog();
|
||||
},
|
||||
|
||||
async updateRegistryBackupDialog() {
|
||||
var D = this.registryBackupDialog;
|
||||
this.registryBackupTable.data = [];
|
||||
if (!D.visible) {
|
||||
return;
|
||||
}
|
||||
var backupsJson = await configRepository.getString(
|
||||
'VRCX_VRChatRegistryBackups'
|
||||
);
|
||||
if (!backupsJson) {
|
||||
backupsJson = JSON.stringify([]);
|
||||
}
|
||||
this.registryBackupTable.data = JSON.parse(backupsJson);
|
||||
},
|
||||
|
||||
async promptVrcRegistryBackupName() {
|
||||
var name = await this.$prompt(
|
||||
'Enter a name for the backup',
|
||||
'Backup Name',
|
||||
{
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
inputPattern: /\S+/,
|
||||
inputErrorMessage: 'Name is required',
|
||||
inputValue: 'Backup'
|
||||
}
|
||||
);
|
||||
if (name.action === 'confirm') {
|
||||
this.backupVrcRegistry(name.value);
|
||||
}
|
||||
},
|
||||
|
||||
async backupVrcRegistry(name) {
|
||||
var regJson;
|
||||
if (LINUX) {
|
||||
regJson = await AppApi.GetVRChatRegistryJson();
|
||||
regJson = JSON.parse(regJson);
|
||||
} else {
|
||||
regJson = await AppApi.GetVRChatRegistry();
|
||||
}
|
||||
var newBackup = {
|
||||
name,
|
||||
date: new Date().toJSON(),
|
||||
data: regJson
|
||||
};
|
||||
var backupsJson = await configRepository.getString(
|
||||
'VRCX_VRChatRegistryBackups'
|
||||
);
|
||||
if (!backupsJson) {
|
||||
backupsJson = JSON.stringify([]);
|
||||
}
|
||||
var backups = JSON.parse(backupsJson);
|
||||
backups.push(newBackup);
|
||||
await configRepository.setString(
|
||||
'VRCX_VRChatRegistryBackups',
|
||||
JSON.stringify(backups)
|
||||
);
|
||||
await this.updateRegistryBackupDialog();
|
||||
},
|
||||
|
||||
async deleteVrcRegistryBackup(row) {
|
||||
var backups = this.registryBackupTable.data;
|
||||
$app.removeFromArray(backups, row);
|
||||
await configRepository.setString(
|
||||
'VRCX_VRChatRegistryBackups',
|
||||
JSON.stringify(backups)
|
||||
);
|
||||
await this.updateRegistryBackupDialog();
|
||||
},
|
||||
|
||||
restoreVrcRegistryBackup(row) {
|
||||
this.$confirm('Continue? Restore Backup', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning',
|
||||
callback: (action) => {
|
||||
if (action !== 'confirm') {
|
||||
return;
|
||||
}
|
||||
var data = JSON.stringify(row.data);
|
||||
AppApi.SetVRChatRegistry(data)
|
||||
.then(() => {
|
||||
this.$message({
|
||||
message: 'VRC registry settings restored',
|
||||
type: 'success'
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
this.$message({
|
||||
message: `Failed to restore VRC registry settings, check console for full error: ${e}`,
|
||||
type: 'error'
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
saveVrcRegistryBackupToFile(row) {
|
||||
this.downloadAndSaveJson(row.name, row.data);
|
||||
},
|
||||
|
||||
async openJsonFileSelectorDialogElectron() {
|
||||
return new Promise((resolve) => {
|
||||
const fileInput = document.createElement('input');
|
||||
fileInput.type = 'file';
|
||||
fileInput.accept = '.json';
|
||||
fileInput.style.display = 'none';
|
||||
document.body.appendChild(fileInput);
|
||||
|
||||
fileInput.onchange = function(event) {
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function() {
|
||||
fileInput.remove();
|
||||
resolve(reader.result);
|
||||
};
|
||||
reader.readAsText(file);
|
||||
} else {
|
||||
fileInput.remove();
|
||||
resolve(null);
|
||||
}
|
||||
};
|
||||
|
||||
fileInput.click();
|
||||
});
|
||||
},
|
||||
|
||||
async restoreVrcRegistryFromFile() {
|
||||
if (WINDOWS) {
|
||||
var filePath = await AppApi.OpenFileSelectorDialog(null, ".json", "JSON Files (*.json)|*.json");
|
||||
if (filePath === "") {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var json;
|
||||
if (LINUX) {
|
||||
json = await this.openJsonFileSelectorDialogElectron();
|
||||
} else {
|
||||
json = await AppApi.ReadVrcRegJsonFile(filePath);
|
||||
}
|
||||
|
||||
try {
|
||||
var data = JSON.parse(json);
|
||||
if (!data || typeof data !== 'object') {
|
||||
throw new Error('Invalid JSON');
|
||||
}
|
||||
// quick check to make sure it's a valid registry backup
|
||||
for (var key in data) {
|
||||
var value = data[key];
|
||||
if (
|
||||
typeof value !== 'object' ||
|
||||
typeof value.type !== 'number' ||
|
||||
typeof value.data === 'undefined'
|
||||
) {
|
||||
throw new Error('Invalid JSON');
|
||||
}
|
||||
}
|
||||
AppApi.SetVRChatRegistry(json)
|
||||
.then(() => {
|
||||
this.$message({
|
||||
message: 'VRC registry settings restored',
|
||||
type: 'success'
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
this.$message({
|
||||
message: `Failed to restore VRC registry settings, check console for full error: ${e}`,
|
||||
type: 'error'
|
||||
});
|
||||
});
|
||||
} catch {
|
||||
this.$message({
|
||||
message: 'Invalid JSON',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
deleteVrcRegistry() {
|
||||
this.$confirm('Continue? Delete VRC Registry Settings', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning',
|
||||
callback: (action) => {
|
||||
if (action !== 'confirm') {
|
||||
return;
|
||||
}
|
||||
AppApi.DeleteVRChatRegistryFolder().then(() => {
|
||||
this.$message({
|
||||
message: 'VRC registry settings deleted',
|
||||
type: 'success'
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
clearVrcRegistryDialog() {
|
||||
this.registryBackupTable.data = [];
|
||||
},
|
||||
|
||||
async checkAutoBackupRestoreVrcRegistry() {
|
||||
if (!this.vrcRegistryAutoBackup) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check for auto restore
|
||||
var hasVRChatRegistryFolder =
|
||||
await AppApi.HasVRChatRegistryFolder();
|
||||
if (!hasVRChatRegistryFolder) {
|
||||
var lastBackupDate = await configRepository.getString(
|
||||
'VRCX_VRChatRegistryLastBackupDate'
|
||||
);
|
||||
var lastRestoreCheck = await configRepository.getString(
|
||||
'VRCX_VRChatRegistryLastRestoreCheck'
|
||||
);
|
||||
if (
|
||||
!lastBackupDate ||
|
||||
(lastRestoreCheck &&
|
||||
lastBackupDate &&
|
||||
lastRestoreCheck === lastBackupDate)
|
||||
) {
|
||||
// only ask to restore once and when backup is present
|
||||
return;
|
||||
}
|
||||
// popup message about auto restore
|
||||
this.$alert(
|
||||
$t('dialog.registry_backup.restore_prompt'),
|
||||
$t('dialog.registry_backup.header')
|
||||
);
|
||||
this.showRegistryBackupDialog();
|
||||
await AppApi.FocusWindow();
|
||||
await configRepository.setString(
|
||||
'VRCX_VRChatRegistryLastRestoreCheck',
|
||||
lastBackupDate
|
||||
);
|
||||
} else {
|
||||
await this.autoBackupVrcRegistry();
|
||||
}
|
||||
},
|
||||
|
||||
async autoBackupVrcRegistry() {
|
||||
var date = new Date();
|
||||
var lastBackupDate = await configRepository.getString(
|
||||
'VRCX_VRChatRegistryLastBackupDate'
|
||||
);
|
||||
if (lastBackupDate) {
|
||||
var lastBackup = new Date(lastBackupDate);
|
||||
var diff = date.getTime() - lastBackup.getTime();
|
||||
var diffDays = Math.floor(diff / (1000 * 60 * 60 * 24));
|
||||
if (diffDays < 7) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
var backupsJson = await configRepository.getString(
|
||||
'VRCX_VRChatRegistryBackups'
|
||||
);
|
||||
if (!backupsJson) {
|
||||
backupsJson = JSON.stringify([]);
|
||||
}
|
||||
var backups = JSON.parse(backupsJson);
|
||||
backups.forEach((backup) => {
|
||||
if (backup.name === 'Auto Backup') {
|
||||
// remove old auto backup
|
||||
$app.removeFromArray(backups, backup);
|
||||
}
|
||||
});
|
||||
await configRepository.setString(
|
||||
'VRCX_VRChatRegistryBackups',
|
||||
JSON.stringify(backups)
|
||||
);
|
||||
this.backupVrcRegistry('Auto Backup');
|
||||
await configRepository.setString(
|
||||
'VRCX_VRChatRegistryLastBackupDate',
|
||||
date.toJSON()
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
52
src/classes/vrcxJsonStorage.js
Normal file
@@ -0,0 +1,52 @@
|
||||
import * as workerTimers from 'worker-timers';
|
||||
/* eslint-disable no-unused-vars */
|
||||
let VRCXStorage = {};
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export default class {
|
||||
constructor(_VRCXStorage) {
|
||||
VRCXStorage = _VRCXStorage;
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
VRCXStorage.GetArray = async function (key) {
|
||||
try {
|
||||
var array = JSON.parse(await this.Get(key));
|
||||
if (Array.isArray(array)) {
|
||||
return array;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
VRCXStorage.SetArray = function (key, value) {
|
||||
this.Set(key, JSON.stringify(value));
|
||||
};
|
||||
|
||||
VRCXStorage.GetObject = async function (key) {
|
||||
try {
|
||||
var object = JSON.parse(await this.Get(key));
|
||||
if (object === Object(object)) {
|
||||
return object;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
VRCXStorage.SetObject = function (key, value) {
|
||||
this.Set(key, JSON.stringify(value));
|
||||
};
|
||||
|
||||
workerTimers.setInterval(
|
||||
() => {
|
||||
VRCXStorage.Flush();
|
||||
},
|
||||
5 * 60 * 1000
|
||||
);
|
||||
}
|
||||
}
|
||||
1603
src/classes/vrcxNotifications.js
Normal file
348
src/classes/vrcxUpdater.js
Normal file
@@ -0,0 +1,348 @@
|
||||
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
|
||||
import * as workerTimers from 'worker-timers';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
_data = {
|
||||
VRCXUpdateDialog: {
|
||||
visible: false,
|
||||
updatePending: false,
|
||||
updatePendingIsLatest: false,
|
||||
release: '',
|
||||
releases: [],
|
||||
json: {}
|
||||
},
|
||||
branch: 'Stable',
|
||||
autoUpdateVRCX: 'Auto Download',
|
||||
checkingForVRCXUpdate: false,
|
||||
pendingVRCXInstall: '',
|
||||
pendingVRCXUpdate: false,
|
||||
branches: {
|
||||
Stable: {
|
||||
name: 'Stable',
|
||||
urlReleases: 'https://api0.vrcx.app/releases/stable',
|
||||
urlLatest: 'https://api0.vrcx.app/releases/stable/latest'
|
||||
},
|
||||
Nightly: {
|
||||
name: 'Nightly',
|
||||
urlReleases: 'https://api0.vrcx.app/releases/nightly',
|
||||
urlLatest: 'https://api0.vrcx.app/releases/nightly/latest'
|
||||
}
|
||||
// LinuxTest: {
|
||||
// name: 'LinuxTest',
|
||||
// urlReleases: 'https://api.github.com/repos/rs189/VRCX/releases',
|
||||
// urlLatest:
|
||||
// 'https://api.github.com/repos/rs189/VRCX/releases/latest'
|
||||
// }
|
||||
},
|
||||
updateProgress: 0,
|
||||
updateInProgress: false
|
||||
};
|
||||
|
||||
_methods = {
|
||||
async showVRCXUpdateDialog() {
|
||||
this.$nextTick(() =>
|
||||
$app.adjustDialogZ(this.$refs.VRCXUpdateDialog.$el)
|
||||
);
|
||||
var D = this.VRCXUpdateDialog;
|
||||
D.visible = true;
|
||||
D.updatePendingIsLatest = false;
|
||||
D.updatePending = await AppApi.CheckForUpdateExe();
|
||||
this.loadBranchVersions();
|
||||
},
|
||||
|
||||
async downloadVRCXUpdate(
|
||||
downloadUrl,
|
||||
downloadName,
|
||||
hashUrl,
|
||||
size,
|
||||
releaseName,
|
||||
type
|
||||
) {
|
||||
if (this.updateInProgress) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.updateInProgress = true;
|
||||
this.downloadFileProgress();
|
||||
await AppApi.DownloadUpdate(
|
||||
downloadUrl,
|
||||
downloadName,
|
||||
hashUrl,
|
||||
size
|
||||
);
|
||||
this.pendingVRCXInstall = releaseName;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
this.$message({
|
||||
message: `${$t('message.vrcx_updater.failed_install')} ${err}`,
|
||||
type: 'error'
|
||||
});
|
||||
} finally {
|
||||
this.updateInProgress = false;
|
||||
this.updateProgress = 0;
|
||||
}
|
||||
},
|
||||
|
||||
async cancelUpdate() {
|
||||
await AppApi.CancelUpdate();
|
||||
this.updateInProgress = false;
|
||||
this.updateProgress = 0;
|
||||
},
|
||||
|
||||
async downloadFileProgress() {
|
||||
this.updateProgress = await AppApi.CheckUpdateProgress();
|
||||
if (this.updateInProgress) {
|
||||
workerTimers.setTimeout(() => this.downloadFileProgress(), 150);
|
||||
}
|
||||
},
|
||||
|
||||
updateProgressText() {
|
||||
if (this.updateProgress === 100) {
|
||||
return $t('message.vrcx_updater.checking_hash');
|
||||
}
|
||||
return `${this.updateProgress}%`;
|
||||
},
|
||||
|
||||
installVRCXUpdate() {
|
||||
for (var release of this.VRCXUpdateDialog.releases) {
|
||||
if (release.name !== this.VRCXUpdateDialog.release) {
|
||||
continue;
|
||||
}
|
||||
var downloadUrl = '';
|
||||
var downloadName = '';
|
||||
var hashUrl = '';
|
||||
var size = 0;
|
||||
for (var asset of release.assets) {
|
||||
if (asset.state !== 'uploaded') {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
WINDOWS &&
|
||||
(asset.content_type === 'application/x-msdownload' ||
|
||||
asset.content_type ===
|
||||
'application/x-msdos-program')
|
||||
) {
|
||||
downloadUrl = asset.browser_download_url;
|
||||
downloadName = asset.name;
|
||||
size = asset.size;
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
LINUX &&
|
||||
asset.content_type === 'application/octet-stream'
|
||||
) {
|
||||
downloadUrl = asset.browser_download_url;
|
||||
downloadName = asset.name;
|
||||
size = asset.size;
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
asset.name === 'SHA256SUMS.txt' &&
|
||||
asset.content_type === 'text/plain'
|
||||
) {
|
||||
hashUrl = asset.browser_download_url;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!downloadUrl) {
|
||||
return;
|
||||
}
|
||||
var releaseName = release.name;
|
||||
var type = 'Manual';
|
||||
this.downloadVRCXUpdate(
|
||||
downloadUrl,
|
||||
downloadName,
|
||||
hashUrl,
|
||||
size,
|
||||
releaseName,
|
||||
type
|
||||
);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
async loadBranchVersions() {
|
||||
var D = this.VRCXUpdateDialog;
|
||||
var url = this.branches[this.branch].urlReleases;
|
||||
this.checkingForVRCXUpdate = true;
|
||||
try {
|
||||
var response = await webApiService.execute({
|
||||
url,
|
||||
method: 'GET'
|
||||
});
|
||||
} finally {
|
||||
this.checkingForVRCXUpdate = false;
|
||||
}
|
||||
var json = JSON.parse(response.data);
|
||||
if (this.debugWebRequests) {
|
||||
console.log(json, response);
|
||||
}
|
||||
var releases = [];
|
||||
if (typeof json !== 'object' || json.message) {
|
||||
$app.$message({
|
||||
message: $t('message.vrcx_updater.failed', {
|
||||
message: json.message
|
||||
}),
|
||||
type: 'error'
|
||||
});
|
||||
return;
|
||||
}
|
||||
for (var release of json) {
|
||||
for (var asset of release.assets) {
|
||||
if (
|
||||
(asset.content_type === 'application/x-msdownload' ||
|
||||
asset.content_type ===
|
||||
'application/x-msdos-program') &&
|
||||
asset.state === 'uploaded'
|
||||
) {
|
||||
releases.push(release);
|
||||
}
|
||||
}
|
||||
}
|
||||
D.releases = releases;
|
||||
D.release = json[0].name;
|
||||
this.VRCXUpdateDialog.updatePendingIsLatest = false;
|
||||
if (D.release === this.pendingVRCXInstall) {
|
||||
// update already downloaded and latest version
|
||||
this.VRCXUpdateDialog.updatePendingIsLatest = true;
|
||||
}
|
||||
if (
|
||||
(await configRepository.getString('VRCX_branch')) !==
|
||||
this.branch
|
||||
) {
|
||||
await configRepository.setString('VRCX_branch', this.branch);
|
||||
}
|
||||
},
|
||||
|
||||
async checkForVRCXUpdate() {
|
||||
var currentVersion = this.appVersion.replace(' (Linux)', '');
|
||||
if (
|
||||
!currentVersion ||
|
||||
currentVersion === 'VRCX Nightly Build' ||
|
||||
currentVersion === 'VRCX Build'
|
||||
) {
|
||||
// ignore custom builds
|
||||
return;
|
||||
}
|
||||
if (this.branch === 'Beta') {
|
||||
// move Beta users to stable
|
||||
this.branch = 'Stable';
|
||||
await configRepository.setString('VRCX_branch', this.branch);
|
||||
}
|
||||
if (typeof this.branches[this.branch] === 'undefined') {
|
||||
// handle invalid branch
|
||||
this.branch = 'Stable';
|
||||
await configRepository.setString('VRCX_branch', this.branch);
|
||||
}
|
||||
var url = this.branches[this.branch].urlLatest;
|
||||
this.checkingForVRCXUpdate = true;
|
||||
try {
|
||||
var response = await webApiService.execute({
|
||||
url,
|
||||
method: 'GET'
|
||||
});
|
||||
} finally {
|
||||
this.checkingForVRCXUpdate = false;
|
||||
}
|
||||
this.pendingVRCXUpdate = false;
|
||||
var json = JSON.parse(response.data);
|
||||
if (this.debugWebRequests) {
|
||||
console.log(json, response);
|
||||
}
|
||||
if (json === Object(json) && json.name && json.published_at) {
|
||||
this.VRCXUpdateDialog.updateJson = json;
|
||||
this.changeLogDialog.buildName = json.name;
|
||||
this.changeLogDialog.changeLog = this.changeLogRemoveLinks(
|
||||
json.body
|
||||
);
|
||||
var releaseName = json.name;
|
||||
this.latestAppVersion = releaseName;
|
||||
this.VRCXUpdateDialog.updatePendingIsLatest = false;
|
||||
if (releaseName === this.pendingVRCXInstall) {
|
||||
// update already downloaded
|
||||
this.VRCXUpdateDialog.updatePendingIsLatest = true;
|
||||
} else if (releaseName > currentVersion) {
|
||||
var downloadUrl = '';
|
||||
var downloadName = '';
|
||||
var hashUrl = '';
|
||||
var size = 0;
|
||||
for (var asset of json.assets) {
|
||||
if (asset.state !== 'uploaded') {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
!LINUX &&
|
||||
(asset.content_type ===
|
||||
'application/x-msdownload' ||
|
||||
asset.content_type ===
|
||||
'application/x-msdos-program')
|
||||
) {
|
||||
downloadUrl = asset.browser_download_url;
|
||||
downloadName = asset.name;
|
||||
size = asset.size;
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
LINUX &&
|
||||
asset.content_type === 'application/octet-stream'
|
||||
) {
|
||||
downloadUrl = asset.browser_download_url;
|
||||
downloadName = asset.name;
|
||||
size = asset.size;
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
asset.name === 'SHA256SUMS.txt' &&
|
||||
asset.content_type === 'text/plain'
|
||||
) {
|
||||
hashUrl = asset.browser_download_url;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!downloadUrl) {
|
||||
return;
|
||||
}
|
||||
this.pendingVRCXUpdate = true;
|
||||
this.notifyMenu('settings');
|
||||
var type = 'Auto';
|
||||
if (!API.isLoggedIn) {
|
||||
this.showVRCXUpdateDialog();
|
||||
} else if (this.autoUpdateVRCX === 'Notify') {
|
||||
// this.showVRCXUpdateDialog();
|
||||
} else if (this.autoUpdateVRCX === 'Auto Download') {
|
||||
this.downloadVRCXUpdate(
|
||||
downloadUrl,
|
||||
downloadName,
|
||||
hashUrl,
|
||||
size,
|
||||
releaseName,
|
||||
type
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
restartVRCX(isUpgrade) {
|
||||
if (!LINUX) {
|
||||
AppApi.RestartApplication(isUpgrade);
|
||||
} else {
|
||||
window.electron.restartApp();
|
||||
}
|
||||
},
|
||||
|
||||
async saveAutoUpdateVRCX() {
|
||||
if (this.autoUpdateVRCX === 'Off') {
|
||||
this.pendingVRCXUpdate = false;
|
||||
}
|
||||
await configRepository.setString(
|
||||
'VRCX_autoUpdateVRCX',
|
||||
this.autoUpdateVRCX
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
588
src/classes/websocket.js
Normal file
@@ -0,0 +1,588 @@
|
||||
import * as workerTimers from 'worker-timers';
|
||||
import Noty from 'noty';
|
||||
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
init() {
|
||||
API.webSocket = null;
|
||||
API.lastWebSocketMessage = '';
|
||||
|
||||
API.$on('USER:CURRENT', function () {
|
||||
if ($app.friendLogInitStatus && this.webSocket === null) {
|
||||
this.getAuth();
|
||||
}
|
||||
});
|
||||
|
||||
API.getAuth = function () {
|
||||
return this.call('auth', {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
var args = {
|
||||
json
|
||||
};
|
||||
this.$emit('AUTH', args);
|
||||
return args;
|
||||
});
|
||||
};
|
||||
|
||||
API.$on('AUTH', function (args) {
|
||||
if (args.json.ok) {
|
||||
this.connectWebSocket(args.json.token);
|
||||
}
|
||||
});
|
||||
|
||||
API.connectWebSocket = function (token) {
|
||||
if (this.webSocket !== null) {
|
||||
return;
|
||||
}
|
||||
var socket = new WebSocket(`${API.websocketDomain}/?auth=${token}`);
|
||||
socket.onopen = () => {
|
||||
if ($app.debugWebSocket) {
|
||||
console.log('WebSocket connected');
|
||||
}
|
||||
};
|
||||
socket.onclose = () => {
|
||||
if (this.webSocket === socket) {
|
||||
this.webSocket = null;
|
||||
}
|
||||
try {
|
||||
socket.close();
|
||||
} catch (err) {}
|
||||
if ($app.debugWebSocket) {
|
||||
console.log('WebSocket closed');
|
||||
}
|
||||
workerTimers.setTimeout(() => {
|
||||
if (
|
||||
this.isLoggedIn &&
|
||||
$app.friendLogInitStatus &&
|
||||
this.webSocket === null
|
||||
) {
|
||||
this.getAuth();
|
||||
}
|
||||
}, 5000);
|
||||
};
|
||||
socket.onerror = () => {
|
||||
if (this.errorNoty) {
|
||||
this.errorNoty.close();
|
||||
}
|
||||
this.errorNoty = new Noty({
|
||||
type: 'error',
|
||||
text: 'WebSocket Error'
|
||||
}).show();
|
||||
socket.onclose();
|
||||
};
|
||||
socket.onmessage = ({ data }) => {
|
||||
try {
|
||||
if (this.lastWebSocketMessage === data) {
|
||||
// pls no spam
|
||||
return;
|
||||
}
|
||||
this.lastWebSocketMessage = data;
|
||||
var json = JSON.parse(data);
|
||||
try {
|
||||
json.content = JSON.parse(json.content);
|
||||
} catch (err) {}
|
||||
this.$emit('PIPELINE', {
|
||||
json
|
||||
});
|
||||
if ($app.debugWebSocket && json.content) {
|
||||
var displayName = '';
|
||||
var user = this.cachedUsers.get(json.content.userId);
|
||||
if (user) {
|
||||
displayName = user.displayName;
|
||||
}
|
||||
console.log(
|
||||
'WebSocket',
|
||||
json.type,
|
||||
displayName,
|
||||
json.content
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
this.webSocket = socket;
|
||||
};
|
||||
|
||||
API.$on('LOGOUT', function () {
|
||||
this.closeWebSocket();
|
||||
});
|
||||
|
||||
API.closeWebSocket = function () {
|
||||
var socket = this.webSocket;
|
||||
if (socket === null) {
|
||||
return;
|
||||
}
|
||||
this.webSocket = null;
|
||||
try {
|
||||
socket.close();
|
||||
} catch (err) {}
|
||||
};
|
||||
|
||||
API.reconnectWebSocket = function () {
|
||||
if (!this.isLoggedIn || !$app.friendLogInitStatus) {
|
||||
return;
|
||||
}
|
||||
this.closeWebSocket();
|
||||
this.getAuth();
|
||||
};
|
||||
|
||||
API.$on('PIPELINE', function (args) {
|
||||
var { type, content, err } = args.json;
|
||||
if (typeof err !== 'undefined') {
|
||||
console.error('PIPELINE: error', args);
|
||||
if (this.errorNoty) {
|
||||
this.errorNoty.close();
|
||||
}
|
||||
this.errorNoty = new Noty({
|
||||
type: 'error',
|
||||
text: $app.escapeTag(`WebSocket Error: ${err}`)
|
||||
}).show();
|
||||
return;
|
||||
}
|
||||
if (typeof content === 'undefined') {
|
||||
console.error('PIPELINE: missing content', args);
|
||||
return;
|
||||
}
|
||||
if (typeof content.user !== 'undefined') {
|
||||
// I forgot about this...
|
||||
delete content.user.state;
|
||||
}
|
||||
switch (type) {
|
||||
case 'notification':
|
||||
this.$emit('NOTIFICATION', {
|
||||
json: content,
|
||||
params: {
|
||||
notificationId: content.id
|
||||
}
|
||||
});
|
||||
this.$emit('PIPELINE:NOTIFICATION', {
|
||||
json: content,
|
||||
params: {
|
||||
notificationId: content.id
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'notification-v2':
|
||||
console.log('notification-v2', content);
|
||||
this.$emit('NOTIFICATION:V2', {
|
||||
json: content,
|
||||
params: {
|
||||
notificationId: content.id
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'notification-v2-delete':
|
||||
console.log('notification-v2-delete', content);
|
||||
for (var id of content.ids) {
|
||||
this.$emit('NOTIFICATION:HIDE', {
|
||||
params: {
|
||||
notificationId: id
|
||||
}
|
||||
});
|
||||
this.$emit('NOTIFICATION:SEE', {
|
||||
params: {
|
||||
notificationId: id
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'notification-v2-update':
|
||||
console.log('notification-v2-update', content);
|
||||
this.$emit('NOTIFICATION:V2:UPDATE', {
|
||||
json: content.updates,
|
||||
params: {
|
||||
notificationId: content.id
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'see-notification':
|
||||
this.$emit('NOTIFICATION:SEE', {
|
||||
params: {
|
||||
notificationId: content
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'hide-notification':
|
||||
this.$emit('NOTIFICATION:HIDE', {
|
||||
params: {
|
||||
notificationId: content
|
||||
}
|
||||
});
|
||||
this.$emit('NOTIFICATION:SEE', {
|
||||
params: {
|
||||
notificationId: content
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'response-notification':
|
||||
this.$emit('NOTIFICATION:HIDE', {
|
||||
params: {
|
||||
notificationId: content.notificationId
|
||||
}
|
||||
});
|
||||
this.$emit('NOTIFICATION:SEE', {
|
||||
params: {
|
||||
notificationId: content.notificationId
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'friend-add':
|
||||
this.$emit('USER', {
|
||||
json: content.user,
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
this.$emit('FRIEND:ADD', {
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'friend-delete':
|
||||
this.$emit('FRIEND:DELETE', {
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'friend-online':
|
||||
// Where is instanceId, travelingToWorld, travelingToInstance?
|
||||
// More JANK, what a mess
|
||||
var $location = $utils.parseLocation(content.location);
|
||||
var $travelingToLocation = $utils.parseLocation(
|
||||
content.travelingToLocation
|
||||
);
|
||||
if (content?.user?.id) {
|
||||
this.$emit('USER', {
|
||||
json: {
|
||||
id: content.userId,
|
||||
platform: content.platform,
|
||||
state: 'online',
|
||||
|
||||
location: content.location,
|
||||
worldId: content.worldId,
|
||||
instanceId: $location.instanceId,
|
||||
travelingToLocation:
|
||||
content.travelingToLocation,
|
||||
travelingToWorld: $travelingToLocation.worldId,
|
||||
travelingToInstance:
|
||||
$travelingToLocation.instanceId,
|
||||
|
||||
...content.user
|
||||
},
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.$emit('FRIEND:STATE', {
|
||||
json: {
|
||||
state: 'online'
|
||||
},
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'friend-active':
|
||||
if (content?.user?.id) {
|
||||
this.$emit('USER', {
|
||||
json: {
|
||||
id: content.userId,
|
||||
platform: content.platform,
|
||||
state: 'active',
|
||||
|
||||
location: 'offline',
|
||||
worldId: 'offline',
|
||||
instanceId: 'offline',
|
||||
travelingToLocation: 'offline',
|
||||
travelingToWorld: 'offline',
|
||||
travelingToInstance: 'offline',
|
||||
|
||||
...content.user
|
||||
},
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.$emit('FRIEND:STATE', {
|
||||
json: {
|
||||
state: 'active'
|
||||
},
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'friend-offline':
|
||||
// more JANK, hell yeah
|
||||
this.$emit('USER', {
|
||||
json: {
|
||||
id: content.userId,
|
||||
platform: content.platform,
|
||||
state: 'offline',
|
||||
|
||||
location: 'offline',
|
||||
worldId: 'offline',
|
||||
instanceId: 'offline',
|
||||
travelingToLocation: 'offline',
|
||||
travelingToWorld: 'offline',
|
||||
travelingToInstance: 'offline'
|
||||
},
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'friend-update':
|
||||
this.$emit('USER', {
|
||||
json: content.user,
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'friend-location':
|
||||
var $location = $utils.parseLocation(content.location);
|
||||
var $travelingToLocation = $utils.parseLocation(
|
||||
content.travelingToLocation
|
||||
);
|
||||
if (!content?.user?.id) {
|
||||
var ref = this.cachedUsers.get(content.userId);
|
||||
if (typeof ref !== 'undefined') {
|
||||
this.$emit('USER', {
|
||||
json: {
|
||||
...ref,
|
||||
location: content.location,
|
||||
worldId: content.worldId,
|
||||
instanceId: $location.instanceId,
|
||||
travelingToLocation:
|
||||
content.travelingToLocation,
|
||||
travelingToWorld:
|
||||
$travelingToLocation.worldId,
|
||||
travelingToInstance:
|
||||
$travelingToLocation.instanceId
|
||||
},
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
this.$emit('USER', {
|
||||
json: {
|
||||
location: content.location,
|
||||
worldId: content.worldId,
|
||||
instanceId: $location.instanceId,
|
||||
travelingToLocation: content.travelingToLocation,
|
||||
travelingToWorld: $travelingToLocation.worldId,
|
||||
travelingToInstance:
|
||||
$travelingToLocation.instanceId,
|
||||
...content.user,
|
||||
state: 'online' // JANK
|
||||
},
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'user-update':
|
||||
this.$emit('USER:CURRENT', {
|
||||
json: content.user,
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'user-location':
|
||||
// update current user location
|
||||
if (content.userId !== this.currentUser.id) {
|
||||
console.error('user-location wrong userId', content);
|
||||
break;
|
||||
}
|
||||
|
||||
// content.user: {}
|
||||
// content.world: {}
|
||||
|
||||
this.currentUser.presence.instance = content.instance;
|
||||
this.currentUser.presence.world = content.worldId;
|
||||
$app.setCurrentUserLocation(content.location);
|
||||
break;
|
||||
|
||||
case 'group-joined':
|
||||
// var groupId = content.groupId;
|
||||
// $app.onGroupJoined(groupId);
|
||||
break;
|
||||
|
||||
case 'group-left':
|
||||
// var groupId = content.groupId;
|
||||
// $app.onGroupLeft(groupId);
|
||||
break;
|
||||
|
||||
case 'group-role-updated':
|
||||
var groupId = content.role.groupId;
|
||||
API.getGroup({ groupId, includeRoles: true });
|
||||
console.log('group-role-updated', content);
|
||||
|
||||
// content {
|
||||
// role: {
|
||||
// createdAt: string,
|
||||
// description: string,
|
||||
// groupId: string,
|
||||
// id: string,
|
||||
// isManagementRole: boolean,
|
||||
// isSelfAssignable: boolean,
|
||||
// name: string,
|
||||
// order: number,
|
||||
// permissions: string[],
|
||||
// requiresPurchase: boolean,
|
||||
// requiresTwoFactor: boolean
|
||||
break;
|
||||
|
||||
case 'group-member-updated':
|
||||
var member = content.member;
|
||||
if (!member) {
|
||||
console.error(
|
||||
'group-member-updated missing member',
|
||||
content
|
||||
);
|
||||
break;
|
||||
}
|
||||
var groupId = member.groupId;
|
||||
if (
|
||||
$app.groupDialog.visible &&
|
||||
$app.groupDialog.id === groupId
|
||||
) {
|
||||
$app.getGroupDialogGroup(groupId);
|
||||
}
|
||||
this.$emit('GROUP:MEMBER', {
|
||||
json: member,
|
||||
params: {
|
||||
groupId
|
||||
}
|
||||
});
|
||||
console.log('group-member-updated', member);
|
||||
break;
|
||||
|
||||
case 'instance-queue-joined':
|
||||
case 'instance-queue-position':
|
||||
var instanceId = content.instanceLocation;
|
||||
var position = content.position ?? 0;
|
||||
var queueSize = content.queueSize ?? 0;
|
||||
$app.instanceQueueUpdate(instanceId, position, queueSize);
|
||||
break;
|
||||
|
||||
case 'instance-queue-ready':
|
||||
var instanceId = content.instanceLocation;
|
||||
// var expiry = Date.parse(content.expiry);
|
||||
$app.instanceQueueReady(instanceId);
|
||||
break;
|
||||
|
||||
case 'instance-queue-left':
|
||||
var instanceId = content.instanceLocation;
|
||||
$app.removeQueuedInstance(instanceId);
|
||||
// $app.instanceQueueClear();
|
||||
break;
|
||||
|
||||
case 'content-refresh':
|
||||
var contentType = content.contentType;
|
||||
console.log('content-refresh', content);
|
||||
if (contentType === 'icon') {
|
||||
if (
|
||||
$app.galleryDialogVisible &&
|
||||
!$app.galleryDialogIconsLoading
|
||||
) {
|
||||
$app.refreshVRCPlusIconsTable();
|
||||
}
|
||||
} else if (contentType === 'gallery') {
|
||||
if (
|
||||
$app.galleryDialogVisible &&
|
||||
!$app.galleryDialogGalleryLoading
|
||||
) {
|
||||
$app.refreshGalleryTable();
|
||||
}
|
||||
} else if (contentType === 'emoji') {
|
||||
if (
|
||||
$app.galleryDialogVisible &&
|
||||
!$app.galleryDialogEmojisLoading
|
||||
) {
|
||||
$app.refreshEmojiTable();
|
||||
}
|
||||
} else if (
|
||||
contentType === 'print' ||
|
||||
contentType === 'prints'
|
||||
) {
|
||||
if (
|
||||
$app.galleryDialogVisible &&
|
||||
!$app.galleryDialogPrintsLoading
|
||||
) {
|
||||
$app.refreshPrintTable();
|
||||
}
|
||||
} else if (contentType === 'avatar') {
|
||||
// hmm, utilizing this might be too spamy and cause UI to move around
|
||||
} else if (contentType === 'world') {
|
||||
// hmm
|
||||
} else if (contentType === 'created') {
|
||||
// on avatar upload
|
||||
} else {
|
||||
console.log('Unknown content-refresh', content);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'instance-closed':
|
||||
// TODO: get worldName, groupName, hardClose
|
||||
var noty = {
|
||||
type: 'instance.closed',
|
||||
location: content.instanceLocation,
|
||||
message: 'Instance Closed',
|
||||
created_at: new Date().toJSON()
|
||||
};
|
||||
if (
|
||||
$app.notificationTable.filters[0].value.length === 0 ||
|
||||
$app.notificationTable.filters[0].value.includes(
|
||||
noty.type
|
||||
)
|
||||
) {
|
||||
$app.notifyMenu('notification');
|
||||
}
|
||||
$app.queueNotificationNoty(noty);
|
||||
$app.notificationTable.data.push(noty);
|
||||
$app.updateSharedFeed(true);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log('Unknown pipeline type', args.json);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_data = {};
|
||||
|
||||
_methods = {};
|
||||
}
|
||||
68
src/components/settings/SimpleSwitch.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div class="simple-switch">
|
||||
<div class="name" :style="{ width: longLabel ? '300px' : undefined }">
|
||||
{{ label }}
|
||||
<el-tooltip
|
||||
v-if="tooltip"
|
||||
placement="top"
|
||||
class="tooltip"
|
||||
:content="tooltip"
|
||||
><i class="el-icon-info"
|
||||
/></el-tooltip>
|
||||
</div>
|
||||
|
||||
<el-switch
|
||||
class="switch"
|
||||
:value="value"
|
||||
@change="change"
|
||||
:disabled="disabled"
|
||||
></el-switch>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SimpleSwitch',
|
||||
props: {
|
||||
label: {
|
||||
type: String
|
||||
},
|
||||
value: {
|
||||
type: Boolean
|
||||
},
|
||||
tooltip: {
|
||||
type: String
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean
|
||||
},
|
||||
longLabel: {
|
||||
type: Boolean
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
change(event) {
|
||||
this.$emit('change', event);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.simple-switch {
|
||||
font-size: 12px;
|
||||
margin-top: 5px;
|
||||
display: flex;
|
||||
}
|
||||
.simple-switch > .name {
|
||||
width: 225px;
|
||||
min-width: 225px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.simple-switch > .switch {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.simple-switch .tooltip {
|
||||
margin-left: 5px;
|
||||
}
|
||||
</style>
|
||||
119
src/confusables.js
Normal file
@@ -0,0 +1,119 @@
|
||||
/// Copyright © `2019` `https://github.com/gc/` (MIT License)
|
||||
/// This file doesn't support non latin languages very well, but there's not
|
||||
/// much that can be done about that
|
||||
|
||||
const charToConfusables = new Map([
|
||||
[' ', ' '],
|
||||
['0', '⓿'],
|
||||
['1', '11⓵➊⑴¹𝟏𝟙1𝟷𝟣⒈𝟭1➀₁①❶⥠'],
|
||||
['2', '⓶⒉⑵➋ƻ²ᒿ𝟚2𝟮𝟤ᒾ𝟸Ƨ𝟐②ᴤ₂➁❷ᘝƨ'],
|
||||
['3', '³ȝჳⳌꞫ𝟑ℨ𝟛𝟯𝟥Ꝫ➌ЗȜ⓷ӠƷ3𝟹⑶⒊ʒʓǯǮƺ𝕴ᶾзᦡ➂③₃ᶚᴣᴟ❸ҘҙӬӡӭӟӞ'],
|
||||
['4', '𝟰𝟺𝟦𝟒➍ҶᏎ𝟜ҷ⓸ҸҹӴӵᶣ4чㄩ⁴➃₄④❹Ӌ⑷⒋'],
|
||||
['5', '𝟱⓹➎Ƽ𝟓𝟻𝟝𝟧5➄₅⑤⁵❺ƽ⑸⒌'],
|
||||
['6', 'Ⳓ🄇𝟼Ꮾ𝟲𝟞𝟨𝟔➏⓺Ϭϭ⁶б6ᧈ⑥➅₆❻⑹⒍'],
|
||||
['7', '𝟕𝟟𝟩𝟳𝟽🄈⓻𐓒➐7⁷⑦₇❼➆⑺⒎'],
|
||||
['8', '𐌚🄉➑⓼8𝟠𝟪৪⁸₈𝟴➇⑧❽𝟾𝟖⑻⒏'],
|
||||
['9', '൭Ꝯ𝝑𝞋𝟅🄊𝟡𝟵Ⳋ⓽➒੧৭୨9𝟫𝟿𝟗⁹₉Գ➈⑨❾⑼⒐'],
|
||||
['10', '⓾❿➉➓🔟⑩⑽⒑'],
|
||||
['11', '⑪⑾⒒⓫'],
|
||||
['12', '⑫⑿⒓⓬'],
|
||||
['13', '⑬⒀⒔⓭'],
|
||||
['14', '⑭⒁⒕⓮'],
|
||||
['15', '⑮⒂⒖⓯'],
|
||||
['16', '⑯⒃⒗⓰'],
|
||||
['17', '⑰⒄⒘⓱'],
|
||||
['18', '⑱⒅⒙⓲'],
|
||||
['19', '⑲⒆⒚⓳'],
|
||||
['20', '⑳⒇⒛⓴'],
|
||||
['ae', 'æ'],
|
||||
['OE', 'Œ'],
|
||||
['oe', 'œ'],
|
||||
['pi', 'ᒆ'],
|
||||
['Nj', 'Nj'],
|
||||
['AE', 'ᴁ'],
|
||||
['A', '𝑨𝔄ᗄ𝖠𝗔ꓯ𝞐🄐🄰Ꭿ𐊠𝕬𝜜𝐴ꓮᎪ𝚨ꭺ𝝖🅐Å∀🇦₳🅰𝒜𝘈𝐀𝔸дǺᗅⒶAΑᾋᗩĂÃÅǍȀȂĀȺĄʌΛλƛᴀᴬДАልÄₐᕱªǞӒΆẠẢẦẨẬẮẰẲẴẶᾸᾹᾺΆᾼᾈᾉᾊᾌᾍᾎᾏἈἉἊἋἌἍἎἏḀȦǠӐÀÁÂẤẪ𝛢𝓐𝙰𝘼ᗩ'],
|
||||
['a', '∂⍺ⓐձǟᵃᶏ⒜аɒaαȃȁคǎმäɑāɐąᾄẚạảǡầẵḁȧӑӓãåάὰάăẩằẳặᾀᾁᾂᾃᾅᾆᾰᾱᾲᾳᾴᶐᾶᾷἀἁἂἃἄἅἆἇᾇậắàáâấẫǻⱥ𝐚𝑎𝒂𝒶𝓪𝔞𝕒𝖆𝖺𝗮𝘢𝙖𝚊𝛂𝛼𝜶𝝰𝞪⍶'],
|
||||
['B', '🄑𝔙𝖁ꞵ𝛃𝛽𝜷𝝱𝞫Ᏸ𐌁𝑩𝕭🄱𐊡𝖡𝘽ꓐ𝗕𝘉𝜝𐊂𝚩𝐁𝛣𝝗𝐵𝙱𝔹Ᏼᏼ𝞑Ꞵ𝔅🅑฿𝓑ᗿᗾᗽ🅱ⒷBвϐᗷƁ乃ßცჩ๖βɮБՅ๒ᙖʙᴮᵇጌḄℬΒВẞḂḆɃദᗹᗸᵝᙞᙟᙝᛒᙗᙘᴃ🇧'],
|
||||
['b', 'ꮟᏏ𝐛𝘣𝒷𝔟𝓫𝖇𝖻𝑏𝙗𝕓𝒃𝗯𝚋♭ᑳᒈbᖚᕹᕺⓑḃḅҍъḇƃɓƅᖯƄЬᑲþƂ⒝ЪᶀᑿᒀᒂᒁᑾьƀҌѢѣᔎ'],
|
||||
['C', 'ꞆႠ℃🄒ᏟⲤ🄲ꓚ𐊢𐌂🅲𐐕🅒☾ČÇⒸCↃƇᑕㄈ¢८↻ĈϾՇȻᙅᶜ⒞ĆҀĊ©टƆℂℭϹС匚ḈҪʗᑖᑡᑢᑣᑤᑥⅭ𝐂𝐶𝑪𝒞𝓒𝕮𝖢𝗖𝘊𝘾ᔍ'],
|
||||
['c', '🝌cⅽ𝐜𝑐𝒄𝒸𝓬𝔠𝕔𝖈𝖼𝗰𝘤𝙘𝚌ᴄϲⲥсꮯ𐐽ⲥ𐐽ꮯĉcⓒćčċçҁƈḉȼↄсርᴄϲҫ꒝ςɽϛ𝙲ᑦ᧚𝐜𝑐𝒄𝒸𝓬𝔠𝕔𝖈𝖼𝗰𝘤𝙘𝚌₵🇨ᥴᒼⅽ'],
|
||||
['D', '🄓Ꭰ🄳𝔡𝖉𝔻𝗗𝘋𝙳𝐷𝓓𝐃𝑫𝕯𝖣𝔇𝘿ꭰⅅ𝒟ꓓ🅳🅓ⒹDƉᗪƊÐԺᴅᴰↁḊĐÞⅮᗞᑯĎḌḐḒḎᗫᗬᗟᗠᶛᴆ🇩'],
|
||||
['d', 'Ꮷ𝔡𝖉ᑯꓒ𝓭ᵭ₫ԃⓓdḋďḍḑḓḏđƌɖɗᵈ⒟ԁⅾᶁԀᑺᑻᑼᑽᒄᑰᑱᶑ𝕕𝖽𝑑𝘥𝒅𝙙𝐝𝗱𝚍ⅆ𝒹ʠժ'],
|
||||
['E', '£ᙓ⋿∃ⴺꓱ𝐄𝐸𝔈𝕰𝖤𝘌𝙴𝛦𝜠ꭼ🄔🄴𝙀𝔼𐊆𝚬ꓰ𝝚𝞔𝓔𝑬𝗘🅴🅔ⒺΈEƎἝᕮƐモЄᴇᴱᵉÉ乇ЁɆꂅ€ÈℰΕЕⴹᎬĒĔĖĘĚÊËԐỀẾỄỂẼḔḖẺȄȆẸỆȨḜḘḚἘἙἚἛἜῈΈӖὲέЀϵ🇪'],
|
||||
['e', 'əәⅇꬲꞓ⋴𝛆𝛜𝜀𝜖𝜺𝝐𝝴𝞊𝞮𝟄ⲉꮛ𐐩ꞒⲈ⍷𝑒𝓮𝕖𝖊𝘦𝗲𝚎𝙚𝒆𝔢𝖾𝐞Ҿҿⓔe⒠èᧉéᶒêɘἔềếễ૯ǝєεēҽɛểẽḕḗĕėëẻěȅȇẹệȩɇₑęḝḙḛ℮еԑѐӗᥱёἐἑἒἓἕℯ'],
|
||||
['F', 'ᖵꘘꓞꟻᖷ𝐅𝐹𝑭𝔽𝕱𝖥𝗙𝙁𝙵𝟊℉🄕🄵𐊇𝔉𝘍𐊥ꓝꞘ🅵🅕𝓕ⒻFғҒᖴƑԲϝቻḞℱϜ₣🇫Ⅎ'],
|
||||
['f', '𝐟ᵮ𝑓𝒇𝒻𝓯𝔣𝕗𝖿𝗳𝙛𝚏ꬵꞙẝ𝖋ⓕfƒḟʃբᶠ⒡ſꊰʄ∱ᶂ𝘧'],
|
||||
['G', '𝗚𝘎🄖ꓖᏳ🄶Ꮐᏻ𝔾𝓖𝑮𝕲ꮐ𝒢𝙂𝖦𝙶𝔊𝐺𝐆🅶🅖ⒼGɢƓʛĢᘜᴳǴĠԌĜḠĞǦǤԍ₲🇬⅁'],
|
||||
['g', 'ᶃᶢⓖgǵĝḡğġǧģց૭ǥɠﻭﻮᵍ⒢ℊɡᧁ𝐠𝑔𝒈𝓰𝔤𝕘𝖌𝗀𝗴𝘨𝙜𝚐'],
|
||||
['H', 'Ἤ🄗𝆦🄷𝜢ꓧ𝘏𝐻𝝜𝖧𐋏𝗛ꮋℍᎻℌⲎ𝑯𝞖🅷🅗ዞǶԋⒽHĤᚺḢḦȞḤḨḪĦⱧҢңҤῊΉῌἨἩἪἫἭἮἯᾘᾙᾚᾛᾜᾝᾞᾟӉӈҥΉн卄♓𝓗ℋН𝐇𝙃𝙷ʜ𝛨Η𝚮ᕼӇᴴᵸ🇭'],
|
||||
['h', 'ꞕ৸𝕳ꚕᏲℏӊԊꜧᏂҺ⒣ђⓗhĥḣḧȟḥḩḫẖħⱨհһከኩኪካɦℎ𝐡𝒉𝒽𝓱𝔥𝕙𝖍𝗁𝗵𝘩𝙝𝚑իʰᑋᗁɧんɥ'],
|
||||
['I', 'ⲒἿ🄘🄸ЇꀤᏆ🅸🅘إﺇٳأﺃٲٵⒾI៸ÌÍÎĨĪĬİÏḮỈǏȈȊỊĮḬƗェエῘῙῚΊἸἹἺἻἼἽἾⅠΪΊɪᶦᑊᥣ𝛪𝐈𝙄𝙸𝓵𝙡𝐼ᴵ𝚰𝑰🇮'],
|
||||
['i', '⍳ℹⅈ𝑖𝒊𝒾ı𝚤ɩιιͺ𝛊𝜄𝜾𝞲ꙇӏꭵᎥⓘiìíîĩīĭïḯỉǐȉȋịḭῐῑῒΐῖῗἰἱἲⅰⅼ∣ⵏ│׀ا١۱ߊᛁἳἴἵɨіὶίᶖ𝔦𝚒𝝸𝗂𝐢𝕚𝖎𝗶𝘪𝙞ίⁱᵢ𝓲⒤'],
|
||||
['J', '𝐉𝐽𝑱𝒥𝓙𝔍𝕁𝕵𝖩𝗝𝘑𝙅𝙹ꞲͿꓙ🄙🄹🅹🅙ⒿJЈʝᒍנフĴʆวلյʖᴊᴶﻝጋɈⱼՂๅႱįᎫȷ丿ℐℑᒘᒙᒚᒛᒴᒵᒎᒏ🇯'],
|
||||
['j', '𝚥ꭻⅉⓙjϳʲ⒥ɉĵǰјڶᶨ𝒿𝘫𝗷𝑗𝙟𝔧𝒋𝗃𝓳𝕛𝚓𝖏𝐣'],
|
||||
['K', '𝐊ꝄꝀ𝐾𝑲𝓚𝕶𝖪𝙺𝚱𝝟🄚𝗞🄺𝜥𝘒ꓗ𝙆𝕂Ⲕ𝔎𝛫Ꮶ𝞙𝒦🅺🅚₭ⓀKĸḰќƘкҠκқҟӄʞҚКҡᴋᴷᵏ⒦ᛕЌጕḲΚKҜҝҞĶḴǨⱩϗӃ🇰'],
|
||||
['k', 'ⓚꝁkḱǩḳķḵƙⱪᶄ𝐤𝘬𝗄𝕜𝜅𝜘𝜿𝝒𝝹𝞌𝞳𝙠𝚔𝑘𝒌ϰ𝛋𝛞𝟆𝗸𝓴𝓀'],
|
||||
['L', '𝐋𝐿𝔏𝕃𝕷𝖫𝗟𝘓𝙇ﴼ🄛🄻𐐛Ⳑ𝑳𝙻𐑃𝓛ⳑꮮᏞꓡ🅻🅛ﺈ└ⓁւLĿᒪ乚ՆʟꓶιԼᴸˡĹረḶₗΓլĻᄂⅬℒⱢᥧᥨᒻᒶᒷᶫﺎᒺᒹᒸᒫ⎳ㄥŁⱠﺄȽ🇱'],
|
||||
['l', 'ⓛlŀĺľḷḹļӀℓḽḻłレɭƚɫⱡ|Ɩ⒧ʅǀוןΙІ|ᶩӏ𝓘𝕀𝖨𝗜𝘐𝐥𝑙𝒍𝓁𝔩𝕝𝖑𝗅𝗹𝘭𝚕𝜤𝝞ı𝚤ɩι𝛊𝜄𝜾𝞲'],
|
||||
['M', 'ꮇ🄜🄼𐌑𐊰ꓟⲘᎷ🅼🅜ⓂMмṂ൱ᗰ州ᘻო๓♏ʍᙏᴍᴹᵐ⒨ḾМṀ௱ⅯℳΜϺᛖӍӎ𝐌𝑀𝑴𝓜𝔐𝕄𝕸𝖬𝗠𝘔𝙈𝙼𝚳𝛭𝜧𝝡𝞛🇲'],
|
||||
['m', '₥ᵯ𝖒𝐦𝗆𝔪𝕞𝓂ⓜmനᙢ൩ḿṁⅿϻṃጠɱ៳ᶆ𝙢𝓶𝚖𝑚𝗺᧕᧗'],
|
||||
['N', '𝇙𝇚𝇜🄝𝆧𝙉🄽ℕꓠ𝛮𝝢𝙽𝚴𝑵𝑁Ⲛ𝐍𝒩𝞜𝗡𝘕𝜨𝓝𝖭🅽₦🅝ЙЍⓃҋ៷NᴎɴƝᑎ几иՈռИהЛπᴺᶰŃ刀ክṄⁿÑПΝᴨոϖǸŇṆŅṊṈทŊӢӣӤӥћѝйᥢҊᴻ🇳'],
|
||||
['n', 'ոռח𝒏𝓷𝙣𝑛𝖓𝔫𝗇𝚗𝗻ᥒⓝήnǹᴒńñᾗηṅňṇɲņṋṉղຖՌƞŋ⒩ภกɳпʼnлԉȠἠἡῃդᾐᾑᾒᾓᾔᾕᾖῄῆῇῂἢἣἤἥἦἧὴήበቡቢባቤብቦȵ𝛈𝜂𝜼𝝶𝞰𝕟𝘯𝐧𝓃ᶇᵰᥥ∩'],
|
||||
['O', '𝜽⭘🔿ꭴ⭕⏺🄁🄀Ꭴ𝚯𝚹𝛩𝛳𝜣𝜭𝝝𝝧𝞗𝞡ⴱᎾᏫ⍬𝞱𝝷𝛉𝟎𝜃θ𝟘𝑂𝑶𝓞𝔒𝕆𝕺𝗢𝘖𝙊𝛰㈇ꄲ🄞🔾🄾𐊒𝟬ꓳⲞ𐐄𐊫𐓂𝞞🅞⍥◯ⵁ⊖0⊝𝝤Ѳϴ𝚶𝜪ѺӦӨӪΌʘ𝐎ǑÒŎÓÔÕȌȎㇿ❍ⓄOὋロ❤૦⊕ØФԾΘƠᴼᵒ⒪ŐÖₒ¤◊Φ〇ΟОՕଠഠ௦סỒỐỖỔṌȬṎŌṐṒȮȰȪỎỜỚỠỞỢỌỘǪǬǾƟⵔ߀៰⍜⎔⎕⦰⦱⦲⦳⦴⦵⦶⦷⦸⦹⦺⦻⦼⦽⦾⦿⧀⧁⧂⧃ὈὉὊὌὍ'],
|
||||
['o', 'ంಂംං૦௦۵ℴ𝑜𝒐𝖔ꬽ𝝄𝛔𝜎𝝈𝞂ჿ𝚘০୦ዐ𝛐𝗈𝞼ဝⲟ𝙤၀𐐬𝔬𐓪𝓸🇴⍤○ϙ🅾𝒪𝖮𝟢𝟶𝙾𝘰𝗼𝕠𝜊𝐨𝝾𝞸ᐤⓞѳ᧐ᥲðoఠᦞՓòөӧóºōôǒȏŏồốȍỗổõσṍȭṏὄṑṓȯȫ๏ᴏőöѻоዐǭȱ০୦٥౦೦൦๐໐οօᴑ०੦ỏơờớỡởợọộǫøǿɵծὀὁόὸόὂὃὅ'],
|
||||
['P', '🄟🄿ꓑ𝚸𝙿𝞠𝙋ꮲⲢ𝒫𝝦𝑃𝑷𝗣𝐏𐊕𝜬𝘗𝓟𝖯𝛲Ꮲ🅟Ҏ🅿ⓅPƤᑭ尸Ṗրφքᴘᴾᵖ⒫ṔアקРየᴩⱣℙΡῬᑸᑶᑷᑹᑬᑮ🇵₱'],
|
||||
['p', 'ⲣҏ℗ⓟpṕṗƥᵽῥρрƿǷῤ⍴𝓹𝓅𝐩𝑝𝒑𝔭𝕡𝖕𝗉𝗽𝘱𝙥𝚙𝛒𝝆𝞺𝜌𝞀'],
|
||||
['Q', '🅀🄠Ꝗ🆀🅠ⓆQℚⵕԚ𝐐𝑄𝑸𝒬𝓠𝚀𝘘𝙌𝖰𝕼𝔔𝗤🇶'],
|
||||
['q', '𝓆ꝗ𝗾ⓠqգ⒬۹զᑫɋɊԛ𝗊𝑞𝘲𝕢𝚚𝒒𝖖𝐪𝔮𝓺𝙦'],
|
||||
['R', '℞🄡℟ꭱᏒ𐒴ꮢᎡꓣ🆁🅡ⓇRᴙȒʀᖇя尺ŔЯરƦᴿዪṚɌʁℛℜℝṘŘȐṜŖṞⱤ𝐑𝑅𝑹𝓡𝕽𝖱𝗥𝘙𝙍𝚁ᚱ🇷ᴚ'],
|
||||
['r', '𝚛ꭇᣴℾ𝚪𝛤𝜞𝝘𝞒ⲄГᎱᒥꭈⲅꮁⓡrŕṙřȑȓṛṝŗгՐɾᥬṟɍʳ⒭ɼѓᴦᶉ𝐫𝑟𝒓𝓇𝓻𝔯𝕣𝖗𝗋𝗿𝘳𝙧ᵲґᵣ'],
|
||||
['S', '🅂🄪🄢ꇙ𝓢𝗦Ꮪ𝒮Ꮥ𝚂𝐒ꓢ𝖲𝔖𝙎𐊖𝕾𐐠𝘚𝕊𝑆𝑺🆂🅢ⓈSṨŞֆՏȘˢ⒮ЅṠŠŚṤŜṦṢടᔕᔖᔢᔡᔣᔤ'],
|
||||
['s', 'ᣵⓢꜱ𐑈ꮪsśṥŝṡšṧʂṣṩѕşșȿᶊక𝐬𝑠𝒔𝓈𝓼𝔰𝕤𝖘𝗌𝘀𝘴𝙨𝚜ގ🇸'],
|
||||
['T', '🅃🄣七ፒ𝜯🆃𐌕𝚻𝛵𝕋𝕿𝑻𐊱𐊗𝖳𝙏🝨𝝩𝞣𝚃𝘛𝑇ꓔ⟙𝐓Ⲧ𝗧⊤𝔗Ꭲꭲ𝒯🅣⏇⏉ⓉTтҬҭƬイŦԵτᴛᵀイፕϮŤ⊥ƮΤТ下ṪṬȚŢṰṮ丅丁ᐪ𝛕𝜏𝝉𝞃𝞽𝓣ㄒ🇹ጥ'],
|
||||
['t', 'ⓣtṫẗťṭțȶ੮էʇ†ţṱṯƭŧᵗ⒯ʈեƫ𝐭𝑡𝒕𝓉𝓽𝔱𝕥𝖙𝗍𝘁𝘵𝙩𝚝ナ'],
|
||||
['U', '🅄Џ🄤ሀꓴ𐓎꒤🆄🅤ŨŬŮᑗᑘǓǕǗǙⓊUȖᑌ凵ƱմԱꓵЦŪՄƲᙀᵁᵘ⒰ŰપÜՍÙÚÛṸṺǛỦȔƯỪỨỮỬỰỤṲŲṶṴɄᥩᑧ∪ᘮ⋃𝐔𝑈𝑼𝒰𝓤𝔘𝕌𝖀𝖴𝗨𝘜𝙐𝚄🇺'],
|
||||
['u', '𝘂𝘶𝙪𝚞ꞟꭎꭒ𝛖𝜐𝝊𝞄𝞾𐓶ὺύⓤuùũūừṷṹŭǖữᥙǚǜὗυΰนսʊǘǔúůᴜűųยûṻцሁüᵾᵤµʋủȕȗưứửựụṳṵʉῠῡῢΰῦῧὐὑϋύὒὓὔὕὖᥔ𝐮𝑢𝒖𝓊𝓾𝔲𝕦𝖚𝗎ᶙ'],
|
||||
['V', '𝑉𝒱𝕍𝗩🄥🅅ꓦ𝑽𝖵𝘝Ꮩ𝚅𝙑𝐕🆅🅥ⓋVᐯѴᵛ⒱۷ṾⅴⅤṼ٧ⴸѶᐺᐻ🇻𝓥'],
|
||||
['v', '∨⌄⋁ⅴ𝐯𝑣𝒗𝓋𝔳𝕧𝖛𝗏ꮩሀⓥv𝜐𝝊ṽṿ౮งѵעᴠνטᵥѷ៴ᘁ𝙫𝚟𝛎𝜈𝝂𝝼𝞶𝘷𝘃𝓿'],
|
||||
['W', '𝐖𝑊𝓦𝔚𝕎𝖂𝖶𝗪𝙒𝚆🄦🅆ᏔᎳ𝑾ꓪ𝒲𝘞🆆Ⓦ🅦wWẂᾧᗯᥕ山ѠຟచաЩШώщฬшᙎᵂʷ⒲ฝሠẄԜẀŴẆẈധᘺѿᙡƜ₩🇼'],
|
||||
['w', '𝐰ꝡ𝑤𝒘𝓌𝔀𝔴𝕨𝖜𝗐𝘄𝘸𝙬𝚠աẁꮃẃⓦ⍵ŵẇẅẘẉⱳὼὠὡὢὣωὤὥὦὧῲῳῴῶῷⱲѡԝᴡώᾠᾡᾢᾣᾤᾥᾦɯ𝝕𝟉𝞏'],
|
||||
['X', 'ꭓꭕ𝛘𝜒𝝌𝞆𝟀ⲭ🞨𝑿𝛸🄧🞩🞪🅇🞫🞬𐌗Ⲭꓫ𝖃𝞦𝘟𐊐𝚾𝝬𝜲Ꭓ𐌢𝖷𝑋𝕏𝔛𐊴𝗫🆇🅧❌Ⓧ𝓧XẊ᙭χㄨ𝒳ӾჯӼҳЖΧҲᵡˣ⒳אሸẌꊼⅩХ╳᙮ᕁᕽⅹᚷⵝ𝙓𝚇乂𝐗🇽'],
|
||||
['x', '᙮ⅹ𝑥𝒙𝓍𝔵𝕩𝖝𝗑𝘅ᕁᕽⓧxхẋ×ₓ⤫⤬⨯ẍᶍ𝙭ӽ𝘹𝐱𝚡⨰メ𝔁'],
|
||||
['Y', '𝒴🄨𝓨𝔜𝖄𝖸𝘠𝙔𝚼𝛶𝝪𝞤УᎩᎽⲨ𝚈𝑌𝗬𝐘ꓬ𝒀𝜰𐊲🆈🅨ⓎYὛƳㄚʏ⅄ϔ¥¥ՎϓγץӲЧЎሃŸɎϤΥϒҮỲÝŶỸȲẎỶỴῨῩῪΎὙὝὟΫΎӮӰҰұ𝕐🇾'],
|
||||
['y', '𝐲𝑦𝒚𝓎𝔂𝔶𝕪𝖞𝗒𝘆𝘺𝙮𝚢ʏỿꭚγℽ𝛄𝛾𝜸𝝲𝞬🅈ᎽᎩⓨyỳýŷỹȳẏÿỷуყẙỵƴɏᵞɣʸᶌү⒴ӳӱӯўУʎ'],
|
||||
['Z', '🄩🅉ꓜ𝗭𝐙☡Ꮓ𝘡🆉🅩ⓏZẔƵ乙ẐȤᶻ⒵ŹℤΖŻŽẒⱫ🇿'],
|
||||
['z', '𝑍𝒁𝒵𝓩𝖹𝙕𝚉𝚭𝛧𝜡𝝛𝞕ᵶꮓ𝐳𝑧𝒛𝓏𝔃𝔷𝕫𝖟𝗓𝘇𝘻𝙯𝚣ⓩzźẑżžẓẕƶȥɀᴢጊʐⱬᶎʑᙆ']
|
||||
]);
|
||||
|
||||
/** @copyright Mathias Bynens <https://mathiasbynens.be/>. MIT license. */
|
||||
const regexLineBreakCombiningMarks = /[\0-\x08\x0E-\x1F\x7F-\x84\x86-\x9F\u0300-\u034E\u0350-\u035B\u0363-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u061C\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C00-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D01-\u0D03\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F7E\u0F80-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u180B-\u180D\u1885\u1886\u18A9\u1920-\u192B\u1930-\u193B\u1A17-\u1A1B\u1A7F\u1AB0-\u1ABE\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BE6-\u1BF3\u1C24-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF5\u1DFB-\u1DFF\u200C\u200E\u200F\u202A-\u202E\u2066-\u206F\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3035\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C5\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F\uFFF9-\uFFFB]|\uD800[\uDDFD\uDEE0\uDF76-\uDF7A]|\uD802[\uDE01-\uDE03\uDE05\uDE06\uDE0C-\uDE0F\uDE38-\uDE3A\uDE3F\uDEE5\uDEE6]|\uD804[\uDC00-\uDC02\uDC38-\uDC46\uDC7F-\uDC82\uDCB0-\uDCBA\uDD00-\uDD02\uDD27-\uDD34\uDD73\uDD80-\uDD82\uDDB3-\uDDC0\uDDCA-\uDDCC\uDE2C-\uDE37\uDE3E\uDEDF-\uDEEA\uDF00-\uDF03\uDF3C\uDF3E-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF57\uDF62\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC35-\uDC46\uDCB0-\uDCC3\uDDAF-\uDDB5\uDDB8-\uDDC0\uDDDC\uDDDD\uDE30-\uDE40\uDEAB-\uDEB7]|\uD807[\uDC2F-\uDC36\uDC38-\uDC3F\uDC92-\uDCA7\uDCA9-\uDCB6]|\uD81A[\uDEF0-\uDEF4\uDF30-\uDF36]|\uD81B[\uDF51-\uDF7E\uDF8F-\uDF92]|\uD82F[\uDC9D\uDC9E\uDCA0-\uDCA3]|\uD834[\uDD65-\uDD69\uDD6D-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD838[\uDC00-\uDC06\uDC08-\uDC18\uDC1B-\uDC21\uDC23\uDC24\uDC26-\uDC2A]|\uD83A[\uDCD0-\uDCD6\uDD44-\uDD4A]|\uDB40[\uDC01\uDC20-\uDC7F\uDD00-\uDDEF]/g;
|
||||
/** @copyright Mathias Bynens <https://mathiasbynens.be/>. MIT license. */
|
||||
const regexSymbolWithCombiningMarks = /([\0-\u02FF\u0370-\u1AAF\u1B00-\u1DBF\u1E00-\u20CF\u2100-\uD7FF\uE000-\uFE1F\uFE30-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])([\u0300-\u036F\u1AB0-\u1AFF\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]+)/g;
|
||||
|
||||
const confusablesToChar = new Map();
|
||||
for (const [char_, confusables] of charToConfusables) {
|
||||
for (const confusable of confusables) {
|
||||
confusablesToChar.set(confusable, char_);
|
||||
}
|
||||
}
|
||||
|
||||
const nonConfusables = /^[!-~]*$/;
|
||||
const removeConfusables = function (a) {
|
||||
// Skip if all characters are ok
|
||||
if (nonConfusables.test(a)) {
|
||||
return a;
|
||||
}
|
||||
|
||||
let ret = '';
|
||||
for (const char_ of a.normalize().replace(regexLineBreakCombiningMarks, '').replace(regexSymbolWithCombiningMarks, '$1').replace(/\s/g, '')) {
|
||||
ret += confusablesToChar.get(char_) || char_;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const removeWhitespace = function (a) {
|
||||
return a.replace(/\s/g, '');
|
||||
}
|
||||
|
||||
export { removeConfusables as default, confusablesToChar, charToConfusables, removeWhitespace };
|
||||
40
src/emoji.font.scss
Normal file
@@ -0,0 +1,40 @@
|
||||
body,
|
||||
input,
|
||||
textarea,
|
||||
select,
|
||||
button {
|
||||
font-family: 'Noto Sans JP', 'Noto Sans KR', 'Noto Sans TC', 'Noto Sans SC',
|
||||
'Noto Color Emoji', 'Meiryo UI', 'Malgun Gothic', 'Segoe UI', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
--md-sys-typescale-headline-medium-font: 'Google Sans', 'Noto Sans',
|
||||
'Noto Sans TC', 'Noto Sans JP', 'Noto Sans SC', 'Noto Color Emoji',
|
||||
'Roboto', sans-serif;
|
||||
--md-sys-typescale-headline-small-font: 'Google Sans', 'Noto Sans',
|
||||
'Noto Sans TC', 'Noto Sans JP', 'Noto Sans SC', 'Noto Color Emoji',
|
||||
'Roboto', sans-serif;
|
||||
--md-sys-typescale-title-medium-font: 'Google Sans', 'Noto Sans',
|
||||
'Noto Sans TC', 'Noto Sans JP', 'Noto Sans SC', 'Noto Color Emoji',
|
||||
'Roboto', sans-serif;
|
||||
--md-sys-typescale-label-large-font: 'Google Sans', 'Noto Sans',
|
||||
'Noto Sans TC', 'Noto Sans JP', 'Noto Sans SC', 'Noto Color Emoji',
|
||||
'Roboto', sans-serif;
|
||||
--md-sys-typescale-label-medium-font: 'Google Sans', 'Noto Sans',
|
||||
'Noto Sans TC', 'Noto Sans JP', 'Noto Sans SC', 'Noto Color Emoji',
|
||||
'Roboto', sans-serif;
|
||||
--md-sys-typescale-body-large-font: 'Google Sans', 'Noto Sans',
|
||||
'Noto Sans TC', 'Noto Sans JP', 'Noto Sans SC', 'Noto Color Emoji',
|
||||
'Roboto', sans-serif;
|
||||
--md-sys-typescale-body-medium-font: 'Google Sans', 'Noto Sans',
|
||||
'Noto Sans TC', 'Noto Sans JP', 'Noto Sans SC', 'Noto Color Emoji',
|
||||
'Roboto', sans-serif;
|
||||
--md-sys-typescale-body-small-font: 'Google Sans', 'Noto Sans',
|
||||
'Noto Sans TC', 'Noto Sans JP', 'Noto Sans SC', 'Noto Color Emoji',
|
||||
'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
:root {
|
||||
--font: 'Poppins', 'Noto Sans JP', 'Noto Sans KR', 'Noto Sans TC',
|
||||
'Noto Sans SC', 'Noto Color Emoji', sans-serif;
|
||||
}
|
||||
189
src/flags.scss
Normal file
@@ -0,0 +1,189 @@
|
||||
:root {
|
||||
--offx: 20px; /* flag size */
|
||||
--offy: calc(var(--offx) / 72 * 52);
|
||||
}
|
||||
.flags {
|
||||
background: url('/images/flags.png') no-repeat;
|
||||
background-size: calc(var(--offx) * 6);
|
||||
width: var(--offx);
|
||||
height: var(--offy);
|
||||
transform: translateY(2px); /* move flag down */
|
||||
background-position: calc(var(--offx) * -5) calc(var(--offy) * -4);
|
||||
}
|
||||
span[class='flags'] {
|
||||
background-position: calc(var(--offx) * 1) 0;
|
||||
}
|
||||
.flags.us {
|
||||
background-position: 0 0;
|
||||
}
|
||||
.flags.usw {
|
||||
background-position: 0 0;
|
||||
}
|
||||
.flags.kr {
|
||||
background-position: calc(var(--offx) * -1) 0;
|
||||
}
|
||||
.flags.ru {
|
||||
background-position: calc(var(--offx) * -2) 0;
|
||||
}
|
||||
.flags.es {
|
||||
background-position: calc(var(--offx) * -3) 0;
|
||||
}
|
||||
.flags.pt {
|
||||
background-position: calc(var(--offx) * -4) 0;
|
||||
}
|
||||
.flags.cn {
|
||||
background-position: calc(var(--offx) * -5) 0;
|
||||
}
|
||||
.flags.de {
|
||||
background-position: 0 calc(var(--offy) * -1);
|
||||
}
|
||||
.flags.jp {
|
||||
background-position: calc(var(--offx) * -1) calc(var(--offy) * -1);
|
||||
}
|
||||
.flags.fr {
|
||||
background-position: calc(var(--offx) * -2) calc(var(--offy) * -1);
|
||||
}
|
||||
.flags.se {
|
||||
background-position: calc(var(--offx) * -3) calc(var(--offy) * -1);
|
||||
}
|
||||
.flags.nl {
|
||||
background-position: calc(var(--offx) * -4) calc(var(--offy) * -1);
|
||||
}
|
||||
.flags.pl {
|
||||
background-position: calc(var(--offx) * -5) calc(var(--offy) * -1);
|
||||
}
|
||||
.flags.dk {
|
||||
background-position: 0 calc(var(--offy) * -2);
|
||||
}
|
||||
.flags.no {
|
||||
background-position: calc(var(--offx) * -1) calc(var(--offy) * -2);
|
||||
}
|
||||
.flags.it {
|
||||
background-position: calc(var(--offx) * -2) calc(var(--offy) * -2);
|
||||
}
|
||||
.flags.th {
|
||||
background-position: calc(var(--offx) * -3) calc(var(--offy) * -2);
|
||||
}
|
||||
.flags.fi {
|
||||
background-position: calc(var(--offx) * -4) calc(var(--offy) * -2);
|
||||
}
|
||||
.flags.hu {
|
||||
background-position: calc(var(--offx) * -5) calc(var(--offy) * -2);
|
||||
}
|
||||
.flags.cz {
|
||||
background-position: 0 calc(var(--offy) * -3);
|
||||
}
|
||||
.flags.tr {
|
||||
background-position: calc(var(--offx) * -1) calc(var(--offy) * -3);
|
||||
}
|
||||
.flags.ae {
|
||||
background-position: calc(var(--offx) * -2) calc(var(--offy) * -3);
|
||||
}
|
||||
.flags.ro {
|
||||
background-position: calc(var(--offx) * -3) calc(var(--offy) * -3);
|
||||
}
|
||||
.flags.vn {
|
||||
background-position: calc(var(--offx) * -4) calc(var(--offy) * -3);
|
||||
}
|
||||
.flags.ua {
|
||||
background-position: calc(var(--offx) * -5) calc(var(--offy) * -3);
|
||||
}
|
||||
.flags.gb {
|
||||
background-position: 0 calc(var(--offy) * -4);
|
||||
}
|
||||
.flags.use {
|
||||
background-position: calc(var(--offx) * -1) calc(var(--offy) * -4);
|
||||
}
|
||||
.flags.eu {
|
||||
background-position: calc(var(--offx) * -2) calc(var(--offy) * -4);
|
||||
}
|
||||
.flags.tw {
|
||||
background-position: calc(var(--offx) * -3) calc(var(--offy) * -4);
|
||||
}
|
||||
.flags.mt {
|
||||
background-position: 0 calc(var(--offy) * -5);
|
||||
}
|
||||
.flags.id {
|
||||
background-position: calc(var(--offx) * -1) calc(var(--offy) * -5);
|
||||
}
|
||||
.flags.hr {
|
||||
background-position: calc(var(--offx) * -2) calc(var(--offy) * -5);
|
||||
}
|
||||
.flags.he {
|
||||
background-position: calc(var(--offx) * -3) calc(var(--offy) * -5);
|
||||
}
|
||||
.flags.af {
|
||||
background-position: calc(var(--offx) * -4) calc(var(--offy) * -5);
|
||||
}
|
||||
.flags.be {
|
||||
background-position: calc(var(--offx) * -5) calc(var(--offy) * -5);
|
||||
}
|
||||
.flags.bg {
|
||||
background-position: 0 calc(var(--offy) * -6);
|
||||
}
|
||||
.flags.cy {
|
||||
background-position: calc(var(--offx) * -1) calc(var(--offy) * -6);
|
||||
}
|
||||
.flags.el {
|
||||
background-position: calc(var(--offx) * -2) calc(var(--offy) * -6);
|
||||
}
|
||||
.flags.et {
|
||||
background-position: calc(var(--offx) * -3) calc(var(--offy) * -6);
|
||||
}
|
||||
.flags.ph {
|
||||
background-position: calc(var(--offx) * -4) calc(var(--offy) * -6);
|
||||
}
|
||||
.flags.gd {
|
||||
background-position: calc(var(--offx) * -5) calc(var(--offy) * -6);
|
||||
}
|
||||
.flags.ga {
|
||||
background-position: 0 calc(var(--offy) * -7);
|
||||
}
|
||||
.flags.hi {
|
||||
background-position: calc(var(--offx) * -1) calc(var(--offy) * -7);
|
||||
}
|
||||
.flags.hy {
|
||||
background-position: calc(var(--offx) * -2) calc(var(--offy) * -7);
|
||||
}
|
||||
.flags.is {
|
||||
background-position: calc(var(--offx) * -3) calc(var(--offy) * -7);
|
||||
}
|
||||
.flags.lv {
|
||||
background-position: calc(var(--offx) * -4) calc(var(--offy) * -7);
|
||||
}
|
||||
.flags.lt {
|
||||
background-position: calc(var(--offx) * -5) calc(var(--offy) * -7);
|
||||
}
|
||||
.flags.lb {
|
||||
background-position: 0 calc(var(--offy) * -8);
|
||||
}
|
||||
.flags.mk {
|
||||
background-position: calc(var(--offx) * -1) calc(var(--offy) * -8);
|
||||
}
|
||||
.flags.sk {
|
||||
background-position: calc(var(--offx) * -2) calc(var(--offy) * -8);
|
||||
}
|
||||
.flags.sl {
|
||||
background-position: calc(var(--offx) * -3) calc(var(--offy) * -8);
|
||||
}
|
||||
.flags.nz {
|
||||
background-position: calc(var(--offx) * -4) calc(var(--offy) * -8);
|
||||
}
|
||||
.flags.au {
|
||||
background-position: calc(var(--offx) * -5) calc(var(--offy) * -8);
|
||||
}
|
||||
.flags.eo {
|
||||
background-position: 0 calc(var(--offy) * -9);
|
||||
}
|
||||
.flags.tok {
|
||||
background-position: calc(var(--offx) * -1) calc(var(--offy) * -9);
|
||||
}
|
||||
.flags.my {
|
||||
background-position: calc(var(--offx) * -2) calc(var(--offy) * -9);
|
||||
}
|
||||
.flags.blank {
|
||||
background-position: calc(var(--offx) * -4) calc(var(--offy) * -4);
|
||||
}
|
||||
.flags.unknown {
|
||||
background-position: calc(var(--offx) * -5) calc(var(--offy) * -4);
|
||||
}
|
||||
157
src/index.pug
Normal file
@@ -0,0 +1,157 @@
|
||||
doctype html
|
||||
html
|
||||
head
|
||||
meta(http-equiv="Content-Type" content="text/html;charset=utf-8")
|
||||
meta(http-equiv="Cache-Control" content="no-cache")
|
||||
meta(http-equiv="referrer" content="no-referrer")
|
||||
meta(http-equiv="viewport" content="width=device-width,initial-scale=1,user-scalable=no")
|
||||
title VRCX
|
||||
link(rel="preconnect" href="https://api.vrchat.cloud")
|
||||
link(rel="preconnect" href="https://d348imysud55la.cloudfront.net")
|
||||
link(rel="stylesheet" href="app.css")
|
||||
link(rel="stylesheet" href="flags.css")
|
||||
link(rel="stylesheet" href="animated-emoji.css")
|
||||
body
|
||||
.x-app#x-app(style="display:none" @dragenter.prevent @dragover.prevent @drop.prevent)
|
||||
//- login
|
||||
include ./mixins/loginPage.pug
|
||||
+loginPage()
|
||||
|
||||
//- menu
|
||||
.x-menu-container
|
||||
//- download progress, update pending
|
||||
.pending-update(v-if="updateInProgress" @click="showVRCXUpdateDialog")
|
||||
el-progress(type="circle" width="50" stroke-width="3" :percentage="updateProgress" :format="updateProgressText")
|
||||
.pending-update(v-else-if="pendingVRCXUpdate || pendingVRCXInstall")
|
||||
el-button(type="default" @click="showVRCXUpdateDialog" size="mini" icon="el-icon-download" circle style="font-size:14px;height:50px;width:50px")
|
||||
|
||||
el-menu(ref="menu" collapse @select="selectMenu")
|
||||
mixin menuitem(index, name, icon)
|
||||
el-menu-item(index=index)
|
||||
i(class=icon)
|
||||
template(#title)
|
||||
span= name
|
||||
+menuitem('feed', "{{ $t('nav_tooltip.feed') }}", 'el-icon-news')
|
||||
+menuitem('gameLog', "{{ $t('nav_tooltip.game_log') }}", 'el-icon-s-data')
|
||||
+menuitem('playerList', "{{ $t('nav_tooltip.player_list') }}", 'el-icon-tickets')
|
||||
+menuitem('search', "{{ $t('nav_tooltip.search') }}", 'el-icon-search')
|
||||
+menuitem('favorite', "{{ $t('nav_tooltip.favorites') }}", 'el-icon-star-off')
|
||||
+menuitem('friendLog', "{{ $t('nav_tooltip.friend_log') }}", 'el-icon-notebook-2')
|
||||
+menuitem('moderation', "{{ $t('nav_tooltip.moderation') }}", 'el-icon-finished')
|
||||
+menuitem('notification', "{{ $t('nav_tooltip.notification') }}", 'el-icon-bell')
|
||||
+menuitem('friendsList', "{{ $t('nav_tooltip.friend_list') }}", 'el-icon-s-management')
|
||||
+menuitem('profile', "{{ $t('nav_tooltip.profile') }}", 'el-icon-user')
|
||||
+menuitem('settings', "{{ $t('nav_tooltip.settings') }}", 'el-icon-s-tools')
|
||||
|
||||
//- ### Tabs ###
|
||||
template(v-if="API.isLoggedIn")
|
||||
|
||||
//- feed
|
||||
include ./mixins/tabs/feed.pug
|
||||
+feedTab()
|
||||
|
||||
//- gameLog
|
||||
include ./mixins/tabs/gameLog.pug
|
||||
+gameLogTab()
|
||||
|
||||
//- playerList
|
||||
include ./mixins/tabs/playerList.pug
|
||||
+playerListTab()
|
||||
|
||||
//- search
|
||||
include ./mixins/tabs/search.pug
|
||||
+searchTab()
|
||||
|
||||
//- favorite
|
||||
include ./mixins/tabs/favorites.pug
|
||||
+favoritesTab()
|
||||
|
||||
//- friendLog
|
||||
include ./mixins/tabs/friendLog.pug
|
||||
+friendLogTab()
|
||||
|
||||
//- moderation
|
||||
include ./mixins/tabs/moderation.pug
|
||||
+moderationTab()
|
||||
|
||||
//- notification
|
||||
include ./mixins/tabs/notifications.pug
|
||||
+notificationsTab()
|
||||
|
||||
//- profile
|
||||
include ./mixins/tabs/profile.pug
|
||||
+profileTab()
|
||||
|
||||
//- friends list
|
||||
include ./mixins/tabs/friendsList.pug
|
||||
+friendsListTab()
|
||||
|
||||
//- settings
|
||||
include ./mixins/tabs/settings.pug
|
||||
+settingsTab()
|
||||
|
||||
include ./mixins/friendsListSidebar.pug
|
||||
+friendsListSidebar()
|
||||
|
||||
//- ## Dialogs ## -\\
|
||||
include ./mixins/dialogs/userDialog.pug
|
||||
+userDialog()
|
||||
|
||||
include ./mixins/dialogs/worldDialog.pug
|
||||
+worldDialog()
|
||||
|
||||
include ./mixins/dialogs/avatarDialog.pug
|
||||
+avatarDialog()
|
||||
|
||||
include ./mixins/dialogs/groupDialog.pug
|
||||
+groupDialog()
|
||||
|
||||
include ./mixins/dialogs/favoritesDialog.pug
|
||||
+favoritesDialog()
|
||||
|
||||
include ./mixins/dialogs/images.pug
|
||||
+images()
|
||||
|
||||
include ./mixins/dialogs/newInstance.pug
|
||||
+newInstance()
|
||||
|
||||
include ./mixins/dialogs/feedFilters.pug
|
||||
+feedFilters()
|
||||
|
||||
include ./mixins/dialogs/openSourceSoftwareNotice.pug
|
||||
+openSourceSoftwareNotice()
|
||||
|
||||
include ./mixins/dialogs/groups.pug
|
||||
+groups()
|
||||
|
||||
include ./mixins/dialogs/currentUser.pug
|
||||
+currentUser()
|
||||
|
||||
include ./mixins/dialogs/invites.pug
|
||||
+invites()
|
||||
|
||||
include ./mixins/dialogs/launch.pug
|
||||
+launch()
|
||||
|
||||
include ./mixins/dialogs/screenshotMetadata.pug
|
||||
+screenshotMetadata()
|
||||
|
||||
include ./mixins/dialogs/vrcx.pug
|
||||
+vrcx()
|
||||
|
||||
include ./mixins/dialogs/settings.pug
|
||||
+settings()
|
||||
|
||||
include ./mixins/dialogs/previousInstances.pug
|
||||
+previousInstances()
|
||||
|
||||
include ./mixins/dialogs/tags.pug
|
||||
+tags()
|
||||
|
||||
include ./mixins/dialogs/boops.pug
|
||||
+boops()
|
||||
|
||||
//- el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="templateDialog" :visible.sync="templateDialog.visible" :title="$t('dialog.template_dialog.header')" width="450px")
|
||||
|
||||
script(src="vendor.js")
|
||||
script(src="app.js")
|
||||
33
src/ipc-electron/interopApi.js
Normal file
@@ -0,0 +1,33 @@
|
||||
class InteropApi {
|
||||
constructor() {
|
||||
return new Proxy(this, {
|
||||
get(target, prop) {
|
||||
if (WINDOWS) {
|
||||
return undefined;
|
||||
}
|
||||
// If the property is not a method of InteropApi,
|
||||
// treat it as a .NET class name
|
||||
if (typeof prop === 'string' && !target[prop]) {
|
||||
return new Proxy({}, {
|
||||
get(_, methodName) {
|
||||
// Return a method that calls the .NET method dynamically
|
||||
return async (...args) => {
|
||||
return await target.callMethod(prop, methodName, ...args);
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
return target[prop];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async callMethod(className, methodName, ...args) {
|
||||
return window.interopApi.callDotNetMethod(className, methodName, args)
|
||||
.then(result => {
|
||||
return result;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default new InteropApi();
|
||||
2012
src/localization/en/en.json
Normal file
1784
src/localization/es/en.json
Normal file
2006
src/localization/fr/en.json
Normal file
1734
src/localization/hu/en.json
Normal file
1911
src/localization/ja/en.json
Normal file
1734
src/localization/ko/en.json
Normal file
229
src/localization/localizationHelperCLI.js
Normal file
@@ -0,0 +1,229 @@
|
||||
// Because this isn't a package (just a loose js file), we have to use require
|
||||
// statements
|
||||
|
||||
const process = require("node:process")
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const yargs = require("yargs/yargs");
|
||||
const { hideBin } = require("yargs/helpers")
|
||||
|
||||
const getLocalizationObjects = function* () {
|
||||
const localeFolder = './src/localization';
|
||||
const folders = fs.readdirSync(localeFolder, { withFileTypes: true }).filter(file => file.isDirectory());
|
||||
for (const folder of folders) {
|
||||
const filePath = path.join(localeFolder, folder.name, "en.json");
|
||||
const jsonStr = fs.readFileSync(filePath);
|
||||
yield [filePath, JSON.parse(jsonStr)];
|
||||
}
|
||||
}
|
||||
|
||||
const addKey = function (obj, objects, value, above_key) {
|
||||
console.log(`Adding key to ${obj.language} at path '${objects.join('.')}' with value '${value}' above key '${above_key}'`);
|
||||
|
||||
let currentObj = obj;
|
||||
let i = 0;
|
||||
|
||||
// Last element is final key not object so loop n - 1 times
|
||||
for (; i < objects.length - 1; i++) {
|
||||
if (!Object.hasOwn(currentObj, objects[i])) {
|
||||
currentObj[objects[i]] = {};
|
||||
}
|
||||
|
||||
currentObj = currentObj[objects[i]];
|
||||
}
|
||||
InsertKeyInObj(currentObj, objects[i], value, above_key);
|
||||
}
|
||||
|
||||
// Shamelessly stolen from https://stackoverflow.com/a/55017155/11030436
|
||||
const InsertKeyInObj = (obj, key, value, above_key) => {
|
||||
const keys = Object.keys(obj);
|
||||
if (keys.length === 0 || !(Object.hasOwn(obj, above_key))) {
|
||||
obj[key] = value;
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Reconstruct object from scratch, inserting our new key when the next
|
||||
// key is the above_key
|
||||
|
||||
// Again utilize the dummy key in case we're adding above the first key
|
||||
keys.unshift('dummy');
|
||||
obj.dummy = {};
|
||||
const ret = keys.reduce((newObj, currKey, i) => {
|
||||
if (currKey !== key) {
|
||||
newObj[currKey] = obj[currKey];
|
||||
}
|
||||
|
||||
if (i < keys.length - 1 && keys[i + 1] === above_key) {
|
||||
newObj[key] = value;
|
||||
}
|
||||
|
||||
return newObj;
|
||||
}, {})
|
||||
delete ret.dummy;
|
||||
|
||||
// Clear keys on old object
|
||||
for (const key of keys) {
|
||||
delete obj[key];
|
||||
}
|
||||
|
||||
// Assign new properties to old object
|
||||
Object.assign(obj, ret);
|
||||
}
|
||||
|
||||
const addLocalizationKey = (key, value, above_key) => {
|
||||
const objects = key.split('.');
|
||||
|
||||
for (const [localePath, localeObj] of getLocalizationObjects()) {
|
||||
addKey(localeObj, objects, value, above_key);
|
||||
fs.writeFileSync(localePath, `${JSON.stringify(localeObj, null, 4)}\n`);
|
||||
}
|
||||
|
||||
console.log(`\`${key}:${value}\` added to every localization file!`);
|
||||
}
|
||||
|
||||
const removeKey = (obj, objects, i = 0) => {
|
||||
console.log(`Removing key from ${obj.language} at path '${objects.join('.')}'`);
|
||||
if (!(Object.hasOwn(obj, objects[i]))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (objects.length - 1 === i) {
|
||||
delete obj[objects[i]];
|
||||
} else {
|
||||
removeKey(obj[objects[i]], objects, i + 1);
|
||||
if (Object.keys(obj[objects[i]]).length === 0) {
|
||||
delete obj[objects[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const removeLocalizationKey = (key) => {
|
||||
const objects = key.split('.');
|
||||
for (const [localePath, localeObj] of getLocalizationObjects()) {
|
||||
removeKey(localeObj, objects, 0);
|
||||
|
||||
// All the localization files seem to have a trailing new line, so add
|
||||
// one ourselves
|
||||
fs.writeFileSync(localePath, `${JSON.stringify(localeObj, null, 4)}\n`);
|
||||
}
|
||||
|
||||
console.log(`\`${key}\` removed from every localization file!`);
|
||||
}
|
||||
|
||||
// Yes this code is extremely slow, but it doesn't run very often so.
|
||||
const Validate = function () {
|
||||
const files = [...getLocalizationObjects()];
|
||||
const enIndex = files.findIndex(file => path.dirname(file[0]).endsWith("en"));
|
||||
const [_, enObj] = files.splice(enIndex, 1)[0];
|
||||
|
||||
const traverse = function (obj, predicate, pathes = []) {
|
||||
for (const key in obj) {
|
||||
if (typeof obj[key] === 'string' || obj[key] instanceof String) {
|
||||
predicate(obj, key, pathes);
|
||||
} else {
|
||||
traverse(obj[key], predicate, [...pathes, key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let hasRemoved = false;
|
||||
for (const [_, localeObj] of files) {
|
||||
toRemove = []
|
||||
traverse(localeObj, (_, key, pathes) => {
|
||||
let currObj = enObj;
|
||||
for (const pathSegment of pathes) {
|
||||
if (Object.hasOwn(currObj, pathSegment)) {
|
||||
currObj = currObj[pathSegment]
|
||||
} else {
|
||||
toRemove.push([...pathes, key]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!Object.hasOwn(currObj, key)) {
|
||||
toRemove.push([...pathes, key]);
|
||||
}
|
||||
});
|
||||
|
||||
// Remove after traversal finishes to not modify while iterating
|
||||
for (const toRemovePath of toRemove) {
|
||||
removeKey(localeObj, toRemovePath);
|
||||
hasRemoved = true;
|
||||
}
|
||||
}
|
||||
|
||||
toAdd = []
|
||||
traverse(enObj, (obj, key, pathes) => {
|
||||
// Add above_key to the toAdd entry
|
||||
if (toAdd.length > 0 && typeof toAdd.at(-1)[3] === 'undefined' && toAdd.at(-1)[1].at(-2) === pathes.at(-1)) {
|
||||
toAdd.at(-1)[3] = key;
|
||||
}
|
||||
|
||||
for (const [_, localeObj] of files) {
|
||||
let currObj = localeObj;
|
||||
for (const pathSegment of pathes) {
|
||||
if (Object.hasOwn(currObj, pathSegment)) {
|
||||
currObj = currObj[pathSegment];
|
||||
} else {
|
||||
toAdd.push([localeObj, [...pathes, key], obj[key], undefined]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Object.hasOwn(currObj, key)) {
|
||||
toAdd.push([localeObj, [...pathes, key], obj[key], undefined]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for (const addObj of toAdd) {
|
||||
addKey(...addObj);
|
||||
}
|
||||
|
||||
if (toAdd.length > 0 || hasRemoved) {
|
||||
for (const [localePath, localeObj] of files) {
|
||||
fs.writeFileSync(localePath, `${JSON.stringify(localeObj, null, 4)}\n`);
|
||||
}
|
||||
} else {
|
||||
console.log("validation passed!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const cliParser = yargs(hideBin(process.argv))
|
||||
.command({
|
||||
command: 'add <key> <value> [above_key]',
|
||||
aliases: ['a', 'replace', 'r'],
|
||||
desc: 'adds or replaces a key and value to all localization files above `above_key`',
|
||||
handler: (argv) => addLocalizationKey(argv.key, argv.value, argv.above_key)
|
||||
})
|
||||
.command({
|
||||
command: 'remove <key>',
|
||||
aliases: ['rm', 'r'],
|
||||
desc: 'removes key from all localization files',
|
||||
handler: (argv) => removeLocalizationKey(argv.key)
|
||||
})
|
||||
.command({
|
||||
command: 'validate',
|
||||
aliases: [],
|
||||
desc: 'removes keys from other languages that don\'t exist in the en translation and adds keys that don\'t exist in other languages',
|
||||
handler: Validate
|
||||
})
|
||||
.demandCommand(1)
|
||||
.example([
|
||||
['$0 add foo.bar "I\'m adding a key!"', 'Adding a key as `foo.bar`'],
|
||||
['$0 remove foo.bar', 'removes the foo.bar key'],
|
||||
['$0 add foo.bar "I\'m adding a key!" baz', 'Adding a key aboe the existing `foo.baz` key']
|
||||
])
|
||||
.help(false)
|
||||
.version(false)
|
||||
|
||||
cliParser
|
||||
.wrap(cliParser.terminalWidth())
|
||||
.command({
|
||||
command: 'help',
|
||||
aliases: ['h'],
|
||||
desc: 'Shows the help message',
|
||||
handler: () => cliParser.showHelp()
|
||||
})
|
||||
.fail(() => cliParser.showHelp())
|
||||
.parse()
|
||||
63
src/localization/localizedStrings.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import en from './en/en.json' assert { type: 'JSON' };
|
||||
import elements_en from 'element-ui/lib/locale/lang/en';
|
||||
|
||||
import es from './es/en.json' assert { type: 'JSON' };
|
||||
import elements_es from 'element-ui/lib/locale/lang/es';
|
||||
|
||||
import fr from './fr/en.json' assert { type: 'JSON' };
|
||||
import elements_fr from 'element-ui/lib/locale/lang/fr';
|
||||
|
||||
// import hu from './hu/en.json' assert { type: 'JSON' };
|
||||
// import elements_hu from 'element-ui/lib/locale/lang/hu';
|
||||
|
||||
import ja from './ja/en.json' assert { type: 'JSON' };
|
||||
import elements_ja from 'element-ui/lib/locale/lang/ja';
|
||||
|
||||
import ko from './ko/en.json' assert { type: 'JSON' };
|
||||
import elements_ko from 'element-ui/lib/locale/lang/ko';
|
||||
|
||||
import pl from './pl/en.json' assert { type: 'JSON' };
|
||||
import elements_pl from 'element-ui/lib/locale/lang/pl';
|
||||
|
||||
import pt from './pt/en.json' assert { type: 'JSON' };
|
||||
import elements_pt from 'element-ui/lib/locale/lang/pt';
|
||||
|
||||
import ru_RU from './ru/en.json' assert { type: 'JSON' };
|
||||
import elements_ru from 'element-ui/lib/locale/lang/ru-RU';
|
||||
|
||||
import vi from './vi/en.json' assert { type: 'JSON' };
|
||||
import elements_vi from 'element-ui/lib/locale/lang/vi';
|
||||
|
||||
import zh_CN from './zh-CN/en.json' assert { type: 'JSON' };
|
||||
import elements_zh_CN from 'element-ui/lib/locale/lang/zh-CN';
|
||||
|
||||
import zh_TW from './zh-TW/en.json' assert { type: 'JSON' };
|
||||
import elements_zh_TW from 'element-ui/lib/locale/lang/zh-TW';
|
||||
|
||||
const localized_en = { ...en, ...elements_en };
|
||||
const localized_es = { ...es, ...elements_es };
|
||||
const localized_fr = { ...fr, ...elements_fr };
|
||||
// const localized_hu = { ...hu, ...elements_hu };
|
||||
const localized_ja = { ...ja, ...elements_ja };
|
||||
const localized_ko = { ...ko, ...elements_ko };
|
||||
const localized_pl = { ...pl, ...elements_pl };
|
||||
const localized_pt = { ...pt, ...elements_pt };
|
||||
const localized_ru = { ...ru_RU, ...elements_ru };
|
||||
const localized_vi = { ...vi, ...elements_vi };
|
||||
const localized_zh_CN = { ...zh_CN, ...elements_zh_CN };
|
||||
const localized_zh_TW = { ...zh_TW, ...elements_zh_TW };
|
||||
|
||||
export {
|
||||
localized_en as en,
|
||||
localized_es as es,
|
||||
localized_fr as fr,
|
||||
// localized_hu as hu,
|
||||
localized_ja as ja_JP,
|
||||
localized_ko as ko,
|
||||
localized_pl as pl,
|
||||
localized_pt as pt,
|
||||
localized_ru as ru_RU,
|
||||
localized_vi as vi,
|
||||
localized_zh_CN as zh_CN,
|
||||
localized_zh_TW as zh_TW
|
||||
};
|
||||
1734
src/localization/pl/en.json
Normal file
1734
src/localization/pt/en.json
Normal file
1937
src/localization/ru/en.json
Normal file
1734
src/localization/vi/en.json
Normal file
1939
src/localization/zh-CN/en.json
Normal file
1939
src/localization/zh-TW/en.json
Normal file
10
src/masks/askme.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 28.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1"
|
||||
id="svg1" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512"
|
||||
style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<g id="Layer_4">
|
||||
<path d="M0,0v238.8c103.3-41.5,200.8-38.3,257.6,18.5c56.3,56.3,59.9,152.5,19.6,254.7H512V0H0z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 645 B |
7
src/masks/busy.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 28.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<path d="M0,0v512h512V0H0z M456,271.76c0,22.71-18,41.13-40.2,41.13H96.2c-22.2,0-40.2-18.41-40.2-41.13v-31.53
|
||||
c0-22.71,18-41.13,40.2-41.13h319.6c22.2,0,40.2,18.41,40.2,41.13V271.76z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 541 B |
10
src/masks/joinme.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 28.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1"
|
||||
id="svg1" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512"
|
||||
style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<path d="M0,512h512V0H0V512z M83.7,290.7l140.2-179c5.3-6.6,11.9-12.1,19.8-14.3c5-1.9,10.9-1.8,16.1-1.8c11,0,21.5,4.4,28.2,12.8
|
||||
l138.1,183.9c12,15,7.6,36.9-9.9,49.1c-5.9,4.1-12.4,6.5-19,7.5c-3.4,0.8-7,1.2-10.7,1.2h-77.4l-109.4,0h-80.7
|
||||
c-2.9,0-5.6-0.3-8.3-0.7c-6.8-0.7-13.4-3-19.3-7.1C74.1,330,70.6,307,83.7,290.7z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 843 B |
10
src/masks/phone.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 28.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 256 256" style="enable-background:new 0 0 256 256;" xml:space="preserve">
|
||||
<g id="Layer_2_00000065047169181629844270000002708237785512253600_">
|
||||
<path d="M188.7,10H67.3c-9,0-16.3,7.4-16.4,16.4v203.2c0,9,7.4,16.4,16.4,16.4h121.4c9,0,16.3-7.4,16.3-16.4V26.4
|
||||
C205,17.4,197.7,10,188.7,10z M79.1,44.3h97v139.1h-97C79.1,183.4,79.1,44.3,79.1,44.3z M117.5,217.7c0-5.9,4.8-10.7,10.7-10.7
|
||||
s10.7,4.8,10.7,10.7s-4.8,10.7-10.7,10.7S117.5,223.6,117.5,217.7z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 737 B |
13
src/masks/usercutout.svg
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 28.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_2_00000039816400109898662970000002014992858927017630_"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512"
|
||||
style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{stroke:#000000;stroke-miterlimit:10;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M437,512h75v-75C512,478.4,478.4,512,437,512z"/>
|
||||
<path class="st0" d="M0,0v512h437c-41.4,0-75-33.6-75-75s33.6-75,75-75s75,33.6,75,75V0H0z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 659 B |
11
src/masks/usercutoutmobile.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 28.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{stroke:#000000;stroke-miterlimit:10;}
|
||||
</style>
|
||||
<g id="Layer_2_00000039816400109898662970000002014992858927017630_">
|
||||
<path class="st0" d="M372,345.7c0-14.8,12-26.7,26.7-26.7H512V0H0v512h372V345.7z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 591 B |
123
src/mixins/dialogs/avatarDialog.pug
Normal file
@@ -0,0 +1,123 @@
|
||||
|
||||
mixin avatarDialog()
|
||||
el-dialog.x-dialog.x-avatar-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="avatarDialog" :visible.sync="avatarDialog.visible" :show-close="false" width="600px")
|
||||
div(v-loading="avatarDialog.loading")
|
||||
div(style="display:flex")
|
||||
el-popover(placement="right" width="500px" trigger="click")
|
||||
img.x-link(slot="reference" v-lazy="avatarDialog.ref.thumbnailImageUrl" style="flex:none;width:160px;height:120px;border-radius:12px")
|
||||
img.x-link(v-lazy="avatarDialog.ref.imageUrl" style="width:500px;height:375px" @click="showFullscreenImageDialog(avatarDialog.ref.imageUrl)")
|
||||
div(style="flex:1;display:flex;align-items:center;margin-left:15px")
|
||||
div(style="flex:1")
|
||||
div
|
||||
span.dialog-title(v-text="avatarDialog.ref.name")
|
||||
div(style="margin-top:5px")
|
||||
span.x-link.x-grey(v-text="avatarDialog.ref.authorName" @click="showUserDialog(avatarDialog.ref.authorId)" style="font-family:monospace")
|
||||
div
|
||||
el-tag(v-if="avatarDialog.ref.releaseStatus === 'public'" type="success" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.avatar.tags.public') }}
|
||||
el-tag(v-else type="danger" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.avatar.tags.private') }}
|
||||
el-tag.x-tag-platform-pc(v-if="avatarDialog.isPC" type="info" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") PC
|
||||
span.x-grey(v-if="avatarDialog.platformInfo.pc" style=";margin-left:5px;border-left:inherit;padding-left:5px") {{ avatarDialog.platformInfo.pc.performanceRating }}
|
||||
span.x-grey(v-if="avatarDialog.bundleSizes['standalonewindows']" style=";margin-left:5px;border-left:inherit;padding-left:5px") {{ avatarDialog.bundleSizes['standalonewindows'].fileSize }}
|
||||
el-tag.x-tag-platform-quest(v-if="avatarDialog.isQuest" type="info" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") Android
|
||||
span.x-grey(v-if="avatarDialog.platformInfo.android" style=";margin-left:5px;border-left:inherit;padding-left:5px") {{ avatarDialog.platformInfo.android.performanceRating }}
|
||||
span.x-grey(v-if="avatarDialog.bundleSizes['android']" style="margin-left:5px;border-left:inherit;padding-left:5px") {{ avatarDialog.bundleSizes['android'].fileSize }}
|
||||
el-tag.x-tag-platform-ios(v-if="avatarDialog.isIos" type="info" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") iOS
|
||||
span.x-grey(v-if="avatarDialog.platformInfo.ios" style=";margin-left:5px;border-left:inherit;padding-left:5px") {{ avatarDialog.platformInfo.ios.performanceRating }}
|
||||
span.x-grey(v-if="avatarDialog.bundleSizes['ios']" style="margin-left:5px;border-left:inherit;padding-left:5px") {{ avatarDialog.bundleSizes['ios'].fileSize }}
|
||||
el-tag.x-link(v-if="avatarDialog.inCache" type="info" effect="plain" size="mini" @click="openFolderGeneric(avatarDialog.cachePath)" style="margin-right:5px;margin-top:5px")
|
||||
span(v-text="avatarDialog.cacheSize")
|
||||
| {{ $t('dialog.avatar.tags.cache') }}
|
||||
el-tag(v-if="avatarDialog.isQuestFallback" type="info" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.avatar.tags.fallback') }}
|
||||
el-tag(v-if="avatarDialog.hasImposter" type="info" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.avatar.tags.impostor') }}
|
||||
span.x-grey(v-if="avatarDialog.imposterVersion" style="margin-left:5px;border-left:inherit;padding-left:5px") v{{ avatarDialog.imposterVersion }}
|
||||
el-tag(v-if="avatarDialog.ref.unityPackageUrl" type="success" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.avatar.tags.future_proofing') }}
|
||||
div
|
||||
template(v-for="tag in avatarDialog.ref.tags")
|
||||
el-tag(v-if="tag.startsWith('content_')" :key="tag" effect="plain" size="mini" style="margin-right:5px;margin-top:5px")
|
||||
template(v-if="tag === 'content_horror'") {{ $t('dialog.avatar.tags.content_horror') }}
|
||||
template(v-else-if="tag === 'content_gore'") {{ $t('dialog.avatar.tags.content_gore') }}
|
||||
template(v-else-if="tag === 'content_violence'") {{ $t('dialog.avatar.tags.content_violence') }}
|
||||
template(v-else-if="tag === 'content_adult'") {{ $t('dialog.avatar.tags.content_adult') }}
|
||||
template(v-else-if="tag === 'content_sex'") {{ $t('dialog.avatar.tags.content_sex') }}
|
||||
template(v-else) {{ tag.replace('content_', '') }}
|
||||
div(style="margin-top:5px")
|
||||
span(v-show="avatarDialog.ref.name !== avatarDialog.ref.description" v-text="avatarDialog.ref.description" style="font-size:12px")
|
||||
div(style="flex:none;margin-left:10px")
|
||||
el-tooltip(v-if="avatarDialog.inCache" placement="top" :content="$t('dialog.avatar.actions.delete_cache_tooltip')" :disabled="hideTooltips")
|
||||
el-button(icon="el-icon-delete" circle @click="deleteVRChatCache(avatarDialog.ref)" :disabled="isGameRunning && avatarDialog.cacheLocked")
|
||||
el-tooltip(v-if="avatarDialog.isFavorite" placement="top" :content="$t('dialog.avatar.actions.favorite_tooltip')" :disabled="hideTooltips")
|
||||
el-button(type="warning" icon="el-icon-star-on" circle @click="avatarDialogCommand('Add Favorite')" style="margin-left:5px")
|
||||
el-tooltip(v-else placement="top" :content="$t('dialog.avatar.actions.favorite_tooltip')" :disabled="hideTooltips")
|
||||
el-button(type="default" icon="el-icon-star-off" circle @click="avatarDialogCommand('Add Favorite')" style="margin-left:5px")
|
||||
el-dropdown(trigger="click" @command="avatarDialogCommand" size="small" style="margin-left:5px")
|
||||
el-button(:type="avatarDialog.isBlocked ? 'danger' : 'default'" icon="el-icon-more" circle)
|
||||
el-dropdown-menu(#default="dropdown")
|
||||
el-dropdown-item(icon="el-icon-refresh" command="Refresh") {{ $t('dialog.avatar.actions.refresh') }}
|
||||
el-dropdown-item(icon="el-icon-check" :disabled="API.currentUser.currentAvatar === avatarDialog.id" command="Select Avatar") {{ $t('dialog.avatar.actions.select') }}
|
||||
el-dropdown-item(v-if="/quest/.test(avatarDialog.ref.tags)" icon="el-icon-check" command="Select Fallback Avatar") {{ $t('dialog.avatar.actions.select_fallback') }}
|
||||
el-dropdown-item(v-if="avatarDialog.isBlocked" icon="el-icon-circle-check" command="Unblock Avatar" style="color:#F56C6C") {{ $t('dialog.avatar.actions.unblock') }}
|
||||
el-dropdown-item(v-else icon="el-icon-circle-close" command="Block Avatar") {{ $t('dialog.avatar.actions.block') }}
|
||||
el-dropdown-item(v-if="avatarDialog.ref.authorId !== API.currentUser.id" icon="el-icon-picture-outline" command="Previous Images") {{ $t('dialog.avatar.actions.show_previous_images') }}
|
||||
template(v-if="avatarDialog.ref.authorId === API.currentUser.id")
|
||||
el-dropdown-item(v-if="avatarDialog.ref.releaseStatus === 'public'" icon="el-icon-user-solid" command="Make Private" divided) {{ $t('dialog.avatar.actions.make_private') }}
|
||||
el-dropdown-item(v-else icon="el-icon-user" command="Make Public" divided) {{ $t('dialog.avatar.actions.make_public') }}
|
||||
el-dropdown-item(icon="el-icon-edit" command="Rename") {{ $t('dialog.avatar.actions.rename') }}
|
||||
el-dropdown-item(icon="el-icon-edit" command="Change Description") {{ $t('dialog.avatar.actions.change_description') }}
|
||||
el-dropdown-item(icon="el-icon-edit" command="Change Content Tags") {{ $t('dialog.avatar.actions.change_content_tags') }}
|
||||
el-dropdown-item(icon="el-icon-picture-outline" command="Change Image") {{ $t('dialog.avatar.actions.change_image') }}
|
||||
el-dropdown-item(v-if="avatarDialog.ref.unityPackageUrl" icon="el-icon-download" command="Download Unity Package") {{ $t('dialog.avatar.actions.download_package') }}
|
||||
el-dropdown-item(v-if="avatarDialog.hasImposter" icon="el-icon-refresh" command="Regenerate Imposter" style="color:#F56C6C") {{ $t('dialog.avatar.actions.regenerate_impostor') }}
|
||||
el-dropdown-item(v-if="avatarDialog.hasImposter" icon="el-icon-delete" command="Delete Imposter" style="color:#F56C6C") {{ $t('dialog.avatar.actions.delete_impostor') }}
|
||||
el-dropdown-item(v-else icon="el-icon-user" command="Create Imposter") {{ $t('dialog.avatar.actions.create_impostor') }}
|
||||
el-dropdown-item(icon="el-icon-delete" command="Delete" style="color:#F56C6C" divided) {{ $t('dialog.avatar.actions.delete') }}
|
||||
el-tabs
|
||||
el-tab-pane(:label="$t('dialog.avatar.info.header')")
|
||||
.x-friend-list
|
||||
.x-friend-item(style="width:100%;cursor:default")
|
||||
.detail
|
||||
span.name(style="margin-bottom:5px") {{ $t('dialog.avatar.info.memo') }}
|
||||
el-input.extra(v-model="avatarDialog.memo" @change="onAvatarMemoChange" size="mini" type="textarea" :rows="2" :autosize="{minRows: 1, maxRows: 20}" :placeholder="$t('dialog.avatar.info.memo_placeholder')" resize="none")
|
||||
.x-friend-item(style="width:100%;cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.avatar.info.id') }}
|
||||
span.extra {{ avatarDialog.id }}
|
||||
el-tooltip(placement="top" :content="$t('dialog.avatar.info.id_tooltip')" :disabled="hideTooltips")
|
||||
el-dropdown(trigger="click" @click.native.stop size="mini" style="margin-left:5px")
|
||||
el-button(type="default" icon="el-icon-s-order" size="mini" circle)
|
||||
el-dropdown-menu(#default="dropdown")
|
||||
el-dropdown-item(@click.native="copyAvatarId(avatarDialog.id)") {{ $t('dialog.avatar.info.copy_id') }}
|
||||
el-dropdown-item(@click.native="copyAvatarUrl(avatarDialog.id)") {{ $t('dialog.avatar.info.copy_url') }}
|
||||
.x-friend-item(style="cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.avatar.info.created_at') }}
|
||||
span.extra {{ avatarDialog.ref.created_at | formatDate('long') }}
|
||||
.x-friend-item(style="cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.avatar.info.last_updated') }}
|
||||
span.extra(v-if="avatarDialog.lastUpdated") {{ avatarDialog.lastUpdated | formatDate('long') }}
|
||||
span.extra(v-else) {{ avatarDialog.ref.updated_at | formatDate('long') }}
|
||||
.x-friend-item(style="cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.avatar.info.version') }}
|
||||
span.extra(v-if="avatarDialog.ref.version !== 0" v-text="avatarDialog.ref.version")
|
||||
span.extra(v-else) -
|
||||
.x-friend-item(style="width:100%;cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.avatar.info.platform') }}
|
||||
span.extra(v-if="avatarDialogPlatform" v-text="avatarDialogPlatform")
|
||||
span.extra(v-else) -
|
||||
el-tab-pane(:label="$t('dialog.avatar.json.header')")
|
||||
el-button(type="default" @click="refreshAvatarDialogTreeData()" size="mini" icon="el-icon-refresh" circle)
|
||||
el-tooltip(placement="top" :content="$t('dialog.avatar.json.file_analysis')" :disabled="hideTooltips")
|
||||
el-button(type="default" @click="getAvatarFileAnalysis" size="mini" icon="el-icon-s-data" circle style="margin-left:5px")
|
||||
el-button(type="default" @click="downloadAndSaveJson(avatarDialog.id, avatarDialog.ref)" size="mini" icon="el-icon-download" circle style="margin-left:5px")
|
||||
el-tree(v-if="Object.keys(avatarDialog.fileAnalysis).length > 0" :data="avatarDialog.fileAnalysis" style="margin-top:5px;font-size:12px")
|
||||
template(#default="scope")
|
||||
span
|
||||
span(v-text="scope.data.key" style="font-weight:bold;margin-right:5px")
|
||||
span(v-if="!scope.data.children" v-text="scope.data.value")
|
||||
el-tree(:data="avatarDialog.treeData" style="margin-top:5px;font-size:12px")
|
||||
template(#default="scope")
|
||||
span
|
||||
span(v-text="scope.data.key" style="font-weight:bold;margin-right:5px")
|
||||
span(v-if="!scope.data.children" v-text="scope.data.value")
|
||||
53
src/mixins/dialogs/boops.pug
Normal file
@@ -0,0 +1,53 @@
|
||||
mixin boops()
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="sendBoopDialog" :visible.sync="sendBoopDialog.visible" :title="$t('dialog.boop_dialog.header')" width="450px")
|
||||
div(v-if="sendBoopDialog.visible")
|
||||
el-select(v-model="sendBoopDialog.userId" :placeholder="$t('dialog.new_instance.instance_creator_placeholder')" filterable style="width:100%")
|
||||
el-option-group(v-if="vipFriends.length" :label="$t('side_panel.favorite')")
|
||||
el-option.x-friend-item(v-for="friend in vipFriends" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto")
|
||||
template(v-if="friend.ref")
|
||||
.avatar(:class="userStatusClass(friend.ref)")
|
||||
img(v-lazy="userImage(friend.ref)")
|
||||
.detail
|
||||
span.name(v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
|
||||
span(v-else v-text="friend.id")
|
||||
el-option-group(v-if="onlineFriends.length" :label="$t('side_panel.online')")
|
||||
el-option.x-friend-item(v-for="friend in onlineFriends" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto")
|
||||
template(v-if="friend.ref")
|
||||
.avatar(:class="userStatusClass(friend.ref)")
|
||||
img(v-lazy="userImage(friend.ref)")
|
||||
.detail
|
||||
span.name(v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
|
||||
span(v-else v-text="friend.id")
|
||||
el-option-group(v-if="activeFriends.length" :label="$t('side_panel.active')")
|
||||
el-option.x-friend-item(v-for="friend in activeFriends" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto")
|
||||
template(v-if="friend.ref")
|
||||
.avatar
|
||||
img(v-lazy="userImage(friend.ref)")
|
||||
.detail
|
||||
span.name(v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
|
||||
span(v-else v-text="friend.id")
|
||||
el-option-group(v-if="offlineFriends.length" :label="$t('side_panel.offline')")
|
||||
el-option.x-friend-item(v-for="friend in offlineFriends" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto")
|
||||
template(v-if="friend.ref")
|
||||
.avatar
|
||||
img(v-lazy="userImage(friend.ref)")
|
||||
.detail
|
||||
span.name(v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
|
||||
span(v-else v-text="friend.id")
|
||||
br
|
||||
br
|
||||
el-select(v-model="sendBoopDialog.fileId" clearable :placeholder="$t('dialog.boop_dialog.select_emoji')" size="small" style="width:100%" popper-class="max-height-el-select")
|
||||
el-option-group(:label="$t('dialog.boop_dialog.my_emojis')")
|
||||
el-option(v-if="image.versions && image.versions.length > 0" v-for="image in emojiTable" :key="image.id" :value="image.id" style="width:100%;height:100%")
|
||||
.vrcplus-icon(v-if="image.versions[image.versions.length - 1].file.url" style="overflow:hidden;width:200px;height:200px;padding:10px")
|
||||
template(v-if="image.frames")
|
||||
.avatar(:style="generateEmojiStyle(image.versions[image.versions.length - 1].file.url, image.framesOverTime, image.frames, image.loopStyle)")
|
||||
template(v-else)
|
||||
img.avatar(v-lazy="image.versions[image.versions.length - 1].file.url" style="width:200px;height:200px")
|
||||
el-option-group(:label="$t('dialog.boop_dialog.default_emojis')")
|
||||
el-option(v-for="emojiName in photonEmojis" :key="emojiName" :value="getEmojiValue(emojiName)" style="width:100%;height:100%")
|
||||
span(v-text="emojiName")
|
||||
template(#footer)
|
||||
el-button(size="small" @click="showGalleryDialog(2)") {{ $t('dialog.boop_dialog.emoji_manager') }}
|
||||
el-button(size="small" @click="sendBoopDialog.visible = false") {{ $t('dialog.boop_dialog.cancel') }}
|
||||
el-button(size="small" @click="sendBoop" :disabled="!sendBoopDialog.userId") {{ $t('dialog.boop_dialog.send') }}
|
||||
189
src/mixins/dialogs/currentUser.pug
Normal file
@@ -0,0 +1,189 @@
|
||||
mixin currentUser()
|
||||
//- dialog: social status
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="socialStatusDialog" :visible.sync="socialStatusDialog.visible" :title="$t('dialog.social_status.header')" width="400px")
|
||||
div(v-loading="socialStatusDialog.loading")
|
||||
el-collapse(style="border:0")
|
||||
el-collapse-item
|
||||
template(slot="title")
|
||||
span(style="font-size:16px") {{ $t('dialog.social_status.history') }}
|
||||
data-tables(v-bind="socialStatusHistoryTable" @row-click="setSocialStatusFromHistory" style="cursor:pointer")
|
||||
el-table-column(:label="$t('table.social_status.no')" prop="no" width="50")
|
||||
el-table-column(:label="$t('table.social_status.status')" prop="status")
|
||||
el-select(v-model="socialStatusDialog.status" style="display:block;margin-top:10px")
|
||||
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') }}
|
||||
el-option(v-if="API.currentUser.$isModerator" :label="$t('dialog.user.status.offline')" value="offline").
|
||||
#[i.x-user-status.offline] {{ $t('dialog.user.status.offline') }}
|
||||
el-input(v-model="socialStatusDialog.statusDescription" :placeholder="$t('dialog.social_status.status_placeholder')" maxlength="32" show-word-limit style="display:block;margin-top:10px")
|
||||
template(#footer)
|
||||
el-button(type="primary" size="small" :disabled="socialStatusDialog.loading" @click="saveSocialStatus") {{ $t('dialog.social_status.update') }}
|
||||
|
||||
//- dialog: language
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="languageDialog" :visible.sync="languageDialog.visible" :title="$t('dialog.language.header')" width="400px")
|
||||
div(v-loading="languageDialog.loading")
|
||||
div(style="margin:5px 0")
|
||||
el-tag(v-for="item in API.currentUser.$languages" :key="item.key" size="small" type="info" effect="plain" closable @close="removeUserLanguage(item.key)" style="margin-right:5px")
|
||||
span.flags(:class="languageClass(item.key)" style="display:inline-block;margin-right:5px")
|
||||
| {{ item.value }} ({{ item.key }})
|
||||
div(v-if="languageDialog.languageChoice === true")
|
||||
el-select(v-model="languageDialog.languageValue" :placeholder="$t('dialog.language.select_language')" size="mini")
|
||||
el-option(v-for="item in languageDialog.languages" :key="item.key" :value="item.key" :label="item.value")
|
||||
span.flags(:class="languageClass(item.key)" style="display:inline-block;margin-right:5px")
|
||||
| {{ item.value }} ({{ item.key }})
|
||||
el-button(@click="languageDialog.languageChoice=false; addUserLanguage(languageDialog.languageValue)" size="mini") {{ $t('dialog.language.ok') }}
|
||||
el-button(@click="languageDialog.languageChoice=false" size="mini" style="margin-left:0") {{ $t('dialog.language.cancel') }}
|
||||
div(v-else)
|
||||
el-button(@click="languageDialog.languageValue='';languageDialog.languageChoice=true" size="mini") {{ $t('dialog.language.add_language') }}
|
||||
|
||||
//- dialog: bio
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="bioDialog" :visible.sync="bioDialog.visible" :title="$t('dialog.bio.header')" width="600px")
|
||||
div(v-loading="bioDialog.loading")
|
||||
el-input(type="textarea" v-model="bioDialog.bio" size="mini" maxlength="512" show-word-limit :autosize="{ minRows:2, maxRows:5 }" :placeholder="$t('dialog.bio.bio_placeholder')")
|
||||
el-input(v-for="(link, index) in bioDialog.bioLinks" :key="index" :value="link" v-model="bioDialog.bioLinks[index]" size="small" style="margin-top:5px")
|
||||
img(slot="prepend" :src="getFaviconUrl(link)" style="width:16px;height:16px")
|
||||
el-button(slot="append" icon="el-icon-delete" @click="bioDialog.bioLinks.splice(index, 1)")
|
||||
el-button(@click="bioDialog.bioLinks.push('')" :disabled="bioDialog.bioLinks.length >= 3" size="mini" style="margin-top:5px") {{ $t('dialog.bio.add_link') }}
|
||||
template(#footer)
|
||||
el-button(type="primary" size="small" :disabled="bioDialog.loading" @click="saveBio") {{ $t('dialog.bio.update') }}
|
||||
|
||||
//- dialog: pronouns
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="pronounsDialog" :visible.sync="pronounsDialog.visible" :title="$t('dialog.pronouns.header')" width="600px")
|
||||
div(v-loading="pronounsDialog.loading")
|
||||
el-input(type="textarea" v-model="pronounsDialog.pronouns" size="mini" maxlength="32" show-word-limit :autosize="{ minRows:2, maxRows:5 }" :placeholder="$t('dialog.pronouns.pronouns_placeholder')")
|
||||
template(#footer)
|
||||
el-button(type="primary" size="small" :disabled="pronounsDialog.loading" @click="savePronouns") {{ $t('dialog.pronouns.update') }}
|
||||
|
||||
//- dialog: Gallery/VRCPlusIcons
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="galleryDialog" :visible.sync="galleryDialogVisible" :title="$t('dialog.gallery_icons.header')" width="100%")
|
||||
el-tabs(type="card" ref="galleryTabs")
|
||||
el-tab-pane(v-if="galleryDialogVisible" v-loading="galleryDialogGalleryLoading")
|
||||
span(slot="label") {{ $t('dialog.gallery_icons.gallery') }}
|
||||
span(style="color:#909399;font-size:12px;margin-left:5px") {{ galleryTable.length }}/64
|
||||
input(type="file" accept="image/*" @change="onFileChangeGallery" id="GalleryUploadButton" style="display:none")
|
||||
span {{ $t('dialog.gallery_icons.recommended_image_size') }}: 1200x900px (4:3)
|
||||
br
|
||||
br
|
||||
el-button-group
|
||||
el-button(type="default" size="small" @click="refreshGalleryTable" icon="el-icon-refresh") {{ $t('dialog.gallery_icons.refresh') }}
|
||||
el-button(type="default" size="small" @click="displayGalleryUpload" icon="el-icon-upload2" :disabled="!API.currentUser.$isVRCPlus") {{ $t('dialog.gallery_icons.upload') }}
|
||||
el-button(type="default" size="small" @click="setProfilePicOverride('')" icon="el-icon-close" :disabled="!API.currentUser.profilePicOverride") {{ $t('dialog.gallery_icons.clear') }}
|
||||
br
|
||||
.x-friend-item(v-if="image.versions && image.versions.length > 0" v-for="image in galleryTable" :key="image.id" style="display:inline-block;margin-top:10px;width:unset;cursor:default")
|
||||
.vrcplus-icon(v-if="image.versions[image.versions.length - 1].file.url" @click="setProfilePicOverride(image.id)" :class="{ 'current-vrcplus-icon': compareCurrentProfilePic(image.id) }")
|
||||
img.avatar(v-lazy="image.versions[image.versions.length - 1].file.url")
|
||||
div(style="float:right;margin-top:5px")
|
||||
el-button(type="default" @click="showFullscreenImageDialog(image.versions[image.versions.length - 1].file.url)" size="mini" icon="el-icon-picture-outline" circle)
|
||||
el-button(type="default" @click="deleteGalleryImage(image.id)" size="mini" icon="el-icon-delete" circle style="margin-left:5px")
|
||||
el-tab-pane(v-if="galleryDialogVisible" v-loading="galleryDialogIconsLoading")
|
||||
span(slot="label") {{ $t('dialog.gallery_icons.icons') }}
|
||||
span(style="color:#909399;font-size:12px;margin-left:5px") {{ VRCPlusIconsTable.length }}/64
|
||||
input(type="file" accept="image/*" @change="onFileChangeVRCPlusIcon" id="VRCPlusIconUploadButton" style="display:none")
|
||||
span {{ $t('dialog.gallery_icons.recommended_image_size') }}: 2048x2048px (1:1)
|
||||
br
|
||||
br
|
||||
el-button-group
|
||||
el-button(type="default" size="small" @click="refreshVRCPlusIconsTable" icon="el-icon-refresh") {{ $t('dialog.gallery_icons.refresh') }}
|
||||
el-button(type="default" size="small" @click="displayVRCPlusIconUpload" icon="el-icon-upload2" :disabled="!API.currentUser.$isVRCPlus") {{ $t('dialog.gallery_icons.upload') }}
|
||||
el-button(type="default" size="small" @click="setVRCPlusIcon('')" icon="el-icon-close" :disabled="!API.currentUser.userIcon") {{ $t('dialog.gallery_icons.clear') }}
|
||||
br
|
||||
.x-friend-item(v-if="image.versions && image.versions.length > 0" v-for="image in VRCPlusIconsTable" :key="image.id" style="display:inline-block;margin-top:10px;width:unset;cursor:default")
|
||||
.vrcplus-icon(v-if="image.versions[image.versions.length - 1].file.url" @click="setVRCPlusIcon(image.id)" :class="{ 'current-vrcplus-icon': compareCurrentVRCPlusIcon(image.id) }")
|
||||
img.avatar(v-lazy="image.versions[image.versions.length - 1].file.url")
|
||||
div(style="float:right;margin-top:5px")
|
||||
el-button(type="default" @click="showFullscreenImageDialog(image.versions[image.versions.length - 1].file.url)" size="mini" icon="el-icon-picture-outline" circle)
|
||||
el-button(type="default" @click="deleteVRCPlusIcon(image.id)" size="mini" icon="el-icon-delete" circle style="margin-left:5px")
|
||||
el-tab-pane(v-if="galleryDialogVisible" v-loading="galleryDialogEmojisLoading")
|
||||
span(slot="label") {{ $t('dialog.gallery_icons.emojis') }}
|
||||
span(style="color:#909399;font-size:12px;margin-left:5px") {{ emojiTable.length }}/9
|
||||
input(type="file" accept="image/*" @change="onFileChangeEmoji" id="EmojiUploadButton" style="display:none")
|
||||
span {{ $t('dialog.gallery_icons.recommended_image_size') }}: 1024x1024px (1:1)
|
||||
br
|
||||
br
|
||||
el-button-group(style="margin-right:10px")
|
||||
el-button(type="default" size="small" @click="refreshEmojiTable" icon="el-icon-refresh") {{ $t('dialog.gallery_icons.refresh') }}
|
||||
el-button(type="default" size="small" @click="displayEmojiUpload" icon="el-icon-upload2" :disabled="!API.currentUser.$isVRCPlus") {{ $t('dialog.gallery_icons.upload') }}
|
||||
el-select(v-model="emojiAnimationStyle" popper-class="max-height-el-select")
|
||||
el-option-group {{ $t('dialog.gallery_icons.emoji_animation_styles') }}
|
||||
el-option.x-friend-item(v-for="(fileName, styleName) in emojiAnimationStyleList" :key="fileName" :label="styleName" :value="styleName" style="height:auto")
|
||||
.avatar(style="width:200px;height:200px")
|
||||
img(v-lazy="`${emojiAnimationStyleUrl}${fileName}`")
|
||||
.detail
|
||||
span.name(v-text="styleName" style="margin-right:100px")
|
||||
el-checkbox(v-model="emojiAnimType" style="margin-left:10px;margin-right:10px")
|
||||
span {{ $t('dialog.gallery_icons.emoji_animation_type') }}
|
||||
template(v-if="emojiAnimType")
|
||||
span(style="margin-right:10px") {{ $t('dialog.gallery_icons.emoji_animation_fps') }}
|
||||
el-input-number(size="small" v-model="emojiAnimFps" :min="1" :max="64" style="margin-right:10px;width:112px")
|
||||
span(style="margin-right:10px") {{ $t('dialog.gallery_icons.emoji_animation_frame_count') }}
|
||||
el-input-number(size="small" v-model="emojiAnimFrameCount" :min="2" :max="64" style="margin-right:10px;width:112px")
|
||||
el-checkbox(v-model="emojiAnimLoopPingPong" style="margin-left:10px;margin-right:10px")
|
||||
span {{ $t('dialog.gallery_icons.emoji_loop_pingpong') }}
|
||||
br
|
||||
br
|
||||
span {{ $t('dialog.gallery_icons.flipbook_info') }}
|
||||
br
|
||||
.x-friend-item(v-if="image.versions && image.versions.length > 0" v-for="image in emojiTable" :key="image.id" style="display:inline-block;margin-top:10px;width:unset;cursor:default")
|
||||
.vrcplus-icon(v-if="image.versions[image.versions.length - 1].file.url" style="overflow:hidden" @click="showFullscreenImageDialog(image.versions[image.versions.length - 1].file.url, getEmojiFileName(image))")
|
||||
template(v-if="image.frames")
|
||||
.avatar(:style="generateEmojiStyle(image.versions[image.versions.length - 1].file.url, image.framesOverTime, image.frames, image.loopStyle)")
|
||||
template(v-else)
|
||||
img.avatar(v-lazy="image.versions[image.versions.length - 1].file.url")
|
||||
div(style="display:inline-block;margin:5px")
|
||||
span(v-if="image.loopStyle === 'pingpong'") #[i.el-icon-refresh.el-icon--left]
|
||||
span(style="margin-right:5px") {{ image.animationStyle }}
|
||||
span(v-if="image.framesOverTime" style="margin-right:5px") {{ image.framesOverTime }}fps
|
||||
span(v-if="image.frames" style="margin-right:5px") {{ image.frames }}frames
|
||||
br
|
||||
div(style="float:right;margin-top:5px")
|
||||
el-button(type="default" @click="showFullscreenImageDialog(image.versions[image.versions.length - 1].file.url, getEmojiFileName(image))" size="mini" icon="el-icon-picture-outline" circle)
|
||||
el-button(type="default" @click="deleteEmoji(image.id)" size="mini" icon="el-icon-delete" circle style="margin-left:5px")
|
||||
el-tab-pane(v-if="galleryDialogVisible" v-loading="galleryDialogStickersLoading")
|
||||
span(slot="label") {{ $t('dialog.gallery_icons.stickers') }}
|
||||
span(style="color:#909399;font-size:12px;margin-left:5px") {{ stickerTable.length }}/9
|
||||
input(type="file" accept="image/*" @change="onFileChangeSticker" id="StickerUploadButton" style="display:none")
|
||||
span {{ $t('dialog.gallery_icons.recommended_image_size') }}: 1024x1024px (1:1)
|
||||
br
|
||||
br
|
||||
el-button-group
|
||||
el-button(type="default" size="small" @click="refreshStickerTable" icon="el-icon-refresh") {{ $t('dialog.gallery_icons.refresh') }}
|
||||
el-button(type="default" size="small" @click="displayStickerUpload" icon="el-icon-upload2" :disabled="!API.currentUser.$isVRCPlus") {{ $t('dialog.gallery_icons.upload') }}
|
||||
br
|
||||
.x-friend-item(v-if="image.versions && image.versions.length > 0" v-for="image in stickerTable" :key="image.id" style="display:inline-block;margin-top:10px;width:unset;cursor:default")
|
||||
.vrcplus-icon(v-if="image.versions[image.versions.length - 1].file.url" style="overflow:hidden" @click="showFullscreenImageDialog(image.versions[image.versions.length - 1].file.url)")
|
||||
img.avatar(v-lazy="image.versions[image.versions.length - 1].file.url")
|
||||
div(style="float:right;margin-top:5px")
|
||||
el-button(type="default" @click="showFullscreenImageDialog(image.versions[image.versions.length - 1].file.url)" size="mini" icon="el-icon-picture-outline" circle)
|
||||
el-button(type="default" @click="deleteSticker(image.id)" size="mini" icon="el-icon-delete" circle style="margin-left:5px")
|
||||
el-tab-pane(v-if="galleryDialogVisible" v-loading="galleryDialogPrintsLoading")
|
||||
span(slot="label") {{ $t('dialog.gallery_icons.prints') }}
|
||||
span(style="color:#909399;font-size:12px;margin-left:5px") {{ printTable.length }}/64
|
||||
input(type="file" accept="image/*" @change="onFileChangePrint" id="PrintUploadButton" style="display:none")
|
||||
span {{ $t('dialog.gallery_icons.recommended_image_size') }}: 1920x1080px (16:9)
|
||||
br
|
||||
br
|
||||
el-button-group
|
||||
el-button(type="default" size="small" @click="refreshPrintTable" icon="el-icon-refresh") {{ $t('dialog.gallery_icons.refresh') }}
|
||||
el-button(type="default" size="small" @click="displayPrintUpload" icon="el-icon-upload2" :disabled="!API.currentUser.$isVRCPlus") {{ $t('dialog.gallery_icons.upload') }}
|
||||
el-input(type="textarea" v-model="printUploadNote" size="mini" rows="1" resize="none" maxlength="32" style="margin-left:10px;width:300px" :placeholder="$t('dialog.gallery_icons.note')")
|
||||
br
|
||||
.x-friend-item(v-for="image in printTable" :key="image.id" style="display:inline-block;margin-top:10px;width:unset;cursor:default")
|
||||
.vrcplus-icon(style="overflow:hidden" @click="showFullscreenImageDialog(image.files.image, getPrintFileName(image))")
|
||||
img.avatar(v-lazy="image.files.image")
|
||||
div(style="margin-top:5px;width:208px")
|
||||
span.x-ellipsis(v-if="image.note" v-text="image.note" style="display:block")
|
||||
span(v-else style="display:block")
|
||||
location.x-ellipsis(v-if="image.worldId" :location="image.worldId" :hint="image.worldName" style="display:block")
|
||||
span(v-else style="display:block")
|
||||
display-name.x-ellipsis(v-if="image.authorId" :userid="image.authorId" :hint="image.authorName" style="color:#909399;font-family:monospace;display:block")
|
||||
span(v-else style="font-family:monospace;display:block")
|
||||
span.x-ellipsis(v-if="image.createdAt" style="color:#909399;font-family:monospace;font-size:11px;display:block") {{ image.createdAt | formatDate('long') }}
|
||||
span(v-else style="display:block")
|
||||
div(style="float:right")
|
||||
el-button(type="default" @click="showFullscreenImageDialog(image.files.image, getPrintFileName(image))" size="mini" icon="el-icon-picture-outline" circle)
|
||||
el-button(type="default" @click="deletePrint(image.id)" size="mini" icon="el-icon-delete" circle style="margin-left:5px")
|
||||
241
src/mixins/dialogs/favoritesDialog.pug
Normal file
@@ -0,0 +1,241 @@
|
||||
mixin favoritesDialog()
|
||||
//- dialog: favorite
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="favoriteDialog" :visible.sync="favoriteDialog.visible" :title="$t('dialog.favorite.header')" width="300px")
|
||||
div(v-if="favoriteDialog.visible" v-loading="favoriteDialog.loading")
|
||||
span(style="display:block;text-align:center") {{ $t('dialog.favorite.vrchat_favorites') }}
|
||||
template(v-if="favoriteDialog.currentGroup && favoriteDialog.currentGroup.key")
|
||||
el-button(style="display:block;width:100%;margin:10px 0" @click="deleteFavoriteNoConfirm(favoriteDialog.objectId)") #[i.el-icon-check] {{ favoriteDialog.currentGroup.displayName }} ({{ favoriteDialog.currentGroup.count }} / {{ favoriteDialog.currentGroup.capacity }})
|
||||
template(v-else)
|
||||
el-button(v-for="group in favoriteDialog.groups" :key="group" style="display:block;width:100%;margin:10px 0" @click="addFavorite(group)") {{ group.displayName }} ({{ group.count }} / {{ group.capacity }})
|
||||
div(v-if="favoriteDialog.visible && favoriteDialog.type === 'world'" style="margin-top:20px")
|
||||
span(style="display:block;text-align:center") {{ $t('dialog.favorite.local_favorites') }}
|
||||
template(v-for="group in localWorldFavoriteGroups" :key="group")
|
||||
el-button(v-if="hasLocalWorldFavorite(favoriteDialog.objectId, group)" style="display:block;width:100%;margin:10px 0" @click="removeLocalWorldFavorite(favoriteDialog.objectId, group)") #[i.el-icon-check] {{ group }} ({{ getLocalWorldFavoriteGroupLength(group) }})
|
||||
el-button(v-else style="display:block;width:100%;margin:10px 0" @click="addLocalWorldFavorite(favoriteDialog.objectId, group)") {{ group }} ({{ getLocalWorldFavoriteGroupLength(group) }})
|
||||
div(v-if="favoriteDialog.visible && favoriteDialog.type === 'avatar'" style="margin-top:20px")
|
||||
span(style="display:block;text-align:center") {{ $t('dialog.favorite.local_avatar_favorites') }}
|
||||
template(v-for="group in localAvatarFavoriteGroups" :key="group")
|
||||
el-button(v-if="hasLocalAvatarFavorite(favoriteDialog.objectId, group)" style="display:block;width:100%;margin:10px 0" @click="removeLocalAvatarFavorite(favoriteDialog.objectId, group)") #[i.el-icon-check] {{ group }} ({{ getLocalAvatarFavoriteGroupLength(group) }})
|
||||
el-button(v-else style="display:block;width:100%;margin:10px 0" :disabled="!isLocalUserVrcplusSupporter()" @click="addLocalAvatarFavorite(favoriteDialog.objectId, group)") {{ group }} ({{ getLocalAvatarFavoriteGroupLength(group) }})
|
||||
|
||||
//- dialog: export friends list
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" :visible.sync="exportFriendsListDialog" :title="$t('dialog.export_friends_list.header')" width="650px")
|
||||
el-tabs(type="card")
|
||||
el-tab-pane(:label="$t('dialog.export_friends_list.csv')")
|
||||
el-input(type="textarea" v-if="exportFriendsListDialog" v-model="exportFriendsListCsv" size="mini" rows="15" resize="none" readonly style="margin-top:15px" @click.native="$event.target.tagName === 'TEXTAREA' && $event.target.select()")
|
||||
el-tab-pane(:label="$t('dialog.export_friends_list.json')")
|
||||
el-input(type="textarea" v-if="exportFriendsListDialog" v-model="exportFriendsListJson" size="mini" rows="15" resize="none" readonly style="margin-top:15px" @click.native="$event.target.tagName === 'TEXTAREA' && $event.target.select()")
|
||||
|
||||
//- dialog: export avatars list
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" :visible.sync="exportAvatarsListDialog" :title="$t('dialog.export_own_avatars.header')" width="650px")
|
||||
el-input(type="textarea" v-if="exportAvatarsListDialog" v-model="exportAvatarsListCsv" size="mini" rows="15" resize="none" readonly style="margin-top:15px" @click.native="$event.target.tagName === 'TEXTAREA' && $event.target.select()")
|
||||
|
||||
//- dialog: export world list
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="worldExportDialogRef" :visible.sync="worldExportDialogVisible" :title="$t('dialog.world_export.header')" width="650px")
|
||||
el-checkbox-group(v-model="exportSelectedOptions" @change="updateWorldExportDialog()" style="margin-bottom:10px")
|
||||
template(v-for="option in exportSelectOptions" :key="option.value")
|
||||
el-checkbox(:label="option.label")
|
||||
el-dropdown(@click.native.stop trigger="click" size="small")
|
||||
el-button(size="mini")
|
||||
span(v-if="worldExportFavoriteGroup") {{ worldExportFavoriteGroup.displayName }} ({{ worldExportFavoriteGroup.count }}/{{ worldExportFavoriteGroup.capacity }}) #[i.el-icon-arrow-down.el-icon--right]
|
||||
span(v-else) All Favorites #[i.el-icon-arrow-down.el-icon--right]
|
||||
el-dropdown-menu(#default="dropdown")
|
||||
el-dropdown-item(style="display:block;margin:10px 0" @click.native="selectWorldExportGroup(null)") None
|
||||
template(v-for="groupAPI in API.favoriteWorldGroups" :key="groupAPI.name")
|
||||
el-dropdown-item(style="display:block;margin:10px 0" @click.native="selectWorldExportGroup(groupAPI)") {{ groupAPI.displayName }} ({{ groupAPI.count }}/{{ groupAPI.capacity }})
|
||||
el-dropdown(@click.native.stop trigger="click" size="small" style="margin-left:10px")
|
||||
el-button(size="mini")
|
||||
span(v-if="worldExportLocalFavoriteGroup") {{ worldExportLocalFavoriteGroup }} ({{ getLocalWorldFavoriteGroupLength(worldExportLocalFavoriteGroup) }}) #[i.el-icon-arrow-down.el-icon--right]
|
||||
span(v-else) Select Group #[i.el-icon-arrow-down.el-icon--right]
|
||||
el-dropdown-menu(#default="dropdown")
|
||||
el-dropdown-item(style="display:block;margin:10px 0" @click.native="selectWorldExportLocalGroup(null)") None
|
||||
template(v-for="group in localWorldFavoriteGroups" :key="group")
|
||||
el-dropdown-item(style="display:block;margin:10px 0" @click.native="selectWorldExportLocalGroup(group)") {{ group }} ({{ localWorldFavorites[group].length }})
|
||||
br
|
||||
el-input(type="textarea" v-if="worldExportDialogVisible" v-model="worldExportContent" size="mini" rows="15" resize="none" readonly style="margin-top:15px" @click.native="handleCopyWorldExportData")
|
||||
|
||||
//- dialog: World import dialog
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="worldImportDialog" :visible.sync="worldImportDialog.visible" :title="$t('dialog.world_import.header')" width="650px")
|
||||
div(style="display:flex;align-items:center;justify-content:space-between")
|
||||
div(style="font-size:12px") {{ $t('dialog.world_import.description') }}
|
||||
div(style="display:flex;align-items:center;")
|
||||
div(v-if="worldImportDialog.progress") {{ $t('dialog.world_import.process_progress') }} {{ worldImportDialog.progress }} / {{ worldImportDialog.progressTotal }} #[i.el-icon-loading(style="margin:0 5px")]
|
||||
el-button(v-if="worldImportDialog.loading" size="small" @click="cancelWorldImport") {{ $t('dialog.world_import.cancel') }}
|
||||
el-button(v-else size="small" @click="processWorldImportList" :disabled="!worldImportDialog.input") {{ $t('dialog.world_import.process_list') }}
|
||||
el-input(type="textarea" v-model="worldImportDialog.input" size="mini" rows="10" resize="none" style="margin-top:10px")
|
||||
div(style="display:flex;align-items:center;justify-content:space-between;margin-top:5px")
|
||||
div
|
||||
el-dropdown(@click.native.stop trigger="click" size="small" style="margin-right:5px")
|
||||
el-button(size="mini")
|
||||
span(v-if="worldImportDialog.worldImportFavoriteGroup") {{ worldImportDialog.worldImportFavoriteGroup.displayName }} ({{ worldImportDialog.worldImportFavoriteGroup.count }}/{{ worldImportDialog.worldImportFavoriteGroup.capacity }}) #[i.el-icon-arrow-down.el-icon--right]
|
||||
span(v-else) {{ $t('dialog.world_import.select_vrchat_group_placeholder') }} #[i.el-icon-arrow-down.el-icon--right]
|
||||
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="selectWorldImportGroup(groupAPI)" :disabled="groupAPI.count >= groupAPI.capacity") {{ groupAPI.displayName }} ({{ groupAPI.count }}/{{ groupAPI.capacity }})
|
||||
el-dropdown(@click.native.stop trigger="click" size="small" style="margin:5px")
|
||||
el-button(size="mini")
|
||||
span(v-if="worldImportDialog.worldImportLocalFavoriteGroup") {{ worldImportDialog.worldImportLocalFavoriteGroup }} ({{ getLocalWorldFavoriteGroupLength(worldImportDialog.worldImportLocalFavoriteGroup) }}) #[i.el-icon-arrow-down.el-icon--right]
|
||||
span(v-else) {{ $t('dialog.world_import.select_local_group_placeholder') }} #[i.el-icon-arrow-down.el-icon--right]
|
||||
el-dropdown-menu(#default="dropdown")
|
||||
template(v-for="group in localWorldFavoriteGroups" :key="group")
|
||||
el-dropdown-item(style="display:block;margin:10px 0" @click.native="selectWorldImportLocalGroup(group)" ) {{ group }} ({{ getLocalWorldFavoriteGroupLength(group) }})
|
||||
span(v-if="worldImportDialog.worldImportFavoriteGroup" style="margin-left:5px") {{ worldImportTable.data.length }} / {{ worldImportDialog.worldImportFavoriteGroup.capacity - worldImportDialog.worldImportFavoriteGroup.count }}
|
||||
div
|
||||
el-button(size="small" @click="clearWorldImportTable" :disabled="worldImportTable.data.length === 0") {{ $t('dialog.world_import.clear_table') }}
|
||||
el-button(size="small" type="primary" @click="importWorldImportTable" style="margin:5px" :disabled="worldImportTable.data.length === 0 || (!worldImportDialog.worldImportFavoriteGroup && !worldImportDialog.worldImportLocalFavoriteGroup)") {{ $t('dialog.world_import.import') }}
|
||||
span(v-if="worldImportDialog.importProgress" style="margin:10px") #[i.el-icon-loading(style="margin-right:5px")] {{ $t('dialog.world_import.import_progress') }} {{ worldImportDialog.importProgress }}/{{ worldImportDialog.importProgressTotal }}
|
||||
br
|
||||
template(v-if="worldImportDialog.errors")
|
||||
el-button(size="small" @click="worldImportDialog.errors = ''") {{ $t('dialog.world_import.clear_errors') }}
|
||||
h2(style="font-weight:bold;margin:5px 0") {{ $t('dialog.world_import.errors') }}
|
||||
pre(v-text="worldImportDialog.errors" style="white-space:pre-wrap;font-size:12px")
|
||||
data-tables(v-if="worldImportDialog.visible" v-bind="worldImportTable" v-loading="worldImportDialog.loading" style="margin-top:10px")
|
||||
el-table-column(:label="$t('table.import.image')" width="70" prop="thumbnailImageUrl")
|
||||
template(v-once #default="scope")
|
||||
el-popover(placement="right" height="500px" trigger="hover")
|
||||
img.friends-list-avatar(slot="reference" v-lazy="scope.row.thumbnailImageUrl")
|
||||
img.friends-list-avatar(v-lazy="scope.row.imageUrl" style="height:500px;cursor:pointer" @click="showFullscreenImageDialog(scope.row.imageUrl)")
|
||||
el-table-column(:label="$t('table.import.name')" prop="name")
|
||||
template(v-once #default="scope")
|
||||
span.x-link(v-text="scope.row.name" @click="showWorldDialog(scope.row.id)")
|
||||
el-table-column(:label="$t('table.import.author')" width="120" prop="authorName")
|
||||
template(v-once #default="scope")
|
||||
span.x-link(v-text="scope.row.authorName" @click="showUserDialog(scope.row.authorId)")
|
||||
el-table-column(:label="$t('table.import.status')" width="70" prop="releaseStatus")
|
||||
template(v-once #default="scope")
|
||||
span(v-text="scope.row.releaseStatus.charAt(0).toUpperCase() + scope.row.releaseStatus.slice(1)" :style="{ color: scope.row.releaseStatus === 'public' ? '#67c23a' : scope.row.releaseStatus === 'private' ? '#f56c6c' : undefined }")
|
||||
el-table-column(:label="$t('table.import.action')" width="90" align="right")
|
||||
template(v-once #default="scope")
|
||||
el-button(type="text" icon="el-icon-close" size="mini" @click="deleteItemWorldImport(scope.row)")
|
||||
|
||||
//- dialog: export avatar list
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="avatarExportDialogRef" :visible.sync="avatarExportDialogVisible" :title="$t('dialog.avatar_export.header')" width="650px")
|
||||
el-checkbox-group(v-model="exportSelectedOptions" @change="updateAvatarExportDialog()" style="margin-bottom:10px")
|
||||
template(v-for="option in exportSelectOptions" :key="option.value")
|
||||
el-checkbox(:label="option.label")
|
||||
el-dropdown(@click.native.stop trigger="click" size="small")
|
||||
el-button(size="mini")
|
||||
span(v-if="avatarExportFavoriteGroup") {{ avatarExportFavoriteGroup.displayName }} ({{ avatarExportFavoriteGroup.count }}/{{ avatarExportFavoriteGroup.capacity }}) #[i.el-icon-arrow-down.el-icon--right]
|
||||
span(v-else) All Favorites #[i.el-icon-arrow-down.el-icon--right]
|
||||
el-dropdown-menu(#default="dropdown")
|
||||
el-dropdown-item(style="display:block;margin:10px 0" @click.native="selectAvatarExportGroup(null)") All Favorites
|
||||
template(v-for="groupAPI in API.favoriteAvatarGroups" :key="groupAPI.name")
|
||||
el-dropdown-item(style="display:block;margin:10px 0" @click.native="selectAvatarExportGroup(groupAPI)") {{ groupAPI.displayName }} ({{ groupAPI.count }}/{{ groupAPI.capacity }})
|
||||
el-dropdown(@click.native.stop trigger="click" size="small" style="margin-left:10px")
|
||||
el-button(size="mini")
|
||||
span(v-if="avatarExportLocalFavoriteGroup") {{ avatarExportLocalFavoriteGroup }} ({{ getLocalAvatarFavoriteGroupLength(avatarExportLocalFavoriteGroup) }}) #[i.el-icon-arrow-down.el-icon--right]
|
||||
span(v-else) Select Group #[i.el-icon-arrow-down.el-icon--right]
|
||||
el-dropdown-menu(#default="dropdown")
|
||||
el-dropdown-item(style="display:block;margin:10px 0" @click.native="selectAvatarExportLocalGroup(null)") None
|
||||
template(v-for="group in localAvatarFavoriteGroups" :key="group")
|
||||
el-dropdown-item(style="display:block;margin:10px 0" @click.native="selectAvatarExportLocalGroup(group)" ) {{ group }} ({{ getLocalAvatarFavoriteGroupLength(group) }})
|
||||
br
|
||||
el-input(type="textarea" v-if="avatarExportDialogVisible" v-model="avatarExportContent" size="mini" rows="15" resize="none" readonly style="margin-top:15px" @click.native="handleCopyAvatarExportData")
|
||||
|
||||
//- dialog: Avatar import dialog
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="avatarImportDialog" :visible.sync="avatarImportDialog.visible" :title="$t('dialog.avatar_import.header')" width="650px")
|
||||
div(style="display:flex;align-items:center;justify-content:space-between")
|
||||
div(style="font-size:12px") {{ $t('dialog.avatar_import.description') }}
|
||||
div(style="display:flex;align-items:center;")
|
||||
div(v-if="avatarImportDialog.progress") {{ $t('dialog.avatar_import.process_progress') }} {{ avatarImportDialog.progress }} / {{ avatarImportDialog.progressTotal }} #[i.el-icon-loading(style="margin:0 5px")]
|
||||
el-button(v-if="avatarImportDialog.loading" size="small" @click="cancelAvatarImport") {{ $t('dialog.avatar_import.cancel') }}
|
||||
el-button(v-else size="small" @click="processAvatarImportList" :disabled="!avatarImportDialog.input") {{ $t('dialog.avatar_import.process_list') }}
|
||||
el-input(type="textarea" v-model="avatarImportDialog.input" size="mini" rows="10" resize="none" style="margin-top:10px")
|
||||
div(style="display:flex;align-items:center;justify-content:space-between;margin-top:5px")
|
||||
div
|
||||
el-dropdown(@click.native.stop trigger="click" size="small")
|
||||
el-button(size="mini")
|
||||
span(v-if="avatarImportDialog.avatarImportFavoriteGroup") {{ avatarImportDialog.avatarImportFavoriteGroup.displayName }} ({{ avatarImportDialog.avatarImportFavoriteGroup.count }}/{{ avatarImportDialog.avatarImportFavoriteGroup.capacity }}) #[i.el-icon-arrow-down.el-icon--right]
|
||||
span(v-else) {{ $t('dialog.avatar_import.select_group_placeholder') }} #[i.el-icon-arrow-down.el-icon--right]
|
||||
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="selectAvatarImportGroup(groupAPI)" :disabled="groupAPI.count >= groupAPI.capacity") {{ groupAPI.displayName }} ({{ groupAPI.count }}/{{ groupAPI.capacity }})
|
||||
el-dropdown(@click.native.stop trigger="click" size="small" style="margin:5px")
|
||||
el-button(size="mini")
|
||||
span(v-if="avatarImportDialog.avatarImportLocalFavoriteGroup") {{ avatarImportDialog.avatarImportLocalFavoriteGroup }} ({{ getLocalAvatarFavoriteGroupLength(avatarImportDialog.avatarImportLocalFavoriteGroup) }}) #[i.el-icon-arrow-down.el-icon--right]
|
||||
span(v-else) {{ $t('dialog.avatar_import.select_group_placeholder') }} #[i.el-icon-arrow-down.el-icon--right]
|
||||
el-dropdown-menu(#default="dropdown")
|
||||
template(v-for="group in localAvatarFavoriteGroups" :key="group")
|
||||
el-dropdown-item(style="display:block;margin:10px 0" @click.native="selectAvatarImportLocalGroup(group)" ) {{ group }} ({{ getLocalAvatarFavoriteGroupLength(group) }})
|
||||
span(v-if="avatarImportDialog.avatarImportFavoriteGroup" style="margin-left:5px") {{ avatarImportTable.data.length }} / {{ avatarImportDialog.avatarImportFavoriteGroup.capacity - avatarImportDialog.avatarImportFavoriteGroup.count }}
|
||||
div
|
||||
el-button(size="small" @click="clearAvatarImportTable") {{ $t('dialog.avatar_import.clear_table') }}
|
||||
el-button(size="small" type="primary" @click="importAvatarImportTable" style="margin:5px" :disabled="avatarImportTable.data.length === 0 || (!avatarImportDialog.avatarImportFavoriteGroup && !avatarImportDialog.avatarImportLocalFavoriteGroup)") {{ $t('dialog.avatar_import.import') }}
|
||||
span(v-if="avatarImportDialog.importProgress" style="margin:10px") #[i.el-icon-loading(style="margin-right:5px")] {{ $t('dialog.avatar_import.import_progress') }} {{ avatarImportDialog.importProgress }}/{{ avatarImportDialog.importProgressTotal }}
|
||||
br
|
||||
template(v-if="avatarImportDialog.errors")
|
||||
el-button(size="small" @click="avatarImportDialog.errors = ''") {{ $t('dialog.avatar_import.clear_errors') }}
|
||||
h2(style="font-weight:bold;margin:5px 0") {{ $t('dialog.avatar_import.errors') }}
|
||||
pre(v-text="avatarImportDialog.errors" style="white-space:pre-wrap;font-size:12px")
|
||||
data-tables(v-if="avatarImportDialog.visible" v-bind="avatarImportTable" v-loading="avatarImportDialog.loading" style="margin-top:10px")
|
||||
el-table-column(:label="$t('table.import.image')" width="70" prop="thumbnailImageUrl")
|
||||
template(v-once #default="scope")
|
||||
el-popover(placement="right" height="500px" trigger="hover")
|
||||
img.friends-list-avatar(slot="reference" v-lazy="scope.row.thumbnailImageUrl")
|
||||
img.friends-list-avatar(v-lazy="scope.row.imageUrl" style="height:500px;cursor:pointer" @click="showFullscreenImageDialog(scope.row.imageUrl)")
|
||||
el-table-column(:label="$t('table.import.name')" prop="name")
|
||||
template(v-once #default="scope")
|
||||
span.x-link(v-text="scope.row.name" @click="showAvatarDialog(scope.row.id)")
|
||||
el-table-column(:label="$t('table.import.author')" width="120" prop="authorName")
|
||||
template(v-once #default="scope")
|
||||
span.x-link(v-text="scope.row.authorName" @click="showUserDialog(scope.row.authorId)")
|
||||
el-table-column(:label="$t('table.import.status')" width="70" prop="releaseStatus")
|
||||
template(v-once #default="scope")
|
||||
span(v-text="scope.row.releaseStatus.charAt(0).toUpperCase() + scope.row.releaseStatus.slice(1)" :style="{ color: scope.row.releaseStatus === 'public' ? '#67c23a' : scope.row.releaseStatus === 'private' ? '#f56c6c' : undefined }")
|
||||
el-table-column(:label="$t('table.import.action')" width="90" align="right")
|
||||
template(v-once #default="scope")
|
||||
el-button(type="text" icon="el-icon-close" size="mini" @click="deleteItemAvatarImport(scope.row)")
|
||||
|
||||
//- dialog: export friend list
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="friendExportDialogRef" :visible.sync="friendExportDialogVisible" :title="$t('dialog.friend_export.header')" width="650px")
|
||||
el-dropdown(@click.native.stop trigger="click" size="small")
|
||||
el-button(size="mini")
|
||||
span(v-if="friendExportFavoriteGroup") {{ friendExportFavoriteGroup.displayName }} ({{ friendExportFavoriteGroup.count }}/{{ friendExportFavoriteGroup.capacity }}) #[i.el-icon-arrow-down.el-icon--right]
|
||||
span(v-else) All Favorites #[i.el-icon-arrow-down.el-icon--right]
|
||||
el-dropdown-menu(#default="dropdown")
|
||||
el-dropdown-item(style="display:block;margin:10px 0" @click.native="selectFriendExportGroup(null)") All Favorites
|
||||
template(v-for="groupAPI in API.favoriteFriendGroups" :key="groupAPI.name")
|
||||
el-dropdown-item(style="display:block;margin:10px 0" @click.native="selectFriendExportGroup(groupAPI)") {{ groupAPI.displayName }} ({{ groupAPI.count }}/{{ groupAPI.capacity }})
|
||||
br
|
||||
el-input(type="textarea" v-if="friendExportDialogVisible" v-model="friendExportContent" size="mini" rows="15" resize="none" readonly style="margin-top:15px" @click.native="handleCopyFriendExportData")
|
||||
|
||||
//- dialog: Friend import dialog
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="friendImportDialog" :visible.sync="friendImportDialog.visible" :title="$t('dialog.friend_import.header')" width="650px")
|
||||
div(style="display:flex;align-items:center;justify-content:space-between")
|
||||
div(style="font-size:12px") {{ $t('dialog.friend_import.description') }}
|
||||
div(style="display:flex;align-items:center;")
|
||||
div(v-if="friendImportDialog.progress") {{ $t('dialog.friend_import.process_progress') }} {{ friendImportDialog.progress }} / {{ friendImportDialog.progressTotal }} #[i.el-icon-loading(style="margin:0 5px")]
|
||||
el-button(v-if="friendImportDialog.loading" size="small" @click="cancelFriendImport") {{ $t('dialog.friend_import.cancel') }}
|
||||
el-button(v-else size="small" @click="processFriendImportList" :disabled="!friendImportDialog.input") {{ $t('dialog.friend_import.process_list') }}
|
||||
el-input(type="textarea" v-model="friendImportDialog.input" size="mini" rows="10" resize="none" style="margin-top:10px")
|
||||
div(style="display:flex;align-items:center;justify-content:space-between;margin-top:5px")
|
||||
div
|
||||
el-dropdown(@click.native.stop trigger="click" size="small")
|
||||
el-button(size="mini")
|
||||
span(v-if="friendImportDialog.friendImportFavoriteGroup") {{ friendImportDialog.friendImportFavoriteGroup.displayName }} ({{ friendImportDialog.friendImportFavoriteGroup.count }}/{{ friendImportDialog.friendImportFavoriteGroup.capacity }}) #[i.el-icon-arrow-down.el-icon--right]
|
||||
span(v-else) {{ $t('dialog.friend_import.select_group_placeholder') }} #[i.el-icon-arrow-down.el-icon--right]
|
||||
el-dropdown-menu(#default="dropdown")
|
||||
template(v-for="groupAPI in API.favoriteFriendGroups" :key="groupAPI.name")
|
||||
el-dropdown-item(style="display:block;margin:10px 0" @click.native="selectFriendImportGroup(groupAPI)" :disabled="groupAPI.count >= groupAPI.capacity") {{ groupAPI.displayName }} ({{ groupAPI.count }}/{{ groupAPI.capacity }})
|
||||
span(v-if="friendImportDialog.friendImportFavoriteGroup" style="margin-left:5px") {{ friendImportTable.data.length }} / {{ friendImportDialog.friendImportFavoriteGroup.capacity - friendImportDialog.friendImportFavoriteGroup.count }}
|
||||
div
|
||||
el-button(size="small" @click="clearFriendImportTable" :disabled="friendImportTable.data.length === 0") {{ $t('dialog.friend_import.clear_table') }}
|
||||
el-button(size="small" type="primary" @click="importFriendImportTable" style="margin:5px" :disabled="friendImportTable.data.length === 0 || !friendImportDialog.friendImportFavoriteGroup") {{ $t('dialog.friend_import.import') }}
|
||||
span(v-if="friendImportDialog.importProgress" style="margin:10px") #[i.el-icon-loading(style="margin-right:5px")] {{ $t('dialog.friend_import.import_progress') }} {{ friendImportDialog.importProgress }}/{{ friendImportDialog.importProgressTotal }}
|
||||
br
|
||||
template(v-if="friendImportDialog.errors")
|
||||
el-button(size="small" @click="friendImportDialog.errors = ''") {{ $t('dialog.friend_import.clear_errors') }}
|
||||
h2(style="font-weight:bold;margin:5px 0") {{ $t('dialog.friend_import.errors') }}
|
||||
pre(v-text="friendImportDialog.errors" style="white-space:pre-wrap;font-size:12px")
|
||||
data-tables(v-if="friendImportDialog.visible" v-bind="friendImportTable" v-loading="friendImportDialog.loading" style="margin-top:10px")
|
||||
el-table-column(:label="$t('table.import.image')" width="70" prop="currentAvatarThumbnailImageUrl")
|
||||
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.import.name')" prop="displayName")
|
||||
template(v-once #default="scope")
|
||||
span.x-link(v-text="scope.row.displayName" @click="showUserDialog(scope.row.id)")
|
||||
el-table-column(:label="$t('table.import.action')" width="90" align="right")
|
||||
template(v-once #default="scope")
|
||||
el-button(type="text" icon="el-icon-close" size="mini" @click="deleteItemFriendImport(scope.row)")
|
||||
503
src/mixins/dialogs/feedFilters.pug
Normal file
@@ -0,0 +1,503 @@
|
||||
mixin feedFilters()
|
||||
//- dialog: Noty feed filters
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="notyFeedFiltersDialog" :visible.sync="notyFeedFiltersDialog.visible" :title="$t('dialog.shared_feed_filters.notification')" width="550px")
|
||||
.toggle-list
|
||||
.toggle-item
|
||||
span.toggle-name OnPlayerJoining
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.OnPlayerJoining" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
.toggle-item
|
||||
span.toggle-name OnPlayerJoined
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.OnPlayerJoined" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
el-radio-button(label="Everyone") {{ $t('dialog.shared_feed_filters.everyone') }}
|
||||
.toggle-item
|
||||
span.toggle-name OnPlayerLeft
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.OnPlayerLeft" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
el-radio-button(label="Everyone") {{ $t('dialog.shared_feed_filters.everyone') }}
|
||||
.toggle-item
|
||||
span.toggle-name Online
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.Online" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
.toggle-item
|
||||
span.toggle-name Offline
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.Offline" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
.toggle-item
|
||||
span.toggle-name GPS
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.GPS" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
.toggle-item
|
||||
span.toggle-name Status
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.Status" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
.toggle-item
|
||||
span.toggle-name Invite
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.invite" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
.toggle-item
|
||||
span.toggle-name Request Invite
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.requestInvite" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
.toggle-item
|
||||
span.toggle-name Invite Response
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.inviteResponse" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
.toggle-item
|
||||
span.toggle-name Request Invite Response
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.requestInviteResponse" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
.toggle-item
|
||||
span.toggle-name Friend Request
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.friendRequest" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name New Friend
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.Friend" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Unfriend
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.Unfriend" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Display Name Change
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.DisplayName" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
.toggle-item
|
||||
span.toggle-name Trust Level Change
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.TrustLevel" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
//- .toggle-item
|
||||
//- span.toggle-name Boop
|
||||
//- el-radio-group(v-model="sharedFeedFilters.noty.boop" size="mini" @change="saveSharedFeedFilters")
|
||||
//- el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
//- el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Group Change
|
||||
el-tooltip(placement="top" style="margin-left:5px" content="When you've left or been kicked from a group, group name changed, group owner changed, role added/removed")
|
||||
i.el-icon-info
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.groupChange" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Group Announcement
|
||||
el-radio-group(v-model="sharedFeedFilters.noty['group.announcement']" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Group Join
|
||||
el-tooltip(placement="top" style="margin-left:5px" content="When your request to join a group has been approved")
|
||||
i.el-icon-info
|
||||
el-radio-group(v-model="sharedFeedFilters.noty['group.informative']" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Group Invite
|
||||
el-tooltip(placement="top" style="margin-left:5px" content="When someone invites you to join a group")
|
||||
i.el-icon-info
|
||||
el-radio-group(v-model="sharedFeedFilters.noty['group.invite']" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Group Join Request
|
||||
el-tooltip(placement="top" style="margin-left:5px" content="When someone requests to join a group you're a moderator for")
|
||||
i.el-icon-info
|
||||
el-radio-group(v-model="sharedFeedFilters.noty['group.joinRequest']" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Group Transfer Request
|
||||
el-radio-group(v-model="sharedFeedFilters.noty['group.transfer']" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Instance Queue Ready
|
||||
el-radio-group(v-model="sharedFeedFilters.noty['group.queueReady']" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Instance Closed
|
||||
el-tooltip(placement="top" style="margin-left:5px" content="When the instance you're in has been closed preventing anyone from joining")
|
||||
i.el-icon-info
|
||||
el-radio-group(v-model="sharedFeedFilters.noty['instance.closed']" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Video Play
|
||||
el-tooltip(placement="top" style="margin-left:5px" content="Requires VRCX YouTube API option enabled")
|
||||
i.el-icon-warning
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.VideoPlay" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Miscellaneous Events
|
||||
el-tooltip(placement="top" style="margin-left:5px" content="Misc event from VRC game log: VRC crash auto rejoin, shader keyword limit, joining instance blocked by master, error loading video, audio device changed, error joining instance, kicked from instance, VRChat failing to start OSC server, etc...")
|
||||
i.el-icon-info
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.Event" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name External App
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.External" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Blocked Player Joins
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.BlockedOnPlayerJoined" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
el-radio-button(label="Everyone") {{ $t('dialog.shared_feed_filters.everyone') }}
|
||||
.toggle-item
|
||||
span.toggle-name Blocked Player Leaves
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.BlockedOnPlayerLeft" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
el-radio-button(label="Everyone") {{ $t('dialog.shared_feed_filters.everyone') }}
|
||||
.toggle-item
|
||||
span.toggle-name Muted Player Joins
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.MutedOnPlayerJoined" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
el-radio-button(label="Everyone") {{ $t('dialog.shared_feed_filters.everyone') }}
|
||||
.toggle-item
|
||||
span.toggle-name Muted Player Leaves
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.MutedOnPlayerLeft" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
el-radio-button(label="Everyone") {{ $t('dialog.shared_feed_filters.everyone') }}
|
||||
.toggle-item
|
||||
span.toggle-name Lobby Avatar Change
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.AvatarChange" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
el-radio-button(label="Everyone") {{ $t('dialog.shared_feed_filters.everyone') }}
|
||||
template(v-if="photonLoggingEnabled")
|
||||
br
|
||||
.toggle-item
|
||||
span.toggle-name Photon Event Logging
|
||||
.toggle-item
|
||||
span.toggle-name Portal Spawn
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.PortalSpawn" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
el-radio-button(label="Everyone") {{ $t('dialog.shared_feed_filters.everyone') }}
|
||||
.toggle-item
|
||||
span.toggle-name Lobby ChatBox Message
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.ChatBoxMessage" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
el-radio-button(label="Everyone") {{ $t('dialog.shared_feed_filters.everyone') }}
|
||||
.toggle-item
|
||||
span.toggle-name Blocked
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.Blocked" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Unblocked
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.Unblocked" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Muted
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.Muted" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Unmuted
|
||||
el-radio-group(v-model="sharedFeedFilters.noty.Unmuted" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
template(#footer)
|
||||
el-button(size="small" @click="resetNotyFeedFilters") {{ $t('dialog.shared_feed_filters.reset') }}
|
||||
el-button(size="small" type="primary" style="margin-left:10px" @click="notyFeedFiltersDialog.visible = false") {{ $t('dialog.shared_feed_filters.close') }}
|
||||
|
||||
//- dialog: wrist feed filters
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="wristFeedFiltersDialog" :visible.sync="wristFeedFiltersDialog.visible" :title="$t('dialog.shared_feed_filters.wrist')" width="550px")
|
||||
.toggle-list
|
||||
.toggle-item
|
||||
span.toggle-name Self Location
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.Location" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name OnPlayerJoining
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.OnPlayerJoining" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
.toggle-item
|
||||
span.toggle-name OnPlayerJoined
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.OnPlayerJoined" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
el-radio-button(label="Everyone") {{ $t('dialog.shared_feed_filters.everyone') }}
|
||||
.toggle-item
|
||||
span.toggle-name OnPlayerLeft
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.OnPlayerLeft" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
el-radio-button(label="Everyone") {{ $t('dialog.shared_feed_filters.everyone') }}
|
||||
.toggle-item
|
||||
span.toggle-name Online
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.Online" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
.toggle-item
|
||||
span.toggle-name Offline
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.Offline" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
.toggle-item
|
||||
span.toggle-name GPS
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.GPS" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
.toggle-item
|
||||
span.toggle-name Status
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.Status" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
.toggle-item
|
||||
span.toggle-name Invite
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.invite" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
.toggle-item
|
||||
span.toggle-name Request Invite
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.requestInvite" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
.toggle-item
|
||||
span.toggle-name Invite Response
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.inviteResponse" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
.toggle-item
|
||||
span.toggle-name Request Invite Response
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.requestInviteResponse" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
.toggle-item
|
||||
span.toggle-name Friend Request
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.friendRequest" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name New Friend
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.Friend" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Unfriend
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.Unfriend" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Display Name Change
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.DisplayName" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
.toggle-item
|
||||
span.toggle-name Trust Level Change
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.TrustLevel" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
//- .toggle-item
|
||||
//- span.toggle-name Boop
|
||||
//- el-radio-group(v-model="sharedFeedFilters.wrist.boop" size="mini" @change="saveSharedFeedFilters")
|
||||
//- el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
//- el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Group Change
|
||||
el-tooltip(placement="top" style="margin-left:5px" content="When you've left or been kicked from a group, group name changed, group owner changed, role added/removed")
|
||||
i.el-icon-info
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.groupChange" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Group Announcement
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist['group.announcement']" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Group Join
|
||||
el-tooltip(placement="top" style="margin-left:5px" content="When your request to join a group has been approved")
|
||||
i.el-icon-info
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist['group.informative']" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Group Invite
|
||||
el-tooltip(placement="top" style="margin-left:5px" content="When someone invites you to join a group")
|
||||
i.el-icon-info
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist['group.invite']" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Group Join Request
|
||||
el-tooltip(placement="top" style="margin-left:5px" content="When someone requests to join a group you're a moderator for")
|
||||
i.el-icon-info
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist['group.joinRequest']" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Group Transfer Request
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist['group.transfer']" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Instance Queue Ready
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist['group.queueReady']" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Instance Closed
|
||||
el-tooltip(placement="top" style="margin-left:5px" content="When the instance you're in has been closed preventing anyone from joining")
|
||||
i.el-icon-info
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist['instance.closed']" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Video Play
|
||||
el-tooltip(placement="top" style="margin-left:5px" content="Requires VRCX YouTube API option enabled")
|
||||
i.el-icon-warning
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.VideoPlay" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Miscellaneous Events
|
||||
el-tooltip(placement="top" style="margin-left:5px" content="Misc event from VRC game log: VRC crash auto rejoin, shader keyword limit, joining instance blocked by master, error loading video, audio device changed, error joining instance, kicked from instance, VRChat failing to start OSC server, etc...")
|
||||
i.el-icon-info
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.Event" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name External App
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.External" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Blocked Player Joins
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.BlockedOnPlayerJoined" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
el-radio-button(label="Everyone") {{ $t('dialog.shared_feed_filters.everyone') }}
|
||||
.toggle-item
|
||||
span.toggle-name Blocked Player Leaves
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.BlockedOnPlayerLeft" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
el-radio-button(label="Everyone") {{ $t('dialog.shared_feed_filters.everyone') }}
|
||||
.toggle-item
|
||||
span.toggle-name Muted Player Joins
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.MutedOnPlayerJoined" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
el-radio-button(label="Everyone") {{ $t('dialog.shared_feed_filters.everyone') }}
|
||||
.toggle-item
|
||||
span.toggle-name Muted Player Leaves
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.MutedOnPlayerLeft" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
el-radio-button(label="Everyone") {{ $t('dialog.shared_feed_filters.everyone') }}
|
||||
.toggle-item
|
||||
span.toggle-name Lobby Avatar Change
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.AvatarChange" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
el-radio-button(label="Everyone") {{ $t('dialog.shared_feed_filters.everyone') }}
|
||||
template(v-if="photonLoggingEnabled")
|
||||
br
|
||||
.toggle-item
|
||||
span.toggle-name Photon Event Logging
|
||||
.toggle-item
|
||||
span.toggle-name Portal Spawn
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.PortalSpawn" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
el-radio-button(label="Everyone") {{ $t('dialog.shared_feed_filters.everyone') }}
|
||||
.toggle-item
|
||||
span.toggle-name Lobby ChatBox Message
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.ChatBoxMessage" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="VIP") {{ $t('dialog.shared_feed_filters.favorite') }}
|
||||
el-radio-button(label="Friends") {{ $t('dialog.shared_feed_filters.friends') }}
|
||||
el-radio-button(label="Everyone") {{ $t('dialog.shared_feed_filters.everyone') }}
|
||||
.toggle-item
|
||||
span.toggle-name Blocked
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.Blocked" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Unblocked
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.Unblocked" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Muted
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.Muted" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
.toggle-item
|
||||
span.toggle-name Unmuted
|
||||
el-radio-group(v-model="sharedFeedFilters.wrist.Unmuted" size="mini" @change="saveSharedFeedFilters")
|
||||
el-radio-button(label="Off") {{ $t('dialog.shared_feed_filters.off') }}
|
||||
el-radio-button(label="On") {{ $t('dialog.shared_feed_filters.on') }}
|
||||
template(#footer)
|
||||
el-button(size="small" @click="resetWristFeedFilters") {{ $t('dialog.shared_feed_filters.reset') }}
|
||||
el-button(size="small" type="primary" @click="wristFeedFiltersDialog.visible = false") {{ $t('dialog.shared_feed_filters.close') }}
|
||||
348
src/mixins/dialogs/groupDialog.pug
Normal file
@@ -0,0 +1,348 @@
|
||||
|
||||
mixin groupDialog()
|
||||
el-dialog.x-dialog.x-group-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="groupDialog" :visible.sync="groupDialog.visible" :show-close="false" width="770px")
|
||||
.group-banner-image
|
||||
el-popover(placement="right" width="500px" trigger="click")
|
||||
img.x-link(slot="reference" v-lazy="groupDialog.ref.bannerUrl" style="flex:none;width:100%;aspect-ratio:6/1;object-fit:cover;border-radius:4px")
|
||||
img.x-link(v-lazy="groupDialog.ref.bannerUrl" style="width:854px;height:480px" @click="showFullscreenImageDialog(groupDialog.ref.bannerUrl)")
|
||||
.group-body(v-loading="groupDialog.loading")
|
||||
div(style="display:flex")
|
||||
el-popover(placement="right" width="500px" trigger="click")
|
||||
img.x-link(slot="reference" v-lazy="groupDialog.ref.iconUrl" style="flex:none;width:120px;height:120px;border-radius:12px")
|
||||
img.x-link(v-lazy="groupDialog.ref.iconUrl" style="width:500px;height:500px" @click="showFullscreenImageDialog(groupDialog.ref.iconUrl)")
|
||||
div(style="flex:1;display:flex;align-items:center;margin-left:15px")
|
||||
.group-header(style="flex:1")
|
||||
span(v-if="groupDialog.ref.ownerId === API.currentUser.id" style="margin-right:5px") 👑
|
||||
span.dialog-title(v-text="groupDialog.ref.name" style="margin-right:5px")
|
||||
span.group-discriminator.x-grey(style="font-family:monospace;font-size:12px;margin-right:5px") {{ groupDialog.ref.shortCode }}.{{ groupDialog.ref.discriminator }}
|
||||
el-tooltip(v-for="item in groupDialog.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-right:5px")
|
||||
div(style="margin-top:5px")
|
||||
span.x-link.x-grey(v-text="groupDialog.ownerDisplayName" @click="showUserDialog(groupDialog.ref.ownerId)" style="font-family:monospace")
|
||||
.group-tags
|
||||
el-tag(v-if="groupDialog.ref.isVerified" type="info" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.group.tags.verified') }}
|
||||
|
||||
el-tag(v-if="groupDialog.ref.privacy === 'private'" type="danger" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.group.tags.private') }}
|
||||
el-tag(v-if="groupDialog.ref.privacy === 'default'" type="success" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.group.tags.public') }}
|
||||
|
||||
el-tag(v-if="groupDialog.ref.joinState === 'open'" type="success" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.group.tags.open') }}
|
||||
el-tag(v-else-if="groupDialog.ref.joinState === 'request'" type="warning" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.group.tags.request') }}
|
||||
el-tag(v-else-if="groupDialog.ref.joinState === 'invite'" type="danger" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.group.tags.invite') }}
|
||||
el-tag(v-else-if="groupDialog.ref.joinState === 'closed'" type="danger" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.group.tags.closed') }}
|
||||
|
||||
el-tag(v-if="groupDialog.inGroup" type="info" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.group.tags.joined') }}
|
||||
el-tag(v-if="groupDialog.ref.myMember && groupDialog.ref.myMember.bannedAt" type="danger" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.group.tags.banned') }}
|
||||
|
||||
template(v-if="groupDialog.inGroup && groupDialog.ref.myMember")
|
||||
el-tag(v-if="groupDialog.ref.myMember.visibility === 'visible'" type="info" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.group.tags.visible') }}
|
||||
el-tag(v-else-if="groupDialog.ref.myMember.visibility === 'friends'" type="info" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.group.tags.friends') }}
|
||||
el-tag(v-else-if="groupDialog.ref.myMember.visibility === 'hidden'" type="info" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.group.tags.hidden') }}
|
||||
el-tag(v-if="groupDialog.ref.myMember.isSubscribedToAnnouncements" type="info" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.group.tags.subscribed') }}
|
||||
|
||||
.group-description(style="margin-top:5px")
|
||||
span(v-show="groupDialog.ref.name !== groupDialog.ref.description" v-text="groupDialog.ref.description" style="font-size:12px")
|
||||
div(style="flex:none;margin-left:10px")
|
||||
template(v-if="groupDialog.inGroup && groupDialog.ref?.myMember")
|
||||
el-tooltip(v-if="groupDialog.ref.myMember?.isRepresenting" placement="top" :content="$t('dialog.group.actions.unrepresent_tooltip')" :disabled="hideTooltips")
|
||||
el-button(type="warning" icon="el-icon-star-on" circle @click="clearGroupRepresentation(groupDialog.id)" style="margin-left:5px")
|
||||
el-tooltip(v-else placement="top" :content="$t('dialog.group.actions.represent_tooltip')" :disabled="hideTooltips")
|
||||
span
|
||||
el-button(type="default" icon="el-icon-star-off" circle @click="setGroupRepresentation(groupDialog.id)" style="margin-left:5px" :disabled="groupDialog.ref.privacy === 'private'")
|
||||
template(v-else-if="groupDialog.ref.myMember?.membershipStatus === 'requested'")
|
||||
el-tooltip(placement="top" :content="$t('dialog.group.actions.cancel_join_request_tooltip')" :disabled="hideTooltips")
|
||||
span
|
||||
el-button(type="default" icon="el-icon-close" circle @click="cancelGroupRequest(groupDialog.id)" style="margin-left:5px")
|
||||
template(v-else-if="groupDialog.ref.myMember?.membershipStatus === 'invited'")
|
||||
el-tooltip(placement="top" :content="$t('dialog.group.actions.pending_request_tooltip')" :disabled="hideTooltips")
|
||||
span
|
||||
el-button(type="default" icon="el-icon-check" circle @click="joinGroup(groupDialog.id)" style="margin-left:5px")
|
||||
template(v-else)
|
||||
el-tooltip(v-if="groupDialog.ref.joinState === 'request'" placement="top" :content="$t('dialog.group.actions.request_join_tooltip')" :disabled="hideTooltips")
|
||||
el-button(type="default" icon="el-icon-message" circle @click="joinGroup(groupDialog.id)" style="margin-left:5px")
|
||||
el-tooltip(v-if="groupDialog.ref.joinState === 'invite'" placement="top" :content="$t('dialog.group.actions.invite_required_tooltip')" :disabled="hideTooltips")
|
||||
span
|
||||
el-button(type="default" icon="el-icon-message" disabled circle style="margin-left:5px")
|
||||
el-tooltip(v-if="groupDialog.ref.joinState === 'open'" placement="top" :content="$t('dialog.group.actions.join_group_tooltip')" :disabled="hideTooltips")
|
||||
el-button(type="default" icon="el-icon-check" circle @click="joinGroup(groupDialog.id)" style="margin-left:5px")
|
||||
el-dropdown(trigger="click" @command="groupDialogCommand" size="small" style="margin-left:5px")
|
||||
el-button(:type="groupDialog.ref.membershipStatus === 'userblocked' ? 'danger' : 'default'" icon="el-icon-more" circle)
|
||||
el-dropdown-menu(#default="dropdown")
|
||||
el-dropdown-item(icon="el-icon-refresh" command="Refresh") {{ $t('dialog.group.actions.refresh') }}
|
||||
el-dropdown-item(icon="el-icon-share" command="Share") {{ $t('dialog.group.actions.share') }}
|
||||
template(v-if="groupDialog.inGroup")
|
||||
template(v-if="groupDialog.ref.myMember")
|
||||
el-dropdown-item(v-if="groupDialog.ref.myMember.isSubscribedToAnnouncements" icon="el-icon-close" command="Unsubscribe To Announcements" divided) {{ $t('dialog.group.actions.unsubscribe') }}
|
||||
el-dropdown-item(v-else icon="el-icon-check" command="Subscribe To Announcements" divided) {{ $t('dialog.group.actions.subscribe') }}
|
||||
el-dropdown-item(v-if="hasGroupPermission(groupDialog.ref, 'group-invites-manage')" icon="el-icon-message" command="Invite To Group") {{ $t('dialog.group.actions.invite_to_group') }}
|
||||
template(v-if="hasGroupPermission(groupDialog.ref, 'group-announcement-manage')")
|
||||
el-dropdown-item(icon="el-icon-tickets" command="Create Post") {{ $t('dialog.group.actions.create_post') }}
|
||||
//- template(v-if="hasGroupPermission(groupDialog.ref, 'group-members-manage')")
|
||||
el-dropdown-item(icon="el-icon-s-operation" command="Moderation Tools") {{ $t('dialog.group.actions.moderation_tools') }}
|
||||
template(v-if="groupDialog.ref.myMember && groupDialog.ref.privacy === 'default'")
|
||||
el-dropdown-item(icon="el-icon-view" command="Visibility Everyone" divided) #[i.el-icon-check(v-if="groupDialog.ref.myMember.visibility === 'visible'")] {{ $t('dialog.group.actions.visibility_everyone') }}
|
||||
el-dropdown-item(icon="el-icon-view" command="Visibility Friends") #[i.el-icon-check(v-if="groupDialog.ref.myMember.visibility === 'friends'")] {{ $t('dialog.group.actions.visibility_friends') }}
|
||||
el-dropdown-item(icon="el-icon-view" command="Visibility Hidden") #[i.el-icon-check(v-if="groupDialog.ref.myMember.visibility === 'hidden'")] {{ $t('dialog.group.actions.visibility_hidden') }}
|
||||
el-dropdown-item(icon="el-icon-delete" command="Leave Group" style="color:#F56C6C" divided) {{ $t('dialog.group.actions.leave') }}
|
||||
template(v-else)
|
||||
el-dropdown-item(v-if="groupDialog.ref.membershipStatus === 'userblocked'" icon="el-icon-circle-check" command="Unblock Group" style="color:#F56C6C" divided) {{ $t('dialog.group.actions.unblock') }}
|
||||
el-dropdown-item(v-else icon="el-icon-circle-close" command="Block Group" divided) {{ $t('dialog.group.actions.block') }}
|
||||
el-tabs(ref="groupDialogTabs" @tab-click="groupDialogTabClick")
|
||||
el-tab-pane(:label="$t('dialog.group.info.header')")
|
||||
.group-banner-image-info
|
||||
el-popover(placement="right" width="500px" trigger="click")
|
||||
img.x-link(slot="reference" v-lazy="groupDialog.ref.bannerUrl" style="flex:none;width:100%;aspect-ratio:6/1;object-fit:cover;border-radius:4px")
|
||||
img.x-link(v-lazy="groupDialog.ref.bannerUrl" style="width:854px;height:480px" @click="showFullscreenImageDialog(groupDialog.ref.bannerUrl)")
|
||||
.x-friend-list(style="max-height:none")
|
||||
span(v-if="groupDialog.instances.length" style="font-size:12px;font-weight:bold;margin:5px") {{ $t('dialog.group.info.instances') }}
|
||||
div(v-for="room in groupDialog.instances" :key="room.tag" style="width:100%")
|
||||
div(style="margin:5px 0")
|
||||
location(:location="room.tag")
|
||||
el-tooltip(placement="top" content="Invite yourself" :disabled="hideTooltips")
|
||||
invite-yourself(:location="room.tag" style="margin-left:5px")
|
||||
el-tooltip(placement="top" content="Refresh player count" :disabled="hideTooltips")
|
||||
el-button(@click="refreshInstancePlayerCount(room.tag)" size="mini" icon="el-icon-refresh" style="margin-left:5px" circle)
|
||||
last-join(:location="room.tag" :currentlocation="lastLocation.location")
|
||||
instance-info(:location="room.tag" :instance="room.ref" :friendcount="room.friendCount" :updateelement="updateInstanceInfo")
|
||||
.x-friend-list(style="margin:10px 0;padding:0;max-height:unset" v-if="room.users.length")
|
||||
.x-friend-item(v-for="user in room.users" :key="user.id" @click="showUserDialog(user.id)" class="x-friend-item-border")
|
||||
.avatar(:class="userStatusClass(user)")
|
||||
img(v-lazy="userImage(user)")
|
||||
.detail
|
||||
span.name(v-text="user.displayName" :style="{'color':user.$userColour}")
|
||||
span.extra(v-if="user.location === 'traveling'")
|
||||
i.el-icon-loading(style="margin-right:5px")
|
||||
timer(:epoch="user.$travelingToTime")
|
||||
span.extra(v-else)
|
||||
timer(:epoch="user.$location_at")
|
||||
.x-friend-item(style="width:100%;cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.group.info.announcement') }}
|
||||
span(style="display:block" v-text="groupDialog.announcement.title")
|
||||
div(v-if="groupDialog.announcement.imageUrl" style="display:inline-block;margin-right:5px")
|
||||
el-popover(placement="right" width="500px" trigger="click")
|
||||
img.x-link(slot="reference" v-lazy="groupDialog.announcement.imageUrl" style="flex:none;width:60px;height:60px;border-radius:4px;object-fit:cover")
|
||||
img.x-link(v-lazy="groupDialog.announcement.imageUrl" style="height:500px" @click="showFullscreenImageDialog(groupDialog.announcement.imageUrl)")
|
||||
pre.extra(style="display:inline-block;vertical-align:top;font-family:inherit;font-size:12px;white-space:pre-wrap;margin:0") {{ groupDialog.announcement.text || '-' }}
|
||||
br
|
||||
.extra(v-if="groupDialog.announcement.id" style="float:right;margin-left:5px")
|
||||
el-tooltip(v-if="groupDialog.announcement.roleIds.length" placement="top")
|
||||
template(#content)
|
||||
span {{ $t('dialog.group.posts.visibility') }}
|
||||
br
|
||||
template(v-for="roleId in groupDialog.announcement.roleIds" :key="roleId")
|
||||
span(v-for="(role, rIndex) in groupDialog.ref.roles" :key="rIndex" v-if="role.id === roleId" v-text="role.name")
|
||||
span(v-if="groupDialog.announcement.roleIds.indexOf(roleId) < groupDialog.announcement.roleIds.length - 1") ,
|
||||
i.el-icon-view(style="margin-right:5px")
|
||||
display-name(:userid="groupDialog.announcement.authorId" style="margin-right:5px")
|
||||
span(v-if="groupDialog.announcement.editorId" style="margin-right:5px") ({{ $t('dialog.group.posts.edited_by') }} #[display-name(:userid="groupDialog.announcement.editorId")])
|
||||
el-tooltip(placement="bottom")
|
||||
template(#content)
|
||||
span {{ $t('dialog.group.posts.created_at') }} {{ groupDialog.announcement.createdAt | formatDate('long') }}
|
||||
template(v-if="groupDialog.announcement.updatedAt !== groupDialog.announcement.createdAt")
|
||||
br
|
||||
span {{ $t('dialog.group.posts.edited_at') }} {{ groupDialog.announcement.updatedAt | formatDate('long') }}
|
||||
timer(:epoch="Date.parse(groupDialog.announcement.updatedAt)")
|
||||
template(v-if="hasGroupPermission(groupDialog.ref, 'group-announcement-manage')")
|
||||
el-tooltip(placement="top" :content="$t('dialog.group.posts.edit_tooltip')" :disabled="hideTooltips")
|
||||
el-button(type="text" icon="el-icon-edit" size="mini" style="margin-left:5px" @click="showGroupPostEditDialog(groupDialog.id, groupDialog.announcement)")
|
||||
el-tooltip(placement="top" :content="$t('dialog.group.posts.delete_tooltip')" :disabled="hideTooltips")
|
||||
el-button(type="text" icon="el-icon-delete" size="mini" style="margin-left:5px" @click="confirmDeleteGroupPost(groupDialog.announcement)")
|
||||
.x-friend-item(style="width:100%;cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.group.info.rules') }}
|
||||
pre.extra(style="font-family:inherit;font-size:12px;white-space:pre-wrap;margin:0 0.5em 0 0") {{ groupDialog.ref.rules || '-' }}
|
||||
.x-friend-item(style="cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.group.info.members') }}
|
||||
.extra {{ groupDialog.ref.memberCount }} ({{ groupDialog.ref.onlineMemberCount }})
|
||||
.x-friend-item(style="cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.group.info.created_at') }}
|
||||
span.extra {{ groupDialog.ref.createdAt | formatDate('long') }}
|
||||
.x-friend-item(style="cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.group.info.links') }}
|
||||
div(v-if="groupDialog.ref.links && groupDialog.ref.links.length > 0" style="margin-top:5px")
|
||||
el-tooltip(v-if="link" v-for="(link, index) in groupDialog.ref.links" :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)")
|
||||
.extra(v-else) -
|
||||
.x-friend-item(style="width:350px;cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.group.info.url') }}
|
||||
span.extra {{ groupDialog.ref.$url }}
|
||||
el-tooltip(placement="top" :content="$t('dialog.group.info.url_tooltip')" :disabled="hideTooltips")
|
||||
el-button(type="default" @click="copyGroupUrl(groupDialog.ref.$url)" size="mini" icon="el-icon-s-order" circle style="margin-left:5px")
|
||||
.x-friend-item(style="width:350px;cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.group.info.id') }}
|
||||
span.extra {{ groupDialog.id }}
|
||||
el-tooltip(placement="top" :content="$t('dialog.group.info.id_tooltip')" :disabled="hideTooltips")
|
||||
el-button(type="default" @click="copyGroupId(groupDialog.id)" size="mini" icon="el-icon-s-order" circle style="margin-left:5px")
|
||||
div(v-if="groupDialog.ref.membershipStatus === 'member'" style="width:100%;margin-top:10px;border-top:1px solid #e4e7ed14")
|
||||
div(style="width:100%;display:flex;margin-top:10px")
|
||||
.x-friend-item(style="cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.group.info.joined_at') }}
|
||||
span.extra {{ groupDialog.ref.myMember.joinedAt | formatDate('long') }}
|
||||
.x-friend-item(style="cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.group.info.roles') }}
|
||||
span.extra(v-if="groupDialog.memberRoles.length === 0") -
|
||||
span.extra(v-else)
|
||||
template(v-for="(role, rIndex) in groupDialog.memberRoles" :key="rIndex")
|
||||
el-tooltip(placement="top")
|
||||
template(#content)
|
||||
span {{ $t('dialog.group.info.role') }} {{ role.name }}
|
||||
br
|
||||
span {{ $t('dialog.group.info.role_description') }} {{ role.description }}
|
||||
br
|
||||
span(v-if="role.updatedAt") {{ $t('dialog.group.info.role_updated_at') }} {{ role.updatedAt | formatDate('long') }}
|
||||
span(v-else) {{ $t('dialog.group.info.role_created_at') }} {{ role.createdAt | formatDate('long') }}
|
||||
br
|
||||
span {{ $t('dialog.group.info.role_permissions') }}
|
||||
br
|
||||
template(v-for="(permission, pIndex) in role.permissions" :key="pIndex")
|
||||
span {{ permission }}
|
||||
br
|
||||
span {{ role.name }}{{ rIndex < groupDialog.memberRoles.length - 1 ? ', ' : '' }}
|
||||
el-tab-pane(:label="$t('dialog.group.posts.header')")
|
||||
template(v-if="groupDialog.visible")
|
||||
span(style="margin-right:10px") {{ $t('dialog.group.posts.posts_count') }} {{ groupDialog.posts.length }}
|
||||
el-input(v-model="groupDialog.postsSearch" @input="updateGroupPostSearch" clearable size="mini" :placeholder="$t('dialog.group.posts.search_placeholder')" style="width:89%;margin-bottom:10px")
|
||||
.x-friend-list
|
||||
.x-friend-item(v-for="post in groupDialog.postsFiltered" :key="post.id" style="width:100%;cursor:default")
|
||||
.detail
|
||||
span(style="display:block" v-text="post.title")
|
||||
div(v-if="post.imageUrl" style="display:inline-block;margin-right:5px")
|
||||
el-popover(placement="right" width="500px" trigger="click")
|
||||
img.x-link(slot="reference" v-lazy="post.imageUrl" style="flex:none;width:60px;height:60px;border-radius:4px;object-fit:cover")
|
||||
img.x-link(v-lazy="post.imageUrl" style="height:500px" @click="showFullscreenImageDialog(post.imageUrl)")
|
||||
pre.extra(style="display:inline-block;vertical-align:top;font-family:inherit;font-size:12px;white-space:pre-wrap;margin:0") {{ post.text || '-' }}
|
||||
br
|
||||
.extra(v-if="post.authorId" style="float:right;margin-left:5px")
|
||||
el-tooltip(v-if="post.roleIds.length" placement="top")
|
||||
template(#content)
|
||||
span {{ $t('dialog.group.posts.visibility') }}
|
||||
br
|
||||
template(v-for="roleId in post.roleIds" :key="roleId")
|
||||
span(v-for="(role, rIndex) in groupDialog.ref.roles" :key="rIndex" v-if="role.id === roleId" v-text="role.name")
|
||||
span(v-if="post.roleIds.indexOf(roleId) < post.roleIds.length - 1") ,
|
||||
i.el-icon-view(style="margin-right:5px")
|
||||
display-name(:userid="post.authorId" style="margin-right:5px")
|
||||
span(v-if="post.editorId" style="margin-right:5px") ({{ $t('dialog.group.posts.edited_by') }} #[display-name(:userid="post.editorId")])
|
||||
el-tooltip(placement="bottom")
|
||||
template(#content)
|
||||
span {{ $t('dialog.group.posts.created_at') }} {{ post.createdAt | formatDate('long') }}
|
||||
template(v-if="post.updatedAt !== post.createdAt")
|
||||
br
|
||||
span {{ $t('dialog.group.posts.edited_at') }} {{ post.updatedAt | formatDate('long') }}
|
||||
timer(:epoch="Date.parse(post.updatedAt)")
|
||||
template(v-if="hasGroupPermission(groupDialog.ref, 'group-announcement-manage')")
|
||||
el-tooltip(placement="top" :content="$t('dialog.group.posts.edit_tooltip')" :disabled="hideTooltips")
|
||||
el-button(type="text" icon="el-icon-edit" size="mini" style="margin-left:5px" @click="showGroupPostEditDialog(groupDialog.id, post)")
|
||||
el-tooltip(placement="top" :content="$t('dialog.group.posts.delete_tooltip')" :disabled="hideTooltips")
|
||||
el-button(type="text" icon="el-icon-delete" size="mini" style="margin-left:5px" @click="confirmDeleteGroupPost(post)")
|
||||
el-tab-pane(:label="$t('dialog.group.members.header')")
|
||||
template(v-if="groupDialog.visible")
|
||||
span(v-if="hasGroupPermission(groupDialog.ref, 'group-members-viewall')" style="font-weight:bold;font-size:16px") {{ $t('dialog.group.members.all_members') }}
|
||||
span(v-else style="font-weight:bold;font-size:16px") {{ $t('dialog.group.members.friends_only') }}
|
||||
div(style="margin-top:10px")
|
||||
el-button(type="default" @click="loadAllGroupMembers" size="mini" icon="el-icon-refresh" :loading="isGroupMembersLoading" circle)
|
||||
el-button(type="default" @click="downloadAndSaveJson(`${groupDialog.id}_members`, groupDialog.members)" size="mini" icon="el-icon-download" circle style="margin-left:5px")
|
||||
span(v-if="groupDialog.memberSearch.length" style="font-size:14px;margin-left:5px;margin-right:5px") {{ groupDialog.memberSearchResults.length }}/{{ groupDialog.ref.memberCount }}
|
||||
span(v-else style="font-size:14px;margin-left:5px;margin-right:5px") {{ groupDialog.members.length }}/{{ groupDialog.ref.memberCount }}
|
||||
div(v-if="hasGroupPermission(groupDialog.ref, 'group-members-manage')" style="float:right")
|
||||
span(style="margin-right:5px") {{ $t('dialog.group.members.sort_by') }}
|
||||
el-dropdown(@click.native.stop trigger="click" size="small" style="margin-right:5px" :disabled="isGroupMembersLoading || groupDialog.memberSearch.length")
|
||||
el-button(size="mini")
|
||||
span {{ groupDialog.memberSortOrder.name }} #[i.el-icon-arrow-down.el-icon--right]
|
||||
el-dropdown-menu(#default="dropdown")
|
||||
el-dropdown-item(v-for="(item) in groupDialogSortingOptions" v-text="item.name" @click.native="setGroupMemberSortOrder(item)")
|
||||
span(style="margin-right:5px") {{ $t('dialog.group.members.filter') }}
|
||||
el-dropdown(@click.native.stop trigger="click" size="small" style="margin-right:5px" :disabled="isGroupMembersLoading || groupDialog.memberSearch.length")
|
||||
el-button(size="mini")
|
||||
span {{ groupDialog.memberFilter.name }} #[i.el-icon-arrow-down.el-icon--right]
|
||||
el-dropdown-menu(#default="dropdown")
|
||||
el-dropdown-item(v-for="(item) in groupDialogFilterOptions" v-text="item.name" @click.native="setGroupMemberFilter(item)")
|
||||
el-dropdown-item(v-for="(item) in groupDialog.ref.roles" v-if="!item.defaultRole" v-text="item.name" @click.native="setGroupMemberFilter(item)")
|
||||
el-input(v-model="groupDialog.memberSearch" @input="groupMembersSearch" clearable size="mini" :placeholder="$t('dialog.group.members.search')" style="margin-top:10px;margin-bottom:10px")
|
||||
.x-friend-list(v-if="groupDialog.memberSearch.length" v-loading="isGroupMembersLoading" style="margin-top:10px;overflow:auto;max-height:250px;min-width:130px")
|
||||
.x-friend-item(v-for="user in groupDialog.memberSearchResults" :key="user.id" @click="showUserDialog(user.userId)" class="x-friend-item-border")
|
||||
.avatar
|
||||
img(v-lazy="userImage(user.user)")
|
||||
.detail
|
||||
span.name(v-text="user.user.displayName" :style="{'color':user.user.$userColour}")
|
||||
span.extra
|
||||
template(v-if="hasGroupPermission(groupDialog.ref, 'group-members-manage')")
|
||||
el-tooltip(v-if="user.isRepresenting" placement="top" :content="$t('dialog.group.members.representing')")
|
||||
i.el-icon-collection-tag(style="margin-right:5px")
|
||||
el-tooltip(v-if="user.visibility !== 'visible'" placement="top")
|
||||
template(#content)
|
||||
span {{ $t('dialog.group.members.visibility') }} {{ user.visibility }}
|
||||
i.el-icon-view(style="margin-right:5px")
|
||||
el-tooltip(v-if="!user.isSubscribedToAnnouncements" placement="top" :content="$t('dialog.group.members.unsubscribed_announcements')")
|
||||
i.el-icon-chat-line-square(style="margin-right:5px")
|
||||
el-tooltip(v-if="user.managerNotes" placement="top")
|
||||
template(#content)
|
||||
span {{ $t('dialog.group.members.manager_notes') }}
|
||||
br
|
||||
span {{ user.managerNotes }}
|
||||
i.el-icon-edit-outline(style="margin-right:5px")
|
||||
template(v-for="roleId in user.roleIds" :key="roleId")
|
||||
span(v-for="(role, rIndex) in groupDialog.ref.roles" :key="rIndex" v-if="role.id === roleId" v-text="role.name")
|
||||
span(v-if="user.roleIds.indexOf(roleId) < user.roleIds.length - 1") ,
|
||||
ul.infinite-list.x-friend-list(v-else-if="groupDialog.members.length > 0" v-infinite-scroll="loadMoreGroupMembers" style="margin-top:10px;overflow:auto;max-height:250px;min-width:130px")
|
||||
li.infinite-list-item.x-friend-item(v-for="user in groupDialog.members" :key="user.id" @click="showUserDialog(user.userId)" class="x-friend-item-border")
|
||||
.avatar
|
||||
img(v-lazy="userImage(user.user)")
|
||||
.detail
|
||||
span.name(v-text="user.user.displayName" :style="{'color':user.user.$userColour}")
|
||||
span.extra
|
||||
template(v-if="hasGroupPermission(groupDialog.ref, 'group-members-manage')")
|
||||
el-tooltip(v-if="user.isRepresenting" placement="top" :content="$t('dialog.group.members.representing')")
|
||||
i.el-icon-collection-tag(style="margin-right:5px")
|
||||
el-tooltip(v-if="user.visibility !== 'visible'" placement="top")
|
||||
template(#content)
|
||||
span {{ $t('dialog.group.members.visibility') }} {{ user.visibility }}
|
||||
i.el-icon-view(style="margin-right:5px")
|
||||
el-tooltip(v-if="!user.isSubscribedToAnnouncements" placement="top" :content="$t('dialog.group.members.unsubscribed_announcements')")
|
||||
i.el-icon-chat-line-square(style="margin-right:5px")
|
||||
el-tooltip(v-if="user.managerNotes" placement="top")
|
||||
template(#content)
|
||||
span {{ $t('dialog.group.members.manager_notes') }}
|
||||
br
|
||||
span {{ user.managerNotes }}
|
||||
i.el-icon-edit-outline(style="margin-right:5px")
|
||||
template(v-for="roleId in user.roleIds" :key="roleId")
|
||||
span(v-for="(role, rIndex) in groupDialog.ref.roles" :key="rIndex" v-if="role.id === roleId" v-text="role.name")
|
||||
span(v-if="user.roleIds.indexOf(roleId) < user.roleIds.length - 1") ,
|
||||
.x-friend-item(v-if="!isGroupMembersDone" v-loading="isGroupMembersLoading" style="width:100%;height:45px;text-align:center" @click="loadMoreGroupMembers")
|
||||
.detail(v-if="!isGroupMembersLoading")
|
||||
span.name {{ $t('dialog.group.members.load_more') }}
|
||||
el-tab-pane(:label="$t('dialog.group.gallery.header')")
|
||||
el-button(type="default" size="mini" icon="el-icon-refresh" @click="getGroupGalleries" :loading="isGroupGalleryLoading" circle)
|
||||
el-tabs(type="card" v-loading="isGroupGalleryLoading" ref="groupDialogGallery" style="margin-top:10px")
|
||||
template(v-for="(gallery, index) in groupDialog.ref.galleries")
|
||||
el-tab-pane
|
||||
span(slot="label")
|
||||
span(v-text="gallery.name" style="font-weight:bold;font-size:16px")
|
||||
i.x-status-icon(style="margin-left:5px" :class="groupGalleryStatus(gallery)")
|
||||
span(style="color:#909399;font-size:12px;margin-left:5px") {{ groupDialog.galleries[gallery.id] ? groupDialog.galleries[gallery.id].length : 0 }}
|
||||
span(v-text="gallery.description" style="color:#c7c7c7;padding:10px")
|
||||
el-carousel(:interval="0" height="600px" style="margin-top:10px")
|
||||
el-carousel-item(v-for="image in groupDialog.galleries[gallery.id]" :key="image.id")
|
||||
el-popover(placement="top" width="700px" trigger="click")
|
||||
img.x-link(slot="reference" v-lazy="image.imageUrl" style="width:100%;height:100%;object-fit:contain")
|
||||
img.x-link(v-lazy="image.imageUrl" style="height:700px" @click="showFullscreenImageDialog(image.imageUrl)")
|
||||
el-tab-pane(:label="$t('dialog.group.json.header')")
|
||||
el-button(type="default" @click="refreshGroupDialogTreeData()" size="mini" icon="el-icon-refresh" circle)
|
||||
el-button(type="default" @click="downloadAndSaveJson(groupDialog.id, groupDialog.ref)" size="mini" icon="el-icon-download" circle style="margin-left:5px")
|
||||
el-tree(:data="groupDialog.treeData" style="margin-top:5px;font-size:12px")
|
||||
template(#default="scope")
|
||||
span
|
||||
span(v-text="scope.data.key" style="font-weight:bold;margin-right:5px")
|
||||
span(v-if="!scope.data.children" v-text="scope.data.value")
|
||||
334
src/mixins/dialogs/groups.pug
Normal file
@@ -0,0 +1,334 @@
|
||||
mixin groups()
|
||||
//- dialog: invite group
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="inviteGroupDialog" :visible.sync="inviteGroupDialog.visible" :title="$t('dialog.invite_to_group.header')" width="450px")
|
||||
div(v-if="inviteGroupDialog.visible" v-loading="inviteGroupDialog.loading")
|
||||
span {{ $t('dialog.invite_to_group.description') }}
|
||||
br
|
||||
el-select(v-model="inviteGroupDialog.groupId" clearable :placeholder="$t('dialog.invite_to_group.choose_group_placeholder')" filterable :disabled="inviteGroupDialog.loading" @change="isAllowedToInviteToGroup" style="margin-top:15px")
|
||||
el-option-group(v-if="API.currentUserGroups.size" :label="$t('dialog.invite_to_group.groups')" style="width:410px")
|
||||
el-option.x-friend-item(v-for="group in API.currentUserGroups.values()" :key="group.id" :label="group.name" :value="group.id" style="height:auto")
|
||||
.avatar
|
||||
img(v-lazy="group.iconUrl")
|
||||
.detail
|
||||
span.name(v-text="group.name")
|
||||
el-select(v-model="inviteGroupDialog.userIds" multiple clearable :placeholder="$t('dialog.invite_to_group.choose_friends_placeholder')" filterable :disabled="inviteGroupDialog.loading" style="width:100%;margin-top:15px")
|
||||
el-option-group(v-if="inviteGroupDialog.userId" :label="$t('dialog.invite_to_group.selected_users')")
|
||||
el-option.x-friend-item(:key="inviteGroupDialog.userObject.id" :label="inviteGroupDialog.userObject.displayName" :value="inviteGroupDialog.userObject.id" style="height:auto")
|
||||
template(v-if="inviteGroupDialog.userObject.id")
|
||||
.avatar(:class="userStatusClass(inviteGroupDialog.userObject)")
|
||||
img(v-lazy="userImage(inviteGroupDialog.userObject)")
|
||||
.detail
|
||||
span.name(v-text="inviteGroupDialog.userObject.displayName" :style="{'color':inviteGroupDialog.userObject.$userColour}")
|
||||
span(v-else v-text="inviteGroupDialog.userId")
|
||||
el-option-group(v-if="vipFriends.length" :label="$t('side_panel.favorite')")
|
||||
el-option.x-friend-item(v-for="friend in vipFriends" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto")
|
||||
template(v-if="friend.ref")
|
||||
.avatar(:class="userStatusClass(friend.ref)")
|
||||
img(v-lazy="userImage(friend.ref)")
|
||||
.detail
|
||||
span.name(v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
|
||||
span(v-else v-text="friend.id")
|
||||
el-option-group(v-if="onlineFriends.length" :label="$t('side_panel.online')")
|
||||
el-option.x-friend-item(v-for="friend in onlineFriends" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto")
|
||||
template(v-if="friend.ref")
|
||||
.avatar(:class="userStatusClass(friend.ref)")
|
||||
img(v-lazy="userImage(friend.ref)")
|
||||
.detail
|
||||
span.name(v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
|
||||
span(v-else v-text="friend.id")
|
||||
el-option-group(v-if="activeFriends.length" :label="$t('side_panel.active')")
|
||||
el-option.x-friend-item(v-for="friend in activeFriends" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto")
|
||||
template(v-if="friend.ref")
|
||||
.avatar
|
||||
img(v-lazy="userImage(friend.ref)")
|
||||
.detail
|
||||
span.name(v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
|
||||
span(v-else v-text="friend.id")
|
||||
el-option-group(v-if="offlineFriends.length" :label="$t('side_panel.offline')")
|
||||
el-option.x-friend-item(v-for="friend in offlineFriends" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto")
|
||||
template(v-if="friend.ref")
|
||||
.avatar
|
||||
img(v-lazy="userImage(friend.ref)")
|
||||
.detail
|
||||
span.name(v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
|
||||
span(v-else v-text="friend.id")
|
||||
template(#footer)
|
||||
el-button(type="primary" size="small" :disabled="inviteGroupDialog.loading || !inviteGroupDialog.userIds.length" @click="sendGroupInvite()") Invite
|
||||
|
||||
//- dialog: group moderation
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="groupMemberModeration" :visible.sync="groupMemberModeration.visible" :title="$t('dialog.group_member_moderation.header')" width="90vw")
|
||||
div(v-if="groupMemberModeration.visible")
|
||||
h3(v-text="groupMemberModeration.groupRef.name")
|
||||
el-tabs(type="card" style="height:100%")
|
||||
el-tab-pane(:label="$t('dialog.group_member_moderation.members')")
|
||||
div(style="margin-top:10px")
|
||||
el-button(type="default" @click="loadAllGroupMembers" size="mini" icon="el-icon-refresh" :loading="isGroupMembersLoading" circle)
|
||||
span(style="font-size:14px;margin-left:5px;margin-right:5px") {{ groupMemberModerationTable.data.length }}/{{ groupMemberModeration.groupRef.memberCount }}
|
||||
div(style="float:right;margin-top:5px")
|
||||
span(style="margin-right:5px") {{ $t('dialog.group.members.sort_by') }}
|
||||
el-dropdown(@click.native.stop trigger="click" size="small" style="margin-right:5px" :disabled="isGroupMembersLoading || groupDialog.memberSearch.length || !hasGroupPermission(groupDialog.ref, 'group-bans-manage')")
|
||||
el-button(size="mini")
|
||||
span {{ groupDialog.memberSortOrder.name }} #[i.el-icon-arrow-down.el-icon--right]
|
||||
el-dropdown-menu(#default="dropdown")
|
||||
el-dropdown-item(v-for="(item) in groupDialogSortingOptions" v-text="item.name" @click.native="setGroupMemberSortOrder(item)")
|
||||
span(style="margin-right:5px") {{ $t('dialog.group.members.filter') }}
|
||||
el-dropdown(@click.native.stop trigger="click" size="small" style="margin-right:5px" :disabled="isGroupMembersLoading || groupDialog.memberSearch.length || !hasGroupPermission(groupDialog.ref, 'group-bans-manage')")
|
||||
el-button(size="mini")
|
||||
span {{ groupDialog.memberFilter.name }} #[i.el-icon-arrow-down.el-icon--right]
|
||||
el-dropdown-menu(#default="dropdown")
|
||||
el-dropdown-item(v-for="(item) in groupDialogFilterOptions" v-text="item.name" @click.native="setGroupMemberFilter(item)")
|
||||
el-dropdown-item(v-for="(item) in groupDialog.ref.roles" v-if="!item.defaultRole" v-text="item.name" @click.native="setGroupMemberFilter(item)")
|
||||
el-input(v-model="groupDialog.memberSearch" :disabled="!hasGroupPermission(groupDialog.ref, 'group-bans-manage')" @input="groupMembersSearch" clearable size="mini" :placeholder="$t('dialog.group.members.search')" style="margin-top:10px;margin-bottom:10px")
|
||||
br
|
||||
el-button(size="small" @click="selectAllGroupMembers") {{ $t('dialog.group_member_moderation.select_all') }}
|
||||
data-tables(v-bind="groupMemberModerationTable" style="margin-top:10px")
|
||||
el-table-column(width="55" prop="$selected" :key="groupMemberModerationTableForceUpdate")
|
||||
template(v-once #default="scope")
|
||||
el-button(type="text" size="mini" @click.stop)
|
||||
el-checkbox(v-model="scope.row.$selected" @change="groupMemberModerationTableSelectionChange(scope.row)")
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.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.user)")
|
||||
img.friends-list-avatar(v-lazy="userImageFull(scope.row.user)" style="height:500px;cursor:pointer" @click="showFullscreenImageDialog(userImageFull(scope.row.user))")
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.display_name')" width="160" prop="$displayName" sortable)
|
||||
template(v-once #default="scope")
|
||||
span(style="cursor:pointer" @click="showUserDialog(scope.row.userId)")
|
||||
span(v-if="randomUserColours" v-text="scope.row.user.displayName" :style="{'color':scope.row.user.$userColour}")
|
||||
span(v-else v-text="scope.row.user.displayName")
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.roles')" prop="roleIds" sortable)
|
||||
template(v-once #default="scope")
|
||||
template(v-for="roleId in scope.row.roleIds" :key="roleId")
|
||||
span(v-for="(role, rIndex) in groupMemberModeration.groupRef.roles" :key="rIndex" v-if="role.id === roleId" v-text="role.name")
|
||||
span(v-if="scope.row.roleIds.indexOf(roleId) < scope.row.roleIds.length - 1") ,
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.notes')" prop="managerNotes" sortable)
|
||||
template(v-once #default="scope")
|
||||
span(v-text="scope.row.managerNotes" @click.stop)
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.joined_at')" width="170" prop="joinedAt" sortable)
|
||||
template(v-once #default="scope")
|
||||
span {{ scope.row.joinedAt | formatDate('long') }}
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.visibility')" width="120" prop="visibility" sortable)
|
||||
template(v-once #default="scope")
|
||||
span(v-text="scope.row.visibility")
|
||||
el-tab-pane(:label="$t('dialog.group_member_moderation.bans')" :disabled="!hasGroupPermission(groupDialog.ref, 'group-bans-manage')")
|
||||
div(style="margin-top:10px")
|
||||
el-button(type="default" @click="getAllGroupBans(groupMemberModeration.id)" size="mini" icon="el-icon-refresh" :loading="isGroupMembersLoading" circle)
|
||||
span(style="font-size:14px;margin-left:5px;margin-right:5px") {{ groupBansModerationTable.data.length }}
|
||||
br
|
||||
el-input(v-model="groupBansModerationTable.filters[0].value" clearable size="mini" :placeholder="$t('dialog.group.members.search')" style="margin-top:10px;margin-bottom:10px")
|
||||
br
|
||||
el-button(size="small" @click="selectAllGroupBans") {{ $t('dialog.group_member_moderation.select_all') }}
|
||||
data-tables(v-bind="groupBansModerationTable" style="margin-top:10px")
|
||||
el-table-column(width="55" prop="$selected" :key="groupMemberModerationTableForceUpdate")
|
||||
template(v-once #default="scope")
|
||||
el-button(type="text" size="mini" @click.stop)
|
||||
el-checkbox(v-model="scope.row.$selected" @change="groupMemberModerationTableSelectionChange(scope.row)")
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.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.user)")
|
||||
img.friends-list-avatar(v-lazy="userImageFull(scope.row.user)" style="height:500px;cursor:pointer" @click="showFullscreenImageDialog(userImageFull(scope.row.user))")
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.display_name')" width="160" prop="$displayName" sortable)
|
||||
template(v-once #default="scope")
|
||||
span(style="cursor:pointer" @click="showUserDialog(scope.row.userId)")
|
||||
span(v-if="randomUserColours" v-text="scope.row.user.displayName" :style="{'color':scope.row.user.$userColour}")
|
||||
span(v-else v-text="scope.row.user.displayName")
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.roles')" prop="roleIds" sortable)
|
||||
template(v-once #default="scope")
|
||||
template(v-for="roleId in scope.row.roleIds" :key="roleId")
|
||||
span(v-for="(role, rIndex) in groupMemberModeration.groupRef.roles" :key="rIndex" v-if="role.id === roleId" v-text="role.name")
|
||||
span(v-if="scope.row.roleIds.indexOf(roleId) < scope.row.roleIds.length - 1") ,
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.notes')" prop="managerNotes" sortable)
|
||||
template(v-once #default="scope")
|
||||
span(v-text="scope.row.managerNotes" @click.stop)
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.joined_at')" width="170" prop="joinedAt" sortable)
|
||||
template(v-once #default="scope")
|
||||
span {{ scope.row.joinedAt | formatDate('long') }}
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.banned_at')" width="170" prop="joinedAt" sortable)
|
||||
template(v-once #default="scope")
|
||||
span {{ scope.row.bannedAt | formatDate('long') }}
|
||||
el-tab-pane(:label="$t('dialog.group_member_moderation.invites')" :disabled="!hasGroupPermission(groupDialog.ref, 'group-invites-manage')")
|
||||
div(style="margin-top:10px")
|
||||
el-button(type="default" @click="getAllGroupInvitesAndJoinRequests(groupMemberModeration.id)" size="mini" icon="el-icon-refresh" :loading="isGroupMembersLoading" circle)
|
||||
br
|
||||
el-tabs
|
||||
el-tab-pane
|
||||
span(slot="label")
|
||||
span(v-text="$t('dialog.group_member_moderation.sent_invites')" style="font-weight:bold;font-size:16px")
|
||||
span(style="color:#909399;font-size:12px;margin-left:5px") {{ groupInvitesModerationTable.data.length }}
|
||||
el-button(size="small" @click="selectAllGroupInvites") {{ $t('dialog.group_member_moderation.select_all') }}
|
||||
data-tables(v-bind="groupInvitesModerationTable" style="margin-top:10px")
|
||||
el-table-column(width="55" prop="$selected" :key="groupMemberModerationTableForceUpdate")
|
||||
template(v-once #default="scope")
|
||||
el-button(type="text" size="mini" @click.stop)
|
||||
el-checkbox(v-model="scope.row.$selected" @change="groupMemberModerationTableSelectionChange(scope.row)")
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.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.user)")
|
||||
img.friends-list-avatar(v-lazy="userImageFull(scope.row.user)" style="height:500px;cursor:pointer" @click="showFullscreenImageDialog(userImageFull(scope.row.user))")
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.display_name')" width="160" prop="$displayName" sortable)
|
||||
template(v-once #default="scope")
|
||||
span(style="cursor:pointer" @click="showUserDialog(scope.row.userId)")
|
||||
span(v-if="randomUserColours" v-text="scope.row.user.displayName" :style="{'color':scope.row.user.$userColour}")
|
||||
span(v-else v-text="scope.row.user.displayName")
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.notes')" prop="managerNotes" sortable)
|
||||
template(v-once #default="scope")
|
||||
span(v-text="scope.row.managerNotes" @click.stop)
|
||||
br
|
||||
el-button(@click="groupMembersDeleteSentInvite" :disabled="groupMemberModeration.progressCurrent || !hasGroupPermission(groupDialog.ref, 'group-invites-manage')") {{ $t('dialog.group_member_moderation.delete_sent_invite') }}
|
||||
el-tab-pane
|
||||
span(slot="label")
|
||||
span(v-text="$t('dialog.group_member_moderation.join_requests')" style="font-weight:bold;font-size:16px")
|
||||
span(style="color:#909399;font-size:12px;margin-left:5px") {{ groupJoinRequestsModerationTable.data.length }}
|
||||
el-button(size="small" @click="selectAllGroupJoinRequests") {{ $t('dialog.group_member_moderation.select_all') }}
|
||||
data-tables(v-bind="groupJoinRequestsModerationTable" style="margin-top:10px")
|
||||
el-table-column(width="55" prop="$selected" :key="groupMemberModerationTableForceUpdate")
|
||||
template(v-once #default="scope")
|
||||
el-button(type="text" size="mini" @click.stop)
|
||||
el-checkbox(v-model="scope.row.$selected" @change="groupMemberModerationTableSelectionChange(scope.row)")
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.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.user)")
|
||||
img.friends-list-avatar(v-lazy="userImageFull(scope.row.user)" style="height:500px;cursor:pointer" @click="showFullscreenImageDialog(userImageFull(scope.row.user))")
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.display_name')" width="160" prop="$displayName" sortable)
|
||||
template(v-once #default="scope")
|
||||
span(style="cursor:pointer" @click="showUserDialog(scope.row.userId)")
|
||||
span(v-if="randomUserColours" v-text="scope.row.user.displayName" :style="{'color':scope.row.user.$userColour}")
|
||||
span(v-else v-text="scope.row.user.displayName")
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.notes')" prop="managerNotes" sortable)
|
||||
template(v-once #default="scope")
|
||||
span(v-text="scope.row.managerNotes" @click.stop)
|
||||
br
|
||||
el-button(@click="groupMembersAcceptInviteRequest" :disabled="groupMemberModeration.progressCurrent || !hasGroupPermission(groupDialog.ref, 'group-invites-manage')") {{ $t('dialog.group_member_moderation.accept_join_requests') }}
|
||||
el-button(@click="groupMembersRejectInviteRequest" :disabled="groupMemberModeration.progressCurrent || !hasGroupPermission(groupDialog.ref, 'group-invites-manage')") {{ $t('dialog.group_member_moderation.reject_join_requests') }}
|
||||
el-button(@click="groupMembersBlockJoinRequest" :disabled="groupMemberModeration.progressCurrent || !hasGroupPermission(groupDialog.ref, 'group-invites-manage')") {{ $t('dialog.group_member_moderation.block_join_requests') }}
|
||||
el-tab-pane
|
||||
span(slot="label")
|
||||
span(v-text="$t('dialog.group_member_moderation.blocked_requests')" style="font-weight:bold;font-size:16px")
|
||||
span(style="color:#909399;font-size:12px;margin-left:5px") {{ groupBlockedModerationTable.data.length }}
|
||||
el-button(size="small" @click="selectAllGroupBlocked") {{ $t('dialog.group_member_moderation.select_all') }}
|
||||
data-tables(v-bind="groupBlockedModerationTable" style="margin-top:10px")
|
||||
el-table-column(width="55" prop="$selected" :key="groupMemberModerationTableForceUpdate")
|
||||
template(v-once #default="scope")
|
||||
el-button(type="text" size="mini" @click.stop)
|
||||
el-checkbox(v-model="scope.row.$selected" @change="groupMemberModerationTableSelectionChange(scope.row)")
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.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.user)")
|
||||
img.friends-list-avatar(v-lazy="userImageFull(scope.row.user)" style="height:500px;cursor:pointer" @click="showFullscreenImageDialog(userImageFull(scope.row.user))")
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.display_name')" width="160" prop="$displayName" sortable)
|
||||
template(v-once #default="scope")
|
||||
span(style="cursor:pointer" @click="showUserDialog(scope.row.userId)")
|
||||
span(v-if="randomUserColours" v-text="scope.row.user.displayName" :style="{'color':scope.row.user.$userColour}")
|
||||
span(v-else v-text="scope.row.user.displayName")
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.notes')" prop="managerNotes" sortable)
|
||||
template(v-once #default="scope")
|
||||
span(v-text="scope.row.managerNotes" @click.stop)
|
||||
br
|
||||
el-button(@click="groupMembersDeleteBlockedRequest" :disabled="groupMemberModeration.progressCurrent || !hasGroupPermission(groupDialog.ref, 'group-invites-manage')") {{ $t('dialog.group_member_moderation.delete_blocked_requests') }}
|
||||
el-tab-pane(:label="$t('dialog.group_member_moderation.logs')" :disabled="!hasGroupPermission(groupDialog.ref, 'group-audit-view')")
|
||||
div(style="margin-top:10px")
|
||||
el-button(type="default" @click="getAllGroupLogs(groupMemberModeration.id)" size="mini" icon="el-icon-refresh" :loading="isGroupMembersLoading" circle)
|
||||
span(style="font-size:14px;margin-left:5px;margin-right:5px") {{ groupLogsModerationTable.data.length }}
|
||||
br
|
||||
el-select(v-model="groupMemberModeration.selectedAuditLogTypes" multiple collapse-tags :placeholder="$t('dialog.group_member_moderation.filter_type')")
|
||||
el-option-group(:label="$t('dialog.group_member_moderation.select_type')")
|
||||
el-option.x-friend-item(v-for="type in groupMemberModeration.auditLogTypes" :key="type" :label="getAuditLogTypeName(type)" :value="type")
|
||||
.detail
|
||||
span.name(v-text="getAuditLogTypeName(type)")
|
||||
el-input(v-model="groupLogsModerationTable.filters[0].value" :placeholder="$t('dialog.group_member_moderation.search_placeholder')" style="display:inline-block;width:150px;margin:10px")
|
||||
br
|
||||
data-tables(v-bind="groupLogsModerationTable" style="margin-top:10px")
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.created_at')" width="170" prop="created_at" sortable)
|
||||
template(v-once #default="scope")
|
||||
span {{ scope.row.created_at | formatDate('long') }}
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.type')" width="190" prop="eventType" sortable)
|
||||
template(v-once #default="scope")
|
||||
span(v-text="scope.row.eventType")
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.display_name')" width="160" prop="actorDisplayName" sortable)
|
||||
template(v-once #default="scope")
|
||||
span(style="cursor:pointer" @click="showUserDialog(scope.row.actorId)")
|
||||
span(v-text="scope.row.actorDisplayName")
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.description')" prop="description")
|
||||
template(v-once #default="scope")
|
||||
span(v-text="scope.row.description")
|
||||
el-table-column(:label="$t('dialog.group_member_moderation.data')" prop="data")
|
||||
template(v-once #default="scope")
|
||||
span(v-if="Object.keys(scope.row.data).length" v-text="JSON.stringify(scope.row.data)")
|
||||
br
|
||||
br
|
||||
span.name {{ $t('dialog.group_member_moderation.user_id') }}
|
||||
br
|
||||
el-input(v-model="groupMemberModeration.selectUserId" size="mini" style="margin-top:5px;width:340px" :placeholder="$t('dialog.group_member_moderation.user_id_placeholder')" clearable)
|
||||
el-button(size="small" @click="selectGroupMemberUserId" :disabled="!groupMemberModeration.selectUserId") {{ $t('dialog.group_member_moderation.select_user') }}
|
||||
br
|
||||
br
|
||||
span.name {{ $t('dialog.group_member_moderation.selected_users') }}
|
||||
el-button(type="default" @click="clearSelectedGroupMembers" size="mini" icon="el-icon-delete" circle style="margin-left:5px")
|
||||
br
|
||||
el-tag(v-for="user in groupMemberModeration.selectedUsersArray" type="info" disable-transitions="true" :key="user.id" style="margin-right:5px;margin-top:5px" closable @close="deleteSelectedGroupMember(user)")
|
||||
span {{ user.user?.displayName }} #[i.el-icon-warning(v-if="user.membershipStatus !== 'member'" style="margin-left:5px")]
|
||||
br
|
||||
br
|
||||
span.name {{ $t('dialog.group_member_moderation.notes') }}
|
||||
el-input.extra(v-model="groupMemberModeration.note" type="textarea" :rows="2" :autosize="{ minRows: 1, maxRows: 20 }" :placeholder="$t('dialog.group_member_moderation.note_placeholder')" size="mini" resize="none" style="margin-top:5px")
|
||||
br
|
||||
br
|
||||
span.name {{ $t('dialog.group_member_moderation.selected_roles') }}
|
||||
br
|
||||
el-select(v-model="groupMemberModeration.selectedRoles" clearable multiple :placeholder="$t('dialog.group_member_moderation.choose_roles_placeholder')" filterable style="margin-top:5px")
|
||||
el-option-group(:label="$t('dialog.group_member_moderation.roles')")
|
||||
el-option.x-friend-item(v-for="role in groupMemberModeration.groupRef.roles" :key="role.id" :label="role.name" :value="role.id" style="height:auto")
|
||||
.detail
|
||||
span.name(v-text="role.name")
|
||||
br
|
||||
br
|
||||
span.name {{ $t('dialog.group_member_moderation.actions') }}
|
||||
br
|
||||
el-button(@click="groupMembersAddRoles" :disabled="!groupMemberModeration.selectedRoles.length || groupMemberModeration.progressCurrent || !hasGroupPermission(groupDialog.ref, 'group-roles-assign')") {{ $t('dialog.group_member_moderation.add_roles') }}
|
||||
el-button(@click="groupMembersRemoveRoles" :disabled="!groupMemberModeration.selectedRoles.length || groupMemberModeration.progressCurrent || !hasGroupPermission(groupDialog.ref, 'group-roles-assign')") {{ $t('dialog.group_member_moderation.remove_roles') }}
|
||||
el-button(@click="groupMembersSaveNote" :disabled="groupMemberModeration.progressCurrent || !hasGroupPermission(groupDialog.ref, 'group-members-manage')") {{ $t('dialog.group_member_moderation.save_note') }}
|
||||
el-button(@click="groupMembersKick" :disabled="groupMemberModeration.progressCurrent || !hasGroupPermission(groupDialog.ref, 'group-members-remove')") {{ $t('dialog.group_member_moderation.kick') }}
|
||||
el-button(@click="groupMembersBan" :disabled="groupMemberModeration.progressCurrent || !hasGroupPermission(groupDialog.ref, 'group-bans-manage')") {{ $t('dialog.group_member_moderation.ban') }}
|
||||
el-button(@click="groupMembersUnban" :disabled="groupMemberModeration.progressCurrent || !hasGroupPermission(groupDialog.ref, 'group-bans-manage')") {{ $t('dialog.group_member_moderation.unban') }}
|
||||
span(v-if="groupMemberModeration.progressCurrent" style="margin-top:10px") #[i.el-icon-loading(style="margin-left:5px;margin-right:5px")] {{ $t('dialog.group_member_moderation.progress') }} {{ groupMemberModeration.progressCurrent }}/{{ groupMemberModeration.progressTotal }}
|
||||
el-button(v-if="groupMemberModeration.progressCurrent" @click="groupMemberModeration.progressTotal = 0" style="margin-left:5px") {{ $t('dialog.group_member_moderation.cancel') }}
|
||||
|
||||
//- dialog: group posts
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="groupPostEditDialog" :visible.sync="groupPostEditDialog.visible" :title="$t('dialog.group_post_edit.header')" width="650px")
|
||||
div(v-if="groupPostEditDialog.visible")
|
||||
h3(v-text="groupPostEditDialog.groupRef.name")
|
||||
el-form(:model="groupPostEditDialog" label-width="150px")
|
||||
el-form-item(:label="$t('dialog.group_post_edit.title')")
|
||||
el-input(v-model="groupPostEditDialog.title" size="mini")
|
||||
el-form-item(:label="$t('dialog.group_post_edit.message')")
|
||||
el-input(v-model="groupPostEditDialog.text" type="textarea" :rows="4" :autosize="{ minRows: 4, maxRows: 20 }" style="margin-top:10px" resize="none")
|
||||
el-form-item
|
||||
el-checkbox(v-if="!groupPostEditDialog.postId" v-model="groupPostEditDialog.sendNotification" size="small") {{ $t('dialog.group_post_edit.send_notification') }}
|
||||
el-form-item(:label="$t('dialog.group_post_edit.post_visibility')")
|
||||
el-radio-group(v-model="groupPostEditDialog.visibility" size="small")
|
||||
el-radio(label="public") {{ $t('dialog.group_post_edit.visibility_public') }}
|
||||
el-radio(label="group") {{ $t('dialog.group_post_edit.visibility_group') }}
|
||||
el-form-item(v-if="groupPostEditDialog.visibility === 'group'" :label="$t('dialog.new_instance.roles')")
|
||||
el-select(v-model="groupPostEditDialog.roleIds" multiple clearable :placeholder="$t('dialog.new_instance.role_placeholder')" style="width:100%")
|
||||
el-option-group(:label="$t('dialog.new_instance.role_placeholder')")
|
||||
el-option.x-friend-item(v-for="role in groupPostEditDialog.groupRef?.roles" :key="role.id" :label="role.name" :value="role.id" style="height:auto;width:478px")
|
||||
.detail
|
||||
span.name(v-text="role.name")
|
||||
el-form-item(:label="$t('dialog.group_post_edit.image')")
|
||||
template(v-if="gallerySelectDialog.selectedFileId")
|
||||
div(style="display:inline-block;flex:none;margin-right:5px")
|
||||
el-popover(placement="right" width="500px" trigger="click")
|
||||
img.x-link(slot="reference" v-lazy="gallerySelectDialog.selectedImageUrl" style="flex:none;width:60px;height:60px;border-radius:4px;object-fit:cover")
|
||||
img.x-link(v-lazy="gallerySelectDialog.selectedImageUrl" style="height:500px" @click="showFullscreenImageDialog(gallerySelectDialog.selectedImageUrl)")
|
||||
el-button(size="mini" @click="clearImageGallerySelect" style="vertical-align:top") {{ $t('dialog.invite_message.clear_selected_image') }}
|
||||
template(v-else)
|
||||
el-button(size="mini" @click="showGallerySelectDialog" style="margin-right:5px") {{ $t('dialog.invite_message.select_image') }}
|
||||
|
||||
template(#footer)
|
||||
el-button(size="small" @click="groupPostEditDialog.visible = false") {{ $t('dialog.group_post_edit.cancel') }}
|
||||
el-button(v-if="groupPostEditDialog.postId" size="small" @click="editGroupPost") {{ $t('dialog.group_post_edit.edit_post') }}
|
||||
el-button(v-else size="small" @click="createGroupPost") {{ $t('dialog.group_post_edit.create_post') }}
|
||||
61
src/mixins/dialogs/images.pug
Normal file
@@ -0,0 +1,61 @@
|
||||
mixin images()
|
||||
//- dialog: Change avatar image
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="changeAvatarImageDialog" :visible.sync="changeAvatarImageDialogVisible" :title="$t('dialog.change_content_image.avatar')" width="850px")
|
||||
div(v-if="changeAvatarImageDialogVisible" v-loading="changeAvatarImageDialogLoading")
|
||||
input(type="file" accept="image/*" @change="onFileChangeAvatarImage" id="AvatarImageUploadButton" style="display:none")
|
||||
span {{ $t('dialog.change_content_image.description') }}
|
||||
br
|
||||
el-button-group(style="padding-bottom:10px;padding-top:10px")
|
||||
el-button(type="default" size="small" @click="displayPreviousImages('Avatar', 'Change')" icon="el-icon-refresh") {{ $t('dialog.change_content_image.refresh') }}
|
||||
el-button(type="default" size="small" @click="uploadAvatarImage" icon="el-icon-upload2") {{ $t('dialog.change_content_image.upload') }}
|
||||
//- el-button(type="default" size="small" @click="deleteAvatarImage" icon="el-icon-delete") Delete Latest Image
|
||||
br
|
||||
div(style="display:inline-block" v-for="image in previousImagesTable" :key="image.version" v-if="image.file")
|
||||
.x-change-image-item(@click="setAvatarImage(image)" style="cursor:pointer" :class="{ 'current-image': compareCurrentImage(image) }")
|
||||
img.image(v-lazy="image.file.url")
|
||||
|
||||
//- dialog: Change world image
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="changeWorldImageDialog" :visible.sync="changeWorldImageDialogVisible" :title="$t('dialog.change_content_image.world')" width="850px")
|
||||
div(v-if="changeWorldImageDialogVisible" v-loading="changeWorldImageDialogLoading")
|
||||
input(type="file" accept="image/*" @change="onFileChangeWorldImage" id="WorldImageUploadButton" style="display:none")
|
||||
span {{ $t('dialog.change_content_image.description') }}
|
||||
br
|
||||
el-button-group(style="padding-bottom:10px;padding-top:10px")
|
||||
el-button(type="default" size="small" @click="displayPreviousImages('World', 'Change')" icon="el-icon-refresh") {{ $t('dialog.change_content_image.refresh') }}
|
||||
el-button(type="default" size="small" @click="uploadWorldImage" icon="el-icon-upload2") {{ $t('dialog.change_content_image.upload') }}
|
||||
//- el-button(type="default" size="small" @click="deleteWorldImage" icon="el-icon-delete") Delete Latest Image
|
||||
br
|
||||
div(style="display:inline-block" v-for="image in previousImagesTable" :key="image.version" v-if="image.file")
|
||||
.x-change-image-item(@click="setWorldImage(image)" style="cursor:pointer" :class="{ 'current-image': compareCurrentImage(image) }")
|
||||
img.image(v-lazy="image.file.url")
|
||||
|
||||
//- dialog: Display previous avatar/world images
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="previousImagesDialog" :visible.sync="previousImagesDialogVisible" :title="$t('dialog.previous_images.header')" width="800px")
|
||||
div(v-if="previousImagesDialogVisible")
|
||||
div(style="display:inline-block" v-for="image in previousImagesTable" :key="image.version" v-if="image.file")
|
||||
el-popover.x-change-image-item(placement="right" width="500px" trigger="click")
|
||||
img.x-link(slot="reference" v-lazy="image.file.url")
|
||||
img.x-link(v-lazy="image.file.url" style="width:500px;height:375px" @click="showFullscreenImageDialog(image.file.url)")
|
||||
|
||||
//- dialog: gallery select
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="gallerySelectDialog" :visible.sync="gallerySelectDialog.visible" :title="$t('dialog.gallery_select.header')" width="100%")
|
||||
div(v-if="gallerySelectDialog.visible")
|
||||
span(slot="label") {{ $t('dialog.gallery_select.gallery') }}
|
||||
span(style="color:#909399;font-size:12px;margin-left:5px") {{ galleryTable.length }}/64
|
||||
br
|
||||
input(type="file" accept="image/*" @change="onFileChangeGallery" id="GalleryUploadButton" style="display:none")
|
||||
el-button-group
|
||||
el-button(type="default" size="small" @click="selectImageGallerySelect('', '')" icon="el-icon-close") {{ $t('dialog.gallery_select.none') }}
|
||||
el-button(type="default" size="small" @click="refreshGalleryTable" icon="el-icon-refresh") {{ $t('dialog.gallery_select.refresh') }}
|
||||
el-button(type="default" size="small" @click="displayGalleryUpload" icon="el-icon-upload2" :disabled="!API.currentUser.$isVRCPlus") {{ $t('dialog.gallery_select.upload') }}
|
||||
br
|
||||
.x-friend-item(v-if="image.versions && image.versions.length > 0" v-for="image in galleryTable" :key="image.id" style="display:inline-block;margin-top:10px;width:unset;cursor:default")
|
||||
.vrcplus-icon(v-if="image.versions[image.versions.length - 1].file.url" @click="selectImageGallerySelect(image.versions[image.versions.length - 1].file.url, image.id)")
|
||||
img.avatar(v-lazy="image.versions[image.versions.length - 1].file.url")
|
||||
|
||||
//- dialog: full screen image
|
||||
el-dialog.x-dialog(ref="fullscreenImageDialog" :before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" :visible.sync="fullscreenImageDialog.visible" top="3vh" width="97vw")
|
||||
div(style="margin:0 0 5px 5px")
|
||||
el-button(@click="copyImageUrl(fullscreenImageDialog.imageUrl)" size="mini" icon="el-icon-s-order" circle)
|
||||
el-button(type="default" size="mini" icon="el-icon-download" circle @click="downloadAndSaveImage(fullscreenImageDialog.imageUrl, fullscreenImageDialog.fileName)" style="margin-left:5px")
|
||||
img(v-lazy="fullscreenImageDialog.imageUrl" style="width:100%;height:100vh;object-fit:contain")
|
||||
170
src/mixins/dialogs/invites.pug
Normal file
@@ -0,0 +1,170 @@
|
||||
mixin invites()
|
||||
//- dialog: invite
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="inviteDialog" :visible.sync="inviteDialog.visible" :title="$t('dialog.invite.header')" width="500px")
|
||||
div(v-if="inviteDialog.visible" v-loading="inviteDialog.loading")
|
||||
location(:location="inviteDialog.worldId" :link="false")
|
||||
br
|
||||
el-button(size="mini" v-text="$t('dialog.invite.add_self')" @click="addSelfToInvite" style="margin-top:10px")
|
||||
el-button(size="mini" v-text="$t('dialog.invite.add_friends_in_instance')" @click="addFriendsInInstanceToInvite" :disabled="inviteDialog.friendsInInstance.length === 0" style="margin-top:10px")
|
||||
el-button(size="mini" v-text="$t('dialog.invite.add_favorite_friends')" @click="addFavoriteFriendsToInvite" :disabled="vipFriends.length === 0" style="margin-top:10px")
|
||||
el-select(v-model="inviteDialog.userIds" multiple clearable :placeholder="$t('dialog.invite.select_placeholder')" filterable :disabled="inviteDialog.loading" style="width:100%;margin-top:15px")
|
||||
el-option-group(v-if="API.currentUser" :label="$t('side_panel.me')")
|
||||
el-option.x-friend-item(:label="API.currentUser.displayName" :value="API.currentUser.id" style="height:auto")
|
||||
.avatar(:class="userStatusClass(API.currentUser)")
|
||||
img(v-lazy="userImage(API.currentUser)")
|
||||
.detail
|
||||
span.name(v-text="API.currentUser.displayName")
|
||||
el-option-group(v-if="inviteDialog.friendsInInstance.length" :label="$t('dialog.invite.friends_in_instance')")
|
||||
el-option.x-friend-item(v-for="friend in inviteDialog.friendsInInstance" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto")
|
||||
template(v-if="friend.ref")
|
||||
.avatar(:class="userStatusClass(friend.ref)")
|
||||
img(v-lazy="userImage(friend.ref)")
|
||||
.detail
|
||||
span.name(v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
|
||||
span(v-else v-text="friend.id")
|
||||
el-option-group(v-if="vipFriends.length" :label="$t('side_panel.favorite')")
|
||||
el-option.x-friend-item(v-for="friend in vipFriends" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto")
|
||||
template(v-if="friend.ref")
|
||||
.avatar(:class="userStatusClass(friend.ref)")
|
||||
img(v-lazy="userImage(friend.ref)")
|
||||
.detail
|
||||
span.name(v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
|
||||
span(v-else v-text="friend.id")
|
||||
el-option-group(v-if="onlineFriends.length" :label="$t('side_panel.online')")
|
||||
el-option.x-friend-item(v-for="friend in onlineFriends" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto")
|
||||
template(v-if="friend.ref")
|
||||
.avatar(:class="userStatusClass(friend.ref)")
|
||||
img(v-lazy="userImage(friend.ref)")
|
||||
.detail
|
||||
span.name(v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
|
||||
span(v-else v-text="friend.id")
|
||||
el-option-group(v-if="activeFriends.length" :label="$t('side_panel.active')")
|
||||
el-option.x-friend-item(v-for="friend in activeFriends" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto")
|
||||
template(v-if="friend.ref")
|
||||
.avatar
|
||||
img(v-lazy="userImage(friend.ref)")
|
||||
.detail
|
||||
span.name(v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
|
||||
span(v-else v-text="friend.id")
|
||||
template(#footer)
|
||||
el-button(size="small" :disabled="inviteDialog.loading || !inviteDialog.userIds.length" @click="showSendInviteDialog()") {{ $t('dialog.invite.invite_with_message') }}
|
||||
el-button(type="primary" size="small" :disabled="inviteDialog.loading || !inviteDialog.userIds.length" @click="sendInvite()") {{ $t('dialog.invite.invite') }}
|
||||
|
||||
//- dialog: Edit Invite Message
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="editInviteMessageDialog" :visible.sync="editInviteMessageDialog.visible" :title="$t('dialog.edit_invite_message.header')" width="400px")
|
||||
div(style='font-size:12px')
|
||||
span {{ $t('dialog.edit_invite_message.description') }}
|
||||
el-input(type="textarea" v-model="editInviteMessageDialog.newMessage" size="mini" maxlength="64" show-word-limit :autosize="{ minRows:2, maxRows:5 }" placeholder="" style="margin-top:10px")
|
||||
template(#footer)
|
||||
el-button(type="small" @click="cancelEditInviteMessage") {{ $t('dialog.edit_invite_message.cancel') }}
|
||||
el-button(type="primary" size="small" @click="saveEditInviteMessage") {{ $t('dialog.edit_invite_message.save') }}
|
||||
|
||||
//- dialog: Edit And Send Invite Response Message
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="editAndSendInviteResponseDialog" :visible.sync="editAndSendInviteResponseDialog.visible" :title="$t('dialog.edit_send_invite_response_message.header')" width="400px")
|
||||
div(style='font-size:12px')
|
||||
span {{ $t('dialog.edit_send_invite_response_message.description') }}
|
||||
el-input(type="textarea" v-model="editAndSendInviteResponseDialog.newMessage" size="mini" maxlength="64" show-word-limit :autosize="{ minRows:2, maxRows:5 }" placeholder="" style="margin-top:10px")
|
||||
template(#footer)
|
||||
el-button(type="small" @click="cancelEditAndSendInviteResponse") {{ $t('dialog.edit_send_invite_response_message.cancel') }}
|
||||
el-button(type="primary" size="small" @click="saveEditAndSendInviteResponse") {{ $t('dialog.edit_send_invite_response_message.send') }}
|
||||
|
||||
//- dialog Table: Send Invite Response Message
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="sendInviteResponseDialog" :visible.sync="sendInviteResponseDialogVisible" :title="$t('dialog.invite_response_message.header')" width="800px")
|
||||
template(v-if="API.currentUser.$isVRCPlus")
|
||||
input.inviteImageUploadButton(type="file" accept="image/*" @change="inviteImageUpload")
|
||||
data-tables(v-if="sendInviteResponseDialogVisible" v-bind="inviteResponseMessageTable" @row-click="showSendInviteResponseConfirmDialog" style="margin-top:10px;cursor:pointer")
|
||||
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="70" align="right")
|
||||
template(v-once #default="scope")
|
||||
el-button(type="text" icon="el-icon-edit" size="mini" @click="showEditAndSendInviteResponseDialog('response', scope.row)")
|
||||
template(#footer)
|
||||
el-button(type="small" @click="cancelSendInviteResponse") {{ $t('dialog.invite_response_message.cancel') }}
|
||||
el-button(type="small" @click="API.refreshInviteMessageTableData('response')") {{ $t('dialog.invite_response_message.refresh') }}
|
||||
|
||||
//- dialog Table: Send Invite Request Response Message
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="sendInviteRequestResponseDialog" :visible.sync="sendInviteRequestResponseDialogVisible" :title="$t('dialog.invite_request_response_message.header')" width="800px")
|
||||
template(v-if="API.currentUser.$isVRCPlus")
|
||||
input.inviteImageUploadButton(type="file" accept="image/*" @change="inviteImageUpload")
|
||||
data-tables(v-if="sendInviteRequestResponseDialogVisible" v-bind="inviteRequestResponseMessageTable" @row-click="showSendInviteResponseConfirmDialog" style="margin-top:10px;cursor:pointer")
|
||||
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="70" align="right")
|
||||
template(v-once #default="scope")
|
||||
el-button(type="text" icon="el-icon-edit" size="mini" @click="showEditAndSendInviteResponseDialog('requestResponse', scope.row)")
|
||||
template(#footer)
|
||||
el-button(type="small" @click="cancelSendInviteRequestResponse") {{ $t('dialog.invite_request_response_message.cancel') }}
|
||||
el-button(type="small" @click="API.refreshInviteMessageTableData('requestResponse')") {{ $t('dialog.invite_request_response_message.refresh') }}
|
||||
|
||||
//- dialog: Send Invite Response Message Confirm
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="sendInviteResponseConfirmDialog" :visible.sync="sendInviteResponseConfirmDialog.visible" :title="$t('dialog.invite_response_message.header')" width="400px")
|
||||
div(style='font-size:12px')
|
||||
span {{ $t('dialog.invite_response_message.confirmation') }}
|
||||
template(#footer)
|
||||
el-button(type="small" @click="cancelInviteResponseConfirm") {{ $t('dialog.invite_response_message.cancel') }}
|
||||
el-button(type="primary" size="small" @click="sendInviteResponseConfirm") {{ $t('dialog.invite_response_message.confirm') }}
|
||||
|
||||
//- dialog Table: Send Invite Message
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="sendInviteDialog" :visible.sync="sendInviteDialogVisible" :title="$t('dialog.invite_message.header')" width="800px")
|
||||
template(v-if="API.currentUser.$isVRCPlus")
|
||||
//- template(v-if="gallerySelectDialog.selectedFileId")
|
||||
//- div(style="display:inline-block;flex:none;margin-right:5px")
|
||||
//- el-popover(placement="right" width="500px" trigger="click")
|
||||
//- img.x-link(slot="reference" v-lazy="gallerySelectDialog.selectedImageUrl" style="flex:none;width:60px;height:60px;border-radius:4px;object-fit:cover")
|
||||
//- img.x-link(v-lazy="gallerySelectDialog.selectedImageUrl" style="height:500px" @click="showFullscreenImageDialog(gallerySelectDialog.selectedImageUrl)")
|
||||
//- el-button(size="mini" @click="clearImageGallerySelect" style="vertical-align:top") {{ $t('dialog.invite_message.clear_selected_image') }}
|
||||
//- template(v-else)
|
||||
//- el-button(size="mini" @click="showGallerySelectDialog" style="margin-right:5px") {{ $t('dialog.invite_message.select_image') }}
|
||||
input.inviteImageUploadButton(type="file" accept="image/*" @change="inviteImageUpload")
|
||||
data-tables(v-if="sendInviteDialogVisible" v-bind="inviteMessageTable" @row-click="showSendInviteConfirmDialog" style="margin-top:10px;cursor:pointer")
|
||||
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="70" align="right")
|
||||
template(v-once #default="scope")
|
||||
el-button(type="text" icon="el-icon-edit" size="mini" @click="showEditAndSendInviteDialog('message', scope.row)")
|
||||
template(#footer)
|
||||
el-button(type="small" @click="cancelSendInvite") {{ $t('dialog.invite_message.cancel') }}
|
||||
el-button(type="small" @click="API.refreshInviteMessageTableData('message')") {{ $t('dialog.invite_message.refresh') }}
|
||||
|
||||
//- dialog Table: Send Invite Request Message
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="sendInviteRequestDialog" :visible.sync="sendInviteRequestDialogVisible" :title="$t('dialog.invite_request_message.header')" width="800px")
|
||||
template(v-if="API.currentUser.$isVRCPlus")
|
||||
input.inviteImageUploadButton(type="file" accept="image/*" @change="inviteImageUpload")
|
||||
data-tables(v-if="sendInviteRequestDialogVisible" v-bind="inviteRequestMessageTable" @row-click="showSendInviteConfirmDialog" style="margin-top:10px;cursor:pointer")
|
||||
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="70" align="right")
|
||||
template(v-once #default="scope")
|
||||
el-button(type="text" icon="el-icon-edit" size="mini" @click="showEditAndSendInviteDialog('request', scope.row)")
|
||||
template(#footer)
|
||||
el-button(type="small" @click="cancelSendInviteRequest") {{ $t('dialog.invite_request_message.cancel') }}
|
||||
el-button(type="small" @click="API.refreshInviteMessageTableData('request')") {{ $t('dialog.invite_request_message.refresh') }}
|
||||
|
||||
//- dialog: Send Invite Message Confirm
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="sendInviteConfirmDialog" :visible.sync="sendInviteConfirmDialog.visible" :title="$t('dialog.invite_message.header')" width="400px")
|
||||
div(style='font-size:12px')
|
||||
span {{ $t('dialog.invite_message.confirmation') }}
|
||||
template(#footer)
|
||||
el-button(type="small" @click="cancelInviteConfirm") {{ $t('dialog.invite_message.cancel') }}
|
||||
el-button(type="primary" size="small" @click="sendInviteConfirm") {{ $t('dialog.invite_message.confirm') }}
|
||||
|
||||
//- dialog: Edit And Send Invite Message
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="editAndSendInviteDialog" :visible.sync="editAndSendInviteDialog.visible" :title="$t('dialog.edit_send_invite_message.header')" width="400px")
|
||||
div(style='font-size:12px')
|
||||
span {{ $t('dialog.edit_send_invite_message.description') }}
|
||||
el-input(type="textarea" v-model="editAndSendInviteDialog.newMessage" size="mini" maxlength="64" show-word-limit :autosize="{ minRows:2, maxRows:5 }" placeholder="" style="margin-top:10px")
|
||||
template(#footer)
|
||||
el-button(type="small" @click="cancelEditAndSendInvite") {{ $t('dialog.edit_send_invite_message.cancel') }}
|
||||
el-button(type="primary" size="small" @click="saveEditAndSendInvite") {{ $t('dialog.edit_send_invite_message.send') }}
|
||||
38
src/mixins/dialogs/launch.pug
Normal file
@@ -0,0 +1,38 @@
|
||||
mixin launch()
|
||||
//- dialog: launch
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="launchDialog" :visible.sync="launchDialog.visible" :title="$t('dialog.launch.header')" width="450px")
|
||||
el-form(:model="launchDialog" label-width="80px")
|
||||
el-form-item(:label="$t('dialog.launch.url')")
|
||||
el-input(v-model="launchDialog.url" size="mini" @click.native="$event.target.tagName === 'INPUT' && $event.target.select()" style="width:260px")
|
||||
el-tooltip(placement="right" :content="$t('dialog.launch.copy_tooltip')" :disabled="hideTooltips")
|
||||
el-button(@click="copyInstanceMessage(launchDialog.url)" size="mini" icon="el-icon-s-order" style="margin-right:5px" circle)
|
||||
el-form-item(v-if="launchDialog.shortUrl" :label="$t('dialog.launch.short_url')")
|
||||
el-tooltip(placement="top" style="margin-left:5px" :content="$t('dialog.launch.short_url_notice')")
|
||||
i.el-icon-warning
|
||||
el-input(v-model="launchDialog.shortUrl" size="mini" @click.native="$event.target.tagName === 'INPUT' && $event.target.select()" style="width:241px")
|
||||
el-tooltip(placement="right" :content="$t('dialog.launch.copy_tooltip')" :disabled="hideTooltips")
|
||||
el-button(@click="copyInstanceMessage(launchDialog.shortUrl)" size="mini" icon="el-icon-s-order" style="margin-right:5px" circle)
|
||||
el-form-item(:label="$t('dialog.launch.location')")
|
||||
el-input(v-model="launchDialog.location" size="mini" @click.native="$event.target.tagName === 'INPUT' && $event.target.select()" style="width:260px")
|
||||
el-tooltip(placement="right" :content="$t('dialog.launch.copy_tooltip')" :disabled="hideTooltips")
|
||||
el-button(@click="copyInstanceMessage(launchDialog.location)" size="mini" icon="el-icon-s-order" style="margin-right:5px" circle)
|
||||
template(#footer)
|
||||
el-checkbox(v-model="launchDialog.desktop" @change="saveLaunchDialog" style="float:left;margin-top:5px") {{ $t('dialog.launch.start_as_desktop') }}
|
||||
el-button(size="small" @click="showPreviousInstanceInfoDialog(launchDialog.location)") {{ $t('dialog.launch.info') }}
|
||||
el-button(size="small" @click="showInviteDialog(launchDialog.location)" :disabled="!checkCanInvite(launchDialog.location)") {{ $t('dialog.launch.invite') }}
|
||||
el-button(type="primary" size="small" @click="launchGame(launchDialog.location, launchDialog.shortName, launchDialog.desktop)" :disabled="!launchDialog.secureOrShortName") {{ $t('dialog.launch.launch') }}
|
||||
|
||||
//- dialog: launch options
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="launchOptionsDialog" :visible.sync="launchOptionsDialog.visible" :title="$t('dialog.launch_options.header')" width="600px")
|
||||
div(style="font-size:12px")
|
||||
| {{ $t('dialog.launch_options.description') }} #[br]
|
||||
| {{ $t('dialog.launch_options.example') }} #[el-tag(size="mini") --fps=144]
|
||||
el-input(type="textarea" v-model="launchOptionsDialog.launchArguments" size="mini" show-word-limit :autosize="{ minRows:2, maxRows:5 }" placeholder="" style="margin-top:10px")
|
||||
div(style="font-size:12px;margin-top:10px")
|
||||
| {{ $t('dialog.launch_options.path_override') }}
|
||||
el-input(type="textarea" v-model="launchOptionsDialog.vrcLaunchPathOverride" placeholder="C:\\Program Files (x86)\\Steam\\steamapps\\common\\VRChat" :rows="1" style="display:block;margin-top:10px")
|
||||
template(#footer)
|
||||
div(style="display:flex")
|
||||
el-button(size="small" @click="openExternalLink('https://docs.vrchat.com/docs/launch-options')") {{ $t('dialog.launch_options.vrchat_docs') }}
|
||||
el-button(size="small" @click="openExternalLink('https://docs.unity3d.com/Manual/CommandLineArguments.html')") {{ $t('dialog.launch_options.unity_manual') }}
|
||||
el-button(type="primary" size="small" :disabled="launchOptionsDialog.loading" @click="updateLaunchOptions" style="margin-left:auto") {{ $t('dialog.launch_options.save') }}
|
||||
143
src/mixins/dialogs/newInstance.pug
Normal file
@@ -0,0 +1,143 @@
|
||||
mixin newInstance()
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="newInstanceDialog" :visible.sync="newInstanceDialog.visible" :title="$t('dialog.new_instance.header')" width="650px")
|
||||
el-tabs(type="card" v-model="newInstanceDialog.selectedTab" @tab-click="newInstanceTabClick")
|
||||
el-tab-pane(:label="$t('dialog.new_instance.normal')")
|
||||
el-form(v-if="newInstanceDialog.visible" :model="newInstanceDialog" label-width="150px")
|
||||
el-form-item(:label="$t('dialog.new_instance.access_type')")
|
||||
el-radio-group(v-model="newInstanceDialog.accessType" size="mini" @change="buildInstance")
|
||||
el-radio-button(label="public") {{ $t('dialog.new_instance.access_type_public') }}
|
||||
el-radio-button(label="group") {{ $t('dialog.new_instance.access_type_group') }}
|
||||
el-radio-button(label="friends+") {{ $t('dialog.new_instance.access_type_friend_plus') }}
|
||||
el-radio-button(label="friends") {{ $t('dialog.new_instance.access_type_friend') }}
|
||||
el-radio-button(label="invite+") {{ $t('dialog.new_instance.access_type_invite_plus') }}
|
||||
el-radio-button(label="invite") {{ $t('dialog.new_instance.access_type_invite') }}
|
||||
el-form-item(:label="$t('dialog.new_instance.group_access_type')" v-if="newInstanceDialog.accessType === 'group'")
|
||||
el-radio-group(v-model="newInstanceDialog.groupAccessType" size="mini" @change="buildInstance")
|
||||
el-radio-button(label="members" :disabled="!hasGroupPermission(newInstanceDialog.groupRef, 'group-instance-open-create')") {{ $t('dialog.new_instance.group_access_type_members') }}
|
||||
el-radio-button(label="plus" :disabled="!hasGroupPermission(newInstanceDialog.groupRef, 'group-instance-plus-create')") {{ $t('dialog.new_instance.group_access_type_plus') }}
|
||||
el-radio-button(label="public" :disabled="!hasGroupPermission(newInstanceDialog.groupRef, 'group-instance-public-create') || newInstanceDialog.groupRef.privacy === 'private'") {{ $t('dialog.new_instance.group_access_type_public') }}
|
||||
el-form-item(:label="$t('dialog.new_instance.region')")
|
||||
el-radio-group(v-model="newInstanceDialog.region" size="mini" @change="buildInstance")
|
||||
el-radio-button(label="US West") {{ $t('dialog.new_instance.region_usw') }}
|
||||
el-radio-button(label="US East") {{ $t('dialog.new_instance.region_use') }}
|
||||
el-radio-button(label="Europe") {{ $t('dialog.new_instance.region_eu') }}
|
||||
el-radio-button(label="Japan") {{ $t('dialog.new_instance.region_jp') }}
|
||||
el-form-item(:label="$t('dialog.new_instance.queueEnabled')" v-if="newInstanceDialog.accessType === 'group'")
|
||||
el-checkbox(v-model="newInstanceDialog.queueEnabled" @change="buildInstance")
|
||||
el-form-item(:label="$t('dialog.new_instance.ageGate')" v-if="newInstanceDialog.accessType === 'group'")
|
||||
el-checkbox(v-model="newInstanceDialog.ageGate" @change="buildInstance" :disabled="!hasGroupPermission(newInstanceDialog.groupRef, 'group-instance-age-gated-create')")
|
||||
el-form-item(:label="$t('dialog.new_instance.world_id')")
|
||||
el-input(v-model="newInstanceDialog.worldId" size="mini" @click.native="$event.target.tagName === 'INPUT' && $event.target.select()" @change="buildInstance")
|
||||
el-form-item(:label="$t('dialog.new_instance.group_id')" v-if="newInstanceDialog.accessType === 'group'")
|
||||
el-select(v-model="newInstanceDialog.groupId" clearable :placeholder="$t('dialog.new_instance.group_placeholder')" filterable style="width:100%" @change="buildInstance")
|
||||
el-option-group(:label="$t('dialog.new_instance.group_placeholder')")
|
||||
el-option.x-friend-item(v-if="group && (hasGroupPermission(group, 'group-instance-public-create') || hasGroupPermission(group, 'group-instance-plus-create') || hasGroupPermission(group, 'group-instance-open-create'))" v-for="group in API.currentUserGroups.values()" :key="group.id" :label="group.name" :value="group.id" style="height:auto;width:478px")
|
||||
.avatar
|
||||
img(v-lazy="group.iconUrl")
|
||||
.detail
|
||||
span.name(v-text="group.name")
|
||||
el-form-item(:label="$t('dialog.new_instance.roles')" v-if="newInstanceDialog.accessType === 'group' && newInstanceDialog.groupAccessType === 'members'")
|
||||
el-select(v-model="newInstanceDialog.roleIds" multiple clearable :placeholder="$t('dialog.new_instance.role_placeholder')" style="width:100%" @change="buildInstance")
|
||||
el-option-group(:label="$t('dialog.new_instance.role_placeholder')")
|
||||
el-option.x-friend-item(v-for="role in newInstanceDialog.selectedGroupRoles" :key="role.id" :label="role.name" :value="role.id" style="height:auto;width:478px")
|
||||
.detail
|
||||
span.name(v-text="role.name")
|
||||
template(v-if="newInstanceDialog.instanceCreated")
|
||||
el-form-item(:label="$t('dialog.new_instance.location')")
|
||||
el-input(v-model="newInstanceDialog.location" size="mini" readonly @click.native="$event.target.tagName === 'INPUT' && $event.target.select()")
|
||||
el-form-item(:label="$t('dialog.new_instance.url')")
|
||||
el-input(v-model="newInstanceDialog.url" size="mini" readonly)
|
||||
el-tab-pane(:label="$t('dialog.new_instance.legacy')")
|
||||
el-form(v-if="newInstanceDialog.visible" :model="newInstanceDialog" label-width="150px")
|
||||
el-form-item(:label="$t('dialog.new_instance.access_type')")
|
||||
el-radio-group(v-model="newInstanceDialog.accessType" size="mini" @change="buildLegacyInstance")
|
||||
el-radio-button(label="public") {{ $t('dialog.new_instance.access_type_public') }}
|
||||
el-radio-button(label="group") {{ $t('dialog.new_instance.access_type_group') }}
|
||||
el-radio-button(label="friends+") {{ $t('dialog.new_instance.access_type_friend_plus') }}
|
||||
el-radio-button(label="friends") {{ $t('dialog.new_instance.access_type_friend') }}
|
||||
el-radio-button(label="invite+") {{ $t('dialog.new_instance.access_type_invite_plus') }}
|
||||
el-radio-button(label="invite") {{ $t('dialog.new_instance.access_type_invite') }}
|
||||
el-form-item(:label="$t('dialog.new_instance.group_access_type')" v-if="newInstanceDialog.accessType === 'group'")
|
||||
el-radio-group(v-model="newInstanceDialog.groupAccessType" size="mini" @change="buildLegacyInstance")
|
||||
el-radio-button(label="members") {{ $t('dialog.new_instance.group_access_type_members') }}
|
||||
el-radio-button(label="plus") {{ $t('dialog.new_instance.group_access_type_plus') }}
|
||||
el-radio-button(label="public") {{ $t('dialog.new_instance.group_access_type_public') }}
|
||||
//- el-form-item(label="Strict" v-if="newInstanceDialog.accessType === 'friends' || newInstanceDialog.accessType === 'invite'")
|
||||
//- el-checkbox(v-model="newInstanceDialog.strict") Prevent non friends joining via URL/Instance ID
|
||||
el-form-item(:label="$t('dialog.new_instance.region')")
|
||||
el-radio-group(v-model="newInstanceDialog.region" size="mini" @change="buildLegacyInstance")
|
||||
el-radio-button(label="US West") {{ $t('dialog.new_instance.region_usw') }}
|
||||
el-radio-button(label="US East") {{ $t('dialog.new_instance.region_use') }}
|
||||
el-radio-button(label="Europe") {{ $t('dialog.new_instance.region_eu') }}
|
||||
el-radio-button(label="Japan") {{ $t('dialog.new_instance.region_jp') }}
|
||||
el-form-item(:label="$t('dialog.new_instance.ageGate')" v-if="newInstanceDialog.accessType === 'group'")
|
||||
el-checkbox(v-model="newInstanceDialog.ageGate" @change="buildInstance")
|
||||
el-form-item(:label="$t('dialog.new_instance.world_id')")
|
||||
el-input(v-model="newInstanceDialog.worldId" size="mini" @click.native="$event.target.tagName === 'INPUT' && $event.target.select()" @change="buildLegacyInstance")
|
||||
el-form-item(:label="$t('dialog.new_instance.instance_id')")
|
||||
el-input(v-model="newInstanceDialog.instanceName" :placeholder="$t('dialog.new_instance.instance_id_placeholder')" size="mini" @change="buildLegacyInstance")
|
||||
el-form-item(:label="$t('dialog.new_instance.instance_creator')" v-if="newInstanceDialog.accessType !== 'public' && newInstanceDialog.accessType !== 'group'")
|
||||
el-select(v-model="newInstanceDialog.userId" clearable :placeholder="$t('dialog.new_instance.instance_creator_placeholder')" filterable style="width:100%" @change="buildLegacyInstance")
|
||||
el-option-group(v-if="API.currentUser" :label="$t('side_panel.me')")
|
||||
el-option.x-friend-item(:label="API.currentUser.displayName" :value="API.currentUser.id" style="height:auto")
|
||||
.avatar(:class="userStatusClass(API.currentUser)")
|
||||
img(v-lazy="userImage(API.currentUser)")
|
||||
.detail
|
||||
span.name(v-text="API.currentUser.displayName")
|
||||
el-option-group(v-if="vipFriends.length" :label="$t('side_panel.favorite')")
|
||||
el-option.x-friend-item(v-for="friend in vipFriends" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto")
|
||||
template(v-if="friend.ref")
|
||||
.avatar(:class="userStatusClass(friend.ref)")
|
||||
img(v-lazy="userImage(friend.ref)")
|
||||
.detail
|
||||
span.name(v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
|
||||
span(v-else v-text="friend.id")
|
||||
el-option-group(v-if="onlineFriends.length" :label="$t('side_panel.online')")
|
||||
el-option.x-friend-item(v-for="friend in onlineFriends" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto")
|
||||
template(v-if="friend.ref")
|
||||
.avatar(:class="userStatusClass(friend.ref)")
|
||||
img(v-lazy="userImage(friend.ref)")
|
||||
.detail
|
||||
span.name(v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
|
||||
span(v-else v-text="friend.id")
|
||||
el-option-group(v-if="activeFriends.length" :label="$t('side_panel.active')")
|
||||
el-option.x-friend-item(v-for="friend in activeFriends" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto")
|
||||
template(v-if="friend.ref")
|
||||
.avatar
|
||||
img(v-lazy="userImage(friend.ref)")
|
||||
.detail
|
||||
span.name(v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
|
||||
span(v-else v-text="friend.id")
|
||||
el-option-group(v-if="offlineFriends.length" :label="$t('side_panel.offline')")
|
||||
el-option.x-friend-item(v-for="friend in offlineFriends" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto")
|
||||
template(v-if="friend.ref")
|
||||
.avatar
|
||||
img(v-lazy="userImage(friend.ref)")
|
||||
.detail
|
||||
span.name(v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
|
||||
span(v-else v-text="friend.id")
|
||||
el-form-item(:label="$t('dialog.new_instance.group_id')" v-if="newInstanceDialog.accessType === 'group'")
|
||||
el-select(v-model="newInstanceDialog.groupId" clearable :placeholder="$t('dialog.new_instance.group_placeholder')" filterable style="width:100%" @change="buildLegacyInstance")
|
||||
el-option-group(:label="$t('dialog.new_instance.group_placeholder')")
|
||||
el-option.x-friend-item(v-if="group" v-for="group in API.currentUserGroups.values()" :key="group.id" :label="group.name" :value="group.id" style="height:auto;width:478px")
|
||||
.avatar
|
||||
img(v-lazy="group.iconUrl")
|
||||
.detail
|
||||
span.name(v-text="group.name")
|
||||
el-form-item(:label="$t('dialog.new_instance.location')")
|
||||
el-input(v-model="newInstanceDialog.location" size="mini" readonly @click.native="$event.target.tagName === 'INPUT' && $event.target.select()")
|
||||
el-form-item(:label="$t('dialog.new_instance.url')")
|
||||
el-input(v-model="newInstanceDialog.url" size="mini" readonly)
|
||||
template(#footer v-if="newInstanceDialog.selectedTab === '0'")
|
||||
template(v-if="newInstanceDialog.instanceCreated")
|
||||
el-button(size="small" @click="copyInstanceUrl(newInstanceDialog.location)") {{ $t('dialog.new_instance.copy_url') }}
|
||||
el-button(size="small" @click="selfInvite(newInstanceDialog.location)") {{ $t('dialog.new_instance.self_invite') }}
|
||||
el-button(size="small" @click="showInviteDialog(newInstanceDialog.location)" :disabled="(newInstanceDialog.accessType === 'friends' || newInstanceDialog.accessType === 'invite') && newInstanceDialog.userId !== API.currentUser.id") {{ $t('dialog.new_instance.invite') }}
|
||||
el-button(type="primary" size="small" @click="showLaunchDialog(newInstanceDialog.location, newInstanceDialog.shortName)") {{ $t('dialog.new_instance.launch') }}
|
||||
template(v-else)
|
||||
el-button(type="primary" size="small" @click="createNewInstance()") {{ $t('dialog.new_instance.create_instance') }}
|
||||
template(#footer v-else-if="newInstanceDialog.selectedTab === '1'")
|
||||
el-button(size="small" @click="copyInstanceUrl(newInstanceDialog.location)") {{ $t('dialog.new_instance.copy_url') }}
|
||||
el-button(size="small" @click="selfInvite(newInstanceDialog.location)") {{ $t('dialog.new_instance.self_invite') }}
|
||||
el-button(size="small" @click="showInviteDialog(newInstanceDialog.location)" :disabled="(newInstanceDialog.accessType === 'friends' || newInstanceDialog.accessType === 'invite') && newInstanceDialog.userId !== API.currentUser.id") {{ $t('dialog.new_instance.invite') }}
|
||||
el-button(type="primary" size="small" @click="showLaunchDialog(newInstanceDialog.location, newInstanceDialog.shortName)") {{ $t('dialog.new_instance.launch') }}
|
||||
394
src/mixins/dialogs/openSourceSoftwareNotice.pug
Normal file
@@ -0,0 +1,394 @@
|
||||
mixin openSourceSoftwareNotice()
|
||||
//- dialog: open source software notice
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" :visible.sync="ossDialog" :title="$t('dialog.open_source.header')" width="650px")
|
||||
div(v-if="ossDialog" style="height:350px;overflow:hidden scroll;word-break:break-all")
|
||||
div
|
||||
span {{ $t('dialog.open_source.description') }}
|
||||
div(style="margin-top:15px")
|
||||
p(style="font-weight:bold") animate.css
|
||||
pre(style="font-size:12px;white-space:pre-line").
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 Daniel Eden
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
div(style="margin-top:15px")
|
||||
p(style="font-weight:bold") CefSharp
|
||||
pre(style="font-size:12px;white-space:pre-line").
|
||||
// Copyright © The CefSharp Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
//
|
||||
// * Neither the name of Google Inc. nor the name Chromium Embedded
|
||||
// Framework nor the name CefSharp nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
div(style="margin-top:15px")
|
||||
p(style="font-weight:bold") DiscordRichPresence
|
||||
pre(style="font-size:12px;white-space:pre-line").
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Lachee
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
div(style="margin-top:15px")
|
||||
p(style="font-weight:bold") element
|
||||
pre(style="font-size:12px;white-space:pre-line").
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-present ElemeFE
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
div(style="margin-top:15px")
|
||||
p(style="font-weight:bold") librsync.net
|
||||
pre(style="font-size:12px;white-space:pre-line").
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Brad Dodson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
div(style="margin-top:15px")
|
||||
p(style="font-weight:bold") Newtonsoft.Json
|
||||
pre(style="font-size:12px;white-space:pre-line").
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2007 James Newton-King
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
div(style="margin-top:15px")
|
||||
p(style="font-weight:bold") normalize
|
||||
pre(style="font-size:12px;white-space:pre-line").
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright © Nicolas Gallagher and Jonathan Neal
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
div(style="margin-top:15px")
|
||||
p(style="font-weight:bold") noty
|
||||
pre(style="font-size:12px;white-space:pre-line").
|
||||
Copyright (c) 2012 Nedim Arabacı
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
div(style="margin-top:15px")
|
||||
p(style="font-weight:bold") OpenVR SDK
|
||||
pre(style="font-size:12px;white-space:pre-line").
|
||||
Copyright (c) 2015, Valve Corporation
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
div(style="margin-top:15px")
|
||||
p(style="font-weight:bold") Twemoji
|
||||
pre(style="font-size:12px;white-space:pre-line").
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Twitter
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
div(style="margin-top:15px")
|
||||
p(style="font-weight:bold") SharpDX
|
||||
pre(style="font-size:12px;white-space:pre-line").
|
||||
Copyright (c) 2010-2014 SharpDX - Alexandre Mutel
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
div(style="margin-top:15px")
|
||||
p(style="font-weight:bold") vue
|
||||
pre(style="font-size:12px;white-space:pre-line").
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013-present, Yuxi (Evan) You
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
div(style="margin-top:15px")
|
||||
p(style="font-weight:bold") vue-data-tables
|
||||
pre(style="font-size:12px;white-space:pre-line").
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018 Leon Zhang
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
div(style="margin-top:15px")
|
||||
p(style="font-weight:bold") vue-lazyload
|
||||
pre(style="font-size:12px;white-space:pre-line").
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Awe
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
div(style="margin-top:15px")
|
||||
p(style="font-weight:bold") Encode Sans Font (from Dark Vanilla)
|
||||
pre(style="font-size:12px;white-space:pre-line").
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
Copyright (c) 2020 June 20, Impallari Type, Andres Torresi, Jacques Le Bailly
|
||||
(https://fonts.google.com/specimen/Encode+Sans),
|
||||
with Reserved Font Name: Encode Sans.
|
||||
|
||||
PREAMBLE:
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide development
|
||||
of collaborative font projects, to support the font creation efforts of academic
|
||||
and linguistic communities, and to provide a free and open framework in which
|
||||
fonts may be shared and improved in partnership with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and redistributed
|
||||
freely as long as they are not sold by themselves. The fonts, including any
|
||||
derivative works, can be bundled, embedded, redistributed and/or sold with any
|
||||
software provided that any reserved names are not used by derivative works.
|
||||
The fonts and derivatives, however, cannot be released under any other type of
|
||||
license. The requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
the Font Software, to use, study, copy, merge, embed, modify, redistribute, and
|
||||
sell modified and unmodified copies of the Font Software, subject to the
|
||||
following conditions:
|
||||
|
||||
1. Neither the Font Software nor any of its individual components, in Original or
|
||||
Modified Versions, may be sold by itself.
|
||||
|
||||
2. Original or Modified Versions of the Font Software may be bundled, redistributed
|
||||
and/or sold with any software, provided that each copy contains the above copyright
|
||||
notice and this license. These can be included either as stand-alone text files,
|
||||
human-readable headers or in the appropriate machine-readable metadata fields within
|
||||
text or binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3. No Modified Version of the Font Software may use the Reserved Font Name(s) unless
|
||||
explicit written permission is granted by the corresponding Copyright Holder. This
|
||||
restriction only applies to the primary font name as presented to the users.
|
||||
|
||||
4. The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall
|
||||
not be used to promote, endorse or advertise any Modified Version, except to
|
||||
acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with
|
||||
their explicit written permission.
|
||||
|
||||
5. The Font Software, modified or unmodified, in part or in whole, must be distributed
|
||||
entirely under this license, and must not be distributed under any other license.
|
||||
The requirement for fonts to remain under this license does not apply to any document
|
||||
created using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR
|
||||
OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL,
|
||||
OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER
|
||||
DEALINGS IN THE FONT SOFTWARE.
|
||||
75
src/mixins/dialogs/previousInstances.pug
Normal file
@@ -0,0 +1,75 @@
|
||||
mixin previousInstances()
|
||||
//- dialog Table: Previous Instances User
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="previousInstancesUserDialog" :visible.sync="previousInstancesUserDialog.visible" :title="$t('dialog.previous_instances.header')" width="1000px")
|
||||
span(v-text="previousInstancesUserDialog.userRef.displayName" style="font-size:14px")
|
||||
el-input(v-model="previousInstancesUserDialogTable.filters[0].value" :placeholder="$t('dialog.previous_instances.search_placeholder')" style="display:block;width:150px;margin-top:15px")
|
||||
data-tables(v-if="previousInstancesUserDialog.visible" v-bind="previousInstancesUserDialogTable" v-loading="previousInstancesUserDialog.loading" style="margin-top:10px")
|
||||
el-table-column(:label="$t('table.previous_instances.date')" prop="created_at" sortable width="170")
|
||||
template(v-once #default="scope")
|
||||
span {{ scope.row.created_at | formatDate('long') }}
|
||||
el-table-column(:label="$t('table.previous_instances.world')" prop="name" sortable)
|
||||
template(v-once #default="scope")
|
||||
location(:location="scope.row.location" :hint="scope.row.worldName" :grouphint="scope.row.groupName")
|
||||
el-table-column(:label="$t('table.previous_instances.instance_creator')" prop="location" width="170")
|
||||
template(v-once #default="scope")
|
||||
display-name(:userid="scope.row.$location.userId" :location="scope.row.$location.tag" :key="previousInstancesUserDialog.forceUpdate")
|
||||
el-table-column(:label="$t('table.previous_instances.time')" prop="time" width="100" sortable)
|
||||
template(v-once #default="scope")
|
||||
span(v-text="scope.row.timer")
|
||||
el-table-column(:label="$t('table.previous_instances.action')" width="90" align="right")
|
||||
template(v-once #default="scope")
|
||||
el-button(type="text" icon="el-icon-info" size="mini" @click="showLaunchDialog(scope.row.location)")
|
||||
el-button(type="text" icon="el-icon-tickets" size="mini" @click="showPreviousInstanceInfoDialog(scope.row.location)")
|
||||
el-button(v-if="shiftHeld" style="color:#f56c6c" type="text" icon="el-icon-close" size="mini" @click="deleteGameLogUserInstance(scope.row)")
|
||||
el-button(v-else type="text" icon="el-icon-close" size="mini" @click="deleteGameLogUserInstancePrompt(scope.row)")
|
||||
|
||||
//- dialog Table: Previous Instances World
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="previousInstancesWorldDialog" :visible.sync="previousInstancesWorldDialog.visible" :title="$t('dialog.previous_instances.header')" width="1000px")
|
||||
span(v-text="previousInstancesWorldDialog.worldRef.name" style="font-size:14px")
|
||||
el-input(v-model="previousInstancesWorldDialogTable.filters[0].value" :placeholder="$t('dialog.previous_instances.search_placeholder')" style="display:block;width:150px;margin-top:15px")
|
||||
data-tables(v-if="previousInstancesWorldDialog.visible" v-bind="previousInstancesWorldDialogTable" v-loading="previousInstancesWorldDialog.loading" style="margin-top:10px")
|
||||
el-table-column(:label="$t('table.previous_instances.date')" prop="created_at" sortable width="170")
|
||||
template(v-once #default="scope")
|
||||
span {{ scope.row.created_at | formatDate('long') }}
|
||||
el-table-column(:label="$t('table.previous_instances.instance_name')" prop="name")
|
||||
template(v-once #default="scope")
|
||||
location-world(:locationobject="scope.row.$location" :grouphint="scope.row.groupName" :currentuserid="API.currentUser.id")
|
||||
el-table-column(:label="$t('table.previous_instances.instance_creator')" prop="location")
|
||||
template(v-once #default="scope")
|
||||
display-name(:userid="scope.row.$location.userId" :location="scope.row.$location.tag" :key="previousInstancesWorldDialog.forceUpdate")
|
||||
el-table-column(:label="$t('table.previous_instances.time')" prop="time" width="100" sortable)
|
||||
template(v-once #default="scope")
|
||||
span(v-text="scope.row.timer")
|
||||
el-table-column(:label="$t('table.previous_instances.action')" width="90" align="right")
|
||||
template(v-once #default="scope")
|
||||
el-button(type="text" icon="el-icon-tickets" size="mini" @click="showPreviousInstanceInfoDialog(scope.row.location)")
|
||||
el-button(v-if="shiftHeld" style="color:#f56c6c" type="text" icon="el-icon-close" size="mini" @click="deleteGameLogWorldInstance(scope.row)")
|
||||
el-button(v-else type="text" icon="el-icon-close" size="mini" @click="deleteGameLogWorldInstancePrompt(scope.row)")
|
||||
|
||||
//- dialog Table: Previous Instance Info
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="previousInstanceInfoDialog" :visible.sync="previousInstanceInfoDialog.visible" :title="$t('dialog.previous_instances.info')" width="800px")
|
||||
location(:location="previousInstanceInfoDialog.$location.tag" style="font-size:14px")
|
||||
el-input(v-model="previousInstanceInfoDialogTable.filters[0].value" placeholder="Search" style="display:block;width:150px;margin-top:15px")
|
||||
data-tables(v-if="previousInstanceInfoDialog.visible" v-bind="previousInstanceInfoDialogTable" v-loading="previousInstanceInfoDialog.loading" style="margin-top:10px")
|
||||
el-table-column(:label="$t('table.previous_instances.date')" prop="created_at" sortable width="120")
|
||||
template(v-once #default="scope")
|
||||
el-tooltip(placement="left")
|
||||
template(#content)
|
||||
span {{ scope.row.created_at | formatDate('long') }}
|
||||
span {{ scope.row.created_at | formatDate('short') }}
|
||||
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.previous_instances.display_name')" prop="displayName" sortable)
|
||||
template(v-once #default="scope")
|
||||
span.x-link(v-text="scope.row.displayName" @click="lookupUser(scope.row)")
|
||||
el-table-column(:label="$t('table.previous_instances.time')" prop="time" width="90" sortable)
|
||||
template(v-once #default="scope")
|
||||
span(v-text="scope.row.timer")
|
||||
el-table-column(:label="$t('table.previous_instances.count')" prop="count" width="90" sortable)
|
||||
template(v-once #default="scope")
|
||||
span(v-text="scope.row.count")
|
||||
54
src/mixins/dialogs/screenshotMetadata.pug
Normal file
@@ -0,0 +1,54 @@
|
||||
mixin screenshotMetadata()
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="screenshotMetadataDialog" :visible.sync="screenshotMetadataDialog.visible" :title="$t('dialog.screenshot_metadata.header')" width="1050px")
|
||||
div(v-if="screenshotMetadataDialog.visible" v-loading="screenshotMetadataDialog.loading" @dragover.prevent @dragenter.prevent @drop="handleDrop" style="-webkit-app-region: drag")
|
||||
span(style="margin-left:5px;color:#909399;font-family:monospace") {{ $t('dialog.screenshot_metadata.drag') }}
|
||||
br
|
||||
br
|
||||
el-button(size="small" icon="el-icon-folder-opened" @click="getAndDisplayScreenshotFromFile()") {{ $t('dialog.screenshot_metadata.browse') }}
|
||||
el-button(size="small" icon="el-icon-picture-outline" @click="getAndDisplayLastScreenshot()") {{ $t('dialog.screenshot_metadata.last_screenshot') }}
|
||||
el-button(size="small" icon="el-icon-copy-document" @click="copyImageToClipboard(screenshotMetadataDialog.metadata.filePath)") {{ $t('dialog.screenshot_metadata.copy_image') }}
|
||||
el-button(size="small" icon="el-icon-folder" @click="openImageFolder(screenshotMetadataDialog.metadata.filePath)") {{ $t('dialog.screenshot_metadata.open_folder') }}
|
||||
el-button(v-if="API.currentUser.$isVRCPlus && screenshotMetadataDialog.metadata.filePath" size="small" icon="el-icon-upload2" @click="uploadScreenshotToGallery") {{ $t('dialog.screenshot_metadata.upload') }}
|
||||
br
|
||||
br
|
||||
//- Search bar input
|
||||
el-input(v-model="screenshotMetadataDialog.search" size="small" placeholder="Search" clearable style="width:200px" @input="screenshotMetadataSearch")
|
||||
//- Search index/total label
|
||||
template(v-if="screenshotMetadataDialog.searchIndex != null")
|
||||
span(style="white-space:pre-wrap;font-size:12px;margin-left:10px") {{ (screenshotMetadataDialog.searchIndex + 1) + "/" + screenshotMetadataDialog.searchResults.length }}
|
||||
//- Search type dropdown
|
||||
el-select(v-model="screenshotMetadataDialog.searchType" size="small" placeholder="Search Type" style="width:150px;margin-left:10px" @change="screenshotMetadataSearch")
|
||||
el-option(v-for="type in screenshotMetadataDialog.searchTypes" :key="type" :label="type" :value="type")
|
||||
br
|
||||
br
|
||||
span(v-text="screenshotMetadataDialog.metadata.fileName")
|
||||
br
|
||||
template(v-if="screenshotMetadataDialog.metadata.note")
|
||||
span(v-text="screenshotMetadataDialog.metadata.note")
|
||||
br
|
||||
span(v-if="screenshotMetadataDialog.metadata.dateTime" style="margin-right:5px") {{ screenshotMetadataDialog.metadata.dateTime | formatDate('long') }}
|
||||
span(v-if="screenshotMetadataDialog.metadata.fileResolution" v-text="screenshotMetadataDialog.metadata.fileResolution" style="margin-right:5px")
|
||||
el-tag(v-if="screenshotMetadataDialog.metadata.fileSize" type="info" effect="plain" size="mini" v-text="screenshotMetadataDialog.metadata.fileSize")
|
||||
br
|
||||
location(v-if="screenshotMetadataDialog.metadata.world" :location="screenshotMetadataDialog.metadata.world.instanceId" :hint="screenshotMetadataDialog.metadata.world.name")
|
||||
br
|
||||
display-name(v-if="screenshotMetadataDialog.metadata.author" :userid="screenshotMetadataDialog.metadata.author.id" :hint="screenshotMetadataDialog.metadata.author.displayName" style="color:#909399;font-family:monospace")
|
||||
br
|
||||
el-carousel(ref="screenshotMetadataCarousel" :interval="0" initial-index="1" indicator-position="none" arrow="always" height="600px" style="margin-top:10px" @change="screenshotMetadataCarouselChange")
|
||||
el-carousel-item
|
||||
span(placement="top" width="700px" trigger="click")
|
||||
img.x-link(slot="reference" :src="screenshotMetadataDialog.metadata.previousFilePath" style="width:100%;height:100%;object-fit:contain")
|
||||
el-carousel-item
|
||||
span(placement="top" width="700px" trigger="click" @click="showFullscreenImageDialog(screenshotMetadataDialog.metadata.filePath)")
|
||||
img.x-link(slot="reference" :src="screenshotMetadataDialog.metadata.filePath" style="width:100%;height:100%;object-fit:contain")
|
||||
el-carousel-item
|
||||
span(placement="top" width="700px" trigger="click")
|
||||
img.x-link(slot="reference" :src="screenshotMetadataDialog.metadata.nextFilePath" style="width:100%;height:100%;object-fit:contain")
|
||||
br
|
||||
template(v-if="screenshotMetadataDialog.metadata.error")
|
||||
pre(v-text="screenshotMetadataDialog.metadata.error" style="white-space:pre-wrap;font-size:12px")
|
||||
br
|
||||
span(v-for="user in screenshotMetadataDialog.metadata.players" style="margin-top:5px")
|
||||
span.x-link(v-text="user.displayName" @click="lookupUser(user)")
|
||||
span(v-if="user.pos" v-text="'('+user.pos.x+', '+user.pos.y+', '+user.pos.z+')'" style="margin-left:5px;color:#909399;font-family:monospace")
|
||||
br
|
||||
211
src/mixins/dialogs/settings.pug
Normal file
@@ -0,0 +1,211 @@
|
||||
mixin settings()
|
||||
//- dialog: VRChat Config JSON
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="VRChatConfigDialog" :visible.sync="VRChatConfigDialog.visible" :title="$t('dialog.config_json.header')" width="420px")
|
||||
div(style='font-size:12px;word-break:keep-all')
|
||||
| {{ $t('dialog.config_json.description1') }} #[br]
|
||||
| {{ $t('dialog.config_json.description2') }}
|
||||
br
|
||||
span(style="margin-right:5px") {{ $t('dialog.config_json.cache_size') }}
|
||||
span(v-text="VRChatUsedCacheSize")
|
||||
span /
|
||||
span(v-text="VRChatTotalCacheSize")
|
||||
span GB
|
||||
el-tooltip(placement="top" :content="$t('dialog.config_json.refresh')" :disabled="hideTooltips")
|
||||
el-button(type="default" :loading="VRChatCacheSizeLoading" @click="getVRChatCacheSize" size="small" icon="el-icon-refresh" circle style="margin-left:5px")
|
||||
div(style="margin-top:10px")
|
||||
span(style="margin-right:5px") {{ $t('dialog.config_json.delete_all_cache') }}
|
||||
el-button(size="small" style="margin-left:5px" icon="el-icon-delete" @click="showDeleteAllVRChatCacheConfirm()") {{ $t('dialog.config_json.delete_cache') }}
|
||||
div(style="margin-top:10px")
|
||||
span(style="margin-right:5px") {{ $t('dialog.config_json.delete_old_cache') }}
|
||||
el-button(size="small" style="margin-left:5px" icon="el-icon-folder-delete" @click="sweepVRChatCache()") {{ $t('dialog.config_json.sweep_cache') }}
|
||||
div(style="display:inline-block;margin-top:10px" v-for="(item, value) in VRChatConfigList" :key="value")
|
||||
span(v-text="item.name" style="word-break:keep-all")
|
||||
|:
|
||||
el-input(v-model="VRChatConfigFile[value]" :placeholder="item.default" size="mini" :type="item.type?item.type:'text'" :min="item.min" :max="item.max" style="margin-top:5px")
|
||||
br
|
||||
div(style="display:inline-block;margin-top:10px")
|
||||
span {{ $t('dialog.config_json.camera_resolution') }}
|
||||
br
|
||||
el-dropdown(@command="(command) => setVRChatCameraResolution(command)" size="small" trigger="click" style="margin-top:5px")
|
||||
el-button(size="small")
|
||||
span #[span(v-text="getVRChatCameraResolution()")] #[i.el-icon-arrow-down.el-icon--right]
|
||||
el-dropdown-menu(#default="dropdown")
|
||||
el-dropdown-item(v-for="row in VRChatCameraResolutions" :key="row.index" v-text="row.name" :command="row")
|
||||
br
|
||||
div(style="display:inline-block;margin-top:10px")
|
||||
span {{ $t('dialog.config_json.spout_resolution') }}
|
||||
br
|
||||
el-dropdown(@command="(command) => setVRChatSpoutResolution(command)" size="small" trigger="click" style="margin-top:5px")
|
||||
el-button(size="small")
|
||||
span #[span(v-text="getVRChatSpoutResolution()")] #[i.el-icon-arrow-down.el-icon--right]
|
||||
el-dropdown-menu(#default="dropdown")
|
||||
el-dropdown-item(v-for="row in VRChatScreenshotResolutions" :key="row.index" v-text="row.name" :command="row")
|
||||
br
|
||||
div(style="display:inline-block;margin-top:10px")
|
||||
span {{ $t('dialog.config_json.screenshot_resolution') }}
|
||||
br
|
||||
el-dropdown(@command="(command) => setVRChatScreenshotResolution(command)" size="small" trigger="click" style="margin-top:5px")
|
||||
el-button(size="small")
|
||||
span #[span(v-text="getVRChatScreenshotResolution()")] #[i.el-icon-arrow-down.el-icon--right]
|
||||
el-dropdown-menu(#default="dropdown")
|
||||
el-dropdown-item(v-for="row in VRChatScreenshotResolutions" :key="row.index" v-text="row.name" :command="row")
|
||||
el-checkbox(v-model="VRChatConfigFile.picture_output_split_by_date" style="margin-top:5px;display:block" :checked="true") {{ $t('dialog.config_json.picture_sort_by_date') }}
|
||||
el-checkbox(v-model="VRChatConfigFile.disableRichPresence" style="margin-top:5px;display:block") {{ $t('dialog.config_json.disable_discord_presence') }}
|
||||
template(#footer)
|
||||
div(style="display:flex;align-items:center;justify-content:space-between")
|
||||
div
|
||||
el-button(size="small" @click="openExternalLink('https://docs.vrchat.com/docs/configuration-file')") {{ $t('dialog.config_json.vrchat_docs') }}
|
||||
div
|
||||
el-button(size="small" @click="VRChatConfigDialog.visible = false") {{ $t('dialog.config_json.cancel') }}
|
||||
el-button(size="small" type="primary" :disabled="VRChatConfigDialog.loading" @click="saveVRChatConfigFile") {{ $t('dialog.config_json.save') }}
|
||||
|
||||
//- dialog: YouTube Api Dialog
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="youTubeApiDialog" :visible.sync="youTubeApiDialog.visible" :title="$t('dialog.youtube_api.header')" width="400px")
|
||||
div(style='font-size:12px;')
|
||||
| {{ $t('dialog.youtube_api.description') }} #[br]
|
||||
el-input(type="textarea" v-model="youTubeApiKey" :placeholder="$t('dialog.youtube_api.placeholder')" maxlength="39" show-word-limit style="display:block;margin-top:10px")
|
||||
template(#footer)
|
||||
div(style="display:flex")
|
||||
el-button(size="small" @click="openExternalLink('https://rapidapi.com/blog/how-to-get-youtube-api-key/')") {{ $t('dialog.youtube_api.guide') }}
|
||||
el-button(type="primary" size="small" @click="testYouTubeApiKey" style="margin-left:auto") {{ $t('dialog.youtube_api.save') }}
|
||||
|
||||
//- dialog: Discord username list
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" :visible.sync="discordNamesDialogVisible" :title="$t('dialog.discord_names.header')" width="650px")
|
||||
div(style='font-size:12px;')
|
||||
| {{ $t('dialog.discord_names.description') }}
|
||||
el-input(type="textarea" v-if="discordNamesDialogVisible" v-model="discordNamesContent" size="mini" rows="15" resize="none" readonly style="margin-top:15px")
|
||||
|
||||
//- dialog: Note export dialog
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="noteExportDialog" :visible.sync="noteExportDialog.visible" :title="$t('dialog.note_export.header')" width="1000px")
|
||||
div(style="font-size:12px")
|
||||
| {{ $t('dialog.note_export.description1') }} #[br]
|
||||
| {{ $t('dialog.note_export.description2') }} #[br]
|
||||
| {{ $t('dialog.note_export.description3') }} #[br]
|
||||
| {{ $t('dialog.note_export.description4') }} #[br]
|
||||
| {{ $t('dialog.note_export.description5') }} #[br]
|
||||
| {{ $t('dialog.note_export.description6') }} #[br]
|
||||
| {{ $t('dialog.note_export.description7') }} #[br]
|
||||
| {{ $t('dialog.note_export.description8') }} #[br]
|
||||
el-button(size="small" @click="updateNoteExportDialog" :disabled="noteExportDialog.loading" style="margin-top:10px") {{ $t('dialog.note_export.refresh') }}
|
||||
el-button(size="small" @click="exportNoteExport" :disabled="noteExportDialog.loading" style="margin-top:10px") {{ $t('dialog.note_export.export') }}
|
||||
el-button(v-if="noteExportDialog.loading" size="small" @click="cancelNoteExport" style="margin-top:10px") {{ $t('dialog.note_export.cancel') }}
|
||||
span(v-if="noteExportDialog.loading" style="margin:10px") #[i.el-icon-loading(style="margin-right:5px")] {{ $t('dialog.note_export.progress') }} {{ noteExportDialog.progress }}/{{ noteExportDialog.progressTotal }}
|
||||
template(v-if="noteExportDialog.errors")
|
||||
el-button(size="small" @click="noteExportDialog.errors = ''") {{ $t('dialog.note_export.clear_errors') }}
|
||||
h2(style="font-weight:bold;margin:0") {{ $t('dialog.note_export.errors') }}
|
||||
pre(v-text="noteExportDialog.errors" style="white-space:pre-wrap;font-size:12px")
|
||||
data-tables(v-if="noteExportDialog.visible" v-bind="noteExportTable" v-loading="noteExportDialog.loading" style="margin-top:10px")
|
||||
el-table-column(:label="$t('table.import.image')" width="70" prop="currentAvatarThumbnailImageUrl")
|
||||
template(v-once #default="scope")
|
||||
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.import.name')" width="170" prop="name")
|
||||
template(v-once #default="scope")
|
||||
span.x-link(v-text="scope.row.name" @click="showUserDialog(scope.row.id)")
|
||||
el-table-column(:label="$t('table.import.note')" prop="memo")
|
||||
template(v-once #default="scope")
|
||||
el-input(v-model="scope.row.memo" type="textarea" maxlength="256" show-word-limit :rows="2" :autosize="{ minRows: 1, maxRows: 10 }" size="mini" resize="none")
|
||||
el-table-column(:label="$t('table.import.skip_export')" width="90" align="right")
|
||||
template(v-once #default="scope")
|
||||
el-button(type="text" icon="el-icon-close" size="mini" @click="removeFromNoteExportTable(scope.row)")
|
||||
|
||||
//- dialog: chatbox blacklist
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="chatboxBlacklistDialog" :visible.sync="chatboxBlacklistDialog.visible" :title="$t('dialog.chatbox_blacklist.header')" width="600px")
|
||||
div(v-loading="chatboxBlacklistDialog.loading" v-if="chatboxBlacklistDialog.visible")
|
||||
h2 {{ $t('dialog.chatbox_blacklist.keyword_blacklist') }}
|
||||
el-input(v-for="(item, index) in chatboxBlacklist" :key="index" :value="item" v-model="chatboxBlacklist[index]" size="small" style="margin-top:5px" @change="saveChatboxBlacklist")
|
||||
el-button(slot="append" icon="el-icon-delete" @click="chatboxBlacklist.splice(index, 1); saveChatboxBlacklist()")
|
||||
el-button(@click="chatboxBlacklist.push('')" size="mini" style="margin-top:5px") {{ $t('dialog.chatbox_blacklist.add_item') }}
|
||||
br
|
||||
h2 {{ $t('dialog.chatbox_blacklist.user_blacklist') }}
|
||||
el-tag(v-for="user in chatboxUserBlacklist" type="info" disable-transitions="true" :key="user[0]" style="margin-right:5px;margin-top:5px" closable @close="deleteChatboxUserBlacklist(user[0])")
|
||||
span {{user[1]}}
|
||||
|
||||
//- dialog: Notification position
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="notificationPositionDialog" :visible.sync="notificationPositionDialog.visible" :title="$t('dialog.notification_position.header')" width="400px")
|
||||
div(style='font-size:12px;')
|
||||
| {{ $t('dialog.notification_position.description') }}
|
||||
svg.notification-position(version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 300 200" style="margin-top:15px;" xml:space="preserve")
|
||||
path(style="fill:black;" d="M291.89,5A3.11,3.11,0,0,1,295,8.11V160.64a3.11,3.11,0,0,1-3.11,3.11H8.11A3.11,3.11,0,0,1,5,160.64V8.11A3.11,3.11,0,0,1,8.11,5H291.89m0-5H8.11A8.11,8.11,0,0,0,0,8.11V160.64a8.11,8.11,0,0,0,8.11,8.11H291.89a8.11,8.11,0,0,0,8.11-8.11V8.11A8.11,8.11,0,0,0,291.89,0Z")
|
||||
rect(style="fill:#c4c4c4;" x="5" y="5" width="290" height="158.75" rx="2.5")
|
||||
el-radio-group(v-model="notificationPosition" size="mini" @change="changeNotificationPosition")
|
||||
el-radio(label="topLeft" v-model="notificationPosition" style="margin:0;position:absolute;left:35px;top:120px;")
|
||||
el-radio(label="top" v-model="notificationPosition" style="margin:0;position:absolute;left:195px;top:120px;")
|
||||
el-radio(label="topRight" v-model="notificationPosition" style="margin:0;position:absolute;right:25px;top:120px;")
|
||||
el-radio(label="centerLeft" v-model="notificationPosition" style="margin:0;position:absolute;left:35px;top:200px;")
|
||||
el-radio(label="center" v-model="notificationPosition" style="margin:0;position:absolute;left:195px;top:200px;")
|
||||
el-radio(label="centerRight" v-model="notificationPosition" style="margin:0;position:absolute;right:25px;top:200px;")
|
||||
el-radio(label="bottomLeft" v-model="notificationPosition" style="margin:0;position:absolute;left:35px;top:280px;")
|
||||
el-radio(label="bottom" v-model="notificationPosition" style="margin:0;position:absolute;left:195px;top:280px;")
|
||||
el-radio(label="bottomRight" v-model="notificationPosition" style="margin:0;position:absolute;right:25px;top:280px;")
|
||||
template(#footer)
|
||||
div(style="display:flex")
|
||||
el-button(type="primary" size="small" style="margin-left:auto" @click="notificationPositionDialog.visible = false") {{ $t('dialog.notification_position.ok') }}
|
||||
|
||||
//- dialog: avatar database provider
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="avatarProviderDialog" :visible.sync="avatarProviderDialog.visible" :title="$t('dialog.avatar_database_provider.header')" width="600px")
|
||||
div
|
||||
el-input(v-for="(provider, index) in avatarRemoteDatabaseProviderList" :key="index" :value="provider" v-model="avatarRemoteDatabaseProviderList[index]" @change="saveAvatarProviderList" size="small" style="margin-top:5px")
|
||||
el-button(slot="append" icon="el-icon-delete" @click="removeAvatarProvider(provider)")
|
||||
el-button(@click="avatarRemoteDatabaseProviderList.push('')" size="mini" style="margin-top:5px") {{ $t('dialog.avatar_database_provider.add_provider') }}
|
||||
|
||||
//- dialog: Registry Auto Backup
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @closed="clearVrcRegistryDialog" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="registryBackupDialog" :visible.sync="registryBackupDialog.visible" :title="$t('dialog.registry_backup.header')" width="600px")
|
||||
div(v-if="registryBackupDialog.visible" style="margin-top:10px")
|
||||
div.options-container(style="padding:0")
|
||||
div.options-container-item(style="display:flex;align-items:center;justify-content:space-between")
|
||||
span.name(style="margin-right:24px") {{ $t('dialog.registry_backup.auto_backup') }}
|
||||
el-switch(v-model="vrcRegistryAutoBackup" @change="saveVrcRegistryAutoBackup")
|
||||
data-tables(v-bind="registryBackupTable" style="margin-top:10px")
|
||||
el-table-column(:label="$t('dialog.registry_backup.name')" prop="name")
|
||||
el-table-column(:label="$t('dialog.registry_backup.date')" prop="date")
|
||||
template(v-once #default="scope")
|
||||
span {{ scope.row.date | formatDate('long') }}
|
||||
el-table-column(:label="$t('dialog.registry_backup.action')" width="90" align="right")
|
||||
template(v-once #default="scope")
|
||||
el-tooltip(placement="top" :content="$t('dialog.registry_backup.restore')" :disabled="hideTooltips")
|
||||
el-button(type="text" icon="el-icon-upload2" size="mini" @click="restoreVrcRegistryBackup(scope.row)")
|
||||
el-tooltip(placement="top" :content="$t('dialog.registry_backup.save_to_file')" :disabled="hideTooltips")
|
||||
el-button(type="text" icon="el-icon-download" size="mini" @click="saveVrcRegistryBackupToFile(scope.row)")
|
||||
el-tooltip(placement="top" :content="$t('dialog.registry_backup.delete')" :disabled="hideTooltips")
|
||||
el-button(type="text" icon="el-icon-delete" size="mini" @click="deleteVrcRegistryBackup(scope.row)")
|
||||
div(style="display:flex;align-items:center;justify-content:space-between;margin-top:10px")
|
||||
el-button(type="danger" @click="deleteVrcRegistry" size="small") {{ $t('dialog.registry_backup.reset') }}
|
||||
div
|
||||
el-button(@click="promptVrcRegistryBackupName" size="small") {{ $t('dialog.registry_backup.backup') }}
|
||||
el-button(@click="restoreVrcRegistryFromFile" size="small") {{ $t('dialog.registry_backup.restore_from_file') }}
|
||||
|
||||
|
||||
//- dialog: Enable primary password
|
||||
el-dialog.x-dialog(
|
||||
:visible.sync="enablePrimaryPasswordDialog.visible"
|
||||
:before-close="enablePrimaryPasswordDialog.beforeClose"
|
||||
ref="primaryPasswordDialog"
|
||||
:close-on-click-modal="false"
|
||||
:title="$t('dialog.primary_password.header')"
|
||||
width="400px"
|
||||
)
|
||||
el-input(
|
||||
v-model="enablePrimaryPasswordDialog.password"
|
||||
:placeholder="$t('dialog.primary_password.password_placeholder')"
|
||||
type="password"
|
||||
size="mini"
|
||||
maxlength="32"
|
||||
show-password
|
||||
autofocus
|
||||
)
|
||||
el-input(
|
||||
v-model="enablePrimaryPasswordDialog.rePassword"
|
||||
:placeholder="$t('dialog.primary_password.re_input_placeholder')"
|
||||
type="password"
|
||||
style="margin-top:5px"
|
||||
size="mini"
|
||||
maxlength="32"
|
||||
show-password
|
||||
)
|
||||
template(#footer)
|
||||
el-button(
|
||||
type="primary" size="small" @click="setPrimaryPassword"
|
||||
:disabled="enablePrimaryPasswordDialog.password.length===0||enablePrimaryPasswordDialog.password!==enablePrimaryPasswordDialog.rePassword"
|
||||
) {{ $t('dialog.primary_password.ok') }}
|
||||
67
src/mixins/dialogs/tags.pug
Normal file
@@ -0,0 +1,67 @@
|
||||
mixin tags()
|
||||
//- dialog: Set World Tags
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="setWorldTagsDialog" :visible.sync="setWorldTagsDialog.visible" :title="$t('dialog.set_world_tags.header')" width="400px")
|
||||
el-checkbox(v-model="setWorldTagsDialog.avatarScalingDisabled") {{ $t('dialog.set_world_tags.avatar_scaling_disabled') }}
|
||||
br
|
||||
el-checkbox(v-model="setWorldTagsDialog.focusViewDisabled") {{ $t('dialog.set_world_tags.focus_view_disabled') }}
|
||||
br
|
||||
el-checkbox(v-model="setWorldTagsDialog.stickersDisabled") {{ $t('dialog.set_world_tags.stickers_disabled') }}
|
||||
br
|
||||
el-checkbox(v-model="setWorldTagsDialog.debugAllowed") {{ $t('dialog.set_world_tags.enable_debugging') }}
|
||||
div(style='font-size:12px;margin-top:10px')
|
||||
| {{ $t('dialog.set_world_tags.author_tags') }} #[br]
|
||||
el-input(type="textarea" v-model="setWorldTagsDialog.authorTags" size="mini" show-word-limit :autosize="{ minRows:2, maxRows:5 }" placeholder="" style="margin-top:10px")
|
||||
div(style='font-size:12px;margin-top:10px')
|
||||
| {{ $t('dialog.set_world_tags.content_tags') }} #[br]
|
||||
el-checkbox(v-model="setWorldTagsDialog.contentHorror") {{ $t('dialog.set_world_tags.content_horror') }}
|
||||
br
|
||||
el-checkbox(v-model="setWorldTagsDialog.contentGore") {{ $t('dialog.set_world_tags.content_gore') }}
|
||||
br
|
||||
el-checkbox(v-model="setWorldTagsDialog.contentViolence") {{ $t('dialog.set_world_tags.content_violence') }}
|
||||
br
|
||||
el-checkbox(v-model="setWorldTagsDialog.contentAdult") {{ $t('dialog.set_world_tags.content_adult') }}
|
||||
br
|
||||
el-checkbox(v-model="setWorldTagsDialog.contentSex") {{ $t('dialog.set_world_tags.content_sex') }}
|
||||
//- el-input(type="textarea" v-model="setWorldTagsDialog.contentTags" size="mini" show-word-limit :autosize="{ minRows:2, maxRows:5 }" placeholder="" style="margin-top:10px")
|
||||
template(#footer)
|
||||
div(style="display:flex")
|
||||
el-button(size="small" @click="setWorldTagsDialog.visible = false") {{ $t('dialog.set_world_tags.cancel') }}
|
||||
el-button(type="primary" size="small" @click="saveSetWorldTagsDialog") {{ $t('dialog.set_world_tags.save') }}
|
||||
|
||||
//- dialog: Set Avatar Tags
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="setAvatarTagsDialog" :visible.sync="setAvatarTagsDialog.visible" :title="$t('dialog.set_avatar_tags.header')" width="770px")
|
||||
template(v-if="setAvatarTagsDialog.visible")
|
||||
el-checkbox(v-model="setAvatarTagsDialog.contentHorror" @change="updateSelectedAvatarTags") {{ $t('dialog.set_avatar_tags.content_horror') }}
|
||||
br
|
||||
el-checkbox(v-model="setAvatarTagsDialog.contentGore" @change="updateSelectedAvatarTags") {{ $t('dialog.set_avatar_tags.content_gore') }}
|
||||
br
|
||||
el-checkbox(v-model="setAvatarTagsDialog.contentViolence" @change="updateSelectedAvatarTags") {{ $t('dialog.set_avatar_tags.content_violence') }}
|
||||
br
|
||||
el-checkbox(v-model="setAvatarTagsDialog.contentAdult" @change="updateSelectedAvatarTags") {{ $t('dialog.set_avatar_tags.content_adult') }}
|
||||
br
|
||||
el-checkbox(v-model="setAvatarTagsDialog.contentSex" @change="updateSelectedAvatarTags") {{ $t('dialog.set_avatar_tags.content_sex') }}
|
||||
br
|
||||
el-input(v-model="setAvatarTagsDialog.selectedTagsCsv" @input="updateInputAvatarTags" size="mini" :autosize="{ minRows:2, maxRows:5 }" :placeholder="$t('dialog.set_avatar_tags.custom_tags_placeholder')" style="margin-top:10px")
|
||||
template(v-if="setAvatarTagsDialog.ownAvatars.length === setAvatarTagsDialog.selectedCount")
|
||||
el-button(size="small" @click="setAvatarTagsSelectToggle") {{ $t('dialog.set_avatar_tags.select_none') }}
|
||||
template(v-else)
|
||||
el-button(size="small" @click="setAvatarTagsSelectToggle") {{ $t('dialog.set_avatar_tags.select_all') }}
|
||||
span(style="margin-left:5px") {{ setAvatarTagsDialog.selectedCount }} / {{ setAvatarTagsDialog.ownAvatars.length }}
|
||||
span(v-if="setAvatarTagsDialog.loading" style="margin-left:5px")
|
||||
i.el-icon-loading
|
||||
br
|
||||
.x-friend-list(style="margin-top:10px;min-height:60px;max-height:280px")
|
||||
.x-friend-item(v-for="avatar in setAvatarTagsDialog.ownAvatars" :key="setAvatarTagsDialog.forceUpdate" @click="showAvatarDialog(avatar.id)" class="x-friend-item-border" style="width:350px")
|
||||
.avatar
|
||||
img(v-if="avatar.thumbnailImageUrl" v-lazy="avatar.thumbnailImageUrl")
|
||||
.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.$tagString")
|
||||
el-button(type="text" size="mini" @click.stop style="margin-left:5px")
|
||||
el-checkbox(v-model="avatar.$selected" @change="updateAvatarTagsSelection")
|
||||
template(#footer)
|
||||
el-button(size="small" @click="setAvatarTagsDialog.visible = false") {{ $t('dialog.set_avatar_tags.cancel') }}
|
||||
el-button(type="primary" size="small" @click="saveSetAvatarTagsDialog") {{ $t('dialog.set_avatar_tags.save') }}
|
||||
515
src/mixins/dialogs/userDialog.pug
Normal file
@@ -0,0 +1,515 @@
|
||||
mixin userDialog()
|
||||
el-dialog.x-dialog.x-user-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="userDialog" :visible.sync="userDialog.visible" :show-close="false" width="770px")
|
||||
div(v-loading="userDialog.loading")
|
||||
div(style="display:flex")
|
||||
el-popover(v-if="userDialog.ref.profilePicOverrideThumbnail || userDialog.ref.profilePicOverride" placement="right" width="500px" trigger="click")
|
||||
template(slot="reference")
|
||||
img.x-link(v-if="userDialog.ref.profilePicOverrideThumbnail" v-lazy="userDialog.ref.profilePicOverrideThumbnail" style="flex:none;height:120px;width:213.33px;border-radius:12px;object-fit:cover")
|
||||
img.x-link(v-else v-lazy="userDialog.ref.profilePicOverride" style="flex:none;height:120px;width:213.33px;border-radius:12px;object-fit:cover")
|
||||
img.x-link(v-lazy="userDialog.ref.profilePicOverride" style="height:400px" @click="showFullscreenImageDialog(userDialog.ref.profilePicOverride)")
|
||||
el-popover(v-else placement="right" width="500px" trigger="click")
|
||||
img.x-link(slot="reference" v-lazy="userDialog.ref.currentAvatarThumbnailImageUrl" style="flex:none;height:120px;width:160px;border-radius:12px;object-fit:cover")
|
||||
img.x-link(v-lazy="userDialog.ref.currentAvatarImageUrl" style="height:500px" @click="showFullscreenImageDialog(userDialog.ref.currentAvatarImageUrl)")
|
||||
div(style="flex:1;display:flex;align-items:center;margin-left:15px")
|
||||
div(style="flex:1")
|
||||
div
|
||||
el-tooltip(v-if="userDialog.ref.status" placement="top")
|
||||
template(#content)
|
||||
span(v-if="userDialog.ref.state === 'active'") {{ $t('dialog.user.status.active') }}
|
||||
span(v-else-if="userDialog.ref.state === 'offline'") {{ $t('dialog.user.status.offline') }}
|
||||
span(v-else-if="userDialog.ref.status === 'active'") {{ $t('dialog.user.status.online') }}
|
||||
span(v-else-if="userDialog.ref.status === 'join me'") {{ $t('dialog.user.status.join_me') }}
|
||||
span(v-else-if="userDialog.ref.status === 'ask me'") {{ $t('dialog.user.status.ask_me') }}
|
||||
span(v-else-if="userDialog.ref.status === 'busy'") {{ $t('dialog.user.status.busy') }}
|
||||
span(v-else) {{ $t('dialog.user.status.offline') }}
|
||||
i.x-user-status(:class="userStatusClass(userDialog.ref)")
|
||||
template(v-if="userDialog.previousDisplayNames.length > 0")
|
||||
el-tooltip(placement="bottom")
|
||||
template(#content)
|
||||
span {{ $t('dialog.user.previous_display_names') }}
|
||||
div(v-for="displayName in userDialog.previousDisplayNames" placement="top")
|
||||
span(v-text="displayName")
|
||||
i.el-icon-caret-bottom
|
||||
el-popover(placement="top" trigger="click")
|
||||
span.dialog-title(slot="reference" v-text="userDialog.ref.displayName" style="margin-left:5px;margin-right:5px;cursor:pointer")
|
||||
span(style="display:block;text-align:center;font-family:monospace") {{ userDialog.ref.displayName | textToHex }}
|
||||
el-tooltip(v-if="userDialog.ref.pronouns" placement="top" :content="$t('dialog.user.pronouns')" :disabled="hideTooltips")
|
||||
span.x-grey(v-text="userDialog.ref.pronouns" style="margin-right:5px;font-family:monospace;font-size:12px")
|
||||
el-tooltip(v-for="item in userDialog.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-right:5px")
|
||||
template(v-if="userDialog.ref.id === API.currentUser.id")
|
||||
br
|
||||
el-popover(placement="top" trigger="click")
|
||||
span.x-grey(slot="reference" v-text="API.currentUser.username" style="margin-right:10px;font-family:monospace;font-size:12px;cursor:pointer")
|
||||
span(style="display:block;text-align:center;font-family:monospace") {{ API.currentUser.username | textToHex }}
|
||||
div(style="margin-top:5px")
|
||||
el-tag.name(type="info" effect="plain" size="mini" :class="userDialog.ref.$trustClass" v-text="userDialog.ref.$trustLevel" style="margin-right:5px;margin-top:5px")
|
||||
el-tag.x-tag-friend(v-if="userDialog.isFriend && userDialog.friend" type="info" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.user.tags.friend_no', { number: userDialog.ref.$friendNumber ? userDialog.ref.$friendNumber : "?" }) }}
|
||||
el-tag.x-tag-troll(v-if="userDialog.ref.$isTroll" type="info" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") Nuisance
|
||||
el-tag.x-tag-troll(v-if="userDialog.ref.$isProbableTroll" type="info" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") Almost Nuisance
|
||||
el-tag.x-tag-vip(v-if="userDialog.ref.$isModerator" type="info" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.user.tags.vrchat_team') }}
|
||||
el-tag.x-tag-platform-pc(v-if="userDialog.ref.last_platform === 'standalonewindows'" type="info" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") PC
|
||||
el-tag.x-tag-platform-quest(v-else-if="userDialog.ref.last_platform === 'android'" type="info" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") Android
|
||||
el-tag.x-tag-platform-ios(v-else-if="userDialog.ref.last_platform === 'ios'" type="info" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") iOS
|
||||
el-tag.x-tag-platform-other(v-else-if="userDialog.ref.last_platform" type="info" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ userDialog.ref.last_platform }}
|
||||
el-tag.x-tag-age-verification(v-if="userDialog.ref.ageVerificationStatus && userDialog.ref.ageVerificationStatus !== 'hidden'" type="info" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ userDialog.ref.ageVerificationStatus }}
|
||||
el-tag.name(v-if="userDialog.ref.$customTag" type="info" effect="plain" size="mini" v-text="userDialog.ref.$customTag" :style="{'color':userDialog.ref.$customTagColour, 'border-color':userDialog.ref.$customTagColour}" style="margin-right:5px;margin-top:5px")
|
||||
br
|
||||
template(v-for="badge in userDialog.ref.badges")
|
||||
el-tooltip(placement="top")
|
||||
template(#content)
|
||||
span {{ badge.badgeName }}
|
||||
span(v-if="badge.hidden") (Hidden)
|
||||
el-popover(placement="right" width="300px" trigger="click")
|
||||
img.x-link.x-user-badge(slot="reference" v-lazy="badge.badgeImageUrl" style="flex:none;height:32px;width:32px;border-radius:3px;object-fit:cover;margin-top:5px;margin-right:5px" :class="{'x-user-badge-hidden':badge.hidden}")
|
||||
img.x-link(v-lazy="badge.badgeImageUrl" style="width:300px" @click="showFullscreenImageDialog(badge.badgeImageUrl)")
|
||||
br
|
||||
div(style="display:block;width:300px;word-break:normal")
|
||||
span {{ badge.badgeName }}
|
||||
br
|
||||
span.x-grey(style="font-size:12px") {{ badge.badgeDescription }}
|
||||
br
|
||||
span.x-grey(v-if="badge.assignedAt" style="font-family:monospace;font-size:12px") {{ $t('dialog.user.badges.assigned') }}: {{ badge.assignedAt | formatDate('long') }}
|
||||
template(v-if="userDialog.id === API.currentUser.id")
|
||||
br
|
||||
el-checkbox(@change="toggleBadgeVisibility(badge)" v-model="badge.hidden" style="margin-top:5px") {{ $t('dialog.user.badges.hidden') }}
|
||||
br
|
||||
el-checkbox(@change="toggleBadgeShowcased(badge)" v-model="badge.showcased" style="margin-top:5px") {{ $t('dialog.user.badges.showcased') }}
|
||||
div(style="margin-top:5px")
|
||||
span(v-text="userDialog.ref.statusDescription" style="font-size:12px")
|
||||
div(v-if="userDialog.ref.userIcon" style="flex:none;margin-right:10px")
|
||||
el-popover(placement="right" width="500px" trigger="click")
|
||||
img.x-link(slot="reference" v-lazy="userDialog.ref.userIcon" style="flex:none;width:120px;height:120px;border-radius:12px;object-fit:cover")
|
||||
img.x-link(v-lazy="userDialog.ref.userIcon" style="height:500px" @click="showFullscreenImageDialog(userDialog.ref.userIcon)")
|
||||
div(style="flex:none")
|
||||
template(v-if="(API.currentUser.id !== userDialog.ref.id && userDialog.isFriend) || userDialog.isFavorite")
|
||||
el-tooltip(v-if="userDialog.isFavorite" placement="top" :content="$t('dialog.user.actions.unfavorite_tooltip')" :disabled="hideTooltips")
|
||||
el-button(@click="userDialogCommand('Add Favorite')" type="warning" icon="el-icon-star-on" circle)
|
||||
el-tooltip(v-else placement="top" :content="$t('dialog.user.actions.favorite_tooltip')" :disabled="hideTooltips")
|
||||
el-button(type="default" @click="userDialogCommand('Add Favorite')" icon="el-icon-star-off" circle)
|
||||
el-dropdown(trigger="click" @command="userDialogCommand" size="small")
|
||||
el-button(:type="(userDialog.incomingRequest || userDialog.outgoingRequest) ? 'success' : (userDialog.isBlock || userDialog.isMute) ? 'danger' : 'default'" icon="el-icon-more" circle style="margin-left:5px")
|
||||
el-dropdown-menu(#default="dropdown")
|
||||
el-dropdown-item(icon="el-icon-refresh" command="Refresh") {{ $t('dialog.user.actions.refresh') }}
|
||||
el-dropdown-item(icon="el-icon-share" command="Share") {{ $t('dialog.user.actions.share') }}
|
||||
template(v-if="userDialog.ref.id === API.currentUser.id")
|
||||
el-dropdown-item(icon="el-icon-picture-outline" command="Manage Gallery" divided) {{ $t('dialog.user.actions.manage_gallery_icon') }}
|
||||
el-dropdown-item(icon="el-icon-s-custom" command="Show Avatar Author") {{ $t('dialog.user.actions.show_avatar_author') }}
|
||||
el-dropdown-item(icon="el-icon-s-custom" command="Show Fallback Avatar Details") {{ $t('dialog.user.actions.show_fallback_avatar') }}
|
||||
el-dropdown-item(icon="el-icon-edit" command="Edit Social Status" divided) {{ $t('dialog.user.actions.edit_status') }}
|
||||
el-dropdown-item(icon="el-icon-edit" command="Edit Language") {{ $t('dialog.user.actions.edit_language') }}
|
||||
el-dropdown-item(icon="el-icon-edit" command="Edit Bio") {{ $t('dialog.user.actions.edit_bio') }}
|
||||
el-dropdown-item(icon="el-icon-edit" command="Edit Pronouns") {{ $t('dialog.user.actions.edit_pronouns') }}
|
||||
el-dropdown-item(icon="el-icon-switch-button" command="Logout" divided) {{ $t('dialog.user.actions.logout') }}
|
||||
template(v-else)
|
||||
template(v-if="userDialog.isFriend")
|
||||
el-dropdown-item(icon="el-icon-postcard" command="Request Invite" divided) {{ $t('dialog.user.actions.request_invite') }}
|
||||
el-dropdown-item(icon="el-icon-postcard" command="Request Invite Message") {{ $t('dialog.user.actions.request_invite_with_message') }}
|
||||
template(v-if="lastLocation.location && isGameRunning && checkCanInvite(lastLocation.location)")
|
||||
el-dropdown-item(icon="el-icon-message" command="Invite") {{ $t('dialog.user.actions.invite') }}
|
||||
el-dropdown-item(icon="el-icon-message" command="Invite Message") {{ $t('dialog.user.actions.invite_with_message') }}
|
||||
template(v-else-if="userDialog.incomingRequest")
|
||||
el-dropdown-item(icon="el-icon-check" command="Accept Friend Request") {{ $t('dialog.user.actions.accept_friend_request') }}
|
||||
el-dropdown-item(icon="el-icon-close" command="Decline Friend Request") {{ $t('dialog.user.actions.decline_friend_request') }}
|
||||
el-dropdown-item(v-else-if="userDialog.outgoingRequest" icon="el-icon-close" command="Cancel Friend Request") {{ $t('dialog.user.actions.cancel_friend_request') }}
|
||||
el-dropdown-item(v-else icon="el-icon-plus" command="Send Friend Request") {{ $t('dialog.user.actions.send_friend_request') }}
|
||||
el-dropdown-item(icon="el-icon-message" command="Invite To Group") {{ $t('dialog.user.actions.invite_to_group') }}
|
||||
//- el-dropdown-item(icon="el-icon-thumb" command="Send Boop" :disabled="!API.currentUser.isBoopingEnabled") {{ $t('dialog.user.actions.send_boop') }}
|
||||
el-dropdown-item(icon="el-icon-s-custom" command="Show Avatar Author" divided) {{ $t('dialog.user.actions.show_avatar_author') }}
|
||||
el-dropdown-item(icon="el-icon-s-custom" command="Show Fallback Avatar Details") {{ $t('dialog.user.actions.show_fallback_avatar') }}
|
||||
el-dropdown-item(icon="el-icon-tickets" command="Previous Instances") {{ $t('dialog.user.actions.show_previous_instances') }}
|
||||
el-dropdown-item(v-if="userDialog.ref.currentAvatarImageUrl" icon="el-icon-picture-outline" command="Previous Images") {{ $t('dialog.user.actions.show_previous_images') }}
|
||||
el-dropdown-item(v-if="userDialog.isBlock" icon="el-icon-circle-check" command="Moderation Unblock" divided style="color:#F56C6C") {{ $t('dialog.user.actions.moderation_unblock') }}
|
||||
el-dropdown-item(v-else icon="el-icon-circle-close" command="Moderation Block" divided :disabled="userDialog.ref.$isModerator") {{ $t('dialog.user.actions.moderation_block') }}
|
||||
el-dropdown-item(v-if="userDialog.isMute" icon="el-icon-microphone" command="Moderation Unmute" style="color:#F56C6C") {{ $t('dialog.user.actions.moderation_unmute') }}
|
||||
el-dropdown-item(v-else icon="el-icon-turn-off-microphone" command="Moderation Mute" :disabled="userDialog.ref.$isModerator") {{ $t('dialog.user.actions.moderation_mute') }}
|
||||
el-dropdown-item(v-if="userDialog.isMuteChat" icon="el-icon-chat-line-round" command="Moderation Enable Chatbox" style="color:#F56C6C") {{ $t('dialog.user.actions.moderation_enable_chatbox') }}
|
||||
el-dropdown-item(v-else icon="el-icon-chat-dot-round" command="Moderation Disable Chatbox") {{ $t('dialog.user.actions.moderation_disable_chatbox') }}
|
||||
el-dropdown-item(icon="el-icon-user-solid" command="Show Avatar")
|
||||
i.el-icon-check.el-icon--left(v-if="userDialog.isShowAvatar")
|
||||
span {{ $t('dialog.user.actions.moderation_show_avatar') }}
|
||||
el-dropdown-item(icon="el-icon-user" command="Hide Avatar")
|
||||
i.el-icon-check.el-icon--left(v-if="userDialog.isHideAvatar")
|
||||
span {{ $t('dialog.user.actions.moderation_hide_avatar') }}
|
||||
el-dropdown-item(v-if="userDialog.isInteractOff" icon="el-icon-thumb" command="Moderation Enable Avatar Interaction" style="color:#F56C6C") {{ $t('dialog.user.actions.moderation_enable_avatar_interaction') }}
|
||||
el-dropdown-item(v-else icon="el-icon-circle-close" command="Moderation Disable Avatar Interaction") {{ $t('dialog.user.actions.moderation_disable_avatar_interaction') }}
|
||||
el-dropdown-item(icon="el-icon-s-flag" command="Report Hacking" :disabled="userDialog.ref.$isModerator") {{ $t('dialog.user.actions.report_hacking') }}
|
||||
template(v-if="userDialog.isFriend")
|
||||
el-dropdown-item(icon="el-icon-delete" command="Unfriend" divided style="color:#F56C6C") {{ $t('dialog.user.actions.unfriend') }}
|
||||
el-tabs(ref="userDialogTabs" @tab-click="userDialogTabClick")
|
||||
el-tab-pane(:label="$t('dialog.user.info.header')")
|
||||
template(v-if="isFriendOnline(userDialog.friend) || API.currentUser.id === userDialog.id")
|
||||
div(v-if="userDialog.ref.location" style="display:flex;flex-direction:column;margin-bottom:10px;padding-bottom:10px;border-bottom:1px solid #e4e7ed14")
|
||||
div(style="flex:none")
|
||||
template(v-if="isRealInstance(userDialog.$location.tag)")
|
||||
el-tooltip(placement="top" :content="$t('dialog.user.info.launch_invite_tooltip')" :disabled="hideTooltips")
|
||||
launch(:location="userDialog.$location.tag")
|
||||
el-tooltip(placement="top" :content="$t('dialog.user.info.self_invite_tooltip')" :disabled="hideTooltips")
|
||||
invite-yourself(:location="userDialog.$location.tag" :shortname="userDialog.$location.shortName" style="margin-left:5px")
|
||||
el-tooltip(placement="top" :content="$t('dialog.user.info.refresh_instance_info')" :disabled="hideTooltips")
|
||||
el-button(@click="refreshInstancePlayerCount(userDialog.$location.tag)" size="mini" icon="el-icon-refresh" style="margin-left:5px" circle)
|
||||
last-join(:location="userDialog.$location.tag" :currentlocation="lastLocation.location")
|
||||
instance-info(:location="userDialog.$location.tag" :instance="userDialog.instance.ref" :friendcount="userDialog.instance.friendCount" :updateelement="updateInstanceInfo")
|
||||
location(:location="userDialog.ref.location" :traveling="userDialog.ref.travelingToLocation" style="display:block;margin-top:5px")
|
||||
.x-friend-list(style="flex:1;margin-top:10px;max-height:150px")
|
||||
.x-friend-item(v-if="userDialog.$location.userId" @click="showUserDialog(userDialog.$location.userId)" class="x-friend-item-border")
|
||||
template(v-if="userDialog.$location.user")
|
||||
.avatar(:class="userStatusClass(userDialog.$location.user)")
|
||||
img(v-lazy="userImage(userDialog.$location.user)")
|
||||
.detail
|
||||
span.name(v-text="userDialog.$location.user.displayName" :style="{'color':userDialog.$location.user.$userColour}")
|
||||
span.extra {{ $t('dialog.user.info.instance_creator') }}
|
||||
span(v-else v-text="userDialog.$location.userId")
|
||||
.x-friend-item(v-for="user in userDialog.users" :key="user.id" @click="showUserDialog(user.id)" class="x-friend-item-border")
|
||||
.avatar(:class="userStatusClass(user)")
|
||||
img(v-lazy="userImage(user)")
|
||||
.detail
|
||||
span.name(v-text="user.displayName" :style="{'color':user.$userColour}")
|
||||
span.extra(v-if="user.location === 'traveling'")
|
||||
i.el-icon-loading(style="margin-right:5px")
|
||||
timer(:epoch="user.$travelingToTime")
|
||||
span.extra(v-else)
|
||||
timer(:epoch="user.$location_at")
|
||||
.x-friend-list(style="max-height:none")
|
||||
.x-friend-item(v-if="!hideUserNotes" style="width:100%;cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.user.info.note') }}
|
||||
el-input(v-model="userDialog.note" type="textarea" maxlength="256" show-word-limit :rows="2" :autosize="{ minRows: 1, maxRows: 20 }" @change="checkNote(userDialog.ref, userDialog.note)" @input="cleanNote(userDialog.note)" :placeholder="$t('dialog.user.info.note_placeholder')" size="mini" resize="none")
|
||||
div(style="float:right")
|
||||
i.el-icon-loading(v-if="userDialog.noteSaving" style="margin-left:5px")
|
||||
i.el-icon-more-outline(v-else-if="userDialog.note !== userDialog.ref.note" style="margin-left:5px")
|
||||
el-button(v-if="userDialog.note" type="text" icon="el-icon-delete" size="mini" @click="deleteNote(userDialog.id)" style="margin-left:5px")
|
||||
.x-friend-item(v-if="!hideUserMemos" style="width:100%;cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.user.info.memo') }}
|
||||
el-input.extra(v-model="userDialog.memo" @change="onUserMemoChange" type="textarea" :rows="2" :autosize="{ minRows: 1, maxRows: 20 }" :placeholder="$t('dialog.user.info.memo_placeholder')" size="mini" resize="none")
|
||||
.x-friend-item(style="width:100%;cursor:default")
|
||||
.detail
|
||||
span.name(v-if="userDialog.id !== API.currentUser.id && userDialog.ref.profilePicOverride && userDialog.ref.currentAvatarImageUrl") {{ $t('dialog.user.info.avatar_info_last_seen') }}
|
||||
span.name(v-else) {{ $t('dialog.user.info.avatar_info') }}
|
||||
.extra
|
||||
avatar-info(:imageurl="userDialog.ref.currentAvatarImageUrl" :userid="userDialog.id" :avatartags="userDialog.ref.currentAvatarTags")
|
||||
.x-friend-item(style="width:100%;cursor:default")
|
||||
.detail
|
||||
span.name(style="margin-bottom:5px") {{ $t('dialog.user.info.represented_group') }}
|
||||
.extra(v-if="userDialog.representedGroup?.isRepresenting")
|
||||
div(style="display:inline-block;flex:none;margin-right:5px")
|
||||
el-popover(placement="right" width="500px" trigger="click")
|
||||
img.x-link(slot="reference" v-lazy="userDialog.representedGroup.iconUrl" style="flex:none;width:60px;height:60px;border-radius:4px;object-fit:cover")
|
||||
img.x-link(v-lazy="userDialog.representedGroup.iconUrl" style="height:500px" @click="showFullscreenImageDialog(userDialog.representedGroup.iconUrl)")
|
||||
span(style="vertical-align:top;cursor:pointer" @click="showGroupDialog(userDialog.representedGroup.groupId)")
|
||||
span(v-if="userDialog.representedGroup.ownerId === userDialog.id" style="margin-right:5px") 👑
|
||||
span(v-text="userDialog.representedGroup.name" style="margin-right:5px")
|
||||
span ({{ userDialog.representedGroup.memberCount }})
|
||||
.extra(v-else) -
|
||||
.x-friend-item(style="width:100%;cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.user.info.bio') }}
|
||||
pre.extra(style="font-family:inherit;font-size:12px;white-space:pre-wrap;margin:0 0.5em 0 0") {{ userDialog.ref.bio || '-' }}
|
||||
div(v-if="userDialog.id === API.currentUser.id" style="float:right")
|
||||
el-button(type="text" icon="el-icon-edit" size="mini" @click="showBioDialog" style="margin-left:5px")
|
||||
div(style="margin-top:5px")
|
||||
el-tooltip(v-if="link" v-for="(link, index) in userDialog.ref.bioLinks" :key="index")
|
||||
template(#content)
|
||||
span(v-text="link")
|
||||
img(:src="getFaviconUrl(link)" onerror="this.onerror=null;this.class='el-icon-error'" style="width:16px;height:16px;vertical-align:middle;margin-right:5px;cursor:pointer" @click.stop="openExternalLink(link)")
|
||||
template(v-if="API.currentUser.id !== userDialog.id")
|
||||
.x-friend-item(style="cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.user.info.last_seen') }}
|
||||
el-tooltip(v-if="!hideTooltips" placement="top" style="margin-left:5px" :content="$t('dialog.user.info.accuracy_notice')")
|
||||
i.el-icon-warning
|
||||
span.extra {{ userDialog.lastSeen | formatDate('long') }}
|
||||
.x-friend-item(@click="showPreviousInstancesUserDialog(userDialog.ref)")
|
||||
.detail
|
||||
span.name {{ $t('dialog.user.info.join_count') }}
|
||||
el-tooltip(v-if="!hideTooltips" placement="top" style="margin-left:5px" :content="$t('dialog.user.info.accuracy_notice')")
|
||||
i.el-icon-warning
|
||||
span.extra(v-if="userDialog.joinCount === 0") -
|
||||
span.extra(v-else v-text="userDialog.joinCount")
|
||||
.x-friend-item(style="cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.user.info.time_together') }}
|
||||
el-tooltip(v-if="!hideTooltips" placement="top" style="margin-left:5px" :content="$t('dialog.user.info.accuracy_notice')")
|
||||
i.el-icon-warning
|
||||
span.extra(v-if="userDialog.timeSpent === 0") -
|
||||
span.extra(v-else) {{ timeToText(userDialog.timeSpent) }}
|
||||
template(v-else)
|
||||
.x-friend-item(@click="showPreviousInstancesUserDialog(userDialog.ref)")
|
||||
.detail
|
||||
span.name {{ $t('dialog.user.info.play_time') }}
|
||||
el-tooltip(v-if="!hideTooltips" placement="top" style="margin-left:5px" :content="$t('dialog.user.info.accuracy_notice')")
|
||||
i.el-icon-warning
|
||||
span.extra(v-if="userDialog.timeSpent === 0") -
|
||||
span.extra(v-else) {{ timeToText(userDialog.timeSpent) }}
|
||||
.x-friend-item(style="cursor:default")
|
||||
el-tooltip(placement="top")
|
||||
template(#content)
|
||||
span {{ userOnlineForTimestamp(userDialog) | formatDate('short') }}
|
||||
.detail
|
||||
span.name(v-if="userDialog.ref.state === 'online' && userDialog.ref.$online_for") {{ $t('dialog.user.info.online_for') }}
|
||||
el-tooltip(v-if="!hideTooltips" placement="top" style="margin-left:5px" :content="$t('dialog.user.info.accuracy_notice')")
|
||||
i.el-icon-warning
|
||||
span.name(v-else) {{ $t('dialog.user.info.offline_for') }}
|
||||
el-tooltip(v-if="!hideTooltips" placement="top" style="margin-left:5px" :content="$t('dialog.user.info.accuracy_notice')")
|
||||
i.el-icon-warning
|
||||
span.extra {{ userOnlineFor(userDialog) }}
|
||||
.x-friend-item(style="cursor:default")
|
||||
el-tooltip(placement="top")
|
||||
template(#content)
|
||||
span {{ $t('dialog.user.info.last_login') }} {{ userDialog.ref.last_login | formatDate('short') }}
|
||||
.detail
|
||||
span.name {{ $t('dialog.user.info.last_activity') }}
|
||||
span.extra {{ userDialog.ref.last_activity | formatDate('long') }}
|
||||
.x-friend-item(style="cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.user.info.date_joined') }}
|
||||
span.extra(v-text="userDialog.ref.date_joined")
|
||||
.x-friend-item(v-if="API.currentUser.id !== userDialog.id" style="cursor:default")
|
||||
el-tooltip(placement="top")
|
||||
template(#content v-if="userDialog.dateFriendedInfo.length")
|
||||
template(v-for="ref in userDialog.dateFriendedInfo")
|
||||
span {{ ref.type }}: {{ ref.created_at | formatDate('long') }}
|
||||
br
|
||||
template(#content v-else)
|
||||
span -
|
||||
.detail
|
||||
span.name(v-if="userDialog.unFriended") {{ $t('dialog.user.info.unfriended') }}
|
||||
el-tooltip(v-if="!hideTooltips" placement="top" style="margin-left:5px" :content="$t('dialog.user.info.accuracy_notice')")
|
||||
i.el-icon-warning
|
||||
span.name(v-else) {{ $t('dialog.user.info.friended') }}
|
||||
el-tooltip(v-if="!hideTooltips" placement="top" style="margin-left:5px" :content="$t('dialog.user.info.accuracy_notice')")
|
||||
i.el-icon-warning
|
||||
span.extra {{ userDialog.dateFriended | formatDate('long') }}
|
||||
template(v-if="API.currentUser.id === userDialog.id")
|
||||
.x-friend-item(@click="toggleAvatarCopying")
|
||||
.detail
|
||||
span.name {{ $t('dialog.user.info.avatar_cloning') }}
|
||||
span.extra(v-if="API.currentUser.allowAvatarCopying" style="color:#67C23A") {{ $t('dialog.user.info.avatar_cloning_allow') }}
|
||||
span.extra(v-else style="color:#F56C6C") {{ $t('dialog.user.info.avatar_cloning_deny') }}
|
||||
//- .x-friend-item(@click="toggleAllowBooping")
|
||||
//- .detail
|
||||
//- span.name {{ $t('dialog.user.info.booping') }}
|
||||
//- span.extra(v-if="API.currentUser.isBoopingEnabled" style="color:#67C23A") {{ $t('dialog.user.info.avatar_cloning_allow') }}
|
||||
//- span.extra(v-else style="color:#F56C6C") {{ $t('dialog.user.info.avatar_cloning_deny') }}
|
||||
template(v-else)
|
||||
.x-friend-item(style="cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.user.info.avatar_cloning') }}
|
||||
span.extra(v-if="userDialog.ref.allowAvatarCopying" style="color:#67C23A") {{ $t('dialog.user.info.avatar_cloning_allow') }}
|
||||
span.extra(v-else style="color:#F56C6C") {{ $t('dialog.user.info.avatar_cloning_deny') }}
|
||||
.x-friend-item(v-if="userDialog.ref.id === API.currentUser.id && API.currentUser.homeLocation" @click="showWorldDialog(API.currentUser.homeLocation)" style="width:100%")
|
||||
.detail
|
||||
span.name {{ $t('dialog.user.info.home_location') }}
|
||||
span.extra
|
||||
span(v-text="userDialog.$homeLocationName")
|
||||
el-button(@click.stop="resetHome()" size="mini" icon="el-icon-delete" circle style="margin-left:5px")
|
||||
.x-friend-item(style="width:100%;cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.user.info.id') }}
|
||||
span.extra {{ userDialog.id }}
|
||||
el-tooltip(placement="top" :content="$t('dialog.user.info.id_tooltip')" :disabled="hideTooltips")
|
||||
el-dropdown(trigger="click" @click.native.stop size="mini" style="margin-left:5px")
|
||||
el-button(type="default" icon="el-icon-s-order" size="mini" circle)
|
||||
el-dropdown-menu(#default="dropdown")
|
||||
el-dropdown-item(@click.native="copyUserId(userDialog.id)") {{ $t('dialog.user.info.copy_id') }}
|
||||
el-dropdown-item(@click.native="copyUserURL(userDialog.id)") {{ $t('dialog.user.info.copy_url') }}
|
||||
el-dropdown-item(@click.native="copyUserDisplayName(userDialog.ref.displayName)") {{ $t('dialog.user.info.copy_display_name') }}
|
||||
el-tab-pane(:label="$t('dialog.user.groups.header')")
|
||||
div(style="display:flex;align-items:center;justify-content:space-between")
|
||||
div(style="display:flex;align-items:center")
|
||||
el-button(type="default" :loading="userDialog.isGroupsLoading" @click="getUserGroups(userDialog.id)" size="mini" icon="el-icon-refresh" circle)
|
||||
span(style="margin-left:5px") {{ $t('dialog.user.groups.total_count', { count: userGroups.groups.length }) }}
|
||||
template(v-if="userDialogGroupEditMode")
|
||||
span(style="margin-left:10px;color:#909399;font-size:10px") {{ $t('dialog.user.groups.hold_shift') }}
|
||||
div(style="display:flex;align-items:center;")
|
||||
template(v-if="!userDialogGroupEditMode")
|
||||
span(style="margin-right:5px") {{ $t('dialog.user.groups.sort_by') }}
|
||||
el-dropdown(@click.native.stop trigger="click" size="small" style="margin-right:5px" :disabled="userDialog.isGroupsLoading")
|
||||
el-button(size="mini")
|
||||
span {{ userDialog.groupSorting.name }} #[i.el-icon-arrow-down.el-icon--right]
|
||||
el-dropdown-menu(#default="dropdown")
|
||||
el-dropdown-item(:disabled="item === userDialogGroupSortingOptions.inGame && userDialog.id !== API.currentUser.id" v-for="(item) in userDialogGroupSortingOptions" v-text="item.name" @click.native="setUserDialogGroupSorting(item)")
|
||||
el-button(v-if="userDialogGroupEditMode" size="small" @click="exitEditModeCurrentUserGroups" icon="el-icon-edit" style="margin-right:5px;height:29px;padding:7px 15px;") {{ $t('dialog.user.groups.exit_edit_mode') }}
|
||||
el-button(v-else-if="API.currentUser.id === userDialog.id" size="small" @click="editModeCurrentUserGroups" icon="el-icon-edit" style="margin-right:5px;height:29px;padding: 7px 15px;") {{ $t('dialog.user.groups.edit_mode') }}
|
||||
div(v-loading="userDialog.isGroupsLoading" style="margin-top:10px")
|
||||
template(v-if="userDialogGroupEditMode")
|
||||
.x-friend-list(style="margin-top:10px;margin-bottom:15px;max-height:unset")
|
||||
.x-friend-item(v-for="group in userDialogGroupEditGroups" :key="group.id" @click="showGroupDialog(group.id)" class="x-friend-item-border" style="width:100%")
|
||||
div(@click.stop style="margin-right:3px;margin-left:5px")
|
||||
el-button(@click="moveGroupUp(group.id)" size="mini" icon="el-icon-arrow-up" style="display:block;padding:7px;font-size:9px;margin-left:0")
|
||||
el-button(@click="moveGroupDown(group.id)" size="mini" icon="el-icon-arrow-down" style="display:block;padding:7px;font-size:9px;margin-left:0")
|
||||
div(@click.stop style="margin-right:10px")
|
||||
el-button(@click="moveGroupTop(group.id)" size="mini" icon="el-icon-top" style="display:block;padding:7px;font-size:9px;margin-left:0")
|
||||
el-button(@click="moveGroupBottom(group.id)" size="mini" icon="el-icon-bottom" style="display:block;padding:7px;font-size:9px;margin-left:0")
|
||||
.avatar
|
||||
img(v-lazy="group.iconUrl")
|
||||
.detail
|
||||
span.name(v-text="group.name")
|
||||
span.extra
|
||||
el-tooltip(v-if="group.isRepresenting" placement="top" :content="$t('dialog.group.members.representing')")
|
||||
i.el-icon-collection-tag(style="margin-right:5px")
|
||||
el-tooltip(v-if="group.myMember.visibility !== 'visible'" placement="top")
|
||||
template(#content)
|
||||
span {{ $t('dialog.group.members.visibility') }} {{ group.myMember.visibility }}
|
||||
i.el-icon-view(style="margin-right:5px")
|
||||
span ({{ group.memberCount }})
|
||||
el-dropdown(@click.native.stop :disabled="group.privacy !== 'default'" trigger="click" size="small" style="margin-right:5px")
|
||||
el-button(size="mini")
|
||||
span(v-if="group.myMember.visibility === 'visible'") {{ $t('dialog.group.tags.visible') }}
|
||||
span(v-else-if="group.myMember.visibility === 'friends'") {{ $t('dialog.group.tags.friends') }}
|
||||
span(v-else-if="group.myMember.visibility === 'hidden'") {{ $t('dialog.group.tags.hidden') }}
|
||||
span(v-else) {{ group.myMember.visibility }}
|
||||
i.el-icon-arrow-down.el-icon--right(style="margin-left:5px")
|
||||
el-dropdown-menu
|
||||
el-dropdown-item(@click.native="setGroupVisibility(group.id, 'visible')") #[i.el-icon-check(v-if="group.myMember.visibility === 'visible'")] {{ $t('dialog.group.actions.visibility_everyone') }}
|
||||
el-dropdown-item(@click.native="setGroupVisibility(group.id, 'friends')") #[i.el-icon-check(v-if="group.myMember.visibility === 'friends'")] {{ $t('dialog.group.actions.visibility_friends') }}
|
||||
el-dropdown-item(@click.native="setGroupVisibility(group.id, 'hidden')") #[i.el-icon-check(v-if="group.myMember.visibility === 'hidden'")] {{ $t('dialog.group.actions.visibility_hidden') }}
|
||||
|
||||
//- JSON is missing isSubscribedToAnnouncements, can't be implemented
|
||||
//- el-dropdown(@click.native.stop trigger="click" size="small" style="margin-right:5px")
|
||||
//- el-tooltip(placement="top" :disabled="hideTooltips")
|
||||
//- template(#content)
|
||||
//- span(v-if="group.myMember.isSubscribedToAnnouncements") {{ $t('dialog.group.actions.unsubscribe') }}
|
||||
//- span(v-else) {{ $t('dialog.group.actions.subscribe') }}
|
||||
//- el-button(v-if="group.myMember.isSubscribedToAnnouncements" @click.stop="setGroupSubscription(group.id, false)" circle size="mini")
|
||||
//- i.el-icon-chat-line-square
|
||||
//- el-button(v-else circle @click.stop="setGroupSubscription(group.id, true)" size="mini")
|
||||
//- i.el-icon-chat-square(style="color:#f56c6c")
|
||||
|
||||
el-tooltip(placement="right" :content="$t('dialog.user.groups.leave_group_tooltip')" :disabled="hideTooltips")
|
||||
el-button(v-if="shiftHeld" @click.stop="leaveGroupPrompt(group.id)" size="mini" icon="el-icon-close" circle style="color:#f56c6c;margin-left:5px")
|
||||
el-button(v-else @click.stop="leaveGroupPrompt(group.id)" size="mini" icon="el-icon-delete" circle style="margin-left:5px")
|
||||
template(v-else)
|
||||
template(v-if="userGroups.ownGroups.length > 0")
|
||||
span(style="font-weight:bold;font-size:16px") {{ $t('dialog.user.groups.own_groups') }}
|
||||
span(style="color:#909399;font-size:12px;margin-left:5px") {{ userGroups.ownGroups.length }}/{{ API.cachedConfig?.constants?.GROUPS?.MAX_OWNED }}
|
||||
.x-friend-list(style="margin-top:10px;margin-bottom:15px;min-height:60px")
|
||||
.x-friend-item(v-for="group in userGroups.ownGroups" :key="group.id" @click="showGroupDialog(group.id)" class="x-friend-item-border")
|
||||
.avatar
|
||||
img(v-lazy="group.iconUrl")
|
||||
.detail
|
||||
span.name(v-text="group.name")
|
||||
span.extra
|
||||
el-tooltip(v-if="group.isRepresenting" placement="top" :content="$t('dialog.group.members.representing')")
|
||||
i.el-icon-collection-tag(style="margin-right:5px")
|
||||
el-tooltip(v-if="group.memberVisibility !== 'visible'" placement="top")
|
||||
template(#content)
|
||||
span {{ $t('dialog.group.members.visibility') }} {{ group.memberVisibility }}
|
||||
i.el-icon-view(style="margin-right:5px")
|
||||
span ({{ group.memberCount }})
|
||||
template(v-if="userGroups.mutualGroups.length > 0")
|
||||
span(style="font-weight:bold;font-size:16px") {{ $t('dialog.user.groups.mutual_groups') }}
|
||||
span(style="color:#909399;font-size:12px;margin-left:5px") {{ userGroups.mutualGroups.length }}
|
||||
.x-friend-list(style="margin-top:10px;margin-bottom:15px;min-height:60px")
|
||||
.x-friend-item(v-for="group in userGroups.mutualGroups" :key="group.id" @click="showGroupDialog(group.id)" class="x-friend-item-border")
|
||||
.avatar
|
||||
img(v-lazy="group.iconUrl")
|
||||
.detail
|
||||
span.name(v-text="group.name")
|
||||
span.extra
|
||||
el-tooltip(v-if="group.isRepresenting" placement="top" :content="$t('dialog.group.members.representing')")
|
||||
i.el-icon-collection-tag(style="margin-right:5px")
|
||||
el-tooltip(v-if="group.memberVisibility !== 'visible'" placement="top")
|
||||
template(#content)
|
||||
span {{ $t('dialog.group.members.visibility') }} {{ group.memberVisibility }}
|
||||
i.el-icon-view(style="margin-right:5px")
|
||||
span ({{ group.memberCount }})
|
||||
template(v-if="userGroups.remainingGroups.length > 0")
|
||||
span(style="font-weight:bold;font-size:16px") {{ $t('dialog.user.groups.groups') }}
|
||||
span(style="color:#909399;font-size:12px;margin-left:5px") {{ userGroups.remainingGroups.length }}
|
||||
template(v-if="API.currentUser.id === userDialog.id")
|
||||
|/
|
||||
template(v-if="API.currentUser.$isVRCPlus")
|
||||
| {{ API.cachedConfig?.constants?.GROUPS?.MAX_JOINED_PLUS }}
|
||||
template(v-else)
|
||||
| {{ API.cachedConfig?.constants?.GROUPS?.MAX_JOINED }}
|
||||
.x-friend-list(style="margin-top:10px;margin-bottom:15px;min-height:60px")
|
||||
.x-friend-item(v-for="group in userGroups.remainingGroups" :key="group.id" @click="showGroupDialog(group.id)" class="x-friend-item-border")
|
||||
.avatar
|
||||
img(v-lazy="group.iconUrl")
|
||||
.detail
|
||||
span.name(v-text="group.name")
|
||||
span.extra
|
||||
el-tooltip(v-if="group.isRepresenting" placement="top" :content="$t('dialog.group.members.representing')")
|
||||
i.el-icon-collection-tag(style="margin-right:5px")
|
||||
el-tooltip(v-if="group.memberVisibility !== 'visible'" placement="top")
|
||||
template(#content)
|
||||
span {{ $t('dialog.group.members.visibility') }} {{ group.memberVisibility }}
|
||||
i.el-icon-view(style="margin-right:5px")
|
||||
span ({{ group.memberCount }})
|
||||
el-tab-pane(:label="$t('dialog.user.worlds.header')")
|
||||
div(style="display:flex;align-items:center;justify-content:space-between")
|
||||
div(style="display:flex;align-items:center;")
|
||||
el-button(type="default" :loading="userDialog.isWorldsLoading" @click="refreshUserDialogWorlds()" size="mini" icon="el-icon-refresh" circle)
|
||||
span(style="margin-left:5px") {{ $t('dialog.user.worlds.total_count', { count: userDialog.worlds.length }) }}
|
||||
div(style="display:flex;align-items:center")
|
||||
span(style="margin-right:5px") {{ $t('dialog.user.worlds.sort_by') }}
|
||||
el-dropdown(@click.native.stop trigger="click" size="small" style="margin-right:5px" :disabled="userDialog.isWorldsLoading")
|
||||
el-button(size="mini")
|
||||
span {{ userDialog.worldSorting.name }} #[i.el-icon-arrow-down.el-icon--right]
|
||||
el-dropdown-menu(#default="dropdown")
|
||||
el-dropdown-item(v-for="(item) in userDialogWorldSortingOptions" v-text="item.name" @click.native="setUserDialogWorldSorting(item)")
|
||||
span(style="margin:0 5px") {{ $t('dialog.user.worlds.order_by') }}
|
||||
el-dropdown(@click.native.stop trigger="click" size="small" style="margin-right:5px" :disabled="userDialog.isWorldsLoading")
|
||||
el-button(size="mini")
|
||||
span {{ userDialog.worldOrder.name }} #[i.el-icon-arrow-down.el-icon--right]
|
||||
el-dropdown-menu(#default="dropdown")
|
||||
el-dropdown-item(v-for="(item) in userDialogWorldOrderOptions" v-text="item.name" @click.native="setUserDialogWorldOrder(item)")
|
||||
.x-friend-list(v-loading="userDialog.isWorldsLoading" style="margin-top:10px;min-height:60px")
|
||||
.x-friend-item(v-for="world in userDialog.worlds" :key="world.id" @click="showWorldDialog(world.id)" class="x-friend-item-border")
|
||||
.avatar
|
||||
img(v-lazy="world.thumbnailImageUrl")
|
||||
.detail
|
||||
span.name(v-text="world.name")
|
||||
span.extra(v-if="world.occupants") ({{ world.occupants }})
|
||||
el-tab-pane(:label="$t('dialog.user.favorite_worlds.header')")
|
||||
el-button(type="default" :loading="userDialog.isFavoriteWorldsLoading" @click="getUserFavoriteWorlds(userDialog.id)" size="mini" icon="el-icon-refresh" circle)
|
||||
el-tabs.zero-margin-tabs(type="card" ref="favoriteWorlds" v-loading="userDialog.isFavoriteWorldsLoading" style="margin-top:10px")
|
||||
template(v-for="(list, index) in userFavoriteWorlds" v-if="list")
|
||||
el-tab-pane
|
||||
span(slot="label")
|
||||
span(v-text="list[0]" style="font-weight:bold;font-size:16px")
|
||||
i.x-status-icon(style="margin-left:5px" :class="userFavoriteWorldsStatus(list[1])")
|
||||
span(style="color:#909399;font-size:12px;margin-left:5px") {{ list[2].length }}/{{ API.favoriteLimits.maxFavoritesPerGroup.world }}
|
||||
.x-friend-list(style="margin-top:10px;margin-bottom:15px;min-height:60px")
|
||||
.x-friend-item(v-for="world in list[2]" :key="world.id" @click="showWorldDialog(world.id)" class="x-friend-item-border")
|
||||
.avatar
|
||||
img(v-lazy="world.thumbnailImageUrl")
|
||||
.detail
|
||||
span.name(v-text="world.name")
|
||||
span.extra(v-if="world.occupants") ({{ world.occupants }})
|
||||
el-tab-pane(:label="$t('dialog.user.avatars.header')")
|
||||
div(style="display:flex;align-items:center;justify-content:space-between")
|
||||
div(style="display:flex;align-items:center")
|
||||
el-button(v-if="userDialog.ref.id === API.currentUser.id" type="default" :loading="userDialog.isAvatarsLoading" @click="refreshUserDialogAvatars()" size="mini" icon="el-icon-refresh" circle)
|
||||
el-button(v-else type="default" :loading="userDialog.isAvatarsLoading" @click="setUserDialogAvatarsRemote(userDialog.id)" size="mini" icon="el-icon-refresh" circle)
|
||||
span(style="margin-left:5px") {{ $t('dialog.user.avatars.total_count', { count: userDialogAvatars.length }) }}
|
||||
div
|
||||
el-radio-group(v-if="userDialog.ref.id === API.currentUser.id" v-model="userDialog.avatarSorting" size="mini" @change="changeUserDialogAvatarSorting")
|
||||
el-radio(label="name") {{ $t('dialog.user.avatars.sort_by_name') }}
|
||||
el-radio(label="update") {{ $t('dialog.user.avatars.sort_by_update') }}
|
||||
el-divider(direction="vertical")
|
||||
el-radio-group(v-if="userDialog.ref.id === API.currentUser.id" v-model="userDialog.avatarReleaseStatus" size="mini")
|
||||
el-radio(label="all") {{ $t('dialog.user.avatars.all') }}
|
||||
el-radio(label="public") {{ $t('dialog.user.avatars.public') }}
|
||||
el-radio(label="private") {{ $t('dialog.user.avatars.private') }}
|
||||
.x-friend-list(style="margin-top:10px;min-height:60px")
|
||||
.x-friend-item(v-for="avatar in userDialogAvatars" @click="showAvatarDialog(avatar.id)" class="x-friend-item-border")
|
||||
.avatar
|
||||
img(v-if="avatar.thumbnailImageUrl" v-lazy="avatar.thumbnailImageUrl")
|
||||
.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)
|
||||
el-tab-pane(:label="$t('dialog.user.json.header')")
|
||||
el-button(type="default" @click="refreshUserDialogTreeData()" size="mini" icon="el-icon-refresh" circle)
|
||||
el-button(type="default" @click="downloadAndSaveJson(userDialog.id, userDialog.ref)" size="mini" icon="el-icon-download" circle style="margin-left:5px")
|
||||
el-tree(:data="userDialog.treeData" style="margin-top:5px;font-size:12px")
|
||||
template(#default="scope")
|
||||
span
|
||||
span(v-text="scope.data.key" style="font-weight:bold;margin-right:5px")
|
||||
span(v-if="!scope.data.children" v-text="scope.data.value")
|
||||
33
src/mixins/dialogs/vrcx.pug
Normal file
@@ -0,0 +1,33 @@
|
||||
mixin vrcx()
|
||||
//- dialog: update VRCX
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="VRCXUpdateDialog" :visible.sync="VRCXUpdateDialog.visible" :title="$t('dialog.vrcx_updater.header')" width="400px")
|
||||
div(v-loading="checkingForVRCXUpdate" style="margin-top:15px")
|
||||
template(v-if="updateInProgress")
|
||||
el-progress(:percentage="updateProgress" :format="updateProgressText")
|
||||
br
|
||||
template(v-else)
|
||||
div(v-if="VRCXUpdateDialog.updatePending" style="margin-bottom:15px")
|
||||
span(v-text="pendingVRCXInstall")
|
||||
br
|
||||
span {{ $t('dialog.vrcx_updater.ready_for_update') }}
|
||||
el-select(v-model="branch" @change="loadBranchVersions" style="display:inline-block;width:150px;margin-right:15px")
|
||||
el-option(v-once v-for="branch in branches" :key="branch.name" :label="branch.name" :value="branch.name")
|
||||
el-select(v-model="VRCXUpdateDialog.release" style="display:inline-block;width:150px")
|
||||
el-option(v-for="item in VRCXUpdateDialog.releases" :key="item.name" :label="item.tag_name" :value="item.name")
|
||||
div(v-if="!VRCXUpdateDialog.updatePending && VRCXUpdateDialog.release === appVersion" style="margin-top:15px")
|
||||
span {{ $t('dialog.vrcx_updater.latest_version') }}
|
||||
template(#footer)
|
||||
el-button(v-if="updateInProgress" type="primary" size="small" @click="cancelUpdate") {{ $t('dialog.vrcx_updater.cancel') }}
|
||||
el-button(v-if="VRCXUpdateDialog.release !== pendingVRCXInstall" :disabled="updateInProgress" type="primary" size="small" @click="installVRCXUpdate") {{ $t('dialog.vrcx_updater.download') }}
|
||||
el-button(v-if="!updateInProgress && pendingVRCXInstall" type="primary" size="small" @click="restartVRCX(true)") {{ $t('dialog.vrcx_updater.install') }}
|
||||
|
||||
//- dialog: change log
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="changeLogDialog" :visible.sync="changeLogDialog.visible" :title="$t('dialog.change_log.header')" width="800px" top="5vh")
|
||||
.changelog-dialog(v-if="changeLogDialog.visible")
|
||||
h2(v-text="changeLogDialog.buildName")
|
||||
span {{ $t('dialog.change_log.description') }} #[a.x-link(@click="openExternalLink('https://www.patreon.com/Natsumi_VRCX')") Patreon], #[a.x-link(@click="openExternalLink('https://ko-fi.com/natsumi_sama')") Ko-fi].
|
||||
vue-markdown(:source="changeLogDialog.changeLog" :linkify="false" style="height:62vh;overflow-y:auto;margin-top:10px")
|
||||
template(#footer)
|
||||
el-button(type="small" @click="openExternalLink('https://github.com/vrcx-team/VRCX/releases')") {{ $t('dialog.change_log.github') }}
|
||||
el-button(type="small" @click="openExternalLink('https://patreon.com/Natsumi_VRCX')") {{ $t('dialog.change_log.donate') }}
|
||||
el-button(type="small" @click="changeLogDialog.visible = false") {{ $t('dialog.change_log.close') }}
|
||||
228
src/mixins/dialogs/worldDialog.pug
Normal file
@@ -0,0 +1,228 @@
|
||||
|
||||
mixin worldDialog()
|
||||
el-dialog.x-dialog.x-world-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="worldDialog" :visible.sync="worldDialog.visible" :show-close="false" width="770px")
|
||||
div(v-loading="worldDialog.loading")
|
||||
div(style="display:flex")
|
||||
el-popover(placement="right" width="500px" trigger="click")
|
||||
img.x-link(slot="reference" v-lazy="worldDialog.ref.thumbnailImageUrl" style="flex:none;width:160px;height:120px;border-radius:12px")
|
||||
img.x-link(v-lazy="worldDialog.ref.imageUrl" style="width:500px;height:375px" @click="showFullscreenImageDialog(worldDialog.ref.imageUrl)")
|
||||
div(style="flex:1;display:flex;align-items:center;margin-left:15px")
|
||||
div(style="flex:1")
|
||||
div
|
||||
i.el-icon-s-home(v-show="API.currentUser.$homeLocation && API.currentUser.$homeLocation.worldId === worldDialog.id" style="margin-right:5px")
|
||||
span.dialog-title(v-text="worldDialog.ref.name")
|
||||
div(style="margin-top:5px")
|
||||
span.x-link.x-grey(v-text="worldDialog.ref.authorName" @click="showUserDialog(worldDialog.ref.authorId)" style="font-family:monospace")
|
||||
div
|
||||
el-tag(v-if="worldDialog.ref.$isLabs" type="primary" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.world.tags.labs') }}
|
||||
el-tag(v-else-if="worldDialog.ref.releaseStatus === 'public'" type="success" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.world.tags.public') }}
|
||||
el-tag(v-else type="danger" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.world.tags.private') }}
|
||||
el-tag.x-tag-platform-pc(v-if="worldDialog.isPC" type="info" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") PC
|
||||
span.x-grey(v-if="worldDialog.bundleSizes['standalonewindows']" style=";margin-left:5px;border-left:inherit;padding-left:5px") {{ worldDialog.bundleSizes['standalonewindows'].fileSize }}
|
||||
el-tag.x-tag-platform-quest(v-if="worldDialog.isQuest" type="info" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") Android
|
||||
span.x-grey(v-if="worldDialog.bundleSizes['android']" style="margin-left:5px;border-left:inherit;padding-left:5px") {{ worldDialog.bundleSizes['android'].fileSize }}
|
||||
el-tag.x-tag-platform-ios(v-if="worldDialog.isIos" type="info" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") iOS
|
||||
span.x-grey(v-if="worldDialog.bundleSizes['ios']" style="margin-left:5px;border-left:inherit;padding-left:5px") {{ worldDialog.bundleSizes['ios'].fileSize }}
|
||||
el-tag(v-if="worldDialog.avatarScalingDisabled" type="warning" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.world.tags.avatar_scaling_disabled') }}
|
||||
el-tag(v-if="worldDialog.focusViewDisabled" type="warning" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.world.tags.focus_view_disabled') }}
|
||||
el-tag(v-if="worldDialog.stickersDisabled" type="warning" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.world.tags.stickers_disabled') }}
|
||||
el-tag(v-if="worldDialog.ref.unityPackageUrl" type="success" effect="plain" size="mini" style="margin-right:5px;margin-top:5px") {{ $t('dialog.world.tags.future_proofing') }}
|
||||
el-tag.x-link(v-if="worldDialog.inCache" type="info" effect="plain" size="mini" style="margin-right:5px;margin-top:5px" @click="openFolderGeneric(worldDialog.cachePath)")
|
||||
span(v-text="worldDialog.cacheSize")
|
||||
| {{ $t('dialog.world.tags.cache')}}
|
||||
div
|
||||
template(v-for="tag in worldDialog.ref.tags")
|
||||
el-tag(v-if="tag.startsWith('content_')" :key="tag" effect="plain" size="mini" style="margin-right:5px;margin-top:5px")
|
||||
template(v-if="tag === 'content_horror'") {{ $t('dialog.world.tags.content_horror') }}
|
||||
template(v-else-if="tag === 'content_gore'") {{ $t('dialog.world.tags.content_gore') }}
|
||||
template(v-else-if="tag === 'content_violence'") {{ $t('dialog.world.tags.content_violence') }}
|
||||
template(v-else-if="tag === 'content_adult'") {{ $t('dialog.world.tags.content_adult') }}
|
||||
template(v-else-if="tag === 'content_sex'") {{ $t('dialog.world.tags.content_sex') }}
|
||||
template(v-else) {{ tag.replace('content_', '') }}
|
||||
div(style="margin-top:5px")
|
||||
span(v-show="worldDialog.ref.name !== worldDialog.ref.description" v-text="worldDialog.ref.description" style="font-size:12px")
|
||||
div(style="flex:none;margin-left:10px")
|
||||
el-tooltip(v-if="worldDialog.inCache" placement="top" :content="$t('dialog.world.actions.delete_cache_tooltip')" :disabled="hideTooltips")
|
||||
el-button(icon="el-icon-delete" circle @click="deleteVRChatCache(worldDialog.ref)" :disabled="isGameRunning && worldDialog.cacheLocked")
|
||||
el-tooltip(v-if="worldDialog.isFavorite" placement="top" :content="$t('dialog.world.actions.favorites_tooltip')" :disabled="hideTooltips")
|
||||
el-button(type="default" icon="el-icon-star-on" circle @click="worldDialogCommand('Add Favorite')" style="margin-left:5px")
|
||||
el-tooltip(v-else placement="top" :content="$t('dialog.world.actions.favorites_tooltip')" :disabled="hideTooltips")
|
||||
el-button(type="default" icon="el-icon-star-off" circle @click="worldDialogCommand('Add Favorite')" style="margin-left:5px")
|
||||
el-dropdown(trigger="click" @command="worldDialogCommand" size="small" style="margin-left:5px")
|
||||
el-button(type="default" icon="el-icon-more" circle)
|
||||
el-dropdown-menu(#default="dropdown")
|
||||
el-dropdown-item(icon="el-icon-refresh" command="Refresh") {{ $t('dialog.world.actions.refresh') }}
|
||||
el-dropdown-item(icon="el-icon-share" command="Share") {{ $t('dialog.world.actions.share') }}
|
||||
el-dropdown-item(icon="el-icon-s-flag" command="New Instance" divided) {{ $t('dialog.world.actions.new_instance') }}
|
||||
el-dropdown-item(icon="el-icon-message" command="New Instance and Self Invite") {{ $t('dialog.world.actions.new_instance_and_self_invite') }}
|
||||
el-dropdown-item(v-if="API.currentUser.$homeLocation && API.currentUser.$homeLocation.worldId === worldDialog.id" icon="el-icon-magic-stick" command="Reset Home" divided) {{ $t('dialog.world.actions.reset_home') }}
|
||||
el-dropdown-item(v-else icon="el-icon-s-home" command="Make Home" divided) {{ $t('dialog.world.actions.make_home') }}
|
||||
el-dropdown-item(icon="el-icon-tickets" command="Previous Instances") {{ $t('dialog.world.actions.show_previous_instances') }}
|
||||
template(v-if="API.currentUser.id !== worldDialog.ref.authorId")
|
||||
el-dropdown-item(icon="el-icon-picture-outline" command="Previous Images") {{ $t('dialog.world.actions.show_previous_images') }}
|
||||
el-dropdown-item(:disabled="!worldDialog.hasPersistData" icon="el-icon-upload" command="Delete Persistent Data") {{ $t('dialog.world.actions.delete_persistent_data') }}
|
||||
template(v-else)
|
||||
el-dropdown-item(icon="el-icon-edit" command="Rename") {{ $t('dialog.world.actions.rename') }}
|
||||
el-dropdown-item(icon="el-icon-edit" command="Change Description") {{ $t('dialog.world.actions.change_description') }}
|
||||
el-dropdown-item(icon="el-icon-edit" command="Change Capacity") {{ $t('dialog.world.actions.change_capacity') }}
|
||||
el-dropdown-item(icon="el-icon-edit" command="Change Recommended Capacity") {{ $t('dialog.world.actions.change_recommended_capacity') }}
|
||||
el-dropdown-item(icon="el-icon-edit" command="Change YouTube Preview") {{ $t('dialog.world.actions.change_preview') }}
|
||||
el-dropdown-item(icon="el-icon-edit" command="Change Tags") {{ $t('dialog.world.actions.change_tags') }}
|
||||
el-dropdown-item(icon="el-icon-edit" command="Change Allowed Domains") {{ $t('dialog.world.actions.change_allowed_video_player_domains') }}
|
||||
el-dropdown-item(icon="el-icon-picture-outline" command="Change Image") {{ $t('dialog.world.actions.change_image') }}
|
||||
el-dropdown-item(v-if="worldDialog.ref.unityPackageUrl" icon="el-icon-download" command="Download Unity Package") {{ $t('dialog.world.actions.download_package') }}
|
||||
el-dropdown-item(v-if="worldDialog.ref.tags.includes('system_approved') || worldDialog.ref.tags.includes('system_labs')" icon="el-icon-view" command="Unpublish" divided) {{ $t('dialog.world.actions.unpublish') }}
|
||||
el-dropdown-item(v-else icon="el-icon-view" command="Publish" divided) {{ $t('dialog.world.actions.publish_to_labs') }}
|
||||
el-dropdown-item(:disabled="!worldDialog.hasPersistData" icon="el-icon-upload" command="Delete Persistent Data") {{ $t('dialog.world.actions.delete_persistent_data') }}
|
||||
el-dropdown-item(icon="el-icon-delete" command="Delete" style="color:#F56C6C") {{ $t('dialog.world.actions.delete') }}
|
||||
el-tabs
|
||||
el-tab-pane(:label="$t('dialog.world.instances.header')")
|
||||
div.
|
||||
#[i.el-icon-user] {{ $t('dialog.world.instances.public_count', { count: worldDialog.ref.publicOccupants }) }}
|
||||
#[i.el-icon-user-solid(style="margin-left:10px")] {{ $t('dialog.world.instances.private_count', { count: worldDialog.ref.privateOccupants }) }}
|
||||
#[i.el-icon-check(style="margin-left:10px")] {{ $t('dialog.world.instances.capacity_count', { count: worldDialog.ref.recommendedCapacity, max: worldDialog.ref.capacity }) }}
|
||||
div(v-for="room in worldDialog.rooms" :key="room.id")
|
||||
div(style="margin:5px 0")
|
||||
location-world(:locationobject="room.$location" :currentuserid="API.currentUser.id" :worlddialogshortname="worldDialog.$location.shortName")
|
||||
el-tooltip(placement="top" :content="$t('dialog.world.instances.self_invite_tooltip')" :disabled="hideTooltips")
|
||||
invite-yourself(:location="room.$location.tag" :shortname="room.$location.shortName" style="margin-left:5px")
|
||||
el-tooltip(placement="top" :content="$t('dialog.world.instances.refresh_instance_info')" :disabled="hideTooltips")
|
||||
el-button(@click="refreshInstancePlayerCount(room.tag)" size="mini" icon="el-icon-refresh" style="margin-left:5px" circle)
|
||||
last-join(:location="room.$location.tag" :currentlocation="lastLocation.location")
|
||||
instance-info(:location="room.tag" :instance="room.ref" :friendcount="room.friendCount" :updateelement="updateInstanceInfo")
|
||||
.x-friend-list(style="margin:10px 0;max-height:unset" v-if="room.$location.userId || room.users.length")
|
||||
.x-friend-item(v-if="room.$location.userId" @click="showUserDialog(room.$location.userId)" class="x-friend-item-border")
|
||||
template(v-if="room.$location.user")
|
||||
.avatar(:class="userStatusClass(room.$location.user)")
|
||||
img(v-lazy="userImage(room.$location.user)")
|
||||
.detail
|
||||
span.name(v-text="room.$location.user.displayName" :style="{'color':room.$location.user.$userColour}")
|
||||
span.extra {{ $t('dialog.world.instances.instance_creator') }}
|
||||
span(v-else v-text="room.$location.userId")
|
||||
.x-friend-item(v-for="user in room.users" :key="user.id" @click="showUserDialog(user.id)" class="x-friend-item-border")
|
||||
.avatar(:class="userStatusClass(user)")
|
||||
img(v-lazy="userImage(user)")
|
||||
.detail
|
||||
span.name(v-text="user.displayName" :style="{'color':user.$userColour}")
|
||||
span.extra(v-if="user.location === 'traveling'")
|
||||
i.el-icon-loading(style="margin-right:5px")
|
||||
timer(:epoch="user.$travelingToTime")
|
||||
span.extra(v-else)
|
||||
timer(:epoch="user.$location_at")
|
||||
el-tab-pane(:label="$t('dialog.world.info.header')")
|
||||
.x-friend-list(style="max-height:none")
|
||||
.x-friend-item(style="width:100%;cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.world.info.memo') }}
|
||||
el-input.extra(v-model="worldDialog.memo" @change="onWorldMemoChange" type="textarea" :rows="2" :autosize="{ minRows: 1, maxRows: 20 }" :placeholder="$t('dialog.world.info.memo_placeholder')" size="mini" resize="none")
|
||||
div(style="width:100%;display:flex")
|
||||
.x-friend-item(style="width:100%;cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.world.info.id') }}
|
||||
span.extra {{ worldDialog.id }}
|
||||
el-tooltip(placement="top" :content="$t('dialog.world.info.id_tooltip')" :disabled="hideTooltips")
|
||||
el-dropdown(trigger="click" @click.native.stop size="mini" style="margin-left:5px")
|
||||
el-button(type="default" icon="el-icon-s-order" size="mini" circle)
|
||||
el-dropdown-menu(#default="dropdown")
|
||||
el-dropdown-item(@click.native="copyWorldId(worldDialog.id)") {{ $t('dialog.world.info.copy_id') }}
|
||||
el-dropdown-item(@click.native="copyWorldUrl(worldDialog.id)") {{ $t('dialog.world.info.copy_url') }}
|
||||
el-dropdown-item(@click.native="copyWorldName(worldDialog.ref.name)") {{ $t('dialog.world.info.copy_name') }}
|
||||
.x-friend-item(v-if="worldDialog.ref.previewYoutubeId" style="width:350px" @click="openExternalLink(`https://www.youtube.com/watch?v=${worldDialog.ref.previewYoutubeId}`)")
|
||||
.detail
|
||||
span.name {{ $t('dialog.world.info.youtube_preview') }}
|
||||
span.extra https://www.youtube.com/watch?v={{ worldDialog.ref.previewYoutubeId }}
|
||||
.x-friend-item(style="width:100%;cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.world.info.author_tags') }}
|
||||
span.extra(v-if="worldDialog.ref.tags?.filter(tag => tag.startsWith('author_tag')).length > 0") {{ worldDialog.ref.tags.filter(tag => tag.startsWith('author_tag')).map(tag => tag.replace('author_tag_', '')).join(', ') }}
|
||||
span.extra(v-else) -
|
||||
.x-friend-item(style="cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.world.info.players') }}
|
||||
span.extra {{ worldDialog.ref.occupants | commaNumber }}
|
||||
.x-friend-item(style="cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.world.info.favorites') }}
|
||||
span.extra {{ worldDialog.ref.favorites | commaNumber }}
|
||||
| #[template(v-if="worldDialog.ref.favorites > 0 && worldDialog.ref.visits > 0") ({{ Math.round(((worldDialog.ref.favorites - worldDialog.ref.visits) / worldDialog.ref.visits * 100 + 100) * 100) / 100 }}%)]
|
||||
.x-friend-item(style="cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.world.info.visits') }}
|
||||
span.extra {{ worldDialog.ref.visits | commaNumber }}
|
||||
.x-friend-item(style="cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.world.info.capacity') }}
|
||||
span.extra {{ worldDialog.ref.recommendedCapacity | commaNumber }} ({{ worldDialog.ref.capacity | commaNumber }})
|
||||
.x-friend-item(style="cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.world.info.created_at') }}
|
||||
span.extra {{ worldDialog.ref.created_at | formatDate('long') }}
|
||||
.x-friend-item(style="cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.world.info.last_updated') }}
|
||||
span.extra(v-if="worldDialog.lastUpdated") {{ worldDialog.lastUpdated | formatDate('long') }}
|
||||
span.extra(v-else) {{ worldDialog.ref.updated_at | formatDate('long') }}
|
||||
.x-friend-item(v-if="worldDialog.ref.labsPublicationDate !== 'none'" style="cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.world.info.labs_publication_date') }}
|
||||
span.extra {{ worldDialog.ref.labsPublicationDate | formatDate('long') }}
|
||||
.x-friend-item(v-if="worldDialog.ref.publicationDate !== 'none'" style="cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.world.info.publication_date') }}
|
||||
el-tooltip(v-if="worldDialog.ref.publicationDate && worldDialog.ref.publicationDate !== 'none' && worldDialog.ref.labsPublicationDate && worldDialog.ref.labsPublicationDate !== 'none'" placement="top" style="margin-left:5px")
|
||||
template(#content)
|
||||
span {{ $t('dialog.world.info.time_in_labs') }} {{ timeToText(new Date(worldDialog.ref.publicationDate) - new Date(worldDialog.ref.labsPublicationDate)) }}
|
||||
i.el-icon-arrow-down
|
||||
span.extra {{ worldDialog.ref.publicationDate | formatDate('long') }}
|
||||
.x-friend-item(style="cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.world.info.version') }}
|
||||
span.extra(v-text="worldDialog.ref.version")
|
||||
.x-friend-item(style="cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.world.info.heat') }}
|
||||
span.extra {{ worldDialog.ref.heat | commaNumber }} {{ '🔥'.repeat(worldDialog.ref.heat) }}
|
||||
.x-friend-item(style="cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.world.info.popularity') }}
|
||||
span.extra {{ worldDialog.ref.popularity | commaNumber }} {{ '💖'.repeat(worldDialog.ref.popularity) }}
|
||||
.x-friend-item(style="width:100%;cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.world.info.platform') }}
|
||||
span.extra(v-text="worldDialogPlatform")
|
||||
.x-friend-item(style="cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.world.info.last_visited') }}
|
||||
el-tooltip(v-if="!hideTooltips" placement="top" style="margin-left:5px" :content="$t('dialog.world.info.accuracy_notice')")
|
||||
i.el-icon-warning
|
||||
span.extra {{ worldDialog.lastVisit | formatDate('long') }}
|
||||
.x-friend-item(@click="showPreviousInstancesWorldDialog(worldDialog.ref)")
|
||||
.detail
|
||||
span.name {{ $t('dialog.world.info.visit_count') }}
|
||||
el-tooltip(v-if="!hideTooltips" placement="top" style="margin-left:5px" :content="$t('dialog.world.info.accuracy_notice')")
|
||||
i.el-icon-warning
|
||||
span.extra(v-text="worldDialog.visitCount")
|
||||
.x-friend-item(style="cursor:default")
|
||||
.detail
|
||||
span.name {{ $t('dialog.world.info.time_spent') }}
|
||||
el-tooltip(v-if="!hideTooltips" placement="top" style="margin-left:5px" :content="$t('dialog.world.info.accuracy_notice')")
|
||||
i.el-icon-warning
|
||||
span.extra(v-if="worldDialog.timeSpent === 0") -
|
||||
span.extra(v-else) {{ timeToText(worldDialog.timeSpent) }}
|
||||
el-tab-pane(:label="$t('dialog.world.json.header')")
|
||||
el-button(type="default" @click="refreshWorldDialogTreeData()" size="mini" icon="el-icon-refresh" circle)
|
||||
el-button(type="default" @click="downloadAndSaveJson(worldDialog.id, worldDialog.ref)" size="mini" icon="el-icon-download" circle style="margin-left:5px")
|
||||
el-tree(:data="worldDialog.treeData" style="margin-top:5px;font-size:12px")
|
||||
template(#default="scope")
|
||||
span
|
||||
span(v-text="scope.data.key" style="font-weight:bold;margin-right:5px")
|
||||
span(v-if="!scope.data.children" v-text="scope.data.value")
|
||||
|
||||
//- dialog: change Allowed Video Player Domains
|
||||
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="worldAllowedDomainsDialog" :visible.sync="worldAllowedDomainsDialog.visible" :title="$t('dialog.allowed_video_player_domains.header')" width="600px")
|
||||
div(v-loading="bioDialog.loading")
|
||||
el-input(v-for="(domain, index) in worldAllowedDomainsDialog.urlList" :key="index" :value="domain" v-model="worldAllowedDomainsDialog.urlList[index]" size="small" style="margin-top:5px")
|
||||
el-button(slot="append" icon="el-icon-delete" @click="worldAllowedDomainsDialog.urlList.splice(index, 1)")
|
||||
el-button(@click="worldAllowedDomainsDialog.urlList.push('')" size="mini" style="margin-top:5px") {{ $t('dialog.allowed_video_player_domains.add_domain') }}
|
||||
template(#footer)
|
||||
el-button(type="primary" size="small" :disabled="!worldAllowedDomainsDialog.worldId" @click="saveWorldAllowedDomains") {{ $t('dialog.allowed_video_player_domains.save') }}
|
||||
113
src/mixins/friendsListSidebar.pug
Normal file
@@ -0,0 +1,113 @@
|
||||
|
||||
mixin friendsListSidebar()
|
||||
.x-aside-container(v-show="$refs.menu && $refs.menu.activeIndex !== 'friendsList'" id="aside")
|
||||
div(style="display:flex;align-items:baseline")
|
||||
el-select(v-model="quickSearch" clearable :placeholder="$t('side_panel.search_placeholder')" filterable remote :remote-method="quickSearchRemoteMethod" popper-class="x-quick-search" @change="quickSearchChange" @visible-change="quickSearchVisibleChange" style="flex:1;padding:10px")
|
||||
el-option(v-for="item in quickSearchItems" :key="item.value" :value="item.value" :label="item.label")
|
||||
.x-friend-item
|
||||
template(v-if="item.ref")
|
||||
.detail
|
||||
span.name(v-text="item.ref.displayName" :style="{'color':item.ref.$userColour}")
|
||||
span.extra(v-if="!item.ref.isFriend")
|
||||
span.extra(v-else-if="item.ref.state === 'offline'") {{ $t('side_panel.search_result_active') }}
|
||||
span.extra(v-else-if="item.ref.state === 'active'") {{ $t('side_panel.search_result_offline') }}
|
||||
location.extra(v-else :location="item.ref.location" :traveling="item.ref.travelingToLocation" :link="false")
|
||||
img.avatar(v-lazy="userImage(item.ref)")
|
||||
span(v-else) {{ $t('side_panel.search_result_more') }} #[span(v-text="item.label" style="font-weight:bold")]
|
||||
el-tooltip(placement="bottom" :content="$t('side_panel.direct_access_tooltip')" :disabled="hideTooltips")
|
||||
el-button(type="default" @click="directAccessPaste" size="mini" icon="el-icon-discover" circle)
|
||||
el-tooltip(placement="bottom" :content="$t('side_panel.refresh_tooltip')" :disabled="hideTooltips")
|
||||
el-button(type="default" @click="refreshFriendsList" :loading="API.isRefreshFriendsLoading" size="mini" icon="el-icon-refresh" circle style="margin-right:10px")
|
||||
el-tabs.zero-margin-tabs(stretch="true" style="height:calc(100% - 60px;margin-top:5px")
|
||||
el-tab-pane
|
||||
template(#label)
|
||||
span {{ $t('side_panel.friends') }}
|
||||
span(style="color:#909399;font-size:12px;margin-left:10px") ({{ onlineFriendCount }}/{{ friends.size }})
|
||||
.x-friend-list(style="padding:10px 5px")
|
||||
.x-friend-group.x-link(@click="isFriendsGroupMe = !isFriendsGroupMe; saveFriendsGroupStates()" style="padding:0px 0px 5px")
|
||||
i.el-icon-arrow-right(:class="{ rotate: isFriendsGroupMe }")
|
||||
span(style="margin-left:5px") {{ $t('side_panel.me') }}
|
||||
div(v-show="isFriendsGroupMe")
|
||||
.x-friend-item(:key="API.currentUser.id" @click="showUserDialog(API.currentUser.id)")
|
||||
.avatar(:class="userStatusClass(API.currentUser)")
|
||||
img(v-lazy="userImage(API.currentUser)")
|
||||
.detail
|
||||
span.name(v-text="API.currentUser.displayName" :style="{'color':API.currentUser.$userColour}")
|
||||
location.extra(v-if="isGameRunning && !gameLogDisabled" :location="lastLocation.location" :traveling="lastLocationDestination" :link="false")
|
||||
location.extra(v-else-if="isRealInstance(API.currentUser.$locationTag) || isRealInstance(API.currentUser.$travelingToLocation)" :location="API.currentUser.$locationTag" :traveling="API.currentUser.$travelingToLocation" :link="false")
|
||||
span.extra(v-else v-text="API.currentUser.statusDescription")
|
||||
.x-friend-group.x-link(@click="isVIPFriends = !isVIPFriends; saveFriendsGroupStates()" v-show="vipFriends.length")
|
||||
i.el-icon-arrow-right(:class="{ rotate: isVIPFriends }")
|
||||
span(style="margin-left:5px") {{ $t('side_panel.favorite') }} ― {{ vipFriends.length }}
|
||||
div(v-show="isVIPFriends")
|
||||
.x-friend-item(v-for="friend in vipFriends" :key="friend.id" @click="showUserDialog(friend.id)")
|
||||
template(v-if="friend.ref")
|
||||
.avatar(:class="userStatusClass(friend.ref, friend.pendingOffline)")
|
||||
img(v-lazy="userImage(friend.ref)")
|
||||
.detail
|
||||
span.name(v-if="!hideNicknames && friend.$nickName" :style="{'color':friend.ref.$userColour}") {{ friend.ref.displayName }} ({{ friend.$nickName }})
|
||||
span.name(v-else v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
|
||||
span.extra(v-if="friend.pendingOffline") #[i.el-icon-warning-outline] {{ $t('side_panel.pending_offline') }}
|
||||
location.extra(v-else :location="friend.ref.location" :traveling="friend.ref.travelingToLocation" :link="false")
|
||||
template(v-else)
|
||||
span(v-text="friend.name || friend.id")
|
||||
el-button(type="text" icon="el-icon-close" size="mini" @click.stop="confirmDeleteFriend(friend.id)" style="margin-left:5px")
|
||||
.x-friend-group.x-link(@click="isOnlineFriends = !isOnlineFriends; saveFriendsGroupStates()" v-show="onlineFriends.length")
|
||||
i.el-icon-arrow-right(:class="{ rotate: isOnlineFriends }")
|
||||
span(style="margin-left:5px") {{ $t('side_panel.online') }} ― {{ onlineFriends.length }}
|
||||
div(v-show="isOnlineFriends")
|
||||
.x-friend-item(v-for="friend in onlineFriends" :key="friend.id" @click="showUserDialog(friend.id)")
|
||||
template(v-if="friend.ref")
|
||||
.avatar(:class="userStatusClass(friend.ref, friend.pendingOffline)")
|
||||
img(v-lazy="userImage(friend.ref)")
|
||||
.detail
|
||||
span.name(v-if="!hideNicknames && friend.$nickName" :style="{'color':friend.ref.$userColour}") {{ friend.ref.displayName }} ({{ friend.$nickName }})
|
||||
span.name(v-else v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
|
||||
span.extra(v-if="friend.pendingOffline") #[i.el-icon-warning-outline] {{ $t('side_panel.pending_offline') }}
|
||||
location.extra(v-else :location="friend.ref.location" :traveling="friend.ref.travelingToLocation" :link="false")
|
||||
template(v-else)
|
||||
span(v-text="friend.name || friend.id")
|
||||
el-button(type="text" icon="el-icon-close" size="mini" @click.stop="confirmDeleteFriend(friend.id)" style="margin-left:5px")
|
||||
.x-friend-group.x-link(@click="isActiveFriends = !isActiveFriends; saveFriendsGroupStates()" v-show="activeFriends.length")
|
||||
i.el-icon-arrow-right(:class="{ rotate: isActiveFriends }")
|
||||
span(style="margin-left:5px") {{ $t('side_panel.active') }} ― {{ activeFriends.length }}
|
||||
div(v-show="isActiveFriends")
|
||||
.x-friend-item(v-for="friend in activeFriends" :key="friend.id" @click="showUserDialog(friend.id)")
|
||||
template(v-if="friend.ref")
|
||||
.avatar
|
||||
img(v-lazy="userImage(friend.ref)")
|
||||
.detail
|
||||
span.name(v-if="!hideNicknames && friend.$nickName" :style="{'color':friend.ref.$userColour}") {{ friend.ref.displayName }} ({{ friend.$nickName }})
|
||||
span.name(v-else v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
|
||||
span.extra(v-text="friend.ref.statusDescription" :link="false")
|
||||
template(v-else)
|
||||
span(v-text="friend.name || friend.id")
|
||||
el-button(type="text" icon="el-icon-close" size="mini" @click.stop="confirmDeleteFriend(friend.id)" style="margin-left:5px")
|
||||
.x-friend-group.x-link(@click="isOfflineFriends = !isOfflineFriends; saveFriendsGroupStates()" v-show="offlineFriends.length")
|
||||
i.el-icon-arrow-right(:class="{ rotate: isOfflineFriends }")
|
||||
span(style="margin-left:5px") {{ $t('side_panel.offline') }} ― {{ offlineFriends.length }}
|
||||
div(v-show="isOfflineFriends")
|
||||
.x-friend-item(v-for="friend in offlineFriends" :key="friend.id" @click="showUserDialog(friend.id)")
|
||||
template(v-if="friend.ref")
|
||||
.avatar
|
||||
img(v-lazy="userImage(friend.ref)")
|
||||
.detail
|
||||
span.name(v-if="!hideNicknames && friend.$nickName" :style="{'color':friend.ref.$userColour}") {{ friend.ref.displayName }} ({{ friend.$nickName }})
|
||||
span.name(v-else v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
|
||||
span.extra(v-text="friend.ref.statusDescription")
|
||||
template(v-else)
|
||||
span(v-text="friend.name || friend.id")
|
||||
el-button(type="text" icon="el-icon-close" size="mini" @click.stop="confirmDeleteFriend(friend.id)" style="margin-left:5px")
|
||||
el-tab-pane
|
||||
template(#label)
|
||||
span {{ $t('side_panel.groups') }}
|
||||
span(style="color:#909399;font-size:12px;margin-left:10px") ({{ groupInstances.length }})
|
||||
.x-friend-list(style="padding:10px 5px")
|
||||
.x-friend-item(v-for="ref in groupInstances" :key="ref.instance.id" @click="showGroupDialog(ref.instance.ownerId)")
|
||||
.avatar
|
||||
img(v-lazy="ref.group.iconUrl")
|
||||
.detail
|
||||
span.name
|
||||
span(v-text="ref.group.name")
|
||||
span(style="font-weight:normal;margin-left:5px") ({{ ref.instance.userCount }}/{{ ref.instance.capacity }})
|
||||
location.extra(:location="ref.instance.location" :link="false")
|
||||
49
src/mixins/loginPage.pug
Normal file
@@ -0,0 +1,49 @@
|
||||
mixin loginPage()
|
||||
.x-login-container(v-if="!API.isLoggedIn" v-loading="loginForm.loading")
|
||||
.x-login
|
||||
div(style="position:fixed; top: 0; left: 0; margin:5px")
|
||||
el-tooltip(placement="top" :content="$t('view.login.updater')" :disabled="hideTooltips")
|
||||
el-button(type="default" @click="showVRCXUpdateDialog" size="mini" icon="el-icon-download" circle)
|
||||
el-tooltip(placement="top" :content="$t('view.login.proxy_settings')" :disabled="hideTooltips")
|
||||
el-button(type="default" @click="promptProxySettings" size="mini" icon="el-icon-connection" style="margin-left:5px" circle)
|
||||
|
||||
div.x-login-form-container
|
||||
div
|
||||
h2(style="font-weight:bold;text-align:center;margin:0") {{ $t("view.login.login") }}
|
||||
el-form(ref="loginForm" :model="loginForm" :rules="loginForm.rules" @submit.native.prevent="login()")
|
||||
el-form-item(:label="$t('view.login.field.username')" prop="username" required)
|
||||
el-input(v-model="loginForm.username" name="username" :placeholder="$t('view.login.field.username')" clearable)
|
||||
el-form-item(:label="$t('view.login.field.password')" prop="password" required style="margin-top:10px")
|
||||
el-input(type="password" v-model="loginForm.password" name="password" :placeholder="$t('view.login.field.password')" clearable show-password)
|
||||
el-checkbox(v-model="loginForm.saveCredentials" style="margin-top:15px") {{ $t("view.login.field.saveCredentials") }}
|
||||
el-checkbox(v-model="enableCustomEndpoint" @change="toggleCustomEndpoint" style="margin-top:10px") {{ $t("view.login.field.devEndpoint") }}
|
||||
el-form-item(v-if="enableCustomEndpoint" :label="$t('view.login.field.endpoint')" prop="endpoint" style="margin-top:10px")
|
||||
el-input(v-model="loginForm.endpoint" name="endpoint" :placeholder="API.endpointDomainVrchat" clearable)
|
||||
el-form-item(v-if="enableCustomEndpoint" :label="$t('view.login.field.websocket')" prop="endpoint" style="margin-top:10px")
|
||||
el-input(v-model="loginForm.websocket" name="websocket" :placeholder="API.websocketDomainVrchat" clearable)
|
||||
el-form-item(style="margin-top:15px")
|
||||
el-button(native-type="submit" type="primary" style="width:100%") {{ $t("view.login.login") }}
|
||||
el-button(type="primary" @click="openExternalLink('https://vrchat.com/register')" style="width:100%") {{ $t("view.login.register") }}
|
||||
|
||||
hr.x-vertical-divider(v-if="Object.keys(loginForm.savedCredentials).length !== 0")/
|
||||
|
||||
div(v-if="Object.keys(loginForm.savedCredentials).length !== 0")
|
||||
h2(style="font-weight:bold;text-align:center;margin:0") {{ $t("view.login.savedAccounts") }}
|
||||
.x-scroll-wrapper(style="margin-top:10px")
|
||||
.x-saved-account-list
|
||||
.x-friend-item(v-for="user in loginForm.savedCredentials" :key="user.user.id" @click="relogin(user)")
|
||||
.avatar
|
||||
img(v-lazy="userImage(user.user)")
|
||||
.detail
|
||||
span.name(v-text="user.user.displayName")
|
||||
span.extra(v-text="user.user.username")
|
||||
span.extra(v-text="user.loginParmas.endpoint")
|
||||
el-button(type="default" @click.stop="deleteSavedLogin(user.user.id)" size="mini" icon="el-icon-delete" style="margin-left:10px" circle)
|
||||
|
||||
div.x-legal-notice-container
|
||||
div(style="text-align:center;font-size:12px")
|
||||
p #[a.x-link(@click="openExternalLink('https://vrchat.com/home/password')") {{ $t("view.login.forgotPassword") }}]
|
||||
p © 2019-2024 #[a.x-link(@click="openExternalLink('https://github.com/pypy-vrc')") pypy] & #[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") }}
|
||||
326
src/mixins/tabs/favorites.pug
Normal 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
@@ -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")
|
||||
24
src/mixins/tabs/friendLog.pug
Normal 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]
|
||||
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)")
|
||||
85
src/mixins/tabs/friendsList.pug
Normal 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)")
|
||||
57
src/mixins/tabs/gameLog.pug
Normal 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)")
|
||||
30
src/mixins/tabs/moderation.pug
Normal 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)")
|
||||
95
src/mixins/tabs/notifications.pug
Normal 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)")
|
||||
271
src/mixins/tabs/playerList.pug
Normal 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 }})]
|
||||
| ― #[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)")
|
||||
|
|
||||
span(v-if="!scope.row.inCache" style="color:#aaa") #[i.el-icon-download]
|
||||
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
|
||||
span(v-else-if="scope.row.platform === 'VR'" style="color:#409eff") VR
|
||||
span(v-else-if="scope.row.platform === 'Quest'" style="color:#67c23a") Android
|
||||
span.x-link(v-text="scope.row.avatar.name" @click="showAvatarDialog(scope.row.avatar.id)")
|
||||
|
|
||||
span(v-if="!scope.row.inCache" style="color:#aaa") #[i.el-icon-download]
|
||||
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)")
|
||||
|
|
||||
span(v-if="!scope.row.inCache" style="color:#aaa") #[i.el-icon-download]
|
||||
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
|
||||
span(v-else-if="scope.row.platform === 'VR'" style="color:#409eff") VR
|
||||
span(v-else-if="scope.row.platform === 'Quest'" style="color:#67c23a") Android
|
||||
span.x-link(v-text="scope.row.avatar.name" @click="showAvatarDialog(scope.row.avatar.id)")
|
||||
|
|
||||
span(v-if="!scope.row.inCache" style="color:#aaa") #[i.el-icon-download]
|
||||
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
@@ -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")
|
||||
93
src/mixins/tabs/search.pug
Normal 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') }}
|
||||
603
src/mixins/tabs/settings.pug
Normal 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 © 2019-2024 #[a.x-link(@click="openExternalLink('https://github.com/pypy-vrc')") pypy] & #[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")]
|
||||
80
src/repository/config.js
Normal file
@@ -0,0 +1,80 @@
|
||||
import sqliteService from '../service/sqlite.js';
|
||||
import sharedRepository, { SharedRepository } from './shared.js';
|
||||
import * as workerTimers from 'worker-timers';
|
||||
|
||||
var dirtyKeySet = new Set();
|
||||
|
||||
function transformKey(key) {
|
||||
return `config:${String(key).toLowerCase()}`;
|
||||
}
|
||||
|
||||
async function syncLoop() {
|
||||
if (dirtyKeySet.size > 0) {
|
||||
try {
|
||||
await sqliteService.executeNonQuery('BEGIN');
|
||||
try {
|
||||
for (var key of dirtyKeySet) {
|
||||
var value = await sharedRepository.getString(key);
|
||||
if (value === null) {
|
||||
await sqliteService.executeNonQuery(
|
||||
'DELETE FROM configs WHERE `key` = @key',
|
||||
{
|
||||
'@key': key
|
||||
}
|
||||
);
|
||||
} else {
|
||||
await sqliteService.executeNonQuery(
|
||||
'INSERT OR REPLACE INTO configs (`key`, `value`) VALUES (@key, @value)',
|
||||
{
|
||||
'@key': key,
|
||||
'@value': value
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
dirtyKeySet.clear();
|
||||
} finally {
|
||||
await sqliteService.executeNonQuery('COMMIT');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
workerTimers.setTimeout(() => syncLoop(), 100);
|
||||
}
|
||||
|
||||
class ConfigRepository extends SharedRepository {
|
||||
async init() {
|
||||
await sqliteService.executeNonQuery(
|
||||
'CREATE TABLE IF NOT EXISTS configs (`key` TEXT PRIMARY KEY, `value` TEXT)'
|
||||
);
|
||||
await sqliteService.execute(
|
||||
([key, value]) => sharedRepository.setString(key, value),
|
||||
'SELECT `key`, `value` FROM configs'
|
||||
);
|
||||
syncLoop();
|
||||
}
|
||||
|
||||
async remove(key) {
|
||||
var _key = transformKey(key);
|
||||
await sharedRepository.remove(_key);
|
||||
dirtyKeySet.add(_key);
|
||||
}
|
||||
|
||||
getString(key, defaultValue = null) {
|
||||
var _key = transformKey(key);
|
||||
return sharedRepository.getString(_key, defaultValue);
|
||||
}
|
||||
|
||||
async setString(key, value) {
|
||||
var _key = transformKey(key);
|
||||
var _value = String(value);
|
||||
await sharedRepository.setString(_key, _value);
|
||||
dirtyKeySet.add(_key);
|
||||
}
|
||||
}
|
||||
|
||||
var self = new ConfigRepository();
|
||||
window.configRepository = self;
|
||||
|
||||
export { self as default, ConfigRepository };
|
||||
2731
src/repository/database.js
Normal file
106
src/repository/shared.js
Normal file
@@ -0,0 +1,106 @@
|
||||
// requires binding of SharedVariable
|
||||
|
||||
function transformKey(key) {
|
||||
return String(key).toLowerCase();
|
||||
}
|
||||
|
||||
class SharedRepository {
|
||||
remove(key) {
|
||||
var _key = transformKey(key);
|
||||
return SharedVariable.Remove(_key);
|
||||
}
|
||||
|
||||
async getString(key, defaultValue = null) {
|
||||
var _key = transformKey(key);
|
||||
var value = await SharedVariable.Get(_key);
|
||||
if (value === null || value === undefined) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
async setString(key, value) {
|
||||
var _key = transformKey(key);
|
||||
var _value = String(value);
|
||||
await SharedVariable.Set(_key, _value);
|
||||
}
|
||||
|
||||
async getBool(key, defaultValue = null) {
|
||||
var value = await this.getString(key, null);
|
||||
if (value === null || value === undefined) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value === 'true';
|
||||
}
|
||||
|
||||
async setBool(key, value) {
|
||||
await this.setString(key, value ? 'true' : 'false');
|
||||
}
|
||||
|
||||
async getInt(key, defaultValue = null) {
|
||||
var value = await this.getString(key, null);
|
||||
if (value === null || value === undefined) {
|
||||
return defaultValue;
|
||||
}
|
||||
value = parseInt(value, 10);
|
||||
if (isNaN(value) === true) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
async setInt(key, value) {
|
||||
await this.setString(key, value);
|
||||
}
|
||||
|
||||
async getFloat(key, defaultValue = null) {
|
||||
var value = await this.getString(key, null);
|
||||
if (value === null || value === undefined) {
|
||||
return defaultValue;
|
||||
}
|
||||
value = parseFloat(value);
|
||||
if (isNaN(value) === true) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
async setFloat(key, value) {
|
||||
await this.setString(key, value);
|
||||
}
|
||||
|
||||
async getObject(key, defaultValue = null) {
|
||||
var value = await this.getString(key, null);
|
||||
if (value === null || value === undefined) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
} catch (err) {}
|
||||
if (value !== Object(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
async setObject(key, value) {
|
||||
await this.setString(key, JSON.stringify(value));
|
||||
}
|
||||
|
||||
async getArray(key, defaultValue = null) {
|
||||
var value = await this.getObject(key, null);
|
||||
if (Array.isArray(value) === false) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
async setArray(key, value) {
|
||||
await this.setObject(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
var self = new SharedRepository();
|
||||
window.sharedRepository = self;
|
||||
|
||||
export { self as default, SharedRepository };
|
||||
67
src/security.js
Normal file
@@ -0,0 +1,67 @@
|
||||
const defaultAESKey = new TextEncoder().encode(
|
||||
'https://github.com/pypy-vrc/VRCX'
|
||||
);
|
||||
|
||||
const hexToUint8Array = (hexStr) => {
|
||||
const r = hexStr.match(/.{1,2}/g);
|
||||
if (!r) return null;
|
||||
return new Uint8Array(r.map((b) => parseInt(b, 16)));
|
||||
};
|
||||
|
||||
const uint8ArrayToHex = (arr) =>
|
||||
arr.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');
|
||||
|
||||
function stdAESKey(key) {
|
||||
const tKey = new TextEncoder().encode(key);
|
||||
let sk = tKey;
|
||||
if (key.length < 32) {
|
||||
sk = new Uint8Array(32);
|
||||
sk.set(tKey);
|
||||
sk.set(defaultAESKey.slice(key.length, 32), key.length);
|
||||
}
|
||||
return sk.slice(0, 32);
|
||||
}
|
||||
|
||||
async function encrypt(plaintext, key) {
|
||||
let iv = window.crypto.getRandomValues(new Uint8Array(12));
|
||||
let sharedKey = await window.crypto.subtle.importKey(
|
||||
'raw',
|
||||
stdAESKey(key),
|
||||
{ name: 'AES-GCM', length: 256 },
|
||||
true,
|
||||
['encrypt']
|
||||
);
|
||||
let cipher = await window.crypto.subtle.encrypt(
|
||||
{ name: 'AES-GCM', iv },
|
||||
sharedKey,
|
||||
new TextEncoder().encode(plaintext)
|
||||
);
|
||||
let ciphertext = new Uint8Array(cipher);
|
||||
let encrypted = new Uint8Array(iv.length + ciphertext.byteLength);
|
||||
encrypted.set(iv, 0);
|
||||
encrypted.set(ciphertext, iv.length);
|
||||
return uint8ArrayToHex(encrypted);
|
||||
}
|
||||
|
||||
async function decrypt(ciphertext, key) {
|
||||
let text = hexToUint8Array(ciphertext);
|
||||
if (!text) return '';
|
||||
let sharedKey = await window.crypto.subtle.importKey(
|
||||
'raw',
|
||||
stdAESKey(key),
|
||||
{ name: 'AES-GCM', length: 256 },
|
||||
true,
|
||||
['decrypt']
|
||||
);
|
||||
let plaintext = await window.crypto.subtle.decrypt(
|
||||
{ name: 'AES-GCM', iv: text.slice(0, 12) },
|
||||
sharedKey,
|
||||
text.slice(12)
|
||||
);
|
||||
return new TextDecoder().decode(new Uint8Array(plaintext));
|
||||
}
|
||||
|
||||
export default {
|
||||
decrypt,
|
||||
encrypt
|
||||
};
|
||||
132
src/service/gamelog.js
Normal file
@@ -0,0 +1,132 @@
|
||||
// requires binding of LogWatcher
|
||||
|
||||
class GameLogService {
|
||||
parseRawGameLog(dt, type, args) {
|
||||
var gameLog = {
|
||||
dt,
|
||||
type
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case 'location':
|
||||
gameLog.location = args[0];
|
||||
gameLog.worldName = args[1];
|
||||
break;
|
||||
|
||||
case 'location-destination':
|
||||
gameLog.location = args[0];
|
||||
break;
|
||||
|
||||
case 'player-joined':
|
||||
gameLog.displayName = args[0];
|
||||
gameLog.userId = args[1];
|
||||
break;
|
||||
|
||||
case 'player-left':
|
||||
gameLog.displayName = args[0];
|
||||
gameLog.userId = args[1];
|
||||
break;
|
||||
|
||||
case 'notification':
|
||||
gameLog.json = args[0];
|
||||
break;
|
||||
|
||||
case 'portal-spawn':
|
||||
break;
|
||||
|
||||
case 'event':
|
||||
gameLog.event = args[0];
|
||||
break;
|
||||
|
||||
case 'video-play':
|
||||
gameLog.videoUrl = args[0];
|
||||
gameLog.displayName = args[1];
|
||||
break;
|
||||
|
||||
case 'resource-load-string':
|
||||
case 'resource-load-image':
|
||||
gameLog.resourceUrl = args[0];
|
||||
break;
|
||||
|
||||
case 'video-sync':
|
||||
gameLog.timestamp = args[0];
|
||||
break;
|
||||
|
||||
case 'vrcx':
|
||||
gameLog.data = args[0];
|
||||
break;
|
||||
|
||||
case 'api-request':
|
||||
gameLog.url = args[0];
|
||||
break;
|
||||
|
||||
case 'avatar-change':
|
||||
gameLog.displayName = args[0];
|
||||
gameLog.avatarName = args[1];
|
||||
break;
|
||||
|
||||
case 'photon-id':
|
||||
gameLog.displayName = args[0];
|
||||
gameLog.photonId = args[1];
|
||||
break;
|
||||
|
||||
case 'screenshot':
|
||||
gameLog.screenshotPath = args[0];
|
||||
break;
|
||||
|
||||
case 'vrc-quit':
|
||||
break;
|
||||
|
||||
case 'openvr-init':
|
||||
break;
|
||||
|
||||
case 'desktop-mode':
|
||||
break;
|
||||
|
||||
case 'udon-exception':
|
||||
gameLog.data = args[0];
|
||||
break;
|
||||
|
||||
case 'sticker-spawn':
|
||||
gameLog.userId = args[0];
|
||||
gameLog.displayName = args[1];
|
||||
gameLog.fileId = args[2];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return gameLog;
|
||||
}
|
||||
|
||||
async getAll() {
|
||||
var gameLogs = [];
|
||||
var done = false;
|
||||
while (!done) {
|
||||
var rawGameLogs = await LogWatcher.Get();
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
for (var [fileName, dt, type, ...args] of rawGameLogs) {
|
||||
var gameLog = this.parseRawGameLog(dt, type, args);
|
||||
gameLogs.push(gameLog);
|
||||
}
|
||||
if (rawGameLogs.length === 0) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
return gameLogs;
|
||||
}
|
||||
|
||||
async setDateTill(dateTill) {
|
||||
await LogWatcher.SetDateTill(dateTill);
|
||||
}
|
||||
|
||||
async reset() {
|
||||
await LogWatcher.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
var self = new GameLogService();
|
||||
window.gameLogService = self;
|
||||
|
||||
export { self as default, GameLogService as LogWatcherService };
|
||||
39
src/service/sqlite.js
Normal file
@@ -0,0 +1,39 @@
|
||||
// requires binding of SQLite
|
||||
|
||||
class SQLiteService {
|
||||
async execute(callback, sql, args = null) {
|
||||
if (LINUX) {
|
||||
if (args) {
|
||||
args = new Map(Object.entries(args));
|
||||
}
|
||||
var json = await SQLite.ExecuteJson(sql, args);
|
||||
var items = JSON.parse(json);
|
||||
if (json.status === 'error') {
|
||||
throw new Error(json.message);
|
||||
}
|
||||
items.data.forEach((item) => {
|
||||
callback(item);
|
||||
});
|
||||
return;
|
||||
}
|
||||
var item = await SQLite.Execute(sql, args);
|
||||
if (item.Item1 !== null) {
|
||||
throw item.Item1;
|
||||
}
|
||||
item.Item2?.forEach((item) => {
|
||||
callback(item);
|
||||
});
|
||||
}
|
||||
|
||||
executeNonQuery(sql, args = null) {
|
||||
if (LINUX && args) {
|
||||
args = new Map(Object.entries(args));
|
||||
}
|
||||
return SQLite.ExecuteNonQuery(sql, args);
|
||||
}
|
||||
}
|
||||
|
||||
var self = new SQLiteService();
|
||||
window.sqliteService = self;
|
||||
|
||||
export { self as default, SQLiteService };
|
||||
47
src/service/webapi.js
Normal file
@@ -0,0 +1,47 @@
|
||||
// requires binding of WebApi
|
||||
|
||||
class WebApiService {
|
||||
clearCookies() {
|
||||
return WebApi.ClearCookies();
|
||||
}
|
||||
|
||||
getCookies() {
|
||||
return WebApi.GetCookies();
|
||||
}
|
||||
|
||||
setCookies(cookie) {
|
||||
return WebApi.SetCookies(cookie);
|
||||
}
|
||||
|
||||
async execute(options) {
|
||||
if (!options) {
|
||||
throw new Error('options is required');
|
||||
}
|
||||
if (LINUX) {
|
||||
const requestJson = JSON.stringify(options);
|
||||
var json = await WebApi.ExecuteJson(requestJson);
|
||||
var data = JSON.parse(json);
|
||||
if (data.status === -1) {
|
||||
throw new Error(data.message);
|
||||
}
|
||||
return {
|
||||
status: data.status,
|
||||
data: data.message
|
||||
};
|
||||
}
|
||||
|
||||
var item = await WebApi.Execute(options);
|
||||
if (item.Item1 === -1) {
|
||||
throw item.Item2;
|
||||
}
|
||||
return {
|
||||
status: item.Item1,
|
||||
data: item.Item2
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var self = new WebApiService();
|
||||
window.webApiService = self;
|
||||
|
||||
export { self as default, WebApiService };
|
||||
414
src/theme.dark.scss
Normal file
@@ -0,0 +1,414 @@
|
||||
@charset "utf-8";
|
||||
|
||||
//
|
||||
// Copyright(c) 2019-2021 pypy and individual contributors.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This work is licensed under the terms of the MIT license.
|
||||
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||
//
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
:root {
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
background-color: #101010;
|
||||
}
|
||||
|
||||
body,
|
||||
input,
|
||||
textarea,
|
||||
select,
|
||||
button {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.el-loading-mask {
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
.el-input__inner,
|
||||
.el-textarea__inner,
|
||||
.el-textarea .el-input__count,
|
||||
.el-input .el-input__count .el-input__count-inner {
|
||||
color: #fff;
|
||||
background-color: #444;
|
||||
border: #333;
|
||||
}
|
||||
|
||||
.x-friend-item .el-textarea .el-input__count {
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
// User dialog memo: input count background color
|
||||
.x-friend-item:hover .el-input__count {
|
||||
background: #3e3e3e;
|
||||
}
|
||||
|
||||
.el-input-group__append,
|
||||
.el-input-group__prepend {
|
||||
color: #fff;
|
||||
background-color: #666;
|
||||
border: #555;
|
||||
}
|
||||
|
||||
.el-input-number__decrease,
|
||||
.el-input-number__increase {
|
||||
background: unset;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.el-input-number__decrease {
|
||||
border-right: 1px solid #dcdfe633;
|
||||
}
|
||||
|
||||
.el-input-number__increase {
|
||||
border-left: 1px solid #dcdfe633;
|
||||
}
|
||||
|
||||
.el-table tr,
|
||||
.el-table td.el-table__cell,
|
||||
.el-table th.el-table__cell {
|
||||
background-color: #292929;
|
||||
border-bottom: 1px solid #5f5f5f;
|
||||
}
|
||||
|
||||
.el-table--border::after,
|
||||
.el-table--group::after,
|
||||
.el-table::before {
|
||||
background-color: #5f5f5f;
|
||||
}
|
||||
|
||||
.el-table--striped .el-table__body tr.el-table__row--striped td.el-table__cell {
|
||||
background-color: #202020;
|
||||
}
|
||||
|
||||
.el-table--enable-row-hover .el-table__body tr:hover > td.el-table__cell {
|
||||
background-color: #323232;
|
||||
}
|
||||
|
||||
.el-pagination .btn-next,
|
||||
.el-pagination .btn-prev {
|
||||
color: #bbb;
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
.el-pagination button:disabled {
|
||||
color: #101010;
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
.el-dialog,
|
||||
.el-pager li {
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
.el-pager li {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.el-table {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.el-pagination__total {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.el-tag--plain.el-tag--info {
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
.el-collapse-item .el-tag--mini {
|
||||
background-color: #333;
|
||||
border: transparent;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
color: #c5cad6;
|
||||
}
|
||||
|
||||
.el-button:not(.el-button--text, .el-button--primary, .is-disabled) {
|
||||
background-color: #353535;
|
||||
border-color: #404040;
|
||||
}
|
||||
|
||||
.el-button:not(.el-button--text, .el-button--primary, .is-disabled):focus,
|
||||
.el-button:not(.el-button--text, .el-button--primary, .is-disabled):hover {
|
||||
color: #000;
|
||||
background-color: #737373;
|
||||
border-color: #656565;
|
||||
}
|
||||
|
||||
.el-button.is-disabled,
|
||||
.el-button.is-disabled:focus,
|
||||
.el-button.is-disabled:hover {
|
||||
color: #c0c4cc;
|
||||
background-color: #292929;
|
||||
border-color: #3d3d3d;
|
||||
}
|
||||
|
||||
.el-tabs__item {
|
||||
color: #c2c4ca;
|
||||
}
|
||||
|
||||
.el-tabs--card > .el-tabs__header {
|
||||
border-bottom-color: #5f5f5f;
|
||||
}
|
||||
|
||||
.el-dropdown-menu {
|
||||
background-color: #353535;
|
||||
border-color: #404040;
|
||||
}
|
||||
|
||||
.el-dropdown-menu__item--divided::before {
|
||||
background-color: #404040;
|
||||
}
|
||||
|
||||
.el-dropdown-menu__item {
|
||||
color: #d4d4d4;
|
||||
}
|
||||
|
||||
.el-dropdown-menu__item:focus,
|
||||
.el-dropdown-menu__item:not(.is-disabled):hover {
|
||||
color: #66b1ff;
|
||||
background-color: #444;
|
||||
}
|
||||
|
||||
.el-popper[x-placement^='bottom'] .popper__arrow::after {
|
||||
border-bottom-color: #333;
|
||||
}
|
||||
|
||||
.el-popper[x-placement^='bottom'] .popper__arrow {
|
||||
border-bottom-color: #404040;
|
||||
}
|
||||
|
||||
.el-message-box {
|
||||
background-color: #333;
|
||||
border-color: #5f5f5f;
|
||||
}
|
||||
|
||||
.el-tree {
|
||||
color: #bbb;
|
||||
background: #202020;
|
||||
}
|
||||
|
||||
.el-menu-item i {
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.el-menu-item.notify::after {
|
||||
background: #ebeef5;
|
||||
}
|
||||
|
||||
.el-menu-item.is-active::before {
|
||||
background: #dcdfe6;
|
||||
}
|
||||
|
||||
.el-menu-item:focus,
|
||||
.el-menu-item:hover {
|
||||
background-color: #505050;
|
||||
}
|
||||
|
||||
.el-tabs--card > .el-tabs__header .el-tabs__item {
|
||||
border-left-color: #5f5f5f;
|
||||
}
|
||||
|
||||
.el-tabs--card > .el-tabs__header .el-tabs__item.is-active {
|
||||
border-bottom-color: #9c9c9c;
|
||||
border-left-color: #5f5f5f;
|
||||
}
|
||||
|
||||
.el-tabs--card > .el-tabs__header .el-tabs__nav {
|
||||
border-color: #5f5f5f;
|
||||
}
|
||||
|
||||
.el-collapse-item__header {
|
||||
color: #d0d0d0;
|
||||
background-color: inherit;
|
||||
border-bottom-color: #5f5f5f;
|
||||
}
|
||||
|
||||
.el-collapse-item__wrap {
|
||||
background-color: #333;
|
||||
border-bottom-color: #5f5f5f;
|
||||
}
|
||||
|
||||
.el-message-box__title {
|
||||
color: #c8c8c8;
|
||||
}
|
||||
|
||||
.el-dialog__title {
|
||||
color: #c8c8c8;
|
||||
}
|
||||
|
||||
.el-message-box__content {
|
||||
color: #c8c8c8;
|
||||
}
|
||||
|
||||
.el-collapse-item__content {
|
||||
color: #848484;
|
||||
}
|
||||
|
||||
.el-switch__core {
|
||||
background-color: #212121;
|
||||
border-color: #5f5f5f;
|
||||
}
|
||||
|
||||
.el-popover {
|
||||
color: #c8c8c8;
|
||||
background-color: #333;
|
||||
border-color: #5f5f5f;
|
||||
}
|
||||
|
||||
.el-popper[x-placement^='right'] .popper__arrow::after {
|
||||
border-right-color: #5f5f5f;
|
||||
}
|
||||
|
||||
.el-popper[x-placement^='right'] .popper__arrow {
|
||||
border-right-color: #5f5f5f;
|
||||
}
|
||||
|
||||
.el-switch__label {
|
||||
color: #a0a0a0;
|
||||
}
|
||||
|
||||
.el-table,
|
||||
.el-table__expanded-cell {
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
.el-tree-node__content:hover {
|
||||
background-color: #272727;
|
||||
}
|
||||
|
||||
.el-tree-node:focus > .el-tree-node__content {
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
.el-select-dropdown {
|
||||
background-color: #353535;
|
||||
}
|
||||
|
||||
.el-select-dropdown__item {
|
||||
color: #c8c8c8;
|
||||
}
|
||||
|
||||
.el-select-dropdown.is-multiple .el-select-dropdown__item.selected {
|
||||
background-color: #404040;
|
||||
}
|
||||
|
||||
.el-select-dropdown.is-multiple .el-select-dropdown__item.selected.hover {
|
||||
background-color: #404040;
|
||||
}
|
||||
|
||||
.el-select-dropdown__item.hover,
|
||||
.el-select-dropdown__item:hover {
|
||||
background-color: #3e3e3e;
|
||||
}
|
||||
|
||||
.el-tag.el-tag--info {
|
||||
background-color: #404040;
|
||||
border-color: #252525;
|
||||
}
|
||||
|
||||
.el-table__expanded-cell:hover {
|
||||
background-color: #323232 !important;
|
||||
}
|
||||
|
||||
.el-dialog__body {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.el-radio {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.el-radio-button__inner {
|
||||
color: #fff;
|
||||
background: #565656;
|
||||
border: 1px solid #666666;
|
||||
}
|
||||
|
||||
.el-radio-button:first-child .el-radio-button__inner {
|
||||
border-left: 1px solid rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
.el-radio-button.is-disabled .el-radio-button__inner {
|
||||
background-color: unset;
|
||||
border-color: unset;
|
||||
border: 1px solid #666666;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.el-form-item__label {
|
||||
color: #c8c8c8;
|
||||
}
|
||||
|
||||
.el-checkbox {
|
||||
color: #c8c8c8;
|
||||
}
|
||||
|
||||
.el-input.is-disabled .el-input__inner {
|
||||
background-color: #3b3b3b;
|
||||
}
|
||||
|
||||
.x-app {
|
||||
background-color: #101010;
|
||||
}
|
||||
|
||||
.x-container {
|
||||
background: #222;
|
||||
}
|
||||
|
||||
.x-login-container {
|
||||
background-color: #101010;
|
||||
}
|
||||
|
||||
.x-aside-container {
|
||||
background-color: #171717;
|
||||
}
|
||||
|
||||
.x-friend-list > .x-friend-group {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.x-friend-item:hover,
|
||||
.x-change-image-item:hover {
|
||||
background: #3e3e3e;
|
||||
}
|
||||
|
||||
.x-friend-item > .detail > .name {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.x-friend-item > .detail > .extra {
|
||||
color: #c7c7c7;
|
||||
}
|
||||
|
||||
.x-login-container p {
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
.x-menu-container {
|
||||
background: #303133;
|
||||
}
|
||||
|
||||
.x-grey {
|
||||
color: #b3b3b3;
|
||||
}
|
||||
694
src/theme.darkvanilla.scss
Normal file
@@ -0,0 +1,694 @@
|
||||
/*
|
||||
* VRCX Dark-Vanilla theme by MintLily
|
||||
* https://github.com/MintLily/Dark-Vanilla
|
||||
*/
|
||||
|
||||
:root {
|
||||
--ThemeName: 'Dark Vanilla';
|
||||
--ThemeVers: '2.0.2';
|
||||
--ThemeAuth: 'MintLily';
|
||||
|
||||
--dv_bright: #eecce0;
|
||||
--dv_muted: #906d92;
|
||||
--dv_bright-rgb: 238, 204, 224;
|
||||
--dv_muted-rgb: 144, 109, 146;
|
||||
--dv_bg-top: #1e2427;
|
||||
--dv_bg-mid: #191f22;
|
||||
--dv_bg-bot: #131719;
|
||||
--dv_lg-rounded: 1rem;
|
||||
--dv_md-rounded: 0.6rem;
|
||||
--dv_sm-rounded: 0.45rem;
|
||||
--dv_background-modifier-selected: rgba(var(--dv_bright-rgb), 0.2);
|
||||
--dv_background-modifier-hover: rgba(var(--dv_bright-rgb), 0.3);
|
||||
--font-primary: 'Encode Sans', 'Tofu', 'Helvetica Neue', Helvetica, Arial,
|
||||
sans-serif;
|
||||
--shadow: 0 0 15px 5px rgba(0, 0, 0, 0.35);
|
||||
}
|
||||
|
||||
/* MAIN */
|
||||
html,
|
||||
body,
|
||||
.x-menu-container {
|
||||
background-color: var(--dv_bg-bot);
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Encode Sans';
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-stretch: 100%;
|
||||
src: url(https://fonts.gstatic.com/s/encodesans/v19/LDIhapOFNxEwR-Bd1O9uYNmnUQomAgE25imKSbHLR8A6WQw.woff2)
|
||||
format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169,
|
||||
U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323,
|
||||
U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Encode Sans';
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-stretch: 100%;
|
||||
src: url(https://fonts.gstatic.com/s/encodesans/v19/LDIhapOFNxEwR-Bd1O9uYNmnUQomAgE25imKSbHLRsA6WQw.woff2)
|
||||
format('woff2');
|
||||
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF,
|
||||
U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Encode Sans';
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-stretch: 100%;
|
||||
src: url(https://fonts.gstatic.com/s/encodesans/v19/LDIhapOFNxEwR-Bd1O9uYNmnUQomAgE25imKSbHLSMA6.woff2)
|
||||
format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
|
||||
U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122,
|
||||
U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
body,
|
||||
input,
|
||||
textarea,
|
||||
select,
|
||||
button {
|
||||
font-family: var(--font-primary);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--dv_bright);
|
||||
}
|
||||
|
||||
/* Side Bar */
|
||||
.x-menu-container {
|
||||
border-top-right-radius: var(--dv_lg-rounded);
|
||||
border-bottom-right-radius: var(--dv_lg-rounded);
|
||||
}
|
||||
|
||||
.el-menu-item.is-active {
|
||||
color: var(--dv_bright);
|
||||
}
|
||||
|
||||
.el-menu-item.is-active::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.el-menu-item:focus,
|
||||
.el-menu-item:hover {
|
||||
background-color: var(--dv_background-modifier-hover);
|
||||
}
|
||||
|
||||
li[class='el-menu-item is-active'] {
|
||||
background-color: var(--dv_background-modifier-selected);
|
||||
}
|
||||
|
||||
.el-menu-item:hover i {
|
||||
color: #eee;
|
||||
transition: 0.3s !important;
|
||||
}
|
||||
|
||||
.el-tooltip__popper.is-dark,
|
||||
.el-tooltip__popper[x-placement^='right'] .popper__arrow::after {
|
||||
background-color: var(--dv_bg-top);
|
||||
border-radius: var(--dv_md-rounded);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.el-tooltip__popper[x-placement^='right'] .popper__arrow::after {
|
||||
background-color: var(--dv_bg-top);
|
||||
}
|
||||
|
||||
/* Main Window Content */
|
||||
/* Feed */
|
||||
.x-app {
|
||||
background-color: var(--dv_bg-top);
|
||||
}
|
||||
|
||||
.x-container {
|
||||
background-color: var(--dv_bg-top);
|
||||
}
|
||||
|
||||
.x-container {
|
||||
border-top-left-radius: var(--dv_lg-rounded);
|
||||
border-bottom-left-radius: var(--dv_lg-rounded);
|
||||
}
|
||||
|
||||
.el-select:hover .el-input__inner {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.el-select > .el-input input,
|
||||
.el-select .el-input input {
|
||||
background-color: var(--dv_bg-bot);
|
||||
border-radius: var(--dv_lg-rounded);
|
||||
}
|
||||
|
||||
.el-select-dropdown {
|
||||
background-color: var(--dv_bg-top);
|
||||
border: none;
|
||||
border-radius: var(--dv_lg-rounded);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.el-select-dropdown__item.hover,
|
||||
.el-select-dropdown__item:hover {
|
||||
background-color: var(--dv_background-modifier-hover);
|
||||
transition: 0.2s;
|
||||
border-radius: var(--dv_md-rounded);
|
||||
}
|
||||
|
||||
.el-select-dropdown__item.selected {
|
||||
color: var(--dv_bright);
|
||||
}
|
||||
|
||||
.el-popper[x-placement^='bottom'] .popper__arrow,
|
||||
.el-popper[x-placement^='bottom'] .popper__arrow::after {
|
||||
border-bottom-color: var(--dv_bg-top);
|
||||
}
|
||||
|
||||
.el-select-dropdown__item {
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
.el-input__inner {
|
||||
background-color: var(--dv_bg-bot);
|
||||
border-radius: var(--dv_lg-rounded);
|
||||
}
|
||||
|
||||
.el-table thead {
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
.el-table .descending .sort-caret.descending,
|
||||
.el-table .ascending .sort-caret.ascending {
|
||||
border-top-color: var(--dv_bright);
|
||||
}
|
||||
|
||||
.el-table td.el-table__cell,
|
||||
.el-table th.el-table__cell.is-leaf {
|
||||
border-bottom-color: #5f5f5f;
|
||||
}
|
||||
|
||||
.el-table tr,
|
||||
.el-table td.el-table__cell,
|
||||
.el-table th.el-table__cell {
|
||||
background-color: var(--dv_bg-top);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.el-table--striped .el-table__body tr.el-table__row--striped td.el-table__cell {
|
||||
background-color: var(--dv_bg-top);
|
||||
border: none;
|
||||
}
|
||||
|
||||
table.el-table__body[style^='width:'] tr[class='el-table__row']:hover td,
|
||||
table.el-table__body[style^='width:']
|
||||
tr[class='el-table__row el-table__row--striped']:hover
|
||||
td {
|
||||
background-color: var(--dv_background-modifier-hover);
|
||||
transition: 0.1s;
|
||||
}
|
||||
|
||||
table.el-table__body[style^='width:']
|
||||
tr[class='el-table__row']:hover
|
||||
td[class^='el-table_1_column_1'],
|
||||
table.el-table__body[style^='width:']
|
||||
tr[class='el-table__row el-table__row--striped']:hover
|
||||
td[class^='el-table_1_column_1'] {
|
||||
border-top-left-radius: var(--dv_md-rounded);
|
||||
border-bottom-left-radius: var(--dv_md-rounded);
|
||||
}
|
||||
|
||||
table.el-table__body[style^='width:']
|
||||
tr[class='el-table__row']:hover
|
||||
td[class^='el-table_1_column_5'],
|
||||
table.el-table__body[style^='width:']
|
||||
tr[class='el-table__row el-table__row--striped']:hover
|
||||
td[class^='el-table_1_column_5'] {
|
||||
border-top-right-radius: var(--dv_md-rounded);
|
||||
border-bottom-right-radius: var(--dv_md-rounded);
|
||||
}
|
||||
|
||||
tr[class='el-table__row']:hover .el-table__expand-icon,
|
||||
tr[class='el-table__row el-table__row--striped']:hover .el-table__expand-icon {
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
.el-table__expanded-cell:hover {
|
||||
background-color: var(--dv_background-modifier-hover) !important;
|
||||
transition: 0.1s;
|
||||
border-bottom-left-radius: var(--dv_md-rounded);
|
||||
border-bottom-right-radius: var(--dv_md-rounded);
|
||||
}
|
||||
|
||||
.el-table--enable-row-hover .el-table__body tr:hover > td.el-table__cell {
|
||||
background-color: var(--dv_background-modifier-selected);
|
||||
transition: 0.1s;
|
||||
}
|
||||
|
||||
.el-pagination {
|
||||
padding: 8px;
|
||||
|
||||
> button.btn-prev {
|
||||
border-top-left-radius: var(--dv_sm-rounded);
|
||||
border-bottom-left-radius: var(--dv_sm-rounded);
|
||||
background-color: var(--dv_bg-bot);
|
||||
}
|
||||
|
||||
> button.btn-next {
|
||||
border-top-right-radius: var(--dv_md-rounded);
|
||||
border-bottom-right-radius: var(--dv_sm-rounded);
|
||||
background-color: var(--dv_bg-bot);
|
||||
}
|
||||
|
||||
> ul > li,
|
||||
button:disabled {
|
||||
background-color: var(--dv_bg-bot);
|
||||
color: #c0c4cc;
|
||||
}
|
||||
|
||||
> button:hover {
|
||||
color: var(--dv_bright);
|
||||
}
|
||||
}
|
||||
|
||||
.el-pagination .el-select .el-input .el-input__inner {
|
||||
border-radius: var(--dv_sm-rounded);
|
||||
}
|
||||
|
||||
.el-pager li.active,
|
||||
.el-pager li:hover,
|
||||
.el-button--text:hover {
|
||||
color: var(--dv_bright);
|
||||
}
|
||||
|
||||
.el-message-box {
|
||||
background-color: var(--dv_bg-top);
|
||||
border-radius: var(--dv_lg-rounded);
|
||||
border-color: transparent;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
/* Game Log */
|
||||
table.el-table__body[style^='width:']
|
||||
tr[class='el-table__row']:hover
|
||||
td[class^='el-table_2_column_6'],
|
||||
table.el-table__body[style^='width:']
|
||||
tr[class='el-table__row el-table__row--striped']:hover
|
||||
td[class^='el-table_2_column_6'] {
|
||||
border-top-left-radius: var(--dv_md-rounded);
|
||||
border-bottom-left-radius: var(--dv_md-rounded);
|
||||
}
|
||||
|
||||
table.el-table__body[style^='width:']
|
||||
tr[class='el-table__row']:hover
|
||||
td[class^='el-table_2_column_11'],
|
||||
table.el-table__body[style^='width:']
|
||||
tr[class='el-table__row el-table__row--striped']:hover
|
||||
td[class^='el-table_2_column_11'] {
|
||||
border-top-right-radius: var(--dv_md-rounded);
|
||||
border-bottom-right-radius: var(--dv_md-rounded);
|
||||
}
|
||||
|
||||
/* Search */
|
||||
.el-tabs--card > .el-tabs__header .el-tabs__nav:first-child,
|
||||
.el-tabs--card > .el-tabs__header .el-tabs__nav:first-child:hover {
|
||||
border-top-left-radius: var(--dv_lg-rounded);
|
||||
}
|
||||
|
||||
.el-tabs--card > .el-tabs__header .el-tabs__item.is-active {
|
||||
border-bottom: 2px solid var(--dv_bright);
|
||||
}
|
||||
|
||||
.el-tabs__item:hover {
|
||||
border-bottom: 2px solid var(--dv_muted);
|
||||
}
|
||||
|
||||
.el-tabs--card > .el-tabs__header .el-tabs__nav:last-child,
|
||||
.el-tabs--card > .el-tabs__header .el-tabs__nav:last-child:hover {
|
||||
border-top-right-radius: var(--dv_lg-rounded);
|
||||
}
|
||||
|
||||
.el-tabs__item.is-active,
|
||||
.el-tabs__item:hover {
|
||||
color: var(--dv_bright);
|
||||
}
|
||||
|
||||
.el-button.is-disabled,
|
||||
.el-button.is-disabled:focus,
|
||||
.el-button.is-disabled:hover,
|
||||
.el-button:not(.el-button--text, .el-button--primary, .is-disabled):focus,
|
||||
.el-button:not(.el-button--text, .el-button--primary, .is-disabled):hover {
|
||||
background-color: var(--dv_background-modifier-hover);
|
||||
}
|
||||
|
||||
.el-button-group > .el-button:first-child {
|
||||
border-top-left-radius: var(--dv_md-rounded);
|
||||
border-bottom-left-radius: var(--dv_md-rounded);
|
||||
}
|
||||
|
||||
.el-button-group > .el-button:last-child {
|
||||
border-top-right-radius: var(--dv_md-rounded);
|
||||
border-bottom-right-radius: var(--dv_md-rounded);
|
||||
}
|
||||
|
||||
.el-button-group > .el-button,
|
||||
.el-button:not(.el-button--text, .el-button--primary, .is-disabled) {
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.el-button:not(.el-button--text, .el-button--primary, .is-disabled) {
|
||||
background-color: var(--dv_bg-bot);
|
||||
border-radius: var(--dv_md-rounded);
|
||||
}
|
||||
|
||||
.el-button:not(.el-button--text, .el-button--primary, .is-disabled):focus,
|
||||
.el-button:not(.el-button--text, .el-button--primary, .is-disabled):hover,
|
||||
.el-button:not(.el-button--text, .el-button--primary, .is-disabled):focus,
|
||||
.el-button:not(.el-button--text, .el-button--primary, .is-disabled):hover {
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
.x-friend-item:hover,
|
||||
.x-change-image-item:hover {
|
||||
background-color: var(--dv_background-modifier-hover);
|
||||
border-radius: var(--dv_lg-rounded);
|
||||
}
|
||||
|
||||
/* Favorites */
|
||||
.el-switch.is-checked .el-switch__core,
|
||||
span[class='el-switch__core'][style*='border-color'] {
|
||||
background-color: var(--dv_muted) !important;
|
||||
border-color: var(--dv_muted) !important;
|
||||
}
|
||||
|
||||
.el-switch__label.is-active {
|
||||
color: var(--dv_bright);
|
||||
}
|
||||
|
||||
.el-collapse-item__wrap {
|
||||
background-color: var(--dv_bg-top);
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
|
||||
/* Friend Log */
|
||||
table.el-table__body[style^='width:']
|
||||
tr[class='el-table__row']:hover
|
||||
td[class^='el-table_4_column_21'],
|
||||
table.el-table__body[style^='width:']
|
||||
tr[class='el-table__row el-table__row--striped']:hover
|
||||
td[class^='el-table_4_column_21'] {
|
||||
border-top-left-radius: var(--dv_md-rounded);
|
||||
border-bottom-left-radius: var(--dv_md-rounded);
|
||||
}
|
||||
|
||||
table.el-table__body[style^='width:']
|
||||
tr[class='el-table__row']:hover
|
||||
td[class^='el-table_4_column_24'],
|
||||
table.el-table__body[style^='width:']
|
||||
tr[class='el-table__row el-table__row--striped']:hover
|
||||
td[class^='el-table_4_column_24'] {
|
||||
border-top-right-radius: var(--dv_md-rounded);
|
||||
border-bottom-right-radius: var(--dv_md-rounded);
|
||||
}
|
||||
|
||||
/* Moderation */
|
||||
table.el-table__body[style^='width:']
|
||||
tr[class='el-table__row']:hover
|
||||
td[class^='el-table_4_column_25'],
|
||||
table.el-table__body[style^='width:']
|
||||
tr[class='el-table__row el-table__row--striped']:hover
|
||||
td[class^='el-table_4_column_25'] {
|
||||
border-top-right-radius: var(--dv_md-rounded);
|
||||
border-bottom-right-radius: var(--dv_md-rounded);
|
||||
}
|
||||
|
||||
/* Notification */
|
||||
table.el-table__body[style^='width:']
|
||||
tr[class='el-table__row']:hover
|
||||
td[class^='el-table_4_column_26'],
|
||||
table.el-table__body[style^='width:']
|
||||
tr[class='el-table__row el-table__row--striped']:hover
|
||||
td[class^='el-table_4_column_26'] {
|
||||
border-top-right-radius: var(--dv_md-rounded);
|
||||
border-bottom-right-radius: var(--dv_md-rounded);
|
||||
}
|
||||
|
||||
/* Profile */
|
||||
.el-tree {
|
||||
background-color: var(--dv_bg-bot);
|
||||
}
|
||||
|
||||
div[role='treeitem'][class*='is-focusable'] {
|
||||
background-color: var(--dv_bg-mid);
|
||||
}
|
||||
|
||||
.el-switch__core {
|
||||
background-color: var(--dv_bg-bot);
|
||||
}
|
||||
|
||||
.el-radio-group label,
|
||||
.el-radio-button__inner {
|
||||
background-color: var(--dv_bg-bot);
|
||||
border: none;
|
||||
border-radius: var(--dv_md-rounded);
|
||||
}
|
||||
|
||||
.el-radio-button__orig-radio:checked + .el-radio-button__inner {
|
||||
background-color: var(--dv_muted);
|
||||
border-color: var(--dv_muted);
|
||||
box-shadow: none;
|
||||
border-radius: var(--dv_md-rounded);
|
||||
}
|
||||
|
||||
.el-radio-button__inner:hover {
|
||||
color: #eee;
|
||||
background-color: var(--dv_background-modifier-hover);
|
||||
border-radius: var(--dv_md-rounded);
|
||||
}
|
||||
|
||||
div.options-container[style='margin-top: 45px; border-top: 1px solid rgb(238, 238, 238); padding-top: 30px;']:after {
|
||||
content: var(--ThemeName) ' v' var(--ThemeVers) ' by ' var(--ThemeAuth);
|
||||
color: var(--dv_bright);
|
||||
float: right;
|
||||
padding-bottom: 10px;
|
||||
padding-right: 10px;
|
||||
font-size: 18pt;
|
||||
}
|
||||
|
||||
.el-slider__bar {
|
||||
background-color: var(--dv_muted);
|
||||
}
|
||||
|
||||
.el-slider__button {
|
||||
border-color: var(--dv_muted);
|
||||
}
|
||||
|
||||
.el-dropdown-menu {
|
||||
background-color: var(--dv_bg-top);
|
||||
border: none;
|
||||
border-radius: var(--dv_lg-rounded);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.el-dropdown-menu__item:focus,
|
||||
.el-dropdown-menu__item:not(.is-disabled):hover {
|
||||
color: #eee;
|
||||
background-color: var(--dv_background-modifier-hover);
|
||||
border-radius: var(--dv_md-rounded);
|
||||
}
|
||||
|
||||
.el-input-number__decrease:hover,
|
||||
.el-input-number__increase:hover {
|
||||
color: var(--dv_bright);
|
||||
}
|
||||
|
||||
.el-dialog,
|
||||
.el-pager
|
||||
li:not(li.number, li[class*='btn-quicknext'], li[class*='btn-quickprev']) {
|
||||
background-color: var(--dv_bg-top);
|
||||
border: none;
|
||||
border-radius: var(--dv_lg-rounded);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.el-button--primary {
|
||||
background-color: var(--dv_muted);
|
||||
border-color: var(--dv_muted);
|
||||
border-radius: var(--dv_md-rounded);
|
||||
}
|
||||
|
||||
.el-button--primary:focus,
|
||||
.el-button--primary:hover {
|
||||
background-color: rgba(var(--dv_muted-rgb), 0.6);
|
||||
border-color: var(--dv_muted);
|
||||
}
|
||||
|
||||
.el-textarea__inner {
|
||||
background-color: var(--dv_bg-bot);
|
||||
}
|
||||
|
||||
.el-textarea__inner:hover {
|
||||
background-color: var(--dv_bg-mid);
|
||||
}
|
||||
|
||||
.el-textarea .el-input__count {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.el-popover {
|
||||
background-color: var(--dv_bg-top);
|
||||
border: none;
|
||||
border-radius: var(--dv_lg-rounded);
|
||||
box-shadow: var(--shadow);
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
.el-popper[x-placement^='top'] .popper__arrow,
|
||||
.el-popper[x-placement^='left'] .popper__arrow,
|
||||
.el-popper[x-placement^='right'] .popper__arrow,
|
||||
.el-popper[x-placement^='bottom'] .popper__arrow {
|
||||
display: none;
|
||||
}
|
||||
|
||||
img.x-link,
|
||||
img.friends-list-avatar {
|
||||
border-radius: var(--dv_lg-rounded) !important;
|
||||
}
|
||||
|
||||
.el-tag--mini {
|
||||
height: 30px;
|
||||
padding: 5px 15px;
|
||||
font-size: 10pt;
|
||||
border-radius: var(--dv_md-rounded);
|
||||
}
|
||||
|
||||
.el-tag.el-tag--info,
|
||||
.el-tag--plain.el-tag--success {
|
||||
background-color: var(--dv_bg-bot);
|
||||
}
|
||||
|
||||
.el-tag--plain.el-tag--warning,
|
||||
.el-tag--plain.el-tag--danger,
|
||||
.el-tag--plain {
|
||||
background-color: var(--dv_bg-mid);
|
||||
}
|
||||
|
||||
.el-tag--plain.el-tag--info {
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
.el-tabs__active-bar {
|
||||
background-color: var(--dv_muted);
|
||||
}
|
||||
|
||||
.el-input--mini .el-textarea__inner:hover {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
i[class='el-icon-delete'],
|
||||
i[class='el-icon-switch-button'],
|
||||
.el-dropdown-menu--small
|
||||
.el-dropdown-menu__item.el-dropdown-menu__item--divided:has(
|
||||
i[class='el-icon-switch-button']
|
||||
) {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
i[class='el-icon-star-off']:not(.el-menu-item div.el-tooltip i) {
|
||||
color: #ffd000;
|
||||
}
|
||||
|
||||
.el-dropdown-menu__item--divided {
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.el-dropdown-menu__item--divided::before {
|
||||
background-color: var(--dv_bg-top);
|
||||
}
|
||||
|
||||
.el-tab-pane .el-radio-group[style^='margin-left: '] label {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.el-radio__input.is-checked + .el-radio__label {
|
||||
color: var(--dv_bright);
|
||||
}
|
||||
|
||||
.el-radio__input.is-checked .el-radio__inner {
|
||||
border-color: var(--dv_bright);
|
||||
background-color: var(--dv_bright);
|
||||
}
|
||||
|
||||
.el-input-group__append,
|
||||
.el-input-group__prepend {
|
||||
background-color: var(--dv_bg-mid);
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.el-input-group__append:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.el-dialog__headerbtn:focus .el-dialog__close,
|
||||
.el-dialog__headerbtn:hover .el-dialog__close {
|
||||
color: var(--dv_bright);
|
||||
}
|
||||
|
||||
.x-aside-container {
|
||||
background-color: var(--dv_bg-bot);
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
var(--dv_bg-top) 0%,
|
||||
var(--dv_bg-bot) 25%,
|
||||
var(--dv_bg-bot) 100%
|
||||
);
|
||||
border-top-left-radius: 2rem;
|
||||
border-bottom-left-radius: var(--dv_lg-rounded);
|
||||
}
|
||||
|
||||
.el-dialog__wrapper[style^='z-index: '] {
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.v-modal {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.noty_theme__mint.noty_type__error,
|
||||
.noty_theme__mint.noty_type__success,
|
||||
.noty_theme__mint.noty_type__alert,
|
||||
.noty_theme__mint.noty_type__notification,
|
||||
.noty_theme__mint.noty_type__warning,
|
||||
.noty_theme__mint.noty_type__info,
|
||||
.noty_theme__mint.noty_type__information {
|
||||
border-radius: var(--dv_lg-rounded);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.noty_theme__mint.noty_type__success {
|
||||
color: #fff;
|
||||
background-color: var(--dv_muted);
|
||||
border-bottom: 1px solid var(--dv_bright);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: var(--dv_bg-mid);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--dv_bg-bot);
|
||||
}
|
||||
|
||||
// User dialog memo: input count background color
|
||||
.x-friend-item:hover .el-input__count {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.el-collapse-item .el-tag--mini {
|
||||
border: transparent;
|
||||
background-color: var(--dv_bg-bot);
|
||||
}
|
||||
320
src/theme.darkvanillaold.scss
Normal file
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
* VRCX Dark-Vanilla theme by MintLily
|
||||
* https://github.com/MintLily/Dark-Vanilla
|
||||
*/
|
||||
@import 'theme.dark.scss';
|
||||
:root {
|
||||
--ThemeName: 'Dark Vanilla';
|
||||
--ThemeVersion: 'v1.7';
|
||||
--ThemeAuthor: 'MintLily'; /* Discord: MintLily#0001 */
|
||||
|
||||
--blur: 3px;
|
||||
--blur-more: 8px;
|
||||
--farback: #131719;
|
||||
--mid: #191f22;
|
||||
--top: #1e2427;
|
||||
--top-border: #151a1c;
|
||||
--theme-text: #eecce0;
|
||||
--theme-text-muted: #906d92;
|
||||
--theme-text-rgb: 238, 204, 224;
|
||||
--theme-text-muted-rgb: 144, 109, 146;
|
||||
}
|
||||
|
||||
div.options-container[style='margin-top: 45px; border-top: 1px solid rgb(238, 238, 238); padding-top: 30px;']:after {
|
||||
content: var(--ThemeName) ' ' var(--ThemeVersion) ' by ' var(--ThemeAuthor);
|
||||
color: var(--theme-text);
|
||||
float: right;
|
||||
padding-bottom: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--theme-text) !important;
|
||||
}
|
||||
.x-menu-container {
|
||||
background: var(--top) !important;
|
||||
}
|
||||
.x-container {
|
||||
background: var(--farback) !important;
|
||||
}
|
||||
.x-aside-container {
|
||||
background: var(--mid) !important;
|
||||
}
|
||||
|
||||
.el-tooltip__popper.is-dark {
|
||||
background: rgba(var(--theme-text-muted-rgb), 0.2) !important;
|
||||
backdrop-filter: blur(var(--blur));
|
||||
}
|
||||
|
||||
.el-menu-item:focus,
|
||||
.el-menu-item:hover {
|
||||
background: var(--theme-text-muted) !important;
|
||||
}
|
||||
.el-menu-item.is-active {
|
||||
color: var(--theme-text) !important;
|
||||
}
|
||||
.el-menu-item.is-active::before {
|
||||
background: var(--theme-text) !important;
|
||||
}
|
||||
.el-menu-item.notify::after {
|
||||
background: var(--theme-text) !important;
|
||||
}
|
||||
.el-collapse-item__content,
|
||||
.el-collapse-item__wrap {
|
||||
background: var(--mid) !important;
|
||||
}
|
||||
|
||||
.el-button:not(.el-button--text, .el-button--primary, .is-disabled) {
|
||||
background: var(--top) !important;
|
||||
border: var(--top-border) !important;
|
||||
}
|
||||
|
||||
.el-input__inner,
|
||||
.el-textarea__inner,
|
||||
.el-textarea .el-input__count,
|
||||
.el-input .el-input__count .el-input__count-inner {
|
||||
background: transparent;
|
||||
border: var(--top-border) !important;
|
||||
}
|
||||
|
||||
// User dialog memo: input count background color
|
||||
.x-friend-item:hover .el-input__count {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.el-table th.is-leaf {
|
||||
background: var(--top) !important;
|
||||
/*border: 1px solid var(--top-border) !important;*/
|
||||
}
|
||||
|
||||
.el-table td,
|
||||
.el-table th.is-leaf {
|
||||
background: var(--top) !important;
|
||||
}
|
||||
.el-table--striped .el-table__body tr.el-table__row--striped td {
|
||||
background: var(--mid) !important;
|
||||
}
|
||||
|
||||
.el-dialog,
|
||||
.el-pager li,
|
||||
.el-pagination .btn-next,
|
||||
.el-pagination .btn-prev {
|
||||
background: var(--mid) !important;
|
||||
}
|
||||
.el-pager li.active {
|
||||
color: var(--theme-text) !important;
|
||||
}
|
||||
.el-pager li.btn-quicknext,
|
||||
.el-pager li.btn-quickprev {
|
||||
color: var(--theme-text-muted) !important;
|
||||
}
|
||||
.el-pager li:hover,
|
||||
.el-pagination button:hover {
|
||||
color: var(--theme-text) !important;
|
||||
}
|
||||
|
||||
.x-friend-item:hover,
|
||||
.x-change-image-item:hover {
|
||||
background: var(--theme-text-muted) !important;
|
||||
}
|
||||
|
||||
.el-popover,
|
||||
.el-dropdown-menu {
|
||||
background: var(--top) !important;
|
||||
border: var(--top-border) !important;
|
||||
}
|
||||
|
||||
.el-select-dropdown {
|
||||
background: var(--top) !important;
|
||||
border: var(--top-border) !important;
|
||||
}
|
||||
|
||||
.el-button:not(.el-button--text, .el-button--primary, .is-disabled) {
|
||||
background: var(--mid) !important;
|
||||
}
|
||||
.el-button:not(.el-button--text, .el-button--primary, .is-disabled):focus,
|
||||
.el-button:not(.el-button--text, .el-button--primary, .is-disabled):hover {
|
||||
background: var(--farback) !important;
|
||||
border: var(--top-border) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.el-tree,
|
||||
.el-message-box {
|
||||
background: rgba(38, 50, 56, 0.2) !important;
|
||||
border-color: rgba(38, 50, 56, 0.2) !important;
|
||||
backdrop-filter: blur(var(--blur));
|
||||
}
|
||||
.el-tree-node__content:hover {
|
||||
background: rgba(58, 69, 74, 0.6) !important;
|
||||
backdrop-filter: blur(var(--blur-more));
|
||||
}
|
||||
.el-tree-node:focus > .el-tree-node__content {
|
||||
background: rgba(0, 0, 0, 0.4) !important;
|
||||
}
|
||||
|
||||
.el-tabs__item.is-active,
|
||||
.el-radio__input.is-checked + .el-radio__label {
|
||||
color: var(--theme-text) !important;
|
||||
}
|
||||
.el-tabs__active-bar {
|
||||
background-color: var(--theme-text) !important;
|
||||
}
|
||||
.el-tabs__item:hover {
|
||||
color: var(--theme-text-muted) !important;
|
||||
}
|
||||
.el-radio__input.is-checked .el-radio__inner {
|
||||
border-color: var(--theme-text) !important;
|
||||
background: var(--theme-text) !important;
|
||||
}
|
||||
.el-radio__inner:hover {
|
||||
border-color: var(--theme-text) !important;
|
||||
}
|
||||
|
||||
.el-checkbox__input.is-checked + .el-checkbox__label {
|
||||
color: var(--theme-text-muted) !important;
|
||||
}
|
||||
.el-checkbox__input.is-checked .el-checkbox__inner,
|
||||
.el-checkbox__input.is-indeterminate .el-checkbox__inner {
|
||||
border-color: var(--theme-text-muted) !important;
|
||||
background: var(--theme-text-muted) !important;
|
||||
}
|
||||
|
||||
.el-icon-star-on {
|
||||
color: var(--theme-text) !important;
|
||||
}
|
||||
|
||||
.el-tag.el-tag--info {
|
||||
background: var(--farback) !important;
|
||||
}
|
||||
|
||||
.el-loading-spinner .path {
|
||||
stroke: var(--theme-text) !important;
|
||||
}
|
||||
|
||||
.noty_theme__mint.noty_type__success {
|
||||
background-color: var(--theme-text-muted) !important;
|
||||
border-bottom: var(--theme-text) !important;
|
||||
}
|
||||
|
||||
.noty_theme__mint.noty_type__error {
|
||||
background-color: rgba(0, 0, 0, 0) !important;
|
||||
}
|
||||
|
||||
.el-button--primary {
|
||||
border-color: var(--theme-text) !important;
|
||||
background: var(--theme-text) !important;
|
||||
color: black !important;
|
||||
}
|
||||
.el-button--primary:focus,
|
||||
.el-button--primary:hover {
|
||||
border-color: var(--theme-text-muted) !important;
|
||||
background: var(--theme-text-muted) !important;
|
||||
color: var(--theme-text) !important;
|
||||
}
|
||||
|
||||
.el-button:not(.el-button--text, .el-button--primary, .is-disabled):focus,
|
||||
.el-button:not(.el-button--text, .el-button--primary, .is-disabled):hover {
|
||||
border-color: var(--theme-text-muted) !important;
|
||||
background: var(--theme-text-muted) !important;
|
||||
color: var(--theme-text) !important;
|
||||
}
|
||||
|
||||
.el-radio-button__inner {
|
||||
background-color: var(--top) !important;
|
||||
border-color: var(--top) !important;
|
||||
}
|
||||
.el-radio-button__inner:hover {
|
||||
color: var(--theme-text) !important;
|
||||
}
|
||||
.el-radio-button__orig-radio:checked + .el-radio-button__inner {
|
||||
background-color: var(--theme-text-muted) !important;
|
||||
border-color: var(--theme-text-muted) !important;
|
||||
box-shadow: -1px 0 0 0 var(--theme-text-muted) !important;
|
||||
}
|
||||
.el-switch.is-checked .el-switch__core {
|
||||
background-color: var(--theme-text-muted) !important;
|
||||
border-color: var(--theme-text-muted) !important;
|
||||
}
|
||||
.el-switch__label.is-active {
|
||||
color: var(--theme-text) !important;
|
||||
}
|
||||
.el-tag {
|
||||
background: var(--farback);
|
||||
border-color: var(--farback);
|
||||
color: var(--theme-text);
|
||||
}
|
||||
.el-tabs--card > .el-tabs__header .el-tabs__item.is-active {
|
||||
border-bottom-color: var(--theme-text);
|
||||
}
|
||||
|
||||
.toggle-switch li[data-v-3cf97114] > label {
|
||||
background-color: var(--top) !important;
|
||||
border-color: var(--top) !important;
|
||||
color: lightgrey !important;
|
||||
}
|
||||
|
||||
.toggle-switch li[data-v-3cf97114] > label:hover {
|
||||
background-color: var(--mid) !important;
|
||||
border-color: var(--mid) !important;
|
||||
color: var(--theme-text-muted) !important;
|
||||
}
|
||||
|
||||
.toggle-switch li[data-v-3cf97114] > label.selected {
|
||||
background-color: var(--mid) !important;
|
||||
border-color: var(--mid) !important;
|
||||
color: var(--theme-text) !important;
|
||||
}
|
||||
|
||||
.el-slider__bar {
|
||||
background-color: var(--theme-text-muted) !important;
|
||||
}
|
||||
.el-slider__button {
|
||||
border-color: var(--theme-text-muted) !important;
|
||||
}
|
||||
.el-table .descending .sort-caret.descending,
|
||||
.el-table .ascending .sort-caret.ascending {
|
||||
border-top-color: var(--theme-text) !important;
|
||||
}
|
||||
|
||||
.el-select-dropdown__item.selected {
|
||||
color: var(--theme-text);
|
||||
}
|
||||
.el-select-dropdown__item.hover,
|
||||
.el-select-dropdown__item:hover {
|
||||
background-color: var(--farback);
|
||||
}
|
||||
|
||||
.el-dropdown-menu__item:focus,
|
||||
.el-dropdown-menu__item:not(.is-disabled):hover {
|
||||
background-color: var(--farback);
|
||||
color: var(--theme-text);
|
||||
}
|
||||
|
||||
.el-dialog__headerbtn:focus .el-dialog__close,
|
||||
.el-dialog__headerbtn:hover .el-dialog__close {
|
||||
color: var(--theme-text);
|
||||
}
|
||||
|
||||
.el-progress-bar__inner {
|
||||
background-color: var(--theme-text);
|
||||
}
|
||||
.el-progress-bar__outer {
|
||||
background-color: var(--farback);
|
||||
}
|
||||
.el-button--text:focus,
|
||||
.el-button--text:hover {
|
||||
color: var(--theme-text);
|
||||
}
|
||||
|
||||
path[stroke='#20a0ff'] {
|
||||
stroke: var(--theme-text) !important;
|
||||
}
|
||||
path[stroke='#e5e9f2'] {
|
||||
stroke: var(--farback) !important;
|
||||
}
|
||||
|
||||
.el-collapse-item .el-tag--mini {
|
||||
border: transparent;
|
||||
background-color: #333 !important;
|
||||
}
|
||||
2011
src/theme.material3.scss
Normal file
368
src/theme.pink.scss
Normal file
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
* VRCX Pink theme by Kamiya
|
||||
* https://github.com/kamiya10/VRCX-theme
|
||||
*/
|
||||
@import 'theme.dark.scss';
|
||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap');
|
||||
:root {
|
||||
--theme: #dfa2a2;
|
||||
--bg: #322525;
|
||||
--light-bg: #443030;
|
||||
--lighter-bg: #554040;
|
||||
--lighter-lighter-bg: #655050;
|
||||
--lighter-lighter-lighter-bg: #756060;
|
||||
--lighter-lighter-lighter-lighter-bg: #857070;
|
||||
--lighter-border: #aa6065;
|
||||
--font: 'Poppins', 'Noto Sans JP', 'Noto Sans KR', 'Noto Sans TC',
|
||||
'Noto Sans SC', sans-serif;
|
||||
}
|
||||
body,
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font-family: var(--font);
|
||||
}
|
||||
.el-collapse-item__wrap,
|
||||
.el-table td.el-table__cell,
|
||||
.el-table th.el-table__cell,
|
||||
.el-table tr,
|
||||
.el-table--striped .el-table__body tr.el-table__row--striped td.el-table__cell {
|
||||
background-color: transparent;
|
||||
}
|
||||
.el-table--mini .el-table__expanded-cell[class*='cell']:hover {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
.el-button.is-disabled,
|
||||
.el-button.is-disabled:focus,
|
||||
.el-button.is-disabled:hover,
|
||||
.el-pagination .btn-next,
|
||||
.el-pagination .btn-prev,
|
||||
.el-switch__core,
|
||||
.el-tree,
|
||||
.x-aside-container,
|
||||
.x-container,
|
||||
.x-login-container {
|
||||
background-color: var(--bg);
|
||||
}
|
||||
.el-pager li,
|
||||
.el-pager li.btn-quicknext,
|
||||
.el-pager li.btn-quickprev {
|
||||
color: #cbb;
|
||||
transition: color ease-in-out 0.1s;
|
||||
}
|
||||
.el-pager li:hover {
|
||||
color: #fff;
|
||||
}
|
||||
.el-tree-node:focus > .el-tree-node__content,
|
||||
.el-tree-node__content:hover {
|
||||
background-color: var(--light-bg);
|
||||
}
|
||||
.el-button:not(.el-button--text, .el-button--primary, .is-disabled),
|
||||
.el-color-picker__panel,
|
||||
.el-dialog,
|
||||
.el-input .el-input__count .el-input__count-inner,
|
||||
.el-input__inner,
|
||||
.el-message-box,
|
||||
.el-pager li,
|
||||
.el-radio-button__inner,
|
||||
.el-select-dropdown,
|
||||
.el-textarea .el-input__count,
|
||||
.el-textarea__inner,
|
||||
.x-menu-container {
|
||||
background-color: var(--lighter-bg);
|
||||
}
|
||||
.el-color-picker__panel {
|
||||
border-color: var(--lighter-bg);
|
||||
}
|
||||
.el-button,
|
||||
.el-radio-button__inner {
|
||||
color: #dcc;
|
||||
}
|
||||
.el-button:not(.el-button--text, .el-button--primary, .is-disabled):focus,
|
||||
.el-button:not(.el-button--text, .el-button--primary, .is-disabled):hover,
|
||||
.el-dropdown-menu,
|
||||
.x-change-image-item:hover,
|
||||
.x-friend-item:hover {
|
||||
background-color: var(--lighter-lighter-bg);
|
||||
color: #fff;
|
||||
}
|
||||
.el-button--primary {
|
||||
background-color: var(--theme);
|
||||
border-color: var(--theme);
|
||||
color: #fff;
|
||||
}
|
||||
.el-tooltip__popper.is-dark {
|
||||
background-color: var(--lighter-lighter-lighter-bg);
|
||||
}
|
||||
.el-button--primary:focus,
|
||||
.el-button--primary:hover {
|
||||
background-color: var(--lighter-lighter-lighter-lighter-bg);
|
||||
border-color: var(--lighter-lighter-lighter-lighter-bg);
|
||||
}
|
||||
.el-dialog,
|
||||
.el-dropdown-menu,
|
||||
.el-tooltip__popper.is-dark {
|
||||
box-shadow:
|
||||
0 3px 6px rgba(0, 0, 0, 0.16),
|
||||
0 3px 6px rgba(0, 0, 0, 0.23);
|
||||
}
|
||||
.el-dropdown-menu__item,
|
||||
.x-friend-item {
|
||||
transition:
|
||||
background-color ease-in-out 0.1s,
|
||||
color ease-in-out 0.1s;
|
||||
}
|
||||
.el-dropdown-menu__item:focus,
|
||||
.el-dropdown-menu__item:not(.is-disabled):hover {
|
||||
color: #fff;
|
||||
background-color: var(--lighter-lighter-lighter-bg);
|
||||
}
|
||||
.el-popper[x-placement^='bottom'] .popper__arrow,
|
||||
.el-popper[x-placement^='bottom'] .popper__arrow::after,
|
||||
.el-table th.el-table__cell,
|
||||
.el-table th.el-table__cell.is-leaf {
|
||||
border-bottom-color: var(--lighter-lighter-bg);
|
||||
}
|
||||
.el-table td.el-table__cell,
|
||||
.el-table tr {
|
||||
border-color: transparent;
|
||||
}
|
||||
.el-popper .popper__arrow,
|
||||
.el-popper .popper__arrow::after,
|
||||
.el-popper[x-placement^='top'] .popper__arrow,
|
||||
.el-popper[x-placement^='top'] .popper__arrow::after {
|
||||
border-top-color: var(--lighter-lighter-bg);
|
||||
}
|
||||
.el-dropdown-menu__item--divided::before,
|
||||
.el-menu-item:focus,
|
||||
.el-menu-item:hover,
|
||||
.el-select-dropdown__item.hover,
|
||||
.el-select-dropdown__item:hover,
|
||||
.el-table--enable-row-hover .el-table__body tr:hover > td.el-table__cell {
|
||||
background-color: var(--lighter-lighter-bg);
|
||||
}
|
||||
.el-input .el-input__count .el-input__count-inner,
|
||||
.el-input__inner,
|
||||
.el-textarea .el-input__count,
|
||||
.el-textarea__inner {
|
||||
border: var(--lighter-border);
|
||||
}
|
||||
.el-input-number {
|
||||
background-color: var(--lighter-bg);
|
||||
border: 1px solid #404040;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.el-input-number__decrease {
|
||||
border-right: 1px solid #404040;
|
||||
}
|
||||
.el-input-number__increase {
|
||||
border-left: 1px solid #404040;
|
||||
}
|
||||
.el-dropdown-menu__item--divided {
|
||||
border-top: 2px solid var(--lighter-lighter-lighter-lighter-bg);
|
||||
}
|
||||
.el-radio-button__inner {
|
||||
border: 1px solid var(--lighter-lighter-bg);
|
||||
}
|
||||
.el-checkbox__input.is-checked + .el-checkbox__label,
|
||||
.el-menu-item.is-active,
|
||||
.el-pagination .btn-next:not(:disabled):hover .el-icon,
|
||||
.el-pagination .btn-prev:not(:disabled):hover .el-icon,
|
||||
.el-radio__input.is-checked + .el-radio__label,
|
||||
.el-select-dropdown.is-multiple .el-select-dropdown__item.selected,
|
||||
.el-tabs__item.is-active {
|
||||
color: var(--theme);
|
||||
transition: color ease-in-out 0.1s;
|
||||
}
|
||||
.el-pager .number:first-child {
|
||||
border-top-left-radius: 10px;
|
||||
border-bottom-left-radius: 10px;
|
||||
}
|
||||
.el-pager .number:last-child {
|
||||
border-top-right-radius: 10px;
|
||||
border-bottom-right-radius: 10px;
|
||||
}
|
||||
.el-menu-item i,
|
||||
.el-tabs__item,
|
||||
i.el-icon-close:hover {
|
||||
color: #cbb;
|
||||
transition: color ease-in-out 0.1s;
|
||||
}
|
||||
.el-button--text:focus,
|
||||
.el-button--text:hover,
|
||||
.el-tabs__item:hover {
|
||||
color: #fff;
|
||||
}
|
||||
.el-slider__bar,
|
||||
.el-tabs__active-bar {
|
||||
background-color: var(--theme);
|
||||
}
|
||||
.el-slider__button {
|
||||
border: 2px solid var(--theme);
|
||||
}
|
||||
.el-checkbox__input.is-checked .el-checkbox__inner,
|
||||
.el-checkbox__input.is-indeterminate .el-checkbox__inner,
|
||||
.el-radio-button__orig-radio:checked + .el-radio-button__inner,
|
||||
.el-radio__input.is-checked .el-radio__inner,
|
||||
.el-switch.is-checked .el-switch__core {
|
||||
background-color: var(--theme);
|
||||
border-color: var(--theme);
|
||||
}
|
||||
.el-radio-button__orig-radio:checked + .el-radio-button__inner {
|
||||
box-shadow: -1px 0 0 0 var(--theme);
|
||||
}
|
||||
.el-radio-button__orig-radio:checked + .el-radio-button__inner:hover {
|
||||
color: #fff;
|
||||
}
|
||||
.el-pager li.active,
|
||||
.el-radio-button__inner:not(.is-disabled):hover,
|
||||
.el-select-dropdown__item.selected,
|
||||
.el-switch__label.is-active {
|
||||
color: var(--theme);
|
||||
}
|
||||
.el-tag.el-tag--info {
|
||||
color: #baa;
|
||||
background-color: var(--lighter-lighter-bg);
|
||||
border: 1px solid var(--lighter-lighter-lighter-lighter-bg);
|
||||
}
|
||||
.el-collapse-item__header > span + span,
|
||||
.el-form-item__label,
|
||||
.el-pagination__total,
|
||||
.el-table th.el-table__cell > .cell,
|
||||
.el-table__expand-icon > .el-icon,
|
||||
.x-login-container div[style='text-align: center; font-size: 12px;'] > * {
|
||||
color: #baa !important;
|
||||
}
|
||||
.el-table .ascending .sort-caret.ascending {
|
||||
border-bottom-color: var(--theme);
|
||||
}
|
||||
.el-table .descending .sort-caret.descending {
|
||||
border-top-color: var(--theme);
|
||||
}
|
||||
.el-pagination button:disabled,
|
||||
.el-pagination button:disabled:focus,
|
||||
.el-pagination button:disabled:hover {
|
||||
background-color: transparent;
|
||||
color: transparent !important;
|
||||
}
|
||||
.el-table--border::after,
|
||||
.el-table--group::after,
|
||||
.el-table::before,
|
||||
.el-tabs__nav-wrap::after {
|
||||
background-color: transparent;
|
||||
}
|
||||
.options-container-item .name {
|
||||
color: #eeeaea;
|
||||
}
|
||||
.el-tabs--card > .el-tabs__header,
|
||||
.el-tabs--card > .el-tabs__header .el-tabs__item,
|
||||
.el-tabs--card > .el-tabs__header .el-tabs__item.is-active,
|
||||
.el-tabs--card > .el-tabs__header .el-tabs__nav {
|
||||
border-color: transparent;
|
||||
}
|
||||
.options-container .header,
|
||||
h2 {
|
||||
color: #faeeee;
|
||||
}
|
||||
.options-container .sub-header {
|
||||
color: #988;
|
||||
}
|
||||
.el-table__row td:first-child {
|
||||
padding-left: 5px;
|
||||
border-top-left-radius: 10px;
|
||||
border-bottom-left-radius: 10px;
|
||||
}
|
||||
.el-table__row td:last-child {
|
||||
border-top-right-radius: 10px;
|
||||
border-bottom-right-radius: 10px;
|
||||
}
|
||||
.el-select-dropdown {
|
||||
border-color: var(--lighter-bg);
|
||||
}
|
||||
.el-popover {
|
||||
background-color: var(--lighter-bg);
|
||||
border-color: var(--lighter-border);
|
||||
}
|
||||
.x-menu-container::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
::-webkit-scrollbar-thumb:active {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
.el-radio-button__orig-radio:disabled + .el-radio-button__inner {
|
||||
color: var(--lighter-lighter-lighter-bg);
|
||||
background-color: var(--light-bg);
|
||||
border-color: var(--lighter-bg);
|
||||
}
|
||||
.el-radio-button__orig-radio:disabled:checked + .el-radio-button__inner {
|
||||
color: var(--lighter-lighter-lighter-bg);
|
||||
background-color: var(--lighter-bg);
|
||||
border-color: var(--lighter-bg);
|
||||
}
|
||||
.el-radio-button__orig-radio:disabled:checked + .el-radio-button__inner {
|
||||
box-shadow: none;
|
||||
}
|
||||
body,
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.extra,
|
||||
.dialog-title,
|
||||
.x-link,
|
||||
.el-tree,
|
||||
input[type='text'],
|
||||
input[type='password'] {
|
||||
user-select: text;
|
||||
}
|
||||
.avatar-info-public::selection {
|
||||
color: hsl(100, 54%, 64%);
|
||||
}
|
||||
.avatar-info-own::selection {
|
||||
color: hsl(36, 77%, 72%);
|
||||
}
|
||||
::selection {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
color: #fff;
|
||||
}
|
||||
.el-select-dropdown.is-multiple .el-select-dropdown__item.selected {
|
||||
transition: background-color 0.1s ease-in-out;
|
||||
}
|
||||
.el-select-dropdown.is-multiple .el-select-dropdown__item.selected {
|
||||
background-color: var(--lighter-lighter-lighter-bg);
|
||||
}
|
||||
.el-select-dropdown.is-multiple .el-select-dropdown__item.selected.hover {
|
||||
background-color: var(--lighter-lighter-bg);
|
||||
}
|
||||
.el-select .el-tag__close.el-icon-close {
|
||||
background-color: var(--lighter-lighter-bg);
|
||||
}
|
||||
input[type='checkbox'] + .el-switch__core {
|
||||
width: 36px !important;
|
||||
}
|
||||
input[type='checkbox']:checked + .el-switch__core {
|
||||
border-color: var(--theme) !important;
|
||||
background-color: var(--theme) !important;
|
||||
}
|
||||
.el-loading-spinner .path {
|
||||
stroke: var(--theme);
|
||||
}
|
||||
|
||||
// User dialog memo: input count background color
|
||||
.x-friend-item:hover .el-input__count {
|
||||
background-color: var(--lighter-lighter-bg);
|
||||
}
|
||||
|
||||
.el-collapse-item .el-tag--mini {
|
||||
border: transparent;
|
||||
background-color: #333;
|
||||
}
|
||||