Split component, improve UI, fix and refactor (#1162)

This commit is contained in:
pa
2025-03-01 16:12:21 +09:00
committed by GitHub
parent ef68e40996
commit b988f45f8c
28 changed files with 1098 additions and 791 deletions
+2 -1
View File
@@ -24,7 +24,8 @@
{
"files": "*.vue",
"options": {
"printWidth": 120
"printWidth": 120,
"bracketSameLine": true
}
}
]
+63 -272
View File
@@ -38,11 +38,13 @@ import _vrcxJsonStorage from './classes/vrcxJsonStorage.js';
// tabs
import ModerationTab from './views/tabs/Moderation.vue';
import ChartsTab from './views/tabs/Charts.vue';
import SideBar from './views/SideBar.vue';
import NavMenu from './views/NavMenu.vue';
// components
import SimpleSwitch from './components/settings/SimpleSwitch.vue';
import GroupsSidebar from './components/sidebar/GroupsSidebar.vue';
import PreviousInstanceInfo from './views/dialogs/PreviousInstanceInfo.vue';
import Location from './components/common/Location.vue';
// main app classes
import _sharedFeed from './classes/sharedFeed.js';
@@ -171,14 +173,18 @@ console.log(`isLinux: ${LINUX}`);
// tabs
ModerationTab,
ChartsTab,
// - others
SideBar,
NavMenu,
// components
// - settings
SimpleSwitch,
// components
// - sidebar(friendsListSidebar)
GroupsSidebar,
// - common
Location,
// - dialogs
PreviousInstanceInfo
},
@@ -186,7 +192,10 @@ console.log(`isLinux: ${LINUX}`);
return {
API,
showUserDialog: this.showUserDialog,
adjustDialogZ: this.adjustDialogZ
adjustDialogZ: this.adjustDialogZ,
getWorldName: this.getWorldName,
userImage: this.userImage,
userStatusClass: this.userStatusClass
};
},
el: '#x-app',
@@ -2875,7 +2884,6 @@ console.log(`isLinux: ${LINUX}`);
API.$on('LOGIN', function () {
$app.localFavoriteFriends.clear();
$app.currentUserGroupsInit = false;
$app.localFavoriteFriendsDivideByGroup.clear();
this.cachedFavorites.clear();
this.cachedFavoritesByObjectId.clear();
this.cachedFavoriteGroups.clear();
@@ -2953,12 +2961,6 @@ console.log(`isLinux: ${LINUX}`);
// 애초에 $isDeleted인데 여기로 올 수 가 있나..?
this.cachedFavoritesByObjectId.delete(args.params.objectId);
$app.localFavoriteFriends.delete(args.params.objectId);
$app.localFavoriteFriendsDivideByGroup.forEach((group, key) => {
$app.removeFromArray(group, args.params.objectId);
if (group.length === 0) {
$app.localFavoriteFriendsDivideByGroup.delete(key);
}
});
$app.updateSidebarFriendsList();
if (ref.$isDeleted) {
return;
@@ -3013,12 +3015,6 @@ console.log(`isLinux: ${LINUX}`);
}
this.cachedFavoritesByObjectId.delete(ref.favoriteId);
$app.localFavoriteFriends.delete(ref.favoriteId);
$app.localFavoriteFriendsDivideByGroup.forEach((group, key) => {
$app.removeFromArray(group, ref.favoriteId);
if (group.length === 0) {
$app.localFavoriteFriendsDivideByGroup.delete(key);
}
});
$app.updateSidebarFriendsList();
ref.$isDeleted = true;
API.$emit('FAVORITE:@DELETE', {
@@ -3093,15 +3089,6 @@ console.log(`isLinux: ${LINUX}`);
ref.$isExpired = false;
}
ref.$groupKey = `${ref.type}:${String(ref.tags[0])}`;
if (!$app.localFavoriteFriendsDivideByGroup.has(ref.$groupKey)) {
$app.localFavoriteFriendsDivideByGroup.set(ref.$groupKey, [
ref.favoriteId
]);
} else {
$app.localFavoriteFriendsDivideByGroup
.get(ref.$groupKey)
.push(ref.favoriteId);
}
if (ref.$isDeleted === false && ref.$groupRef === null) {
var group = this.cachedFavoriteGroupsByTypeName.get(ref.$groupKey);
@@ -3115,7 +3102,6 @@ console.log(`isLinux: ${LINUX}`);
API.expireFavorites = function () {
$app.localFavoriteFriends.clear();
$app.localFavoriteFriendsDivideByGroup.clear();
this.cachedFavorites.clear();
this.cachedFavoritesByObjectId.clear();
$app.favoriteObjects.clear();
@@ -3884,10 +3870,12 @@ console.log(`isLinux: ${LINUX}`);
$app.data.debugGameLog = false;
$app.data.debugFriendState = false;
$app.data.menuActiveIndex = 'feed';
$app.methods.notifyMenu = function (index) {
var { menu } = this.$refs;
if (menu.activeIndex !== index) {
var item = menu.items[index];
const navRef = this.$refs.menu.$children[0];
if (this.menuActiveIndex !== index) {
const item = navRef.items[this.menuActiveIndex];
if (item) {
item.$el.classList.add('notify');
}
@@ -3895,7 +3883,8 @@ console.log(`isLinux: ${LINUX}`);
};
$app.methods.selectMenu = function (index) {
var item = this.$refs.menu.items[index];
this.menuActiveIndex = index;
const item = this.$refs.menu.$children[0]?.items[index];
if (item) {
item.$el.classList.remove('notify');
}
@@ -4068,7 +4057,6 @@ console.log(`isLinux: ${LINUX}`);
args.ref.displayName
)}</strong>!`
}).show();
$app.$refs.menu.activeIndex = 'feed';
$app.updateStoredUser(this.currentUser);
});
@@ -4276,56 +4264,6 @@ console.log(`isLinux: ${LINUX}`);
$app.data.sortActiveFriends = false;
$app.data.sortOfflineFriends = false;
$app.methods.saveFriendsGroupStates = async function () {
await configRepository.setBool(
'VRCX_isFriendsGroupMe',
this.isFriendsGroupMe
);
await configRepository.setBool(
'VRCX_isFriendsGroupFavorites',
this.isVIPFriends
);
await configRepository.setBool(
'VRCX_isFriendsGroupOnline',
this.isOnlineFriends
);
await configRepository.setBool(
'VRCX_isFriendsGroupActive',
this.isActiveFriends
);
await configRepository.setBool(
'VRCX_isFriendsGroupOffline',
this.isOfflineFriends
);
};
$app.methods.loadFriendsGroupStates = async function () {
this.isFriendsGroupMe = await configRepository.getBool(
'VRCX_isFriendsGroupMe',
true
);
this.isVIPFriends = await configRepository.getBool(
'VRCX_isFriendsGroupFavorites',
true
);
this.isOnlineFriends = await configRepository.getBool(
'VRCX_isFriendsGroupOnline',
true
);
this.isActiveFriends = await configRepository.getBool(
'VRCX_isFriendsGroupActive',
false
);
this.isOfflineFriends = await configRepository.getBool(
'VRCX_isFriendsGroupOffline',
false
);
};
API.$on('LOGIN', function () {
$app.loadFriendsGroupStates();
});
$app.methods.fetchActiveFriend = function (userId) {
this.pendingActiveFriends.add(userId);
// FIXME: handle error
@@ -4367,11 +4305,6 @@ console.log(`isLinux: ${LINUX}`);
$app.friends.clear();
$app.pendingActiveFriends.clear();
$app.friendNumber = 0;
$app.isFriendsGroupMe = true;
$app.isVIPFriends = true;
$app.isOnlineFriends = true;
$app.isActiveFriends = true;
$app.isOfflineFriends = false;
$app.isGroupInstances = false;
$app.groupInstances = [];
$app.vipFriends_ = [];
@@ -5149,47 +5082,6 @@ console.log(`isLinux: ${LINUX}`);
return this.vipFriends_;
};
// VIP friends divide by group
$app.computed.vipFriendsDivideByGroup = function () {
const array = [];
const helloYesThisIsDynamic = this.vipFriends_;
for (const [key, value] of this.localFavoriteFriendsDivideByGroup) {
let friends = [];
for (const item of value) {
const friend = this.vipFriendsByGroupStatus.find(
(friend) => friend.id === item
);
if (friend) {
friends.push(friend);
}
}
if (friends.length === 0) {
continue;
}
friends.sort(getFriendsSortFunction(this.sidebarSortMethods));
let groupName = API.favoriteFriendGroups.find(
(item) => item.key === key
)?.displayName;
if (!groupName) {
groupName = key;
}
array.push({
key: key,
value: friends,
displayName: groupName
});
}
array.sort((a, b) => {
return a.key.localeCompare(b.key);
});
return array;
};
// Online friends
$app.computed.onlineFriends = function () {
if (!this.sortOnlineFriends) {
@@ -5341,7 +5233,6 @@ console.log(`isLinux: ${LINUX}`);
// #endregion
// #region | App: Quick Search
$app.data.quickSearch = '';
$app.data.quickSearchItems = [];
var localeIncludes = function (str, search, comparer) {
@@ -5469,24 +5360,15 @@ console.log(`isLinux: ${LINUX}`);
const searchText = value.substr(7);
if (this.quickSearchItems.length > 1 && searchText.length) {
this.friendsListSearch = searchText;
this.$refs.menu.activeIndex = 'friendsList';
this.menuActiveIndex = 'friendsList';
} else {
this.$refs.menu.activeIndex = 'search';
this.menuActiveIndex = 'search';
this.searchText = searchText;
this.lookupUser({ displayName: searchText });
}
} else {
this.showUserDialog(value);
}
this.quickSearchVisibleChange(value);
}
};
// NOTE: 그냥 열고 닫고 했을때 changed 이벤트 발생이 안되기 때문에 넣음
$app.methods.quickSearchVisibleChange = function (value) {
if (value) {
this.quickSearch = '';
this.quickSearchItems = [];
this.quickSearchUserHistory();
}
};
@@ -5545,7 +5427,7 @@ console.log(`isLinux: ${LINUX}`);
$app.friendLogInitStatus = false;
$app.notificationInitStatus = false;
await database.initUserTables(args.json.id);
$app.$refs.menu.activeIndex = 'feed';
$app.menuActiveIndex = 'feed';
await $app.updateDatabaseVersion();
// eslint-disable-next-line require-atomic-updates
$app.gameLogTable.data = await database.lookupGameLogDatabase(
@@ -6556,7 +6438,7 @@ console.log(`isLinux: ${LINUX}`);
}
}
this.$refs.searchTab.currentName = '0';
this.$refs.menu.activeIndex = 'search';
this.menuActiveIndex = 'search';
};
// #endregion
@@ -22160,7 +22042,6 @@ console.log(`isLinux: ${LINUX}`);
// #region | Local Favorite Friends
$app.data.localFavoriteFriends = new Set();
$app.data.localFavoriteFriendsDivideByGroup = new Map();
$app.data.localFavoriteFriendsGroups = JSON.parse(
await configRepository.getString(
'VRCX_localFavoriteFriendsGroups',
@@ -22169,7 +22050,6 @@ console.log(`isLinux: ${LINUX}`);
);
$app.methods.updateLocalFavoriteFriends = function () {
this.localFavoriteFriends.clear();
this.localFavoriteFriendsDivideByGroup.clear();
for (const ref of API.cachedFavorites.values()) {
if (
!ref.$isDeleted &&
@@ -22178,17 +22058,6 @@ console.log(`isLinux: ${LINUX}`);
this.localFavoriteFriendsGroups.length === 0)
) {
this.localFavoriteFriends.add(ref.favoriteId);
if (
!this.localFavoriteFriendsDivideByGroup.has(ref.$groupKey)
) {
this.localFavoriteFriendsDivideByGroup.set(ref.$groupKey, [
ref.favoriteId
]);
} else {
this.localFavoriteFriendsDivideByGroup
.get(ref.$groupKey)
.push(ref.favoriteId);
}
}
}
this.updateSidebarFriendsList();
@@ -22736,6 +22605,7 @@ console.log(`isLinux: ${LINUX}`);
$app.data.appLanguage =
(await configRepository.getString('VRCX_appLanguage')) ?? 'en';
$utils.changeCJKorder($app.data.appLanguage);
i18n.locale = $app.data.appLanguage;
$app.methods.initLanguage = async function () {
if (!(await configRepository.getString('VRCX_appLanguage'))) {
@@ -22760,6 +22630,7 @@ console.log(`isLinux: ${LINUX}`);
$app.methods.changeAppLanguage = function (language) {
console.log('Language changed:', language);
this.appLanguage = language;
$utils.changeCJKorder(language);
i18n.locale = language;
configRepository.setString('VRCX_appLanguage', language);
this.applyLanguageStrings();
@@ -23394,123 +23265,6 @@ console.log(`isLinux: ${LINUX}`);
);
};
$app.data.isSidebarGroupByInstanceCollapsed =
await configRepository.getBool(
'VRCX_sidebarGroupByInstanceCollapsed',
false
);
$app.methods.toggleSwitchGroupByInstanceCollapsed = function () {
this.isSidebarGroupByInstanceCollapsed =
!this.isSidebarGroupByInstanceCollapsed;
configRepository.setBool(
'VRCX_sidebarGroupByInstanceCollapsed',
this.isSidebarGroupByInstanceCollapsed
);
};
$app.computed.friendsInSameInstance = function () {
const friendsList = {};
const allFriends = [...this.vipFriends, ...this.onlineFriends];
allFriends.forEach((friend) => {
let locationTag;
if (friend.ref?.$location.isRealInstance) {
locationTag = friend.ref.$location.tag;
} else if (this.lastLocation.friendList.has(friend.id)) {
let $location = $utils.parseLocation(
this.lastLocation.location
);
if ($location.isRealInstance) {
if ($location.tag === 'private') {
locationTag = this.lastLocation.name;
} else {
locationTag = $location.tag;
}
}
}
if (!locationTag) {
return;
}
if (!friendsList[locationTag]) {
friendsList[locationTag] = [];
}
friendsList[locationTag].push(friend);
});
const sortedFriendsList = [];
for (const group of Object.values(friendsList)) {
if (group.length > 1) {
sortedFriendsList.push(
group.sort(
(a, b) => a.ref?.$location_at - b.ref?.$location_at
)
);
}
}
return sortedFriendsList.sort((a, b) => b.length - a.length);
};
$app.computed.onlineFriendsByGroupStatus = function () {
if (
!this.isSidebarGroupByInstance ||
(this.isSidebarGroupByInstance && !this.isHideFriendsInSameInstance)
) {
return this.onlineFriends;
}
const sameInstanceTag = new Set(
this.friendsInSameInstance.flatMap((item) =>
item.map((friend) => friend.ref?.$location.tag)
)
);
return this.onlineFriends.filter(
(item) => !sameInstanceTag.has(item.ref?.$location.tag)
);
};
$app.computed.vipFriendsByGroupStatus = function () {
if (
!this.isSidebarGroupByInstance ||
(this.isSidebarGroupByInstance && !this.isHideFriendsInSameInstance)
) {
return this.vipFriends;
}
const sameInstanceTag = new Set(
this.friendsInSameInstance.flatMap((item) =>
item.map((friend) => friend.ref?.$location.tag)
)
);
return this.vipFriends.filter(
(item) => !sameInstanceTag.has(item.ref?.$location.tag)
);
};
$app.methods.getFriendsLocations = function (friendsArr) {
// prevent the instance title display as "Traveling".
if (!friendsArr?.length) {
return '';
}
for (const friend of friendsArr) {
if (friend.ref?.location !== 'traveling') {
return friend.ref.location;
}
if ($utils.isRealInstance(friend.ref?.travelingToLocation)) {
return friend.ref.travelingToLocation;
}
if (this.lastLocation.friendList.has(friend.id)) {
return this.lastLocation.name;
}
}
return friendsArr[0].ref?.location;
};
// favorites Tab
// - local favorites
// - local world & avatar
@@ -23534,6 +23288,43 @@ console.log(`isLinux: ${LINUX}`);
// #endregion
// #region | Tab Props
$app.computed.sideBarTabProps = function () {
return {
style: { width: `${this.asideWidth}px` },
vipFriends: this.vipFriends,
onlineFriends: this.onlineFriends,
quickSearchRemoteMethod: this.quickSearchRemoteMethod,
quickSearchItems: this.quickSearchItems,
hideTooltips: this.hideTooltips,
onlineFriendCount: this.onlineFriendCount,
friends: this.friends,
isGameRunning: this.isGameRunning,
isSidebarDivideByFriendGroup: this.isSidebarDivideByFriendGroup,
isSidebarGroupByInstance: this.isSidebarGroupByInstance,
isHideFriendsInSameInstance: this.isHideFriendsInSameInstance,
gameLogDisabled: this.gameLogDisabled,
lastLocation: this.lastLocation,
lastLocationDestination: this.lastLocationDestination,
hideNicknames: this.hideNicknames,
activeFriends: this.activeFriends,
offlineFriends: this.offlineFriends,
groupInstances: this.groupInstances,
inGameGroupOrder: this.inGameGroupOrder,
groupedByGroupKeyFavoriteFriends:
this.groupedByGroupKeyFavoriteFriends
};
};
$app.computed.isSideBarTabShow = function () {
return !(
this.menuActiveIndex === 'friendsList' ||
this.menuActiveIndex === 'charts'
);
};
// #endregion
// #region | Electron
if (LINUX) {
+8 -12
View File
@@ -8,7 +8,6 @@
// 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';
@@ -135,14 +134,11 @@
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;
body {
font-family:
'Noto Sans KR', 'Noto Sans JP', 'Noto Sans TC', 'Noto Sans SC',
'Meiryo UI', 'Malgun Gothic', 'Segoe UI', system-ui, sans-serif;
margin: 0;
}
a {
@@ -164,10 +160,9 @@ a {
}
.x-app {
position: absolute;
display: flex;
width: 100%;
height: 100%;
width: 100vw;
height: 100vh;
overflow: hidden auto;
cursor: default;
}
@@ -307,6 +302,7 @@ hr.x-vertical-divider {
.el-popper.x-quick-search {
width: 225px;
min-width: 0 !important;
border: none;
}
.el-popper.x-quick-search .el-select-dropdown__item {
-146
View File
@@ -68,152 +68,6 @@ export default class extends baseClass {
}
});
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
},
isOpenPreviousInstanceInfoDialog: Boolean
},
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;
}
if (this.isOpenPreviousInstanceInfoDialog) {
this.$emit(
'open-previous-instance-info-dialog',
instanceId
);
} else {
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">' +
+24
View File
@@ -351,5 +351,29 @@ export default {
echarts = module;
return echarts;
});
},
// CJK character in Japanese, Korean, Chinese are different
// so change font-family order when users change language to display CJK character correctly
changeCJKorder(lang) {
const otherFonts = window
.getComputedStyle(document.body)
.fontFamily.split(',')
.filter((item) => !item.includes('Noto Sans'))
.join(', ');
const notoSans = 'Noto Sans';
const fontFamilies = {
ja_JP: ['JP', 'KR', 'TC', 'SC'],
ko: ['KR', 'JP', 'TC', 'SC'],
zh_TW: ['TC', 'JP', 'KR', 'SC'],
zh_CN: ['SC', 'JP', 'KR', 'TC']
};
if (fontFamilies[lang]) {
const CJKFamily = fontFamilies[lang]
.map((item) => `${notoSans} ${item}`)
.join(', ');
document.body.style.fontFamily = `${CJKFamily}, ${otherFonts}`;
}
}
};
@@ -7,8 +7,9 @@
class="location"
:location="activityDetailData[0].location"
is-open-previous-instance-info-dialog
@open-previous-instance-info-dialog="$emit('open-previous-instance-info-dialog', $event)"
></location>
@open-previous-instance-info-dialog="
$emit('open-previous-instance-info-dialog', $event)
"></location>
</transition>
</div>
@@ -19,9 +20,13 @@
<script>
import dayjs from 'dayjs';
import utils from '../../classes/utils';
import Location from '../common/Location.vue';
export default {
name: 'InstanceActivityDetail',
components: {
Location
},
inject: ['API', 'showUserDialog'],
props: {
activityDetailData: {
+157
View File
@@ -0,0 +1,157 @@
<template>
<div>
<span v-if="!text" style="color: transparent">-</span>
<span v-show="text">
<span
:class="{ 'x-link': link && location !== 'private' && location !== 'offline' }"
@click="showWorldDialog">
<i v-if="isTraveling" class="el-icon el-icon-loading" style="display: inline-block"></i>
<span>{{ text }}</span>
</span>
<span v-if="groupName" :class="{ 'x-link': link }" @click="showGroupDialog">({{ 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>
</div>
</template>
<script>
import utils from '../../classes/utils';
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: 'Location',
inject: ['API'],
props: {
location: String,
traveling: String,
hint: {
type: String,
default: ''
},
grouphint: {
type: String,
default: ''
},
link: {
type: Boolean,
default: true
},
isOpenPreviousInstanceInfoDialog: Boolean
},
data() {
return {
text: '',
region: this.region,
strict: this.strict,
isTraveling: this.isTraveling,
groupName: this.groupName
};
},
watch: {
location() {
this.parse();
}
},
created() {
this.parse();
},
methods: {
parse() {
if (!this.API) return;
this.isTraveling = false;
this.groupName = '';
let instanceId = this.location;
if (typeof this.traveling !== 'undefined' && this.location === 'traveling') {
instanceId = this.traveling;
this.isTraveling = true;
}
const 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 = this.API.cachedWorlds.get(L.worldId);
if (typeof ref === 'undefined') {
// TODO: USE props
$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;
// TODO: USE props
$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) {
let instanceId = this.location;
if (this.traveling && this.location === 'traveling') {
instanceId = this.traveling;
}
if (!instanceId && this.hint.length === 8) {
// shortName
this.API.$emit('SHOW_WORLD_DIALOG_SHORTNAME', this.hint);
return;
}
if (this.isOpenPreviousInstanceInfoDialog) {
this.$emit('open-previous-instance-info-dialog', instanceId);
} else {
this.API.$emit('SHOW_WORLD_DIALOG', instanceId);
}
}
},
showGroupDialog() {
let location = this.location;
if (this.isTraveling) {
location = this.traveling;
}
if (!location || !this.link) {
return;
}
const L = utils.parseLocation(location);
if (!L.groupId) {
return;
}
this.API.$emit('SHOW_GROUP_DIALOG', L.groupId);
}
}
};
</script>
<style scoped></style>
+130
View File
@@ -0,0 +1,130 @@
<template>
<div class="x-friend-item" @click="$emit('click')">
<template v-if="friend.ref">
<div
class="avatar"
:class="isFriendActiveOrOffline ? undefined : userStatusClass(friend.ref, friend.pendingOffline)">
<img v-lazy="userImage(friend.ref)" />
</div>
<div class="detail">
<span v-if="!hideNicknames && friend.$nickName" class="name" :style="{ color: friend.ref.$userColour }">
{{ friend.ref.displayName }} ({{ friend.$nickName }})
</span>
<span v-else class="name" :style="{ color: friend.ref.$userColour }"
>{{ friend.ref.displayName }}{{ isGroupByInstance && friend.isVIP ? ' ⭐' : '' }}</span
>
<span v-if="isFriendActiveOrOffline" class="extra">{{ friend.ref.statusDescription }}</span>
<template v-else>
<span v-if="friend.pendingOffline" class="extra">
<i class="el-icon-warning-outline" /> {{ $t('side_panel.pending_offline') }}
</span>
<template v-else-if="isGroupByInstance">
<i v-if="isFriendTraveling" class="el-icon el-icon-loading"></i>
<timer
class="extra"
:epoch="epoch"
:style="
isFriendTraveling ? { display: 'inline-block', overflow: 'unset' } : undefined
"></timer>
</template>
<location
v-else
class="extra"
:location="friend.ref.location"
:traveling="friend.ref.travelingToLocation"
:link="false" />
</template>
</div>
</template>
<template v-else-if="!friend.ref && !API.isRefreshFriendsLoading">
<span>{{ friend.name || friend.id }}</span>
<el-button
ttype="text"
icon="el-icon-close"
size="mini"
style="margin-left: 5px"
@click.stop="$emit('confirm-delete-friend', friend.id)">
</el-button>
</template>
<el-skeleton v-else animated class="skeleton" :throttle="100">
<template slot="template">
<div>
<el-skeleton-item variant="circle" />
<div>
<el-skeleton-item variant="text" />
<el-skeleton-item variant="text" />
</div>
</div>
</template>
</el-skeleton>
</div>
</template>
<script>
import Location from '../common/Location.vue';
export default {
name: 'FriendItem',
components: {
Location
},
inject: ['API', 'userImage', 'userStatusClass'],
props: {
friend: {
type: Object,
required: true
},
hideNicknames: {
type: Boolean,
default: false
},
isGroupByInstance: Boolean
},
computed: {
isFriendTraveling() {
return this.friend.ref.location === 'traveling';
},
isFriendActiveOrOffline() {
return this.friend.state === 'active' || this.friend.state === 'offline';
},
epoch() {
return this.isFriendTraveling ? this.friend.ref.$travelingToTime : this.friend.ref.$location_at;
}
}
};
</script>
<style scoped>
.skeleton {
height: 40px;
width: 100%;
::v-deep .el-skeleton {
height: 100%;
> div {
height: 100%;
display: flex;
align-items: center;
> :first-child {
margin-right: 8px;
height: 40px;
width: 40px;
}
> :last-child {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
> :first-child {
width: 50%;
margin-bottom: 4px;
}
> :last-child {
width: 90%;
}
}
}
}
}
</style>
+373
View File
@@ -0,0 +1,373 @@
<template>
<div class="x-friend-list" style="padding: 10px 5px">
<div
class="x-friend-group x-link"
style="padding: 0 0 5px"
@click="
isFriendsGroupMe = !isFriendsGroupMe;
saveFriendsGroupStates();
">
<i class="el-icon-arrow-right" :class="{ rotate: isFriendsGroupMe }"></i>
<span style="margin-left: 5px">{{ $t('side_panel.me') }}</span>
</div>
<div v-show="isFriendsGroupMe">
<div class="x-friend-item" @click="showUserDialog(API.currentUser.id)">
<div class="avatar" :class="userStatusClass(API.currentUser)">
<img v-lazy="userImage(API.currentUser)" />
</div>
<div class="detail">
<span class="name" :style="{ color: API.currentUser.$userColour }">{{
API.currentUser.displayName
}}</span>
<location
v-if="isGameRunning && !gameLogDisabled"
class="extra"
:location="lastLocation.location"
:traveling="lastLocationDestination"
:link="false"></location>
<location
v-else-if="
isRealInstance(API.currentUser.$locationTag) ||
isRealInstance(API.currentUser.$travelingToLocation)
"
class="extra"
:location="API.currentUser.$locationTag"
:traveling="API.currentUser.$travelingToLocation"
:link="false">
</location>
<span v-else class="extra">{{ API.currentUser.statusDescription }}</span>
</div>
</div>
</div>
<div
v-show="vipFriendsByGroupStatus.length"
class="x-friend-group x-link"
@click="
isVIPFriends = !isVIPFriends;
saveFriendsGroupStates();
">
<i class="el-icon-arrow-right" :class="{ rotate: isVIPFriends }"></i>
<span style="margin-left: 5px">
{{ $t('side_panel.favorite') }} &horbar; {{ vipFriendsByGroupStatus.length }}
</span>
</div>
<div v-show="isVIPFriends">
<template v-if="isSidebarDivideByFriendGroup">
<div v-for="group in vipFriendsDivideByGroup" :key="group[0].key">
<transition name="el-fade-in-linear">
<div v-show="group[0].groupName !== ''" style="margin-bottom: 3px">
<span class="extra">{{ group[0].groupName }}</span>
<span class="extra" style="margin-left: 5px">{{ `(${group.length})` }}</span>
</div>
</transition>
<div v-if="group.length" style="margin-bottom: 10px">
<friend-item
v-for="friend in group"
:key="friend.id"
:friend="friend"
:hide-nicknames="hideNicknames"
@click="showUserDialog(friend.id)"
@confirm-delete-friend="$emit('confirm-delete-friend', $event)"></friend-item>
</div>
</div>
</template>
<friend-item
v-for="friend in vipFriendsByGroupStatus"
v-else
:key="friend.id"
:friend="friend"
:hide-nicknames="hideNicknames"
@click="showUserDialog(friend.id)"
@confirm-delete-friend="$emit('confirm-delete-friend', $event)">
</friend-item>
</div>
<template v-if="isSidebarGroupByInstance && friendsInSameInstance.length">
<div class="x-friend-group x-link" @click="toggleSwitchGroupByInstanceCollapsed">
<i class="el-icon-arrow-right" :class="{ rotate: !isSidebarGroupByInstanceCollapsed }"></i>
<span style="margin-left: 5px"
>{{ $t('side_panel.same_instance') }} &horbar; {{ friendsInSameInstance.length }}</span
>
</div>
<div v-show="!isSidebarGroupByInstanceCollapsed">
<div v-for="friendArr in friendsInSameInstance" :key="friendArr[0].ref.$location.tag">
<div style="margin-bottom: 3px">
<location
class="extra"
:location="getFriendsLocations(friendArr)"
style="display: inline"></location>
<span class="extra" style="margin-left: 5px">{{ `(${friendArr.length})` }}</span>
</div>
<div v-if="friendArr && friendArr.length">
<friend-item
v-for="(friend, idx) in friendArr"
:key="friend.id"
:friend="friend"
is-group-by-instance
:style="{ 'margin-bottom': idx === friendArr.length - 1 ? '5px' : undefined }"
@click="showUserDialog(friend.id)"
@confirm-delete-friend="$emit('confirm-delete-friend', $event)">
</friend-item>
</div>
</div>
</div>
</template>
<div
v-show="onlineFriendsByGroupStatus.length"
class="x-friend-group x-link"
@click="
isOnlineFriends = !isOnlineFriends;
saveFriendsGroupStates();
">
<i class="el-icon-arrow-right" :class="{ rotate: isOnlineFriends }"></i>
<span style="margin-left: 5px"
>{{ $t('side_panel.online') }} &horbar; {{ onlineFriendsByGroupStatus.length }}</span
>
</div>
<div v-show="isOnlineFriends">
<friend-item
v-for="friend in onlineFriendsByGroupStatus"
:key="friend.id"
:friend="friend"
:hide-nicknames="hideNicknames"
@click="showUserDialog(friend.id)"
@confirm-delete-friend="$emit('confirm-delete-friend', $event)" />
</div>
<div
v-show="activeFriends.length"
class="x-friend-group x-link"
@click="
isActiveFriends = !isActiveFriends;
saveFriendsGroupStates();
">
<i class="el-icon-arrow-right" :class="{ rotate: isActiveFriends }"></i>
<span style="margin-left: 5px">{{ $t('side_panel.active') }} &horbar; {{ activeFriends.length }}</span>
</div>
<div v-show="isActiveFriends">
<friend-item
v-for="friend in activeFriends"
:key="friend.id"
:friend="friend"
:hide-nicknames="hideNicknames"
@click="showUserDialog(friend.id)"
@confirm-delete-friend="$emit('confirm-delete-friend', $event)"></friend-item>
</div>
<div
v-show="offlineFriends.length"
class="x-friend-group x-link"
@click="
isOfflineFriends = !isOfflineFriends;
saveFriendsGroupStates();
">
<i class="el-icon-arrow-right" :class="{ rotate: isOfflineFriends }"></i>
<span style="margin-left: 5px">{{ $t('side_panel.offline') }} &horbar; {{ offlineFriends.length }}</span>
</div>
<div v-show="isOfflineFriends">
<friend-item
v-for="friend in offlineFriends"
:key="friend.id"
:friend="friend"
:hide-nicknames="hideNicknames"
@click="showUserDialog(friend.id)"
@confirm-delete-friend="$emit('confirm-delete-friend', $event)"></friend-item>
</div>
</div>
</template>
<script>
import FriendItem from './FriendItem.vue';
import Location from '../common/Location.vue';
import configRepository from '../../repository/config';
import utils from '../../classes/utils';
export default {
name: 'FriendsSidebar',
components: {
FriendItem,
Location
},
inject: ['API', 'showUserDialog', 'userImage', 'userStatusClass'],
props: {
// settings
isGameRunning: Boolean,
isSidebarDivideByFriendGroup: Boolean,
isSidebarGroupByInstance: Boolean,
gameLogDisabled: Boolean,
hideNicknames: Boolean,
isHideFriendsInSameInstance: Boolean,
lastLocation: Object,
lastLocationDestination: String,
activeFriends: Array,
offlineFriends: Array,
vipFriends: Array,
onlineFriends: Array,
groupedByGroupKeyFavoriteFriends: Object
},
data() {
return {
isFriendsGroupMe: true,
isVIPFriends: true,
isOnlineFriends: true,
isActiveFriends: true,
isOfflineFriends: true,
isSidebarGroupByInstanceCollapsed: false
};
},
computed: {
friendsInSameInstance() {
const friendsList = {};
const allFriends = [...this.vipFriends, ...this.onlineFriends];
allFriends.forEach((friend) => {
let locationTag;
if (friend.ref?.$location.isRealInstance) {
locationTag = friend.ref.$location.tag;
} else if (this.lastLocation.friendList.has(friend.id)) {
let $location = utils.parseLocation(this.lastLocation.location);
if ($location.isRealInstance) {
if ($location.tag === 'private') {
locationTag = this.lastLocation.name;
} else {
locationTag = $location.tag;
}
}
}
if (!locationTag) {
return;
}
if (!friendsList[locationTag]) {
friendsList[locationTag] = [];
}
friendsList[locationTag].push(friend);
});
const sortedFriendsList = [];
for (const group of Object.values(friendsList)) {
if (group.length > 1) {
sortedFriendsList.push(group.sort((a, b) => a.ref?.$location_at - b.ref?.$location_at));
}
}
return sortedFriendsList.sort((a, b) => b.length - a.length);
},
onlineFriendsByGroupStatus() {
if (
!this.isSidebarGroupByInstance ||
(this.isSidebarGroupByInstance && !this.isHideFriendsInSameInstance)
) {
return this.onlineFriends;
}
const sameInstanceTag = new Set(
this.friendsInSameInstance.flatMap((item) => item.map((friend) => friend.ref?.$location.tag))
);
return this.onlineFriends.filter((item) => !sameInstanceTag.has(item.ref?.$location.tag));
},
vipFriendsByGroupStatus() {
if (
!this.isSidebarGroupByInstance ||
(this.isSidebarGroupByInstance && !this.isHideFriendsInSameInstance)
) {
return this.vipFriends;
}
const sameInstanceTag = new Set(
this.friendsInSameInstance.flatMap((item) => item.map((friend) => friend.ref?.$location.tag))
);
return this.vipFriends.filter((item) => !sameInstanceTag.has(item.ref?.$location.tag));
},
// VIP friends divide by group
vipFriendsDivideByGroup() {
const vipFriendsByGroup = { ...this.groupedByGroupKeyFavoriteFriends };
const result = [];
for (const key in vipFriendsByGroup) {
if (Object.prototype.hasOwnProperty.call(vipFriendsByGroup, key)) {
const groupFriends = vipFriendsByGroup[key];
// sort groupFriends using the order of vipFriends
// avoid unnecessary sorting
let filteredFriends = this.vipFriends.filter((friend) =>
groupFriends.some((item) => item.id === friend.id)
);
if (filteredFriends.length > 0) {
const groupName =
this.API.favoriteFriendGroups.find((item) => item.key === key)?.displayName || '';
result.push(filteredFriends.map((item) => ({ groupName, key, ...item })));
}
}
}
return result.sort((a, b) => a[0].key.localeCompare(b[0].key));
}
},
created() {
configRepository.getBool('VRCX_sidebarGroupByInstanceCollapsed', false).then((value) => {
this.isSidebarGroupByInstanceCollapsed = value;
});
},
methods: {
saveFriendsGroupStates() {
configRepository.setBool('VRCX_isFriendsGroupMe', this.isFriendsGroupMe);
configRepository.setBool('VRCX_isFriendsGroupFavorites', this.isVIPFriends);
configRepository.setBool('VRCX_isFriendsGroupOnline', this.isOnlineFriends);
configRepository.setBool('VRCX_isFriendsGroupActive', this.isActiveFriends);
configRepository.setBool('VRCX_isFriendsGroupOffline', this.isOfflineFriends);
},
async loadFriendsGroupStates() {
this.isFriendsGroupMe = await configRepository.getBool('VRCX_isFriendsGroupMe', true);
this.isVIPFriends = await configRepository.getBool('VRCX_isFriendsGroupFavorites', true);
this.isOnlineFriends = await configRepository.getBool('VRCX_isFriendsGroupOnline', true);
this.isActiveFriends = await configRepository.getBool('VRCX_isFriendsGroupActive', false);
this.isOfflineFriends = await configRepository.getBool('VRCX_isFriendsGroupOffline', false);
},
isRealInstance(locationTag) {
return utils.isRealInstance(locationTag);
},
toggleSwitchGroupByInstanceCollapsed() {
this.isSidebarGroupByInstanceCollapsed = !this.isSidebarGroupByInstanceCollapsed;
configRepository.setBool(
'VRCX_sidebarGroupByInstanceCollapsed',
this.isSidebarGroupByInstanceCollapsed
);
},
getFriendsLocations(friendsArr) {
// prevent the instance title display as "Traveling".
if (!friendsArr?.length) {
return '';
}
for (const friend of friendsArr) {
if (friend.ref?.location !== 'traveling') {
return friend.ref.location;
}
if (utils.isRealInstance(friend.ref?.travelingToLocation)) {
return friend.ref.travelingToLocation;
}
if (this.lastLocation.friendList.has(friend.id)) {
return this.lastLocation.name;
}
}
return friendsArr[0].ref?.location;
}
}
};
</script>
<style scoped>
.x-link:hover {
text-decoration: none;
}
.x-link:hover span {
text-decoration: underline;
}
</style>
+26 -23
View File
@@ -4,47 +4,50 @@
<div
:key="getGroupId(group)"
class="x-friend-group x-link"
:style="{ paddingTop: index === 0 ? '0px' : '10px' }"
>
:style="{ paddingTop: index === 0 ? '0px' : '10px' }">
<div @click="toggleGroupSidebarCollapse(getGroupId(group))" style="display: flex; align-items: center">
<i
class="el-icon-arrow-right"
:style="{
transform: groupInstancesCfg[getGroupId(group)].isCollapsed ? '' : 'rotate(90deg)',
transition: 'transform 0.3s'
}"
></i>
}"></i>
<span style="margin-left: 5px">{{ group[0].group.name }} {{ group.length }}</span>
</div>
</div>
<div
v-if="!groupInstancesCfg[getGroupId(group)].isCollapsed"
v-for="ref in group"
:key="ref.instance.id"
class="x-friend-item"
@click="showGroupDialog(ref.instance.ownerId)"
>
<div class="avatar">
<img v-lazy="ref.group.iconUrl" />
<template v-if="!groupInstancesCfg[getGroupId(group)].isCollapsed">
<div
v-for="ref in group"
:key="ref.instance.id"
class="x-friend-item"
@click="$emit('show-group-dialog', ref.instance.ownerId)">
<div class="avatar">
<img v-lazy="ref.group.iconUrl" />
</div>
<div class="detail">
<span class="name">
<span v-text="ref.group.name"></span>
<span style="font-weight: normal; margin-left: 5px"
>({{ ref.instance.userCount }}/{{ ref.instance.capacity }})</span
>
</span>
<location class="extra" :location="ref.instance.location" :link="false" />
</div>
</div>
<div class="detail">
<span class="name">
<span v-text="ref.group.name"></span>
<span style="font-weight: normal; margin-left: 5px"
>({{ ref.instance.userCount }}/{{ ref.instance.capacity }})</span
>
</span>
<location class="extra" :location="ref.instance.location" :link="false" />
</div>
</div>
</template>
</template>
</div>
</template>
<script>
import Location from '../common/Location.vue';
export default {
name: 'GroupsSidebar',
components: {
Location
},
props: {
groupInstances: {
type: Array,
+13 -24
View File
@@ -36,24 +36,7 @@ html
circle
style='font-size: 14px; height: 50px; width: 50px')
el-menu(ref='menu' collapse @select='selectMenu' default-active='feed')
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('charts', "{{ $t('nav_tooltip.charts') }}", 'el-icon-data-analysis')
+menuitem('profile', "{{ $t('nav_tooltip.profile') }}", 'el-icon-user')
+menuitem('settings', "{{ $t('nav_tooltip.settings') }}", 'el-icon-s-tools')
nav-menu(ref='menu' @select='selectMenu' :menu-active-index='menuActiveIndex')
//- ### Tabs ###
template(v-if='API.isLoggedIn')
@@ -83,7 +66,7 @@ html
//- moderation
moderation-tab(
v-if='$refs.menu?.activeIndex === "moderation"'
v-if='menuActiveIndex === "moderation"'
:table-data='playerModerationTable'
:shift-held='shiftHeld'
:hide-tooltips='hideTooltips')
@@ -103,20 +86,26 @@ html
//- charts
keep-alive
charts-tab(
v-if='$refs.menu?.activeIndex === "charts"'
v-if='menuActiveIndex === "charts"'
:get-world-name='getWorldName'
:is-dark-mode='isDarkMode'
:dt-hour12='dtHour12'
@open-previous-instance-info-dialog='showPreviousInstanceInfoDialog'
:friends-map='friends'
:local-favorite-friends='localFavoriteFriends')
:local-favorite-friends='localFavoriteFriends'
@open-previous-instance-info-dialog='showPreviousInstanceInfoDialog')
//- settings
include ./mixins/tabs/settings.pug
+settingsTab
include ./mixins/friendsListSidebar.pug
+friendsListSidebar
side-bar(
v-show='isSideBarTabShow'
v-bind='sideBarTabProps'
@show-group-dialog='showGroupDialog'
@quick-search-change='quickSearchChange'
@direct-access-paste='directAccessPaste'
@refresh-friends-list='refreshFriendsList'
@confirm-delete-friend='confirmDeleteFriend')
//- ## Dialogs ## -\\
include ./mixins/dialogs/userDialog.pug
-296
View File
@@ -1,296 +0,0 @@
mixin friendsListSidebar
#aside.x-aside-container(
:style='{ width: `${asideWidth}px` }'
v-show='$refs.menu && !($refs.menu.activeIndex === "friendsList" || $refs.menu.activeIndex === "charts") ')
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 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='vipFriendsByGroupStatus.length')
i.el-icon-arrow-right(:class='{ rotate: isVIPFriends }')
span(style='margin-left: 5px') {{ $t('side_panel.favorite') }} &horbar; {{ vipFriendsByGroupStatus.length }}
div(v-show='isVIPFriends')
template(v-if='isSidebarDivideByFriendGroup')
div(v-for='(group, idx) in vipFriendsDivideByGroup' :key='idx')
div(style='margin-bottom: 3px')
span.extra {{ group.displayName }}
span.extra(style='margin-left: 5px') {{ `(${group.value.length})` }}
div(style='margin-bottom: 10px')
.x-friend-item(
v-if='group.value && group.value.length'
v-for='friend in group.value'
: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')
template(v-else)
.x-friend-item(
v-for='friend in vipFriendsByGroupStatus'
: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')
//- Group By Instance
template(v-if='isSidebarGroupByInstance && friendsInSameInstance.length')
.x-friend-group.x-link(@click='toggleSwitchGroupByInstanceCollapsed')
i.el-icon-arrow-right(:class='{ rotate: !isSidebarGroupByInstanceCollapsed }')
span(style='margin-left: 5px') {{ $t('side_panel.same_instance') }} &horbar; {{ friendsInSameInstance.length }}
div(v-show='!isSidebarGroupByInstanceCollapsed')
div(v-for='friendArr in friendsInSameInstance' :key='friendArr[0].ref?.$location.tag')
div(style='margin-bottom: 3px')
location.extra(:location='getFriendsLocations(friendArr)')
span.extra(style='margin-left: 5px') {{ `(${friendArr.length})` }}
div
.x-friend-item(
v-if='friendArr && friendArr.length'
v-for='(friend, idx) in friendArr'
:key='friend.id'
@click='showUserDialog(friend.id)'
:style='{ "margin-bottom": idx === friendArr.length - 1 ? "5px" : undefined }')
template(v-if='friend.ref')
.avatar(:class='userStatusClass(friend.ref, friend.pendingOffline)')
img(v-lazy='userImage(friend.ref)')
.detail
span.name(style='display: flex; align-items: center')
span(
v-if='!hideNicknames && friend.$nickName'
:style='{ color: friend.ref.$userColour }') {{ friend.ref.displayName }} ({{ friend.$nickName }})
span(
v-else
v-text='friend.ref.displayName'
:style='{ color: friend.ref.$userColour }')
span(v-if='friend.isVIP' style='margin-left: 2px') ⭐
span.extra(v-if='friend.pendingOffline') #[i.el-icon-warning-outline] {{ $t('side_panel.pending_offline') }}
template(v-else)
template(v-if='friend.ref.location === "traveling"')
i.el-icon.el-icon-loading(
style='display: inline-block; margin-right: 5px')
timer.extra(
:epoch='friend.ref?.$travelingToTime'
style='display: inline-block; overflow: unset')
template(v-else)
timer.extra(:epoch='friend.ref?.$location_at')
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='onlineFriendsByGroupStatus.length')
i.el-icon-arrow-right(:class='{ rotate: isOnlineFriends }')
span(style='margin-left: 5px') {{ $t('side_panel.online') }} &horbar; {{ onlineFriendsByGroupStatus.length }}
div(v-show='isOnlineFriends')
.x-friend-item(
v-for='friend in onlineFriendsByGroupStatus'
: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') }} &horbar; {{ 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') }} &horbar; {{ 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(lazy)
template(#label)
span {{ $t('side_panel.groups') }}
span(style='color: #909399; font-size: 12px; margin-left: 10px') ({{ groupInstances.length }})
groups-sidebar(
:group-instances='groupInstances'
:group-order='inGameGroupOrder'
@show-group-dialog='showGroupDialog')
+1 -1
View File
@@ -1,5 +1,5 @@
mixin favoritesTab
.x-container(v-show='$refs.menu && $refs.menu.activeIndex === "favorite"')
.x-container(v-show='menuActiveIndex === "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') }}
+1 -1
View File
@@ -1,5 +1,5 @@
mixin feedTab
.x-container(v-show='$refs.menu && $refs.menu.activeIndex === \'feed\'')
.x-container(v-show='menuActiveIndex === "feed"')
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(
+1 -1
View File
@@ -1,5 +1,5 @@
mixin friendLogTab
.x-container(v-if='$refs.menu && $refs.menu.activeIndex === "friendLog"')
.x-container(v-if='menuActiveIndex === "friendLog"')
data-tables(v-bind='friendLogTable' ref='friendLogTableRef')
template(#tool)
div(style='margin: 0 0 10px; display: flex; align-items: center')
+1 -1
View File
@@ -1,5 +1,5 @@
mixin friendsListTab
.x-container(v-show='$refs.menu && $refs.menu.activeIndex === "friendsList"')
.x-container(v-show='menuActiveIndex === "friendsList"')
div(style='padding: 0px 10px 0px 10px')
span.header {{ $t('view.friend_list.header') }}
div(style='float: right; font-size: 13px')
+1 -1
View File
@@ -1,5 +1,5 @@
mixin gameLogTab
.x-container(v-show='$refs.menu && $refs.menu.activeIndex === \'gameLog\'')
.x-container(v-show='menuActiveIndex === "gameLog"')
data-tables(v-bind='gameLogTable' v-loading='gameLogTable.loading')
template(#tool)
div(style='margin: 0 0 10px; display: flex; align-items: center')
+1 -1
View File
@@ -1,5 +1,5 @@
mixin notificationsTab
.x-container(v-if='$refs.menu && $refs.menu.activeIndex === "notification"' v-loading='API.isNotificationsLoading')
.x-container(v-if='menuActiveIndex === "notification"' v-loading='API.isNotificationsLoading')
data-tables.notification-table(v-bind='notificationTable' ref='notificationTableRef')
template(#tool)
div(style='margin: 0 0 10px; display: flex; align-items: center')
+1 -1
View File
@@ -1,5 +1,5 @@
mixin playerListTab
.x-container(v-show='$refs.menu && $refs.menu.activeIndex === \'playerList\'' style='padding-top: 5px')
.x-container(v-show='menuActiveIndex === "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')
+1 -1
View File
@@ -1,5 +1,5 @@
mixin profileTab
.x-container(v-if='$refs.menu && $refs.menu.activeIndex === "profile"')
.x-container(v-if='menuActiveIndex === "profile"')
.options-container(style='margin-top: 0')
span.header {{ $t('view.profile.profile.header') }}
.x-friend-list(style='margin-top: 10px')
+5 -2
View File
@@ -1,5 +1,5 @@
mixin searchTab
.x-container(v-show='$refs.menu && $refs.menu.activeIndex === \'search\'')
.x-container(v-show='menuActiveIndex === "search"')
div(style='margin: 0 0 10px; display: flex; align-items: center')
el-input(
v-model='searchText'
@@ -14,7 +14,10 @@ mixin searchTab
circle
style='flex: none; margin-left: 10px')
el-tabs(ref='searchTab' type='card' style='margin-top: 15px' @tab-click='searchText = ""')
el-tab-pane(:label='$t("view.search.user.header")' v-loading='isSearchUserLoading' style='min-height: 60px')
el-tab-pane(
:label='$t("view.search.user.header")'
v-loading='isSearchUserLoading'
style='min-height: 60px')
el-checkbox(v-model='searchUserByBio' style='margin-left: 10px') {{ $t('view.search.user.search_by_bio') }}
el-checkbox(v-model='searchUserSortByLastLoggedIn' style='margin-left: 10px') {{ $t('view.search.user.sort_by_last_logged_in') }}
.x-friend-list(style='min-height: 500px')
+1 -1
View File
@@ -1,5 +1,5 @@
mixin settingsTab
.x-container(v-if='$refs.menu && $refs.menu.activeIndex === "settings"')
.x-container(v-if='menuActiveIndex === "settings"')
.options-container(style='margin-top: 0; padding: 5px')
span.header {{ $t('view.settings.header') }}
el-tabs(type='card' style='height: calc(100% - 51px)')
+8
View File
@@ -476,3 +476,11 @@ button {
background-color: rgba(0, 0, 0, 0.2);
}
// Date picker end
// el-skeleton
.el-skeleton.is-animated .el-skeleton__item {
background: linear-gradient(90deg, #333 25%, #555 37%, #333 63%);
background-size: 400% 100%;
animation: el-skeleton-loading 1.4s ease infinite;
}
// el-sekeleton end
+5
View File
@@ -2057,3 +2057,8 @@ i.x-user-status {
.el-month-table td .cell:hover {
color: rgb(48, 46, 53);
}
.el-skeleton.is-animated .el-skeleton__item {
background: linear-gradient(90deg, #302E34 25%, #423F46 37%, #302E34 63%);
background-size: 400% 100%;
animation: el-skeleton-loading 1.4s ease infinite;
}
+6
View File
@@ -413,3 +413,9 @@ input[type='checkbox']:checked + .el-switch__core {
.el-date-table td.disabled div {
background-color: #3a2b2b
}
.el-skeleton.is-animated .el-skeleton__item {
background: linear-gradient(90deg, #4a3d3d 25%, #665252 37%, #4a3d3d 63%);
background-size: 400% 100%;
animation: el-skeleton-loading 1.4s ease infinite;
}
+104
View File
@@ -0,0 +1,104 @@
<template>
<el-menu
ref="navRef"
collapse
@select="$emit('select', $event)"
:default-active="menuActiveIndex"
:collapse-transition="false">
<el-menu-item index="feed">
<i class="el-icon-news"></i>
<template slot="title">
<span>{{ $t('nav_tooltip.feed') }}</span>
</template>
</el-menu-item>
<el-menu-item index="gameLog">
<i class="el-icon-s-data"></i>
<template slot="title">
<span>{{ $t('nav_tooltip.game_log') }}</span>
</template>
</el-menu-item>
<el-menu-item index="playerList">
<i class="el-icon-tickets"></i>
<template slot="title">
<span>{{ $t('nav_tooltip.player_list') }}</span>
</template>
</el-menu-item>
<el-menu-item index="search">
<i class="el-icon-search"></i>
<template slot="title">
<span>{{ $t('nav_tooltip.search') }}</span>
</template>
</el-menu-item>
<el-menu-item index="favorite">
<i class="el-icon-star-off"></i>
<template slot="title">
<span>{{ $t('nav_tooltip.favorites') }}</span>
</template>
</el-menu-item>
<el-menu-item index="friendLog">
<i class="el-icon-notebook-2"></i>
<template slot="title">
<span>{{ $t('nav_tooltip.friend_log') }}</span>
</template>
</el-menu-item>
<el-menu-item index="moderation">
<i class="el-icon-finished"></i>
<template slot="title">
<span>{{ $t('nav_tooltip.moderation') }}</span>
</template>
</el-menu-item>
<el-menu-item index="notification">
<i class="el-icon-bell"></i>
<template slot="title">
<span>{{ $t('nav_tooltip.notification') }}</span>
</template>
</el-menu-item>
<el-menu-item index="friendsList">
<i class="el-icon-s-management"></i>
<template slot="title">
<span>{{ $t('nav_tooltip.friend_list') }}</span>
</template>
</el-menu-item>
<el-menu-item index="charts">
<i class="el-icon-data-analysis"></i>
<template slot="title">
<span>{{ $t('nav_tooltip.charts') }}</span>
</template>
</el-menu-item>
<el-menu-item index="profile">
<i class="el-icon-user"></i>
<template slot="title">
<span>{{ $t('nav_tooltip.profile') }}</span>
</template>
</el-menu-item>
<el-menu-item index="settings">
<i class="el-icon-s-tools"></i>
<template slot="title">
<span>{{ $t('nav_tooltip.settings') }}</span>
</template>
</el-menu-item>
</el-menu>
</template>
<script>
export default {
name: 'NavMenu',
props: {
menuActiveIndex: {
type: String,
default: 'feed'
}
}
};
</script>
+152
View File
@@ -0,0 +1,152 @@
<template>
<div id="aside" class="x-aside-container">
<div style="display: flex; align-items: baseline">
<el-select
value=""
clearable
:placeholder="$t('side_panel.search_placeholder')"
filterable
remote
:remote-method="quickSearchRemoteMethod"
popper-class="x-quick-search"
style="flex: 1; padding: 10px"
@change="$emit('quick-search-change', $event)">
<el-option v-for="item in quickSearchItems" :key="item.value" :value="item.value" :label="item.label">
<div class="x-friend-item">
<template v-if="item.ref">
<div class="detail">
<span class="name" :style="{ color: item.ref.$userColour }">{{
item.ref.displayName
}}</span>
<span v-if="!item.ref.isFriend" class="extra"></span>
<span v-else-if="item.ref.state === 'offline'" class="extra">{{
$t('side_panel.search_result_active')
}}</span>
<span v-else-if="item.ref.state === 'active'" class="extra">{{
$t('side_panel.search_result_offline')
}}</span>
<location
v-else
class="extra"
:location="item.ref.location"
:traveling="item.ref.travelingToLocation"
:link="false"></location>
</div>
<img class="avatar" v-lazy="userImage(item.ref)" />
</template>
<span v-else>
{{ $t('side_panel.search_result_more') }}
<span style="font-weight: bold">{{ item.label }}</span>
</span>
</div>
</el-option>
</el-select>
<el-tooltip placement="bottom" :content="$t('side_panel.direct_access_tooltip')" :disabled="hideTooltips">
<el-button
type="default"
size="mini"
icon="el-icon-discover"
circle
@click="$emit('direct-access-paste')"></el-button>
</el-tooltip>
<el-tooltip placement="bottom" :content="$t('side_panel.refresh_tooltip')" :disabled="hideTooltips">
<el-button
type="default"
:loading="API.isRefreshFriendsLoading"
size="mini"
icon="el-icon-refresh"
circle
style="margin-right: 10px"
@click="$emit('refresh-friends-list')"></el-button>
</el-tooltip>
</div>
<el-tabs class="zero-margin-tabs" stretch style="height: calc(100% - 60px); margin-top: 5px">
<el-tab-pane>
<template slot="label">
<span>{{ $t('side_panel.friends') }}</span>
<span style="color: #909399; font-size: 12px; margin-left: 10px">
({{ onlineFriendCount }}/{{ friends.size }})
</span>
</template>
<el-backtop target=".zero-margin-tabs .el-tabs__content" :bottom="20" :right="20"></el-backtop>
<friends-sidebar
@confirm-delete-friend="$emit('confirm-delete-friend', $event)"
:is-game-running="isGameRunning"
:is-sidebar-divide-by-friend-group="isSidebarDivideByFriendGroup"
:is-sidebar-group-by-instance="isSidebarGroupByInstance"
:game-log-disabled="gameLogDisabled"
:last-location="lastLocation"
:last-location-destination="lastLocationDestination"
:hide-nicknames="hideNicknames"
:active-friends="activeFriends"
:offline-friends="offlineFriends"
:online-friends="onlineFriends"
:vip-friends="vipFriends"
:is-hide-friends-in-same-instance="isHideFriendsInSameInstance"
:grouped-by-group-key-favorite-friends="groupedByGroupKeyFavoriteFriends"></friends-sidebar>
</el-tab-pane>
<el-tab-pane lazy>
<template slot="label">
<span>{{ $t('side_panel.groups') }}</span>
<span style="color: #909399; font-size: 12px; margin-left: 10px">
({{ groupInstances.length }})
</span>
</template>
<groups-sidebar
:group-instances="groupInstances"
:group-order="inGameGroupOrder"
@show-group-dialog="$emit('show-group-dialog', $event)"></groups-sidebar>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import FriendsSidebar from '../components/sidebar/FriendsSidebar.vue';
import GroupsSidebar from '../components/sidebar/GroupsSidebar.vue';
import Location from '../components/common/Location.vue';
export default {
name: 'SideBar',
inject: ['API', 'userImage'],
components: {
FriendsSidebar,
GroupsSidebar,
Location
},
props: {
// settings
// remove these props when have a state manager.
hideTooltips: Boolean,
isGameRunning: Boolean,
isSidebarDivideByFriendGroup: Boolean,
isSidebarGroupByInstance: Boolean,
gameLogDisabled: Boolean,
hideNicknames: Boolean,
isHideFriendsInSameInstance: Boolean,
quickSearchRemoteMethod: Function,
quickSearchItems: Array,
onlineFriendCount: Number,
friends: Map,
lastLocation: Object,
lastLocationDestination: String,
// friends
vipFriends: Array,
onlineFriends: Array,
// no
activeFriends: Array,
offlineFriends: Array,
groupInstances: Array,
inGameGroupOrder: Array,
groupedByGroupKeyFavoriteFriends: Object
}
};
</script>
<style scoped>
/* your component styles here */
</style>
+6 -4
View File
@@ -6,16 +6,14 @@
width="800px"
@close="$emit('update:visible', false)"
:fullscreen="fullscreen"
destroy-on-close
>
destroy-on-close>
<div style="display: flex; align-items: center; justify-content: space-between">
<location :location="location.tag" style="font-size: 14px"></location>
<el-input
v-model="dataTable.filters[0].value"
:placeholder="$t('dialog.previous_instances.search_placeholder')"
style="width: 150px"
clearable
></el-input>
clearable></el-input>
</div>
<data-tables v-loading="loading" v-bind="dataTable" style="margin-top: 10px">
<el-table-column :label="$t('table.previous_instances.date')" prop="created_at" sortable width="110">
@@ -63,9 +61,13 @@
import utils from '../../classes/utils';
import database from '../../repository/database';
import dayjs from 'dayjs';
import Location from '../../components/common/Location.vue';
export default {
name: 'PreviousInstanceInfo',
components: {
Location
},
inject: ['adjustDialogZ'],
props: {
visible: {