mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-05-06 14:46:04 +02:00
refactor friends sort
This commit is contained in:
@@ -138,6 +138,29 @@
|
|||||||
params: {}
|
params: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const friendSections = computed(() => [
|
||||||
|
{
|
||||||
|
key: 'friendsInInstance',
|
||||||
|
label: t('dialog.invite.friends_in_instance'),
|
||||||
|
friends: props.inviteDialog?.friendsInInstance ?? []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'vip',
|
||||||
|
label: t('side_panel.favorite'),
|
||||||
|
friends: vipFriends.value
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'online',
|
||||||
|
label: t('side_panel.online'),
|
||||||
|
friends: onlineFriends.value
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'active',
|
||||||
|
label: t('side_panel.active'),
|
||||||
|
friends: activeFriends.value
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
const userPickerGroups = computed(() => {
|
const userPickerGroups = computed(() => {
|
||||||
const groups = [];
|
const groups = [];
|
||||||
|
|
||||||
@@ -156,7 +179,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const addFriendGroup = (key, label, friends) => {
|
const addFriendGroup = ({ key, label, friends }) => {
|
||||||
if (!friends?.length) return;
|
if (!friends?.length) return;
|
||||||
groups.push({
|
groups.push({
|
||||||
key,
|
key,
|
||||||
@@ -174,10 +197,7 @@
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
addFriendGroup('friendsInInstance', t('dialog.invite.friends_in_instance'), props.inviteDialog?.friendsInInstance);
|
friendSections.value.forEach(addFriendGroup);
|
||||||
addFriendGroup('vip', t('side_panel.favorite'), vipFriends.value);
|
|
||||||
addFriendGroup('online', t('side_panel.online'), onlineFriends.value);
|
|
||||||
addFriendGroup('active', t('side_panel.active'), activeFriends.value);
|
|
||||||
|
|
||||||
return groups;
|
return groups;
|
||||||
});
|
});
|
||||||
@@ -194,10 +214,11 @@
|
|||||||
|
|
||||||
const friendById = computed(() => {
|
const friendById = computed(() => {
|
||||||
const map = new Map();
|
const map = new Map();
|
||||||
for (const friend of props.inviteDialog?.friendsInInstance ?? []) map.set(friend.id, friend);
|
for (const section of friendSections.value) {
|
||||||
for (const friend of vipFriends.value) map.set(friend.id, friend);
|
for (const friend of section.friends ?? []) {
|
||||||
for (const friend of onlineFriends.value) map.set(friend.id, friend);
|
map.set(friend.id, friend);
|
||||||
for (const friend of activeFriends.value) map.set(friend.id, friend);
|
}
|
||||||
|
}
|
||||||
return map;
|
return map;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -142,12 +142,36 @@
|
|||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const friendSections = computed(() => [
|
||||||
|
{
|
||||||
|
key: 'vip',
|
||||||
|
label: t('side_panel.favorite'),
|
||||||
|
friends: vipFriends.value
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'online',
|
||||||
|
label: t('side_panel.online'),
|
||||||
|
friends: onlineFriends.value
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'active',
|
||||||
|
label: t('side_panel.active'),
|
||||||
|
friends: activeFriends.value
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'offline',
|
||||||
|
label: t('side_panel.offline'),
|
||||||
|
friends: offlineFriends.value
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
const friendById = computed(() => {
|
const friendById = computed(() => {
|
||||||
const map = new Map();
|
const map = new Map();
|
||||||
for (const friend of vipFriends.value) map.set(friend.id, friend);
|
for (const section of friendSections.value) {
|
||||||
for (const friend of onlineFriends.value) map.set(friend.id, friend);
|
for (const friend of section.friends ?? []) {
|
||||||
for (const friend of activeFriends.value) map.set(friend.id, friend);
|
map.set(friend.id, friend);
|
||||||
for (const friend of offlineFriends.value) map.set(friend.id, friend);
|
}
|
||||||
|
}
|
||||||
return map;
|
return map;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -190,7 +214,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const addFriendGroup = (key, label, friends) => {
|
const addFriendGroup = ({ key, label, friends }) => {
|
||||||
if (!friends?.length) return;
|
if (!friends?.length) return;
|
||||||
groups.push({
|
groups.push({
|
||||||
key,
|
key,
|
||||||
@@ -208,10 +232,7 @@
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
addFriendGroup('vip', t('side_panel.favorite'), vipFriends.value);
|
friendSections.value.forEach(addFriendGroup);
|
||||||
addFriendGroup('online', t('side_panel.online'), onlineFriends.value);
|
|
||||||
addFriendGroup('active', t('side_panel.active'), activeFriends.value);
|
|
||||||
addFriendGroup('offline', t('side_panel.offline'), offlineFriends.value);
|
|
||||||
|
|
||||||
return groups;
|
return groups;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -675,12 +675,36 @@
|
|||||||
return names.slice(0, 3).join(', ') + (names.length > 3 ? ` +${names.length - 3}` : '');
|
return names.slice(0, 3).join(', ') + (names.length > 3 ? ` +${names.length - 3}` : '');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const friendSections = computed(() => [
|
||||||
|
{
|
||||||
|
key: 'vip',
|
||||||
|
label: t('side_panel.favorite'),
|
||||||
|
friends: vipFriends.value
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'online',
|
||||||
|
label: t('side_panel.online'),
|
||||||
|
friends: onlineFriends.value
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'active',
|
||||||
|
label: t('side_panel.active'),
|
||||||
|
friends: activeFriends.value
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'offline',
|
||||||
|
label: t('side_panel.offline'),
|
||||||
|
friends: offlineFriends.value
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
const friendById = computed(() => {
|
const friendById = computed(() => {
|
||||||
const map = new Map();
|
const map = new Map();
|
||||||
for (const friend of vipFriends.value) map.set(friend.id, friend);
|
for (const section of friendSections.value) {
|
||||||
for (const friend of onlineFriends.value) map.set(friend.id, friend);
|
for (const friend of section.friends ?? []) {
|
||||||
for (const friend of activeFriends.value) map.set(friend.id, friend);
|
map.set(friend.id, friend);
|
||||||
for (const friend of offlineFriends.value) map.set(friend.id, friend);
|
}
|
||||||
|
}
|
||||||
return map;
|
return map;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -714,7 +738,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const addFriendGroup = (key, label, friends) => {
|
const addFriendGroup = ({ key, label, friends }) => {
|
||||||
if (!friends?.length) return;
|
if (!friends?.length) return;
|
||||||
groups.push({
|
groups.push({
|
||||||
key,
|
key,
|
||||||
@@ -732,10 +756,7 @@
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
addFriendGroup('vip', t('side_panel.favorite'), vipFriends.value);
|
friendSections.value.forEach(addFriendGroup);
|
||||||
addFriendGroup('online', t('side_panel.online'), onlineFriends.value);
|
|
||||||
addFriendGroup('active', t('side_panel.active'), activeFriends.value);
|
|
||||||
addFriendGroup('offline', t('side_panel.offline'), offlineFriends.value);
|
|
||||||
|
|
||||||
return groups;
|
return groups;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ export async function runUpdateFriendDelayedCheckFlow(
|
|||||||
syncFriendSearchIndex(ctx);
|
syncFriendSearchIndex(ctx);
|
||||||
}
|
}
|
||||||
ctx.isVIP = isVIP;
|
ctx.isVIP = isVIP;
|
||||||
|
friendStore.reindexSortedFriend(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -209,6 +210,7 @@ export async function runUpdateFriendFlow(
|
|||||||
ctx.name = ref.displayName;
|
ctx.name = ref.displayName;
|
||||||
syncFriendSearchIndex(ctx);
|
syncFriendSearchIndex(ctx);
|
||||||
}
|
}
|
||||||
|
friendStore.reindexSortedFriend(ctx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
@@ -248,6 +250,7 @@ export async function runUpdateFriendFlow(
|
|||||||
previousLocationAt: $location_at
|
previousLocationAt: $location_at
|
||||||
});
|
});
|
||||||
ctx.pendingOffline = true;
|
ctx.pendingOffline = true;
|
||||||
|
friendStore.reindexSortedFriend(ctx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ctx.ref = ref;
|
ctx.ref = ref;
|
||||||
@@ -262,6 +265,8 @@ export async function runUpdateFriendFlow(
|
|||||||
$location_at,
|
$location_at,
|
||||||
{ now, nowIso }
|
{ now, nowIso }
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
friendStore.reindexSortedFriend(ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,4 +314,3 @@ export async function runPendingOfflineTickFlow({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -184,6 +184,7 @@ export function applyUser(json) {
|
|||||||
friendCtx.ref = ref;
|
friendCtx.ref = ref;
|
||||||
friendCtx.name = ref.displayName;
|
friendCtx.name = ref.displayName;
|
||||||
syncFriendSearchIndex(friendCtx);
|
syncFriendSearchIndex(friendCtx);
|
||||||
|
friendStore.reindexSortedFriend(friendCtx);
|
||||||
}
|
}
|
||||||
if (ref.id === currentUser.id) {
|
if (ref.id === currentUser.id) {
|
||||||
if (ref.status) {
|
if (ref.status) {
|
||||||
|
|||||||
+256
-46
@@ -1,4 +1,4 @@
|
|||||||
import { computed, reactive, ref, watch } from 'vue';
|
import { computed, reactive, ref, shallowRef, watch } from 'vue';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
@@ -56,6 +56,63 @@ export const useFriendStore = defineStore('Friend', () => {
|
|||||||
const friends = reactive(new Map());
|
const friends = reactive(new Map());
|
||||||
|
|
||||||
const localFavoriteFriends = reactive(new Set());
|
const localFavoriteFriends = reactive(new Set());
|
||||||
|
const sortedFriends = shallowRef([]);
|
||||||
|
let sortedFriendsBatchDepth = 0;
|
||||||
|
let pendingSortedFriendsRebuild = false;
|
||||||
|
|
||||||
|
const derivedDebugCounters = reactive({
|
||||||
|
allFavoriteFriendIds: 0,
|
||||||
|
allFavoriteOnlineFriends: 0,
|
||||||
|
vipFriends: 0,
|
||||||
|
onlineFriends: 0,
|
||||||
|
activeFriends: 0,
|
||||||
|
offlineFriends: 0,
|
||||||
|
friendsInSameInstance: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks recomputes for the hottest friend-derived lists.
|
||||||
|
* Guarded by AppDebug.debugFriendState so normal behavior stays unchanged.
|
||||||
|
* @param {keyof typeof derivedDebugCounters} name
|
||||||
|
* @param {number} resultSize
|
||||||
|
*/
|
||||||
|
function trackDerivedDebug(name, resultSize) {
|
||||||
|
derivedDebugCounters[name] += 1;
|
||||||
|
if (!AppDebug.debugFriendState) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log('[friendStore derived]', {
|
||||||
|
name,
|
||||||
|
count: derivedDebugCounters[name],
|
||||||
|
resultSize,
|
||||||
|
friendCount: friends.size,
|
||||||
|
sortMethods: appearanceSettingsStore.sidebarSortMethods
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function resetDerivedDebugCounters() {
|
||||||
|
for (const key in derivedDebugCounters) {
|
||||||
|
derivedDebugCounters[key] = 0;
|
||||||
|
}
|
||||||
|
if (AppDebug.debugFriendState) {
|
||||||
|
console.log('[friendStore derived] counters reset');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {Record<string, number>}
|
||||||
|
*/
|
||||||
|
function getDerivedDebugCounters() {
|
||||||
|
const snapshot = { ...derivedDebugCounters };
|
||||||
|
if (AppDebug.debugFriendState) {
|
||||||
|
console.log('[friendStore derived] counters snapshot', snapshot);
|
||||||
|
}
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
const allFavoriteFriendIds = computed(() => {
|
const allFavoriteFriendIds = computed(() => {
|
||||||
const favoriteStore = useFavoriteStore();
|
const favoriteStore = useFavoriteStore();
|
||||||
@@ -73,20 +130,131 @@ export const useFriendStore = defineStore('Friend', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
trackDerivedDebug('allFavoriteFriendIds', set.size);
|
||||||
return set;
|
return set;
|
||||||
});
|
});
|
||||||
|
|
||||||
const allFavoriteOnlineFriends = computed(() => {
|
/**
|
||||||
return Array.from(friends.values())
|
*
|
||||||
.filter(
|
* @returns {(a: object, b: object) => number}
|
||||||
(f) =>
|
*/
|
||||||
f.state === 'online' && allFavoriteFriendIds.value.has(f.id)
|
function getSortedFriendsComparator() {
|
||||||
)
|
return getFriendsSortFunction(appearanceSettingsStore.sidebarSortMethods);
|
||||||
.sort(
|
}
|
||||||
getFriendsSortFunction(
|
|
||||||
appearanceSettingsStore.sidebarSortMethods
|
/**
|
||||||
)
|
*
|
||||||
|
* @param {string} id
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
function findSortedFriendIndex(id) {
|
||||||
|
return sortedFriends.value.findIndex((friend) => friend.id === id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function rebuildSortedFriends() {
|
||||||
|
sortedFriends.value = Array.from(friends.values()).sort(
|
||||||
|
getSortedFriendsComparator()
|
||||||
);
|
);
|
||||||
|
pendingSortedFriendsRebuild = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function beginSortedFriendsBatch() {
|
||||||
|
sortedFriendsBatchDepth += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function endSortedFriendsBatch() {
|
||||||
|
if (sortedFriendsBatchDepth === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sortedFriendsBatchDepth -= 1;
|
||||||
|
if (sortedFriendsBatchDepth === 0 && pendingSortedFriendsRebuild) {
|
||||||
|
rebuildSortedFriends();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @template T
|
||||||
|
* @param {() => T} fn
|
||||||
|
* @returns {T}
|
||||||
|
*/
|
||||||
|
function runInSortedFriendsBatch(fn) {
|
||||||
|
beginSortedFriendsBatch();
|
||||||
|
try {
|
||||||
|
return fn();
|
||||||
|
} finally {
|
||||||
|
endSortedFriendsBatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} id
|
||||||
|
*/
|
||||||
|
function removeSortedFriend(id) {
|
||||||
|
if (sortedFriendsBatchDepth > 0) {
|
||||||
|
pendingSortedFriendsRebuild = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const index = findSortedFriendIndex(id);
|
||||||
|
if (index === -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const next = sortedFriends.value.slice();
|
||||||
|
next.splice(index, 1);
|
||||||
|
sortedFriends.value = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {object | string} input
|
||||||
|
*/
|
||||||
|
function reindexSortedFriend(input) {
|
||||||
|
const ctx =
|
||||||
|
typeof input === 'string' ? friends.get(input) : input;
|
||||||
|
if (!ctx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (sortedFriendsBatchDepth > 0) {
|
||||||
|
pendingSortedFriendsRebuild = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const compare = getSortedFriendsComparator();
|
||||||
|
const next = sortedFriends.value.slice();
|
||||||
|
const existingIndex = next.findIndex((friend) => friend.id === ctx.id);
|
||||||
|
if (existingIndex !== -1) {
|
||||||
|
next.splice(existingIndex, 1);
|
||||||
|
}
|
||||||
|
let low = 0;
|
||||||
|
let high = next.length;
|
||||||
|
while (low < high) {
|
||||||
|
const mid = Math.floor((low + high) / 2);
|
||||||
|
if (compare(next[mid], ctx) <= 0) {
|
||||||
|
low = mid + 1;
|
||||||
|
} else {
|
||||||
|
high = mid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next.splice(low, 0, ctx);
|
||||||
|
sortedFriends.value = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
const allFavoriteOnlineFriends = computed(() => {
|
||||||
|
const favoriteIds = allFavoriteFriendIds.value;
|
||||||
|
const result = sortedFriends.value.filter(
|
||||||
|
(f) => f.state === 'online' && favoriteIds.has(f.id)
|
||||||
|
);
|
||||||
|
trackDerivedDebug('allFavoriteOnlineFriends', result.length);
|
||||||
|
return result;
|
||||||
});
|
});
|
||||||
|
|
||||||
const isRefreshFriendsLoading = ref(false);
|
const isRefreshFriendsLoading = ref(false);
|
||||||
@@ -131,50 +299,42 @@ export const useFriendStore = defineStore('Friend', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const vipFriends = computed(() => {
|
const vipFriends = computed(() => {
|
||||||
return Array.from(friends.values())
|
const result = sortedFriends.value.filter(
|
||||||
.filter((f) => f.state === 'online' && f.isVIP)
|
(f) => f.state === 'online' && f.isVIP
|
||||||
.sort(
|
|
||||||
getFriendsSortFunction(
|
|
||||||
appearanceSettingsStore.sidebarSortMethods
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
trackDerivedDebug('vipFriends', result.length);
|
||||||
|
return result;
|
||||||
});
|
});
|
||||||
|
|
||||||
const onlineFriends = computed(() => {
|
const onlineFriends = computed(() => {
|
||||||
return Array.from(friends.values())
|
const result = sortedFriends.value.filter(
|
||||||
.filter((f) => f.state === 'online' && !f.isVIP)
|
(f) => f.state === 'online' && !f.isVIP
|
||||||
.sort(
|
|
||||||
getFriendsSortFunction(
|
|
||||||
appearanceSettingsStore.sidebarSortMethods
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
trackDerivedDebug('onlineFriends', result.length);
|
||||||
|
return result;
|
||||||
});
|
});
|
||||||
|
|
||||||
const activeFriends = computed(() => {
|
const activeFriends = computed(() => {
|
||||||
return Array.from(friends.values())
|
const result = sortedFriends.value.filter((f) => f.state === 'active');
|
||||||
.filter((f) => f.state === 'active')
|
trackDerivedDebug('activeFriends', result.length);
|
||||||
.sort(
|
return result;
|
||||||
getFriendsSortFunction(
|
|
||||||
appearanceSettingsStore.sidebarSortMethods
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const offlineFriends = computed(() => {
|
const offlineFriends = computed(() => {
|
||||||
return Array.from(friends.values())
|
const result = sortedFriends.value.filter(
|
||||||
.filter((f) => f.state === 'offline' || !f.state)
|
(f) => f.state === 'offline' || !f.state
|
||||||
.sort(
|
|
||||||
getFriendsSortFunction(
|
|
||||||
appearanceSettingsStore.sidebarSortMethods
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
trackDerivedDebug('offlineFriends', result.length);
|
||||||
|
return result;
|
||||||
});
|
});
|
||||||
|
|
||||||
const friendsInSameInstance = computed(() => {
|
const friendsInSameInstance = computed(() => {
|
||||||
const friendsList = {};
|
const friendsList = {};
|
||||||
|
|
||||||
const allFriends = [...vipFriends.value, ...onlineFriends.value];
|
sortedFriends.value.forEach((friend) => {
|
||||||
allFriends.forEach((friend) => {
|
if (friend.state !== 'online') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!friend.ref?.$location) {
|
if (!friend.ref?.$location) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -200,23 +360,22 @@ export const useFriendStore = defineStore('Friend', () => {
|
|||||||
const sortedFriendsList = [];
|
const sortedFriendsList = [];
|
||||||
for (const group of Object.values(friendsList)) {
|
for (const group of Object.values(friendsList)) {
|
||||||
if (group.length > 1) {
|
if (group.length > 1) {
|
||||||
sortedFriendsList.push(
|
// Group order already matches the globally sorted online list.
|
||||||
group.sort(
|
sortedFriendsList.push(group);
|
||||||
getFriendsSortFunction(
|
|
||||||
appearanceSettingsStore.sidebarSortMethods
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sortedFriendsList.sort((a, b) => b.length - a.length);
|
const result = sortedFriendsList.sort((a, b) => b.length - a.length);
|
||||||
|
trackDerivedDebug('friendsInSameInstance', result.length);
|
||||||
|
return result;
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => watchState.isLoggedIn,
|
() => watchState.isLoggedIn,
|
||||||
(isLoggedIn) => {
|
(isLoggedIn) => {
|
||||||
friends.clear();
|
friends.clear();
|
||||||
|
sortedFriends.value = [];
|
||||||
|
pendingSortedFriendsRebuild = false;
|
||||||
state.friendNumber = 0;
|
state.friendNumber = 0;
|
||||||
friendLog.clear();
|
friendLog.clear();
|
||||||
friendLogTable.value.data = [];
|
friendLogTable.value.data = [];
|
||||||
@@ -236,6 +395,14 @@ export const useFriendStore = defineStore('Friend', () => {
|
|||||||
{ flush: 'sync' }
|
{ flush: 'sync' }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => appearanceSettingsStore.sidebarSortMethods,
|
||||||
|
() => {
|
||||||
|
rebuildSortedFriends();
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => watchState.isFriendsLoaded,
|
() => watchState.isFriendsLoaded,
|
||||||
(isFriendsLoaded) => {
|
(isFriendsLoaded) => {
|
||||||
@@ -293,13 +460,16 @@ export const useFriendStore = defineStore('Friend', () => {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function updateSidebarFavorites() {
|
function updateSidebarFavorites() {
|
||||||
|
runInSortedFriendsBatch(() => {
|
||||||
for (const ctx of friends.values()) {
|
for (const ctx of friends.values()) {
|
||||||
const isVIP = localFavoriteFriends.has(ctx.id);
|
const isVIP = localFavoriteFriends.has(ctx.id);
|
||||||
if (ctx.isVIP === isVIP) {
|
if (ctx.isVIP === isVIP) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ctx.isVIP = isVIP;
|
ctx.isVIP = isVIP;
|
||||||
|
reindexSortedFriend(ctx);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -320,6 +490,7 @@ export const useFriendStore = defineStore('Friend', () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
friends.delete(id);
|
friends.delete(id);
|
||||||
|
removeSortedFriend(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -327,6 +498,7 @@ export const useFriendStore = defineStore('Friend', () => {
|
|||||||
* @param ref
|
* @param ref
|
||||||
*/
|
*/
|
||||||
function refreshFriendsStatus(ref) {
|
function refreshFriendsStatus(ref) {
|
||||||
|
return runInSortedFriendsBatch(() => {
|
||||||
let id;
|
let id;
|
||||||
const map = new Map();
|
const map = new Map();
|
||||||
for (id of ref.friends) {
|
for (id of ref.friends) {
|
||||||
@@ -359,6 +531,7 @@ export const useFriendStore = defineStore('Friend', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { added, removed };
|
return { added, removed };
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -408,6 +581,29 @@ export const useFriendStore = defineStore('Friend', () => {
|
|||||||
ctx.name = ref.name;
|
ctx.name = ref.name;
|
||||||
}
|
}
|
||||||
friends.set(id, ctx);
|
friends.set(id, ctx);
|
||||||
|
watchState.isLoggedIn = true
|
||||||
|
// Startup fill flow:
|
||||||
|
//
|
||||||
|
// login
|
||||||
|
// -> runInitFriendsListFlow()
|
||||||
|
// -> initFriendLog() / getFriendLog()
|
||||||
|
// -> refreshFriendsStatus(currentUser)
|
||||||
|
// -> addFriend(...)
|
||||||
|
// -> friends.set(id, ctx)
|
||||||
|
// -> reindexSortedFriend(ctx)
|
||||||
|
//
|
||||||
|
// During batch init, reindexSortedFriend() only marks the list dirty.
|
||||||
|
// When the batch ends:
|
||||||
|
// -> rebuildSortedFriends()
|
||||||
|
// -> sortedFriends = sorted(Array.from(friends.values()))
|
||||||
|
//
|
||||||
|
// After full friend payloads arrive:
|
||||||
|
// -> applyUser(friend)
|
||||||
|
// -> update ctx.ref / ctx.name
|
||||||
|
// -> reindexSortedFriend(ctx)
|
||||||
|
// -> batch end
|
||||||
|
// -> rebuildSortedFriends()
|
||||||
|
reindexSortedFriend(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -672,14 +868,17 @@ export const useFriendStore = defineStore('Friend', () => {
|
|||||||
friend.displayName = item.displayName;
|
friend.displayName = item.displayName;
|
||||||
friendListMap.set(item.userId, friend);
|
friendListMap.set(item.userId, friend);
|
||||||
}
|
}
|
||||||
|
runInSortedFriendsBatch(() => {
|
||||||
for (item of friendListMap.values()) {
|
for (item of friendListMap.values()) {
|
||||||
ref = friends.get(item.userId);
|
ref = friends.get(item.userId);
|
||||||
if (ref?.ref) {
|
if (ref?.ref) {
|
||||||
ref.ref.$joinCount = item.joinCount;
|
ref.ref.$joinCount = item.joinCount;
|
||||||
ref.ref.$lastSeen = item.lastSeen;
|
ref.ref.$lastSeen = item.lastSeen;
|
||||||
ref.ref.$timeSpent = item.timeSpent;
|
ref.ref.$timeSpent = item.timeSpent;
|
||||||
|
reindexSortedFriend(ref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -687,12 +886,15 @@ export const useFriendStore = defineStore('Friend', () => {
|
|||||||
*/
|
*/
|
||||||
async function getAllUserMutualCount() {
|
async function getAllUserMutualCount() {
|
||||||
const mutualCountMap = await database.getMutualCountForAllUsers();
|
const mutualCountMap = await database.getMutualCountForAllUsers();
|
||||||
|
runInSortedFriendsBatch(() => {
|
||||||
for (const [userId, mutualCount] of mutualCountMap.entries()) {
|
for (const [userId, mutualCount] of mutualCountMap.entries()) {
|
||||||
const ref = friends.get(userId);
|
const ref = friends.get(userId);
|
||||||
if (ref?.ref) {
|
if (ref?.ref) {
|
||||||
ref.ref.$mutualCount = mutualCount;
|
ref.ref.$mutualCount = mutualCount;
|
||||||
|
reindexSortedFriend(ref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -714,6 +916,7 @@ export const useFriendStore = defineStore('Friend', () => {
|
|||||||
refreshFriendsStatus(currentUser);
|
refreshFriendsStatus(currentUser);
|
||||||
const sqlValues = [];
|
const sqlValues = [];
|
||||||
const friends = await refreshFriends();
|
const friends = await refreshFriends();
|
||||||
|
runInSortedFriendsBatch(() => {
|
||||||
for (const friend of friends) {
|
for (const friend of friends) {
|
||||||
const ref = applyUser(friend);
|
const ref = applyUser(friend);
|
||||||
const row = {
|
const row = {
|
||||||
@@ -725,6 +928,7 @@ export const useFriendStore = defineStore('Friend', () => {
|
|||||||
friendLog.set(friend.id, row);
|
friendLog.set(friend.id, row);
|
||||||
sqlValues.unshift(row);
|
sqlValues.unshift(row);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
database.setFriendLogCurrentArray(sqlValues);
|
database.setFriendLogCurrentArray(sqlValues);
|
||||||
await configRepository.setBool(`friendLogInit_${currentUser.id}`, true);
|
await configRepository.setBool(`friendLogInit_${currentUser.id}`, true);
|
||||||
watchState.isFriendsLoaded = true;
|
watchState.isFriendsLoaded = true;
|
||||||
@@ -808,6 +1012,7 @@ export const useFriendStore = defineStore('Friend', () => {
|
|||||||
const friendRef = friends.get(userId);
|
const friendRef = friends.get(userId);
|
||||||
if (friendRef?.ref) {
|
if (friendRef?.ref) {
|
||||||
friendRef.ref.$friendNumber = friendNumber;
|
friendRef.ref.$friendNumber = friendNumber;
|
||||||
|
reindexSortedFriend(friendRef);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -830,11 +1035,13 @@ export const useFriendStore = defineStore('Friend', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const friendOrder = userStore.currentUser.friends;
|
const friendOrder = userStore.currentUser.friends;
|
||||||
|
runInSortedFriendsBatch(() => {
|
||||||
for (let i = 0; i < friendOrder.length; i++) {
|
for (let i = 0; i < friendOrder.length; i++) {
|
||||||
const userId = friendOrder[i];
|
const userId = friendOrder[i];
|
||||||
state.friendNumber++;
|
state.friendNumber++;
|
||||||
setFriendNumber(state.friendNumber, userId);
|
setFriendNumber(state.friendNumber, userId);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
if (state.friendNumber === 0) {
|
if (state.friendNumber === 0) {
|
||||||
state.friendNumber = friends.size;
|
state.friendNumber = friends.size;
|
||||||
}
|
}
|
||||||
@@ -1179,6 +1386,9 @@ export const useFriendStore = defineStore('Friend', () => {
|
|||||||
getFriendLog,
|
getFriendLog,
|
||||||
tryApplyFriendOrder,
|
tryApplyFriendOrder,
|
||||||
resetFriendLog,
|
resetFriendLog,
|
||||||
|
reindexSortedFriend,
|
||||||
|
resetDerivedDebugCounters,
|
||||||
|
getDerivedDebugCounters,
|
||||||
initFriendLogHistoryTable,
|
initFriendLogHistoryTable,
|
||||||
setIsRefreshFriendsLoading
|
setIsRefreshFriendsLoading
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -371,10 +371,16 @@
|
|||||||
return ids;
|
return ids;
|
||||||
});
|
});
|
||||||
|
|
||||||
const vipFriendsByGroupStatus = computed(() => {
|
const visibleFavoriteOnlineFriends = computed(() => {
|
||||||
const selectedGroups = sidebarFavoriteGroups.value;
|
const selectedGroups = sidebarFavoriteGroups.value;
|
||||||
if (selectedGroups.length === 0) return allFavoriteOnlineFriends.value;
|
if (selectedGroups.length === 0) {
|
||||||
return allFavoriteOnlineFriends.value.filter((f) => displayedVipIds.value.has(f.id));
|
return allFavoriteOnlineFriends.value;
|
||||||
|
}
|
||||||
|
return allFavoriteOnlineFriends.value.filter((friend) => displayedVipIds.value.has(friend.id));
|
||||||
|
});
|
||||||
|
|
||||||
|
const vipFriendsByGroupStatus = computed(() => {
|
||||||
|
return visibleFavoriteOnlineFriends.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
const onlineFriendsByGroupStatus = computed(() => {
|
const onlineFriendsByGroupStatus = computed(() => {
|
||||||
@@ -382,10 +388,11 @@
|
|||||||
if (selectedGroups.length === 0) {
|
if (selectedGroups.length === 0) {
|
||||||
return onlineFriends.value.filter((f) => !allFavoriteFriendIds.value.has(f.id));
|
return onlineFriends.value.filter((f) => !allFavoriteFriendIds.value.has(f.id));
|
||||||
}
|
}
|
||||||
const nonFavOnline = onlineFriends.value.filter((f) => !displayedVipIds.value.has(f.id));
|
const selectedIds = displayedVipIds.value;
|
||||||
|
const nonFavOnline = onlineFriends.value.filter((f) => !selectedIds.has(f.id));
|
||||||
const existingIds = new Set(nonFavOnline.map((f) => f.id));
|
const existingIds = new Set(nonFavOnline.map((f) => f.id));
|
||||||
const unselectedGroupFriends = allFavoriteOnlineFriends.value.filter(
|
const unselectedGroupFriends = allFavoriteOnlineFriends.value.filter(
|
||||||
(f) => !displayedVipIds.value.has(f.id) && !existingIds.has(f.id)
|
(f) => !selectedIds.has(f.id) && !existingIds.has(f.id)
|
||||||
);
|
);
|
||||||
return [...nonFavOnline, ...unselectedGroupFriends].sort(getFriendsSortFunction(sidebarSortMethods.value));
|
return [...nonFavOnline, ...unselectedGroupFriends].sort(getFriendsSortFunction(sidebarSortMethods.value));
|
||||||
});
|
});
|
||||||
@@ -415,7 +422,7 @@
|
|||||||
|
|
||||||
const result = [];
|
const result = [];
|
||||||
for (const { key, groupName, memberIds } of groups) {
|
for (const { key, groupName, memberIds } of groups) {
|
||||||
const filteredFriends = allFavoriteOnlineFriends.value.filter((friend) => memberIds.has(friend.id));
|
const filteredFriends = visibleFavoriteOnlineFriends.value.filter((friend) => memberIds.has(friend.id));
|
||||||
if (filteredFriends.length > 0) {
|
if (filteredFriends.length > 0) {
|
||||||
result.push({ key, groupName, friends: filteredFriends });
|
result.push({ key, groupName, friends: filteredFriends });
|
||||||
}
|
}
|
||||||
@@ -432,6 +439,15 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const searchableEntries = computed(() =>
|
||||||
|
uniqueEntries([
|
||||||
|
...toEntries(allFavoriteOnlineFriends.value),
|
||||||
|
...toEntries(onlineFriends.value),
|
||||||
|
...toEntries(activeFriends.value),
|
||||||
|
...toEntries(offlineFriends.value)
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param groupKey
|
* @param groupKey
|
||||||
@@ -446,14 +462,7 @@
|
|||||||
|
|
||||||
const filteredFriends = computed(() => {
|
const filteredFriends = computed(() => {
|
||||||
if (normalizedSearchTerm.value) {
|
if (normalizedSearchTerm.value) {
|
||||||
const pools = [
|
return searchableEntries.value.filter(({ friend }) => {
|
||||||
...toEntries(allFavoriteOnlineFriends.value),
|
|
||||||
...toEntries(onlineFriends.value),
|
|
||||||
...toEntries(activeFriends.value),
|
|
||||||
...toEntries(offlineFriends.value)
|
|
||||||
];
|
|
||||||
|
|
||||||
return uniqueEntries(pools).filter(({ friend }) => {
|
|
||||||
const haystack =
|
const haystack =
|
||||||
`${friend.displayName ?? friend.name ?? ''} ${friend.signature ?? ''} ${friend.worldName ?? ''}`.toLowerCase();
|
`${friend.displayName ?? friend.name ?? ''} ${friend.signature ?? ''} ${friend.worldName ?? ''}`.toLowerCase();
|
||||||
return haystack.includes(normalizedSearchTerm.value);
|
return haystack.includes(normalizedSearchTerm.value);
|
||||||
|
|||||||
@@ -275,6 +275,36 @@
|
|||||||
|
|
||||||
const shouldHideSameInstance = computed(() => isSidebarGroupByInstance.value && isHideFriendsInSameInstance.value);
|
const shouldHideSameInstance = computed(() => isSidebarGroupByInstance.value && isHideFriendsInSameInstance.value);
|
||||||
|
|
||||||
|
const selectedFavoriteGroupIds = computed(() => {
|
||||||
|
const selectedGroups = sidebarFavoriteGroups.value;
|
||||||
|
const hasFilter = selectedGroups.length > 0;
|
||||||
|
if (!hasFilter) {
|
||||||
|
return allFavoriteFriendIds.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ids = new Set();
|
||||||
|
const remoteFriendsByGroup = groupedByGroupKeyFavoriteFriends.value;
|
||||||
|
for (const key of selectedGroups) {
|
||||||
|
if (key.startsWith('local:')) {
|
||||||
|
const groupName = key.slice(6);
|
||||||
|
const userIds = localFriendFavorites.value?.[groupName];
|
||||||
|
if (userIds) {
|
||||||
|
for (const id of userIds) ids.add(id);
|
||||||
|
}
|
||||||
|
} else if (remoteFriendsByGroup[key]) {
|
||||||
|
for (const friend of remoteFriendsByGroup[key]) ids.add(friend.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ids;
|
||||||
|
});
|
||||||
|
|
||||||
|
const visibleFavoriteOnlineFriends = computed(() => {
|
||||||
|
const filtered = allFavoriteOnlineFriends.value.filter((friend) =>
|
||||||
|
selectedFavoriteGroupIds.value.has(friend.id)
|
||||||
|
);
|
||||||
|
return excludeSameInstance(filtered);
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param list
|
* @param list
|
||||||
@@ -293,23 +323,11 @@
|
|||||||
return excludeSameInstance(onlineFriends.value.filter((f) => !allFavoriteFriendIds.value.has(f.id)));
|
return excludeSameInstance(onlineFriends.value.filter((f) => !allFavoriteFriendIds.value.has(f.id)));
|
||||||
}
|
}
|
||||||
// When group filter is active, friends in unselected groups should appear in the online list
|
// When group filter is active, friends in unselected groups should appear in the online list
|
||||||
const displayedVipIds = new Set();
|
const selectedIds = selectedFavoriteGroupIds.value;
|
||||||
const remoteFriendsByGroup = groupedByGroupKeyFavoriteFriends.value;
|
const nonFavOnline = onlineFriends.value.filter((f) => !selectedIds.has(f.id));
|
||||||
for (const key of selectedGroups) {
|
|
||||||
if (key.startsWith('local:')) {
|
|
||||||
const groupName = key.slice(6);
|
|
||||||
const userIds = localFriendFavorites.value?.[groupName];
|
|
||||||
if (userIds) {
|
|
||||||
for (const id of userIds) displayedVipIds.add(id);
|
|
||||||
}
|
|
||||||
} else if (remoteFriendsByGroup[key]) {
|
|
||||||
for (const f of remoteFriendsByGroup[key]) displayedVipIds.add(f.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const nonFavOnline = onlineFriends.value.filter((f) => !displayedVipIds.has(f.id));
|
|
||||||
const existingIds = new Set(nonFavOnline.map((f) => f.id));
|
const existingIds = new Set(nonFavOnline.map((f) => f.id));
|
||||||
const unselectedGroupFriends = allFavoriteOnlineFriends.value.filter(
|
const unselectedGroupFriends = allFavoriteOnlineFriends.value.filter(
|
||||||
(f) => !displayedVipIds.has(f.id) && !existingIds.has(f.id)
|
(f) => !selectedIds.has(f.id) && !existingIds.has(f.id)
|
||||||
);
|
);
|
||||||
return excludeSameInstance(
|
return excludeSameInstance(
|
||||||
[...nonFavOnline, ...unselectedGroupFriends].sort(getFriendsSortFunction(sidebarSortMethods.value))
|
[...nonFavOnline, ...unselectedGroupFriends].sort(getFriendsSortFunction(sidebarSortMethods.value))
|
||||||
@@ -317,26 +335,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const vipFriendsByGroupStatus = computed(() => {
|
const vipFriendsByGroupStatus = computed(() => {
|
||||||
const selectedGroups = sidebarFavoriteGroups.value;
|
return visibleFavoriteOnlineFriends.value;
|
||||||
const hasFilter = selectedGroups.length > 0;
|
|
||||||
if (!hasFilter) {
|
|
||||||
return excludeSameInstance(allFavoriteOnlineFriends.value);
|
|
||||||
}
|
|
||||||
// Filter to only include VIP friends whose group key is in selectedGroups
|
|
||||||
const allowedIds = new Set();
|
|
||||||
const remoteFriendsByGroup = groupedByGroupKeyFavoriteFriends.value;
|
|
||||||
for (const key of selectedGroups) {
|
|
||||||
if (key.startsWith('local:')) {
|
|
||||||
const groupName = key.slice(6);
|
|
||||||
const userIds = localFriendFavorites.value?.[groupName];
|
|
||||||
if (userIds) {
|
|
||||||
for (const id of userIds) allowedIds.add(id);
|
|
||||||
}
|
|
||||||
} else if (remoteFriendsByGroup[key]) {
|
|
||||||
for (const f of remoteFriendsByGroup[key]) allowedIds.add(f.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return excludeSameInstance(allFavoriteOnlineFriends.value.filter((f) => allowedIds.has(f.id)));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// VIP friends divide by group
|
// VIP friends divide by group
|
||||||
@@ -369,9 +368,7 @@
|
|||||||
// Filter vipFriends per group, preserving vipFriends sort order
|
// Filter vipFriends per group, preserving vipFriends sort order
|
||||||
const result = [];
|
const result = [];
|
||||||
for (const { key, groupName, memberIds } of groups) {
|
for (const { key, groupName, memberIds } of groups) {
|
||||||
const filteredFriends = excludeSameInstance(
|
const filteredFriends = visibleFavoriteOnlineFriends.value.filter((friend) => memberIds.has(friend.id));
|
||||||
allFavoriteOnlineFriends.value.filter((friend) => memberIds.has(friend.id))
|
|
||||||
);
|
|
||||||
if (filteredFriends.length > 0) {
|
if (filteredFriends.length > 0) {
|
||||||
result.push(filteredFriends.map((item) => ({ groupName, key, ...item })));
|
result.push(filteredFriends.map((item) => ({ groupName, key, ...item })));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user