Mutual friends on friendsList

This commit is contained in:
Natsumi
2025-11-20 16:30:26 +11:00
parent b0621c04b8
commit 506b709a21
7 changed files with 82 additions and 4 deletions

View File

@@ -2214,8 +2214,12 @@
}
setUserDialogMutualFriendSorting(userDialog.value.mutualFriendSorting);
},
done: () => {
done: (success) => {
userDialog.value.isMutualFriendsLoading = false;
if (success) {
const mutualIds = userDialog.value.mutualFriends.map((u) => u.id);
database.updateMutualsForFriend(userId, mutualIds);
}
}
});
}

View File

@@ -273,7 +273,8 @@
"filter_placeholder": "Filter",
"refresh_tooltip": "Refresh",
"clear_tooltip": "Clear Results",
"cancel_tooltip": "Cancel"
"cancel_tooltip": "Cancel",
"load_mutual_friends": "Load Mutual Friends"
},
"charts": {
"header": "Charts",
@@ -2201,7 +2202,8 @@
"lastActivity": "Last Activity",
"lastLogin": "Last Login",
"dateJoined": "Date Joined",
"unfriend": "Unfriend"
"unfriend": "Unfriend",
"mutualFriends": "Mutual Friends"
},
"profile": {
"invite_messages": {

View File

@@ -86,6 +86,51 @@ const mutualGraph = {
await sqliteService.executeNonQuery('ROLLBACK');
throw err;
}
},
async updateMutualsForFriend(friendId, mutualIds) {
if (!dbVars.userPrefix || !friendId) {
return;
}
const friendTable = `${dbVars.userPrefix}_mutual_graph_friends`;
const linkTable = `${dbVars.userPrefix}_mutual_graph_links`;
const safeFriendId = friendId.replace(/'/g, "''");
await sqliteService.executeNonQuery(
`INSERT OR REPLACE INTO ${friendTable} (friend_id) VALUES ('${safeFriendId}')`
);
await sqliteService.executeNonQuery(
`DELETE FROM ${linkTable} WHERE friend_id='${safeFriendId}'`
);
let edgeValues = '';
for (const mutual of mutualIds) {
if (!mutual) {
continue;
}
const safeMutualId = String(mutual).replace(/'/g, "''");
edgeValues += `('${safeFriendId}', '${safeMutualId}'),`;
}
if (edgeValues) {
edgeValues = edgeValues.slice(0, -1);
await sqliteService.executeNonQuery(
`INSERT OR REPLACE INTO ${linkTable} (friend_id, mutual_id) VALUES ${edgeValues}`
);
}
},
async getMutualCountForAllUsers() {
const mutualCountMap = new Map();
if (!dbVars.userPrefix) {
return mutualCountMap;
}
const linkTable = `${dbVars.userPrefix}_mutual_graph_links`;
await sqliteService.execute((dbRow) => {
const mutualId = dbRow[0];
const count = dbRow[1];
if (mutualId) {
mutualCountMap.set(mutualId, count);
}
}, `SELECT mutual_id, COUNT(*) FROM ${linkTable} GROUP BY mutual_id`);
return mutualCountMap;
}
};

View File

@@ -915,6 +915,16 @@ export const useFriendStore = defineStore('Friend', () => {
}
}
async function getAllUserMutualCount() {
const mutualCountMap = await database.getMutualCountForAllUsers();
for (const [userId, mutualCount] of mutualCountMap.entries()) {
const ref = friends.get(userId);
if (ref?.ref) {
ref.ref.$mutualCount = mutualCount;
}
}
}
/**
*
* @param {string} id
@@ -1625,6 +1635,7 @@ export const useFriendStore = defineStore('Friend', () => {
refreshFriendsList,
updateOnlineFriendCounter,
getAllUserStats,
getAllUserMutualCount,
initFriendLog,
migrateFriendLog,
getFriendLog,

View File

@@ -523,6 +523,7 @@ export const useUserStore = defineStore('User', () => {
$joinCount: 0,
$timeSpent: 0,
$lastSeen: '',
$mutualCount: 0,
$nickName: '',
$previousLocation: '',
$customTag: '',

View File

@@ -55,6 +55,7 @@ export interface VrcxUser extends GetUserResponse {
$joinCount: number;
$timeSpent: number;
$lastSeen: string;
$mutualCount: number;
$nickName: string;
$previousLocation: string;
$customTag: string;

View File

@@ -4,6 +4,9 @@
<div style="display: flex; align-items: center; justify-content: space-between">
<span class="header">{{ t('view.friend_list.header') }}</span>
<div style="font-size: 13px; display: flex; align-items: center">
<el-button size="small" @click="openChartsTab" style="margin-right: 10px">
{{ t('view.friend_list.load_mutual_friends') }}
</el-button>
<div v-if="friendsListBulkUnfriendMode" style="display: inline-block; margin-right: 10px">
<el-button size="small" @click="showBulkUnfriendSelectionConfirm">
{{ t('view.friend_list.bulk_unfriend_selection') }}
@@ -214,6 +217,11 @@
<span>{{ formatDateFilter(row.$lastSeen, 'long') }}</span>
</template>
</el-table-column>
<el-table-column :label="t('table.friendList.mutualFriends')" width="120" prop="$mutualCount" sortable>
<template #default="{ row }">
<span v-if="row.$mutualCount">{{ row.$mutualCount }}</span>
<span v-else></span> </template
></el-table-column>
<el-table-column
:label="t('table.friendList.lastActivity')"
width="170"
@@ -284,13 +292,14 @@
} from '../../stores';
import { friendRequest, userRequest } from '../../api';
import removeConfusables, { removeWhitespace } from '../../service/confusables';
import { router } from '../../plugin/router';
const { t } = useI18n();
const emit = defineEmits(['lookup-user']);
const { friends } = storeToRefs(useFriendStore());
const { getAllUserStats, confirmDeleteFriend, handleFriendDelete } = useFriendStore();
const { getAllUserStats, getAllUserMutualCount, confirmDeleteFriend, handleFriendDelete } = useFriendStore();
const { randomUserColours } = storeToRefs(useAppearanceSettingsStore());
const { showUserDialog } = useUserStore();
const { stringComparer, friendsListSearch } = storeToRefs(useSearchStore());
@@ -363,6 +372,7 @@
results.push(ctx.ref);
}
getAllUserStats();
getAllUserMutualCount();
nextTick(() => {
friendsListTable.data = results;
friendsListLoading.value = false;
@@ -449,4 +459,8 @@
const bs = b.$languages.map((i) => i.value).sort();
return JSON.stringify(as).localeCompare(JSON.stringify(bs));
}
function openChartsTab() {
router.push({ name: 'charts' });
}
</script>