mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-19 06:43:51 +02:00
refactor global search
This commit is contained in:
@@ -24,6 +24,7 @@ import { useAvatarProviderStore } from '../stores/avatarProvider';
|
||||
import { useAvatarStore } from '../stores/avatar';
|
||||
import { useFavoriteStore } from '../stores/favorite';
|
||||
import { useModalStore } from '../stores/modal';
|
||||
import { syncAvatarSearchIndex, removeAvatarSearchIndex } from './searchIndexCoordinator';
|
||||
import { useUiStore } from '../stores/ui';
|
||||
import { useUserStore } from '../stores/user';
|
||||
import { useVRCXUpdaterStore } from '../stores/vrcxUpdater';
|
||||
@@ -67,6 +68,7 @@ export function applyAvatar(json) {
|
||||
database.addAvatarToCache(avatarRef);
|
||||
}
|
||||
patchAvatarFromEvent(ref);
|
||||
syncAvatarSearchIndex(ref);
|
||||
return ref;
|
||||
}
|
||||
|
||||
@@ -632,3 +634,12 @@ export async function preloadOwnAvatars() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
*/
|
||||
export function removeAvatarFromCache(id) {
|
||||
const avatarStore = useAvatarStore();
|
||||
avatarStore.cachedAvatars.delete(id);
|
||||
removeAvatarSearchIndex(id);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useFriendStore } from '../stores/friend';
|
||||
import { useGeneralSettingsStore } from '../stores/settings/general';
|
||||
import { useUserStore } from '../stores/user';
|
||||
import { useWorldStore } from '../stores/world';
|
||||
import { rebuildFavoriteSearchIndex } from './searchIndexCoordinator';
|
||||
import { applyWorld } from './worldCoordinator';
|
||||
import { runUpdateFriendFlow } from './friendPresenceCoordinator';
|
||||
import { avatarRequest, favoriteRequest, queryRequest } from '../api';
|
||||
@@ -134,6 +135,7 @@ export function handleFavoriteAtDelete(ref) {
|
||||
avatarStore.setAvatarDialogIsFavorite(false);
|
||||
}
|
||||
favoriteStore.countFavoriteGroups();
|
||||
rebuildFavoriteSearchIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -374,6 +376,7 @@ export async function applyFavorite(type, objectId) {
|
||||
}
|
||||
}
|
||||
}
|
||||
rebuildFavoriteSearchIndex();
|
||||
}
|
||||
|
||||
// --- Refresh flows ---
|
||||
@@ -1284,5 +1287,8 @@ export function onLoginStateChanged(isLoggedIn) {
|
||||
friendStore.localFavoriteFriends.clear();
|
||||
if (isLoggedIn) {
|
||||
initFavorites();
|
||||
} else {
|
||||
rebuildFavoriteSearchIndex();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { database } from '../services/database';
|
||||
import { useFeedStore } from '../stores/feed';
|
||||
import { useFriendStore } from '../stores/friend';
|
||||
import { useNotificationStore } from '../stores/notification';
|
||||
import { syncFriendSearchIndex } from './searchIndexCoordinator';
|
||||
import { useSharedFeedStore } from '../stores/sharedFeed';
|
||||
import { useUserStore } from '../stores/user';
|
||||
import { userRequest } from '../api';
|
||||
@@ -117,6 +118,7 @@ export async function runUpdateFriendDelayedCheckFlow(
|
||||
}
|
||||
if (ref?.displayName) {
|
||||
ctx.name = ref.displayName;
|
||||
syncFriendSearchIndex(ctx);
|
||||
}
|
||||
ctx.isVIP = isVIP;
|
||||
}
|
||||
@@ -205,6 +207,7 @@ export async function runUpdateFriendFlow(
|
||||
}
|
||||
if (typeof ref !== 'undefined' && ctx.name !== ref.displayName) {
|
||||
ctx.name = ref.displayName;
|
||||
syncFriendSearchIndex(ctx);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -216,6 +219,7 @@ export async function runUpdateFriendFlow(
|
||||
ctx.isVIP = isVIP;
|
||||
if (typeof ref !== 'undefined') {
|
||||
ctx.name = ref.displayName;
|
||||
syncFriendSearchIndex(ctx);
|
||||
}
|
||||
if (!watchState.isFriendsLoaded) {
|
||||
await runUpdateFriendDelayedCheckFlow(
|
||||
@@ -250,6 +254,7 @@ export async function runUpdateFriendFlow(
|
||||
ctx.isVIP = isVIP;
|
||||
if (typeof ref !== 'undefined') {
|
||||
ctx.name = ref.displayName;
|
||||
syncFriendSearchIndex(ctx);
|
||||
await runUpdateFriendDelayedCheckFlow(
|
||||
ctx,
|
||||
ctx.ref.state,
|
||||
@@ -304,3 +309,4 @@ export async function runPendingOfflineTickFlow({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,10 @@ import { useNotificationStore } from '../stores/notification';
|
||||
import { useSharedFeedStore } from '../stores/sharedFeed';
|
||||
import { useUiStore } from '../stores/ui';
|
||||
import { useUserStore } from '../stores/user';
|
||||
import {
|
||||
removeFriendSearchIndex,
|
||||
syncFriendSearchIndex
|
||||
} from './searchIndexCoordinator';
|
||||
import { watchState } from '../services/watchState';
|
||||
|
||||
import configRepository from '../services/config';
|
||||
@@ -44,6 +48,7 @@ export function handleFriendDelete(args) {
|
||||
D.isFriend = false;
|
||||
runDeleteFriendshipFlow(args.params.userId);
|
||||
friendStore.deleteFriend(args.params.userId);
|
||||
removeFriendSearchIndex(args.params.userId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,6 +58,10 @@ export function handleFriendAdd(args) {
|
||||
const friendStore = useFriendStore();
|
||||
addFriendship(args.params.userId);
|
||||
friendStore.addFriend(args.params.userId);
|
||||
const ctx = friendStore.friends.get(args.params.userId);
|
||||
if (ctx) {
|
||||
syncFriendSearchIndex(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,6 +145,10 @@ export function addFriendship(id) {
|
||||
state.friendNumber
|
||||
);
|
||||
friendStore.addFriend(id, ref.state);
|
||||
const friendCtx = friendStore.friends.get(id);
|
||||
if (friendCtx) {
|
||||
syncFriendSearchIndex(friendCtx);
|
||||
}
|
||||
const friendLogHistory = {
|
||||
created_at: new Date().toJSON(),
|
||||
type: 'Friend',
|
||||
@@ -316,7 +329,16 @@ export function updateUserCurrentStatus(ref) {
|
||||
const appearanceSettingsStore = useAppearanceSettingsStore();
|
||||
|
||||
if (watchState.isFriendsLoaded) {
|
||||
friendStore.refreshFriendsStatus(ref);
|
||||
const { added, removed } = friendStore.refreshFriendsStatus(ref);
|
||||
for (const id of added) {
|
||||
const ctx = friendStore.friends.get(id);
|
||||
if (ctx) {
|
||||
syncFriendSearchIndex(ctx);
|
||||
}
|
||||
}
|
||||
for (const id of removed) {
|
||||
removeFriendSearchIndex(id);
|
||||
}
|
||||
}
|
||||
friendStore.updateOnlineFriendCounter();
|
||||
|
||||
@@ -383,6 +405,7 @@ export function runDeleteFriendshipFlow(
|
||||
uiStore.notifyMenu('friend-log');
|
||||
}
|
||||
friendStore.deleteFriend(id);
|
||||
removeFriendSearchIndex(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { toast } from 'vue-sonner';
|
||||
|
||||
import { AppDebug } from '../services/appConfig';
|
||||
import { migrateMemos } from './memoCoordinator';
|
||||
import { syncFriendSearchIndex } from './searchIndexCoordinator';
|
||||
import { reconnectWebSocket } from '../services/websocket';
|
||||
import { useAuthStore } from '../stores/auth';
|
||||
import { useFriendStore } from '../stores/friend';
|
||||
@@ -56,6 +57,11 @@ export async function runInitFriendsListFlow(t) {
|
||||
}
|
||||
}
|
||||
|
||||
// bulk sync friends to search index after initial load
|
||||
for (const ctx of friendStore.friends.values()) {
|
||||
syncFriendSearchIndex(ctx);
|
||||
}
|
||||
|
||||
friendStore.tryApplyFriendOrder(); // once again
|
||||
friendStore.getAllUserStats(); // joinCount, lastSeen, timeSpent
|
||||
|
||||
|
||||
@@ -18,13 +18,13 @@ import { useNotificationStore } from '../stores/notification';
|
||||
import { useUiStore } from '../stores/ui';
|
||||
import { useUserStore } from '../stores/user';
|
||||
import { useGroupStore } from '../stores/group';
|
||||
import { syncGroupSearchIndex, removeGroupSearchIndex, clearGroupSearchIndex } from './searchIndexCoordinator';
|
||||
import { watchState } from '../services/watchState';
|
||||
|
||||
import configRepository from '../services/config';
|
||||
|
||||
import * as workerTimers from 'worker-timers';
|
||||
|
||||
// ─── Internal helpers (not exported) ─────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* @param ref
|
||||
@@ -48,7 +48,6 @@ function applyGroupLanguage(ref) {
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Core entity application ─────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -132,6 +131,9 @@ export function applyGroup(json) {
|
||||
D.ref = ref;
|
||||
}
|
||||
patchGroupFromEvent(ref);
|
||||
if (groupStore.currentUserGroups.has(ref.id)) {
|
||||
syncGroupSearchIndex(ref);
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
@@ -178,7 +180,6 @@ export function applyGroupMember(json) {
|
||||
return json;
|
||||
}
|
||||
|
||||
// ─── Group change notifications ──────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -274,7 +275,6 @@ function groupRoleChange(ref, oldRoles, newRoles, oldRoleIds, newRoleIds) {
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Dialog flows ────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -461,7 +461,6 @@ export function getGroupDialogGroup(groupId, existingRef) {
|
||||
});
|
||||
}
|
||||
|
||||
// ─── Group lifecycle flows ───────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -508,6 +507,7 @@ export function onGroupJoined(groupId) {
|
||||
name: '',
|
||||
iconUrl: ''
|
||||
});
|
||||
syncGroupSearchIndex({ id: groupId, name: '', ownerId: '', iconUrl: '' });
|
||||
groupRequest.getGroup({ groupId, includeRoles: true }).then((args) => {
|
||||
applyGroup(args.json);
|
||||
saveCurrentUserGroups();
|
||||
@@ -539,6 +539,7 @@ export async function onGroupLeft(groupId) {
|
||||
}
|
||||
if (groupStore.currentUserGroups.has(groupId)) {
|
||||
groupStore.currentUserGroups.delete(groupId);
|
||||
removeGroupSearchIndex(groupId);
|
||||
groupChange(ref, 'Left group');
|
||||
|
||||
// delay to wait for json to be assigned to ref
|
||||
@@ -546,7 +547,6 @@ export async function onGroupLeft(groupId) {
|
||||
}
|
||||
}
|
||||
|
||||
// ─── User group management ───────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -589,6 +589,7 @@ export async function loadCurrentUserGroups(userId, groups) {
|
||||
);
|
||||
groupStore.cachedGroups.clear();
|
||||
groupStore.currentUserGroups.clear();
|
||||
clearGroupSearchIndex();
|
||||
for (const group of savedGroups) {
|
||||
const json = {
|
||||
id: group.id,
|
||||
@@ -602,6 +603,7 @@ export async function loadCurrentUserGroups(userId, groups) {
|
||||
};
|
||||
const ref = applyGroup(json);
|
||||
groupStore.currentUserGroups.set(group.id, ref);
|
||||
syncGroupSearchIndex(ref);
|
||||
}
|
||||
|
||||
if (groups) {
|
||||
@@ -620,6 +622,7 @@ export async function loadCurrentUserGroups(userId, groups) {
|
||||
});
|
||||
const ref = applyGroup(args.json);
|
||||
groupStore.currentUserGroups.set(groupId, ref);
|
||||
syncGroupSearchIndex(ref);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
@@ -643,10 +646,12 @@ export async function getCurrentUserGroups() {
|
||||
});
|
||||
handleGroupList(args);
|
||||
groupStore.currentUserGroups.clear();
|
||||
clearGroupSearchIndex();
|
||||
for (const group of args.json) {
|
||||
const ref = applyGroup(group);
|
||||
if (!groupStore.currentUserGroups.has(group.id)) {
|
||||
groupStore.currentUserGroups.set(group.id, ref);
|
||||
syncGroupSearchIndex(ref);
|
||||
}
|
||||
}
|
||||
const args1 = await groupRequest.getGroupPermissions({
|
||||
@@ -704,7 +709,6 @@ export async function updateInGameGroupOrder() {
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Group actions ───────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
106
src/coordinators/searchIndexCoordinator.js
Normal file
106
src/coordinators/searchIndexCoordinator.js
Normal file
@@ -0,0 +1,106 @@
|
||||
import { watch } from 'vue';
|
||||
|
||||
import { useSearchIndexStore } from '../stores/searchIndex';
|
||||
import { watchState } from '../services/watchState';
|
||||
|
||||
|
||||
/**
|
||||
* @param {object} ctx
|
||||
*/
|
||||
export function syncFriendSearchIndex(ctx) {
|
||||
useSearchIndexStore().syncFriend(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
*/
|
||||
export function removeFriendSearchIndex(id) {
|
||||
useSearchIndexStore().removeFriend(id);
|
||||
}
|
||||
|
||||
export function clearFriendSearchIndex() {
|
||||
useSearchIndexStore().clearFriends();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {object} ref
|
||||
*/
|
||||
export function syncAvatarSearchIndex(ref) {
|
||||
useSearchIndexStore().upsertAvatar(ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
*/
|
||||
export function removeAvatarSearchIndex(id) {
|
||||
useSearchIndexStore().removeAvatar(id);
|
||||
}
|
||||
|
||||
export function clearAvatarSearchIndex() {
|
||||
useSearchIndexStore().clearAvatars();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} ref
|
||||
*/
|
||||
export function syncWorldSearchIndex(ref) {
|
||||
useSearchIndexStore().upsertWorld(ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
*/
|
||||
export function removeWorldSearchIndex(id) {
|
||||
useSearchIndexStore().removeWorld(id);
|
||||
}
|
||||
|
||||
export function clearWorldSearchIndex() {
|
||||
useSearchIndexStore().clearWorlds();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} ref
|
||||
*/
|
||||
export function syncGroupSearchIndex(ref) {
|
||||
useSearchIndexStore().upsertGroup(ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
*/
|
||||
export function removeGroupSearchIndex(id) {
|
||||
useSearchIndexStore().removeGroup(id);
|
||||
}
|
||||
|
||||
export function clearGroupSearchIndex() {
|
||||
useSearchIndexStore().clearGroups();
|
||||
}
|
||||
|
||||
|
||||
export function rebuildFavoriteSearchIndex() {
|
||||
useSearchIndexStore().rebuildFavoritesFromStore();
|
||||
}
|
||||
|
||||
export function clearFavoriteSearchIndex() {
|
||||
useSearchIndexStore().clearFavorites();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a single login-state watcher that clears the entire search index
|
||||
* on every login/logout transition, so individual stores don't need to.
|
||||
*/
|
||||
export function resetSearchIndexOnLogin() {
|
||||
const searchIndexStore = useSearchIndexStore();
|
||||
watch(
|
||||
() => watchState.isLoggedIn,
|
||||
() => {
|
||||
searchIndexStore.clearFriends();
|
||||
searchIndexStore.clearAvatars();
|
||||
searchIndexStore.clearWorlds();
|
||||
searchIndexStore.clearGroups();
|
||||
searchIndexStore.clearFavorites();
|
||||
},
|
||||
{ flush: 'sync' }
|
||||
);
|
||||
}
|
||||
@@ -53,6 +53,8 @@ import { useModerationStore } from '../stores/moderation';
|
||||
import { useNotificationStore } from '../stores/notification';
|
||||
import { usePhotonStore } from '../stores/photon';
|
||||
import { useSearchStore } from '../stores/search';
|
||||
import { syncFriendSearchIndex } from './searchIndexCoordinator';
|
||||
import { removeAvatarFromCache } from './avatarCoordinator';
|
||||
import { useSharedFeedStore } from '../stores/sharedFeed';
|
||||
import { useUiStore } from '../stores/ui';
|
||||
import { useUserStore } from '../stores/user';
|
||||
@@ -181,6 +183,7 @@ export function applyUser(json) {
|
||||
if (friendCtx) {
|
||||
friendCtx.ref = ref;
|
||||
friendCtx.name = ref.displayName;
|
||||
syncFriendSearchIndex(friendCtx);
|
||||
}
|
||||
if (ref.id === currentUser.id) {
|
||||
if (ref.status) {
|
||||
@@ -306,6 +309,7 @@ export function showUserDialog(userId) {
|
||||
} else {
|
||||
ref.$nickName = '';
|
||||
}
|
||||
syncFriendSearchIndex(ref);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -583,7 +587,7 @@ export async function refreshUserDialogAvatars(fileId) {
|
||||
};
|
||||
for (const ref of avatarStore.cachedAvatars.values()) {
|
||||
if (ref.authorId === D.id) {
|
||||
avatarStore.cachedAvatars.delete(ref.id);
|
||||
removeAvatarFromCache(ref.id);
|
||||
}
|
||||
}
|
||||
const map = new Map();
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { removeAvatarFromCache } from './avatarCoordinator';
|
||||
import { removeWorldFromCache } from './worldCoordinator';
|
||||
import { useAvatarStore } from '../stores/avatar';
|
||||
import { useFavoriteStore } from '../stores/favorite';
|
||||
import { useFriendStore } from '../stores/friend';
|
||||
@@ -9,6 +11,7 @@ import { useUserStore } from '../stores/user';
|
||||
import { useWorldStore } from '../stores/world';
|
||||
import { failedGetRequests } from '../services/request';
|
||||
|
||||
|
||||
/**
|
||||
* Clears caches across multiple stores while preserving data that is
|
||||
* still needed (friends, current user, favorites, active instances).
|
||||
@@ -41,7 +44,7 @@ export function clearVRCXCache() {
|
||||
ref.authorId !== userStore.currentUser.id &&
|
||||
!favoriteStore.localWorldFavoritesList.includes(id)
|
||||
) {
|
||||
worldStore.cachedWorlds.delete(id);
|
||||
removeWorldFromCache(id);
|
||||
}
|
||||
});
|
||||
avatarStore.cachedAvatars.forEach((ref, id) => {
|
||||
@@ -51,7 +54,7 @@ export function clearVRCXCache() {
|
||||
!favoriteStore.localAvatarFavoritesList.includes(id) &&
|
||||
!avatarStore.avatarHistory.includes(id)
|
||||
) {
|
||||
avatarStore.cachedAvatars.delete(id);
|
||||
removeAvatarFromCache(id);
|
||||
}
|
||||
});
|
||||
groupStore.cachedGroups.forEach((ref, id) => {
|
||||
|
||||
@@ -20,6 +20,7 @@ import { applyFavorite } from './favoriteCoordinator';
|
||||
import { useFavoriteStore } from '../stores/favorite';
|
||||
import { useInstanceStore } from '../stores/instance';
|
||||
import { useLocationStore } from '../stores/location';
|
||||
import { syncWorldSearchIndex, removeWorldSearchIndex } from './searchIndexCoordinator';
|
||||
import { useUiStore } from '../stores/ui';
|
||||
import { useUserStore } from '../stores/user';
|
||||
import { useWorldStore } from '../stores/world';
|
||||
@@ -221,6 +222,7 @@ export function applyWorld(json) {
|
||||
database.addWorldToCache(ref);
|
||||
}
|
||||
patchWorldFromEvent(ref);
|
||||
syncWorldSearchIndex(ref);
|
||||
return ref;
|
||||
}
|
||||
|
||||
@@ -247,3 +249,12 @@ export async function preloadOwnWorlds() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
*/
|
||||
export function removeWorldFromCache(id) {
|
||||
const worldStore = useWorldStore();
|
||||
worldStore.cachedWorlds.delete(id);
|
||||
removeWorldSearchIndex(id);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user