mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-30 20:23:46 +02:00
Mutual friends tab
This commit is contained in:
@@ -168,6 +168,44 @@ const userReq = {
|
|||||||
};
|
};
|
||||||
return args;
|
return args;
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getMutualCounts(params) {
|
||||||
|
return request(`users/${params.userId}/mutuals`, {
|
||||||
|
method: 'GET'
|
||||||
|
}).then((json) => {
|
||||||
|
const args = {
|
||||||
|
json,
|
||||||
|
params
|
||||||
|
};
|
||||||
|
return args;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getMutualFriends(params) {
|
||||||
|
return request(`users/${params.userId}/mutuals/friends`, {
|
||||||
|
method: 'GET',
|
||||||
|
params
|
||||||
|
}).then((json) => {
|
||||||
|
const args = {
|
||||||
|
json,
|
||||||
|
params
|
||||||
|
};
|
||||||
|
return args;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getMutualGroups(params) {
|
||||||
|
return request(`users/${params.userId}/mutuals/groups`, {
|
||||||
|
method: 'GET',
|
||||||
|
params
|
||||||
|
}).then((json) => {
|
||||||
|
const args = {
|
||||||
|
json,
|
||||||
|
params
|
||||||
|
};
|
||||||
|
return args;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -707,9 +707,9 @@ i.x-status-icon.red {
|
|||||||
border-color: rgb(255, 208, 0) !important;
|
border-color: rgb(255, 208, 0) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.x-tag-vrcplus {
|
.x-tag-mutual-friend {
|
||||||
color: rgb(255, 208, 0);
|
color: #67c23a;
|
||||||
border-color: rgb(255, 208, 0) !important;
|
border-color: #67c23a !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.x-tag-platform-pc {
|
.x-tag-platform-pc {
|
||||||
|
|||||||
@@ -514,6 +514,73 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<el-tab-pane
|
||||||
|
name="Mutual Friends"
|
||||||
|
v-if="userDialog.isFriend"
|
||||||
|
:label="t('dialog.user.mutual_friends.header')"
|
||||||
|
lazy>
|
||||||
|
<div style="display: flex; align-items: center; justify-content: space-between">
|
||||||
|
<div style="display: flex; align-items: center">
|
||||||
|
<el-button
|
||||||
|
type="default"
|
||||||
|
:loading="userDialog.isMutualFriendsLoading"
|
||||||
|
size="small"
|
||||||
|
:icon="Refresh"
|
||||||
|
circle
|
||||||
|
@click="getUserMutualFriends(userDialog.id)">
|
||||||
|
</el-button>
|
||||||
|
<span style="margin-left: 5px">{{
|
||||||
|
t('dialog.user.groups.total_count', { count: userDialog.mutualFriends.length })
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; align-items: center">
|
||||||
|
<span style="margin-right: 5px">{{ t('dialog.user.groups.sort_by') }}</span>
|
||||||
|
<el-dropdown
|
||||||
|
trigger="click"
|
||||||
|
size="small"
|
||||||
|
style="margin-right: 5px"
|
||||||
|
:disabled="userDialog.isMutualFriendsLoading"
|
||||||
|
@click.stop>
|
||||||
|
<el-button size="small">
|
||||||
|
<span
|
||||||
|
>{{ t(userDialog.mutualFriendSorting.name) }}
|
||||||
|
<el-icon style="margin-left: 5px"><ArrowDown /></el-icon>
|
||||||
|
</span>
|
||||||
|
</el-button>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item
|
||||||
|
v-for="(item, key) in userDialogMutualFriendSortingOptions"
|
||||||
|
:key="key"
|
||||||
|
@click="setUserDialogMutualFriendSorting(item)"
|
||||||
|
>{{ t(item.name) }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ul
|
||||||
|
class="x-friend-list"
|
||||||
|
style="margin-top: 10px; overflow: auto; max-height: 250px; min-width: 130px">
|
||||||
|
<li
|
||||||
|
v-for="user in userDialog.mutualFriends"
|
||||||
|
:key="user.id"
|
||||||
|
class="x-friend-item x-friend-item-border"
|
||||||
|
@click="showUserDialog(user.id)">
|
||||||
|
<div class="avatar">
|
||||||
|
<img :src="userImage(user)" loading="lazy" />
|
||||||
|
</div>
|
||||||
|
<div class="detail">
|
||||||
|
<span
|
||||||
|
class="name"
|
||||||
|
:style="{ color: user.$userColour }"
|
||||||
|
v-text="user.displayName"></span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane name="Groups" :label="t('dialog.user.groups.header')" lazy>
|
<el-tab-pane name="Groups" :label="t('dialog.user.groups.header')" lazy>
|
||||||
<div style="display: flex; align-items: center; justify-content: space-between">
|
<div style="display: flex; align-items: center; justify-content: space-between">
|
||||||
<div style="display: flex; align-items: center">
|
<div style="display: flex; align-items: center">
|
||||||
@@ -1253,6 +1320,9 @@
|
|||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
compareByDisplayName,
|
||||||
|
compareByFriendOrder,
|
||||||
|
compareByLastActiveRef,
|
||||||
compareByMemberCount,
|
compareByMemberCount,
|
||||||
compareByName,
|
compareByName,
|
||||||
copyToClipboard,
|
copyToClipboard,
|
||||||
@@ -1298,9 +1368,9 @@
|
|||||||
} from '../../../api';
|
} from '../../../api';
|
||||||
import { getNextDialogIndex, redirectToToolsTab } from '../../../shared/utils/base/ui';
|
import { getNextDialogIndex, redirectToToolsTab } from '../../../shared/utils/base/ui';
|
||||||
import { processBulk, request } from '../../../service/request';
|
import { processBulk, request } from '../../../service/request';
|
||||||
|
import { userDialogGroupSortingOptions, userDialogMutualFriendSortingOptions } from '../../../shared/constants';
|
||||||
import { userDialogWorldOrderOptions, userDialogWorldSortingOptions } from '../../../shared/constants/';
|
import { userDialogWorldOrderOptions, userDialogWorldSortingOptions } from '../../../shared/constants/';
|
||||||
import { database } from '../../../service/database';
|
import { database } from '../../../service/database';
|
||||||
import { userDialogGroupSortingOptions } from '../../../shared/constants';
|
|
||||||
|
|
||||||
import SendInviteDialog from '../InviteDialog/SendInviteDialog.vue';
|
import SendInviteDialog from '../InviteDialog/SendInviteDialog.vue';
|
||||||
import UserSummaryHeader from './UserSummaryHeader.vue';
|
import UserSummaryHeader from './UserSummaryHeader.vue';
|
||||||
@@ -1376,6 +1446,7 @@
|
|||||||
const userDialogGroupEditSelectedGroupIds = ref([]); // selected groups in edit mode
|
const userDialogGroupEditSelectedGroupIds = ref([]); // selected groups in edit mode
|
||||||
|
|
||||||
const userDialogLastActiveTab = ref('Info');
|
const userDialogLastActiveTab = ref('Info');
|
||||||
|
const userDialogLastMutualFriends = ref('');
|
||||||
const userDialogLastGroup = ref('');
|
const userDialogLastGroup = ref('');
|
||||||
const userDialogLastAvatar = ref('');
|
const userDialogLastAvatar = ref('');
|
||||||
const userDialogLastWorld = ref('');
|
const userDialogLastWorld = ref('');
|
||||||
@@ -1499,6 +1570,15 @@
|
|||||||
if (vrchatCredit.value === null) {
|
if (vrchatCredit.value === null) {
|
||||||
getVRChatCredits();
|
getVRChatCredits();
|
||||||
}
|
}
|
||||||
|
} else if (tabName === 'Mutual Friends') {
|
||||||
|
if (userId === currentUser.value.id) {
|
||||||
|
userDialogLastActiveTab.value = 'Info';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (userDialogLastMutualFriends.value !== userId) {
|
||||||
|
userDialogLastMutualFriends.value = userId;
|
||||||
|
getUserMutualFriends(userId);
|
||||||
|
}
|
||||||
} else if (tabName === 'Groups') {
|
} else if (tabName === 'Groups') {
|
||||||
if (userDialogLastGroup.value !== userId) {
|
if (userDialogLastGroup.value !== userId) {
|
||||||
userDialogLastGroup.value = userId;
|
userDialogLastGroup.value = userId;
|
||||||
@@ -2083,6 +2163,38 @@
|
|||||||
userDialog.value.isGroupsLoading = false;
|
userDialog.value.isGroupsLoading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getUserMutualFriends(userId) {
|
||||||
|
userDialog.value.isMutualFriendsLoading = true;
|
||||||
|
userDialog.value.mutualFriends = [];
|
||||||
|
const params = {
|
||||||
|
userId,
|
||||||
|
n: 100,
|
||||||
|
offset: 0
|
||||||
|
};
|
||||||
|
processBulk({
|
||||||
|
fn: userRequest.getMutualFriends,
|
||||||
|
N: -1,
|
||||||
|
params,
|
||||||
|
handle: (args) => {
|
||||||
|
for (const json of args.json) {
|
||||||
|
if (userDialog.value.mutualFriends.some((u) => u.id === json.id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const ref = cachedUsers.get(json.id);
|
||||||
|
if (typeof ref !== 'undefined') {
|
||||||
|
userDialog.value.mutualFriends.push(ref);
|
||||||
|
} else {
|
||||||
|
userDialog.value.mutualFriends.push(json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setUserDialogMutualFriendSorting(userDialog.value.mutualFriendSorting);
|
||||||
|
},
|
||||||
|
done: () => {
|
||||||
|
userDialog.value.isMutualFriendsLoading = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function sortGroupsByInGame(a, b) {
|
function sortGroupsByInGame(a, b) {
|
||||||
const aIndex = inGameGroupOrder.value.indexOf(a?.id);
|
const aIndex = inGameGroupOrder.value.indexOf(a?.id);
|
||||||
const bIndex = inGameGroupOrder.value.indexOf(b?.id);
|
const bIndex = inGameGroupOrder.value.indexOf(b?.id);
|
||||||
@@ -2347,6 +2459,22 @@
|
|||||||
await sortCurrentUserGroups();
|
await sortCurrentUserGroups();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function setUserDialogMutualFriendSorting(sortOrder) {
|
||||||
|
const D = userDialog.value;
|
||||||
|
D.mutualFriendSorting = sortOrder;
|
||||||
|
switch (sortOrder.value) {
|
||||||
|
case 'alphabetical':
|
||||||
|
D.mutualFriends.sort(compareByDisplayName);
|
||||||
|
break;
|
||||||
|
case 'lastActive':
|
||||||
|
D.mutualFriends.sort(compareByLastActiveRef);
|
||||||
|
break;
|
||||||
|
case 'friendOrder':
|
||||||
|
D.mutualFriends.sort(compareByFriendOrder);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function exitEditModeCurrentUserGroups() {
|
async function exitEditModeCurrentUserGroups() {
|
||||||
userDialogGroupEditMode.value = false;
|
userDialogGroupEditMode.value = false;
|
||||||
userDialogGroupEditGroups.value = [];
|
userDialogGroupEditGroups.value = [];
|
||||||
|
|||||||
@@ -116,6 +116,20 @@
|
|||||||
{{ userDialog.ref.$friendNumber ? userDialog.ref.$friendNumber : '' }}
|
{{ userDialog.ref.$friendNumber ? userDialog.ref.$friendNumber : '' }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
|
<el-tooltip
|
||||||
|
v-if="userDialog.mutualFriendCount"
|
||||||
|
placement="top"
|
||||||
|
:content="t('dialog.user.tags.mutual_friends')">
|
||||||
|
<el-tag
|
||||||
|
type="info"
|
||||||
|
effect="plain"
|
||||||
|
size="small"
|
||||||
|
class="x-tag-mutual-friend"
|
||||||
|
style="margin-right: 5px; margin-top: 5px">
|
||||||
|
<i class="ri-group-line"></i>
|
||||||
|
{{ userDialog.mutualFriendCount }}
|
||||||
|
</el-tag>
|
||||||
|
</el-tooltip>
|
||||||
<el-tag
|
<el-tag
|
||||||
v-if="userDialog.ref.$isTroll"
|
v-if="userDialog.ref.$isTroll"
|
||||||
type="info"
|
type="info"
|
||||||
|
|||||||
@@ -791,7 +791,8 @@
|
|||||||
"vrchat_team": "VRChat Team",
|
"vrchat_team": "VRChat Team",
|
||||||
"18_plus_verified": "18+ Verified",
|
"18_plus_verified": "18+ Verified",
|
||||||
"age_verified": "Age Verified",
|
"age_verified": "Age Verified",
|
||||||
"trust_level": "Trust Level"
|
"trust_level": "Trust Level",
|
||||||
|
"mutual_friends": "Mutual Friends"
|
||||||
},
|
},
|
||||||
"badges": {
|
"badges": {
|
||||||
"assigned": "Assigned",
|
"assigned": "Assigned",
|
||||||
@@ -937,6 +938,14 @@
|
|||||||
},
|
},
|
||||||
"json": {
|
"json": {
|
||||||
"header": "JSON"
|
"header": "JSON"
|
||||||
|
},
|
||||||
|
"mutual_friends": {
|
||||||
|
"header": "Mutual Friends",
|
||||||
|
"sorting": {
|
||||||
|
"alphabetical": "Alphabetical",
|
||||||
|
"last_active": "Last Active",
|
||||||
|
"friend_order": "Friend Order"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"world": {
|
"world": {
|
||||||
|
|||||||
@@ -47,8 +47,24 @@ const userDialogGroupSortingOptions = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const userDialogMutualFriendSortingOptions = {
|
||||||
|
alphabetical: {
|
||||||
|
name: 'dialog.user.mutual_friends.sorting.alphabetical',
|
||||||
|
value: 'alphabetical'
|
||||||
|
},
|
||||||
|
lastActive: {
|
||||||
|
name: 'dialog.user.mutual_friends.sorting.last_active',
|
||||||
|
value: 'lastActive'
|
||||||
|
},
|
||||||
|
friendOrder: {
|
||||||
|
name: 'dialog.user.mutual_friends.sorting.friend_order',
|
||||||
|
value: 'friendOrder'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
userDialogWorldSortingOptions,
|
userDialogWorldSortingOptions,
|
||||||
userDialogWorldOrderOptions,
|
userDialogWorldOrderOptions,
|
||||||
userDialogGroupSortingOptions
|
userDialogGroupSortingOptions,
|
||||||
|
userDialogMutualFriendSortingOptions
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ function compareByLastActive(a, b) {
|
|||||||
b.ref?.$online_for &&
|
b.ref?.$online_for &&
|
||||||
a.ref.$online_for === b.ref.$online_for
|
a.ref.$online_for === b.ref.$online_for
|
||||||
) {
|
) {
|
||||||
compareByActivityField(a, b, 'last_login');
|
return compareByActivityField(a, b, 'last_login');
|
||||||
}
|
}
|
||||||
return compareByActivityField(a, b, '$online_for');
|
return compareByActivityField(a, b, '$online_for');
|
||||||
}
|
}
|
||||||
@@ -178,6 +178,16 @@ function compareByLastActive(a, b) {
|
|||||||
return compareByActivityField(a, b, 'last_activity');
|
return compareByActivityField(a, b, 'last_activity');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function compareByLastActiveRef(a, b) {
|
||||||
|
if (a.state === 'online' && b.state === 'online') {
|
||||||
|
if (a.$online_for && b.$online_for && a.$online_for === b.$online_for) {
|
||||||
|
return a.last_login < b.last_login ? 1 : -1;
|
||||||
|
}
|
||||||
|
return a.$online_for < b.$online_for ? 1 : -1;
|
||||||
|
}
|
||||||
|
return a.last_activity < b.last_activity ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* last seen
|
* last seen
|
||||||
* @param {object} a
|
* @param {object} a
|
||||||
@@ -259,6 +269,19 @@ function compareByLocation(a, b) {
|
|||||||
return a.ref.location.localeCompare(b.ref.location);
|
return a.ref.location.localeCompare(b.ref.location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* $friendNumber friend order
|
||||||
|
* @param {object} a
|
||||||
|
* @param {object} b
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function compareByFriendOrder(a, b) {
|
||||||
|
if (typeof a === 'undefined' || typeof b === 'undefined') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return b.$friendNumber - a.$friendNumber;
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
compareByName,
|
compareByName,
|
||||||
compareByCreatedAt,
|
compareByCreatedAt,
|
||||||
@@ -270,7 +293,9 @@ export {
|
|||||||
compareByPrivate,
|
compareByPrivate,
|
||||||
compareByStatus,
|
compareByStatus,
|
||||||
compareByLastActive,
|
compareByLastActive,
|
||||||
|
compareByLastActiveRef,
|
||||||
compareByLastSeen,
|
compareByLastSeen,
|
||||||
compareByLocationAt,
|
compareByLocationAt,
|
||||||
compareByLocation
|
compareByLocation,
|
||||||
|
compareByFriendOrder
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -222,6 +222,10 @@ export const useUserStore = defineStore('User', () => {
|
|||||||
name: 'dialog.user.groups.sorting.alphabetical',
|
name: 'dialog.user.groups.sorting.alphabetical',
|
||||||
value: 'alphabetical'
|
value: 'alphabetical'
|
||||||
},
|
},
|
||||||
|
mutualFriendSorting: {
|
||||||
|
name: 'dialog.user.mutual_friends.sorting.alphabetical',
|
||||||
|
value: 'alphabetical'
|
||||||
|
},
|
||||||
avatarSorting: 'update',
|
avatarSorting: 'update',
|
||||||
avatarReleaseStatus: 'all',
|
avatarReleaseStatus: 'all',
|
||||||
treeData: [],
|
treeData: [],
|
||||||
@@ -257,7 +261,11 @@ export const useUserStore = defineStore('User', () => {
|
|||||||
previousDisplayNames: [],
|
previousDisplayNames: [],
|
||||||
dateFriended: '',
|
dateFriended: '',
|
||||||
unFriended: false,
|
unFriended: false,
|
||||||
dateFriendedInfo: []
|
dateFriendedInfo: [],
|
||||||
|
mutualFriendCount: 0,
|
||||||
|
mutualGroupCount: 0,
|
||||||
|
mutualFriends: [],
|
||||||
|
isMutualFriendsLoading: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const currentTravelers = reactive(new Map());
|
const currentTravelers = reactive(new Map());
|
||||||
@@ -813,6 +821,8 @@ export const useUserStore = defineStore('User', () => {
|
|||||||
D.dateFriended = '';
|
D.dateFriended = '';
|
||||||
D.unFriended = false;
|
D.unFriended = false;
|
||||||
D.dateFriendedInfo = [];
|
D.dateFriendedInfo = [];
|
||||||
|
D.mutualFriendCount = 0;
|
||||||
|
D.mutualGroupCount = 0;
|
||||||
if (userId === currentUser.value.id) {
|
if (userId === currentUser.value.id) {
|
||||||
getWorldName(currentUser.value.homeLocation).then((worldName) => {
|
getWorldName(currentUser.value.homeLocation).then((worldName) => {
|
||||||
D.$homeLocationName = worldName;
|
D.$homeLocationName = worldName;
|
||||||
@@ -951,6 +961,18 @@ export const useUserStore = defineStore('User', () => {
|
|||||||
D.isShowAvatar = true;
|
D.isShowAvatar = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (D.isFriend) {
|
||||||
|
userRequest
|
||||||
|
.getMutualCounts({ userId })
|
||||||
|
.then((args) => {
|
||||||
|
if (args.params.userId === D.id) {
|
||||||
|
D.mutualFriendCount =
|
||||||
|
args.json.friends;
|
||||||
|
D.mutualGroupCount =
|
||||||
|
args.json.groups;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
D.previousDisplayNames =
|
D.previousDisplayNames =
|
||||||
currentUser.value.pastDisplayNames;
|
currentUser.value.pastDisplayNames;
|
||||||
|
|||||||
Reference in New Issue
Block a user