mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-05-06 14:46:04 +02:00
refactor findUserByDisplayName
This commit is contained in:
@@ -88,7 +88,8 @@ export async function tryLoadPlayerList() {
|
|||||||
ctx.userId =
|
ctx.userId =
|
||||||
findUserByDisplayName(
|
findUserByDisplayName(
|
||||||
userStore.cachedUsers,
|
userStore.cachedUsers,
|
||||||
ctx.displayName
|
ctx.displayName,
|
||||||
|
userStore.cachedUserIdsByDisplayName
|
||||||
)?.id ?? '';
|
)?.id ?? '';
|
||||||
}
|
}
|
||||||
const userMap = {
|
const userMap = {
|
||||||
@@ -159,8 +160,11 @@ export function addGameLogEntry(gameLog, location) {
|
|||||||
let userId = String(gameLog.userId || '');
|
let userId = String(gameLog.userId || '');
|
||||||
if (!userId && gameLog.displayName) {
|
if (!userId && gameLog.displayName) {
|
||||||
userId =
|
userId =
|
||||||
findUserByDisplayName(userStore.cachedUsers, gameLog.displayName)
|
findUserByDisplayName(
|
||||||
?.id ?? '';
|
userStore.cachedUsers,
|
||||||
|
gameLog.displayName,
|
||||||
|
userStore.cachedUserIdsByDisplayName
|
||||||
|
)?.id ?? '';
|
||||||
}
|
}
|
||||||
switch (gameLog.type) {
|
switch (gameLog.type) {
|
||||||
case 'location-destination':
|
case 'location-destination':
|
||||||
@@ -408,7 +412,8 @@ export function addGameLogEntry(gameLog, location) {
|
|||||||
if (typeof ref2 === 'undefined') {
|
if (typeof ref2 === 'undefined') {
|
||||||
const foundUser = findUserByDisplayName(
|
const foundUser = findUserByDisplayName(
|
||||||
userStore.cachedUsers,
|
userStore.cachedUsers,
|
||||||
gameLog.displayName
|
gameLog.displayName,
|
||||||
|
userStore.cachedUserIdsByDisplayName
|
||||||
);
|
);
|
||||||
if (foundUser) {
|
if (foundUser) {
|
||||||
photonStore.photonLobby.set(photonId, foundUser);
|
photonStore.photonLobby.set(photonId, foundUser);
|
||||||
|
|||||||
@@ -80,11 +80,14 @@ export function applyUser(json) {
|
|||||||
cachedUsers,
|
cachedUsers,
|
||||||
currentTravelers,
|
currentTravelers,
|
||||||
customUserTags,
|
customUserTags,
|
||||||
|
rebuildCachedUserDisplayNameIndex,
|
||||||
|
setCachedUser,
|
||||||
state,
|
state,
|
||||||
userDialog
|
userDialog
|
||||||
} = userStore;
|
} = userStore;
|
||||||
|
|
||||||
let ref = cachedUsers.get(json.id);
|
let ref = cachedUsers.get(json.id);
|
||||||
|
let previousDisplayName = '';
|
||||||
let hasPropChanged = false;
|
let hasPropChanged = false;
|
||||||
let changedProps = {};
|
let changedProps = {};
|
||||||
sanitizeUserJson(json, getRobotUrl());
|
sanitizeUserJson(json, getRobotUrl());
|
||||||
@@ -111,18 +114,24 @@ export function applyUser(json) {
|
|||||||
ref.$customTag = '';
|
ref.$customTag = '';
|
||||||
ref.$customTagColour = '';
|
ref.$customTagColour = '';
|
||||||
}
|
}
|
||||||
evictMapCache(
|
const { deletedCount } = evictMapCache(
|
||||||
cachedUsers,
|
cachedUsers,
|
||||||
friendStore.friends.size + 300,
|
friendStore.friends.size + 300,
|
||||||
(_value, key) => friendStore.friends.has(key),
|
(_value, key) => friendStore.friends.has(key),
|
||||||
{ logLabel: 'User cache cleanup' }
|
{ logLabel: 'User cache cleanup' }
|
||||||
);
|
);
|
||||||
cachedUsers.set(ref.id, ref);
|
if (deletedCount > 0) {
|
||||||
|
setCachedUser(ref, '', { skipIndex: true });
|
||||||
|
rebuildCachedUserDisplayNameIndex();
|
||||||
|
} else {
|
||||||
|
setCachedUser(ref);
|
||||||
|
}
|
||||||
runUpdateFriendFlow(ref.id);
|
runUpdateFriendFlow(ref.id);
|
||||||
} else {
|
} else {
|
||||||
if (json.state !== 'online') {
|
if (json.state !== 'online') {
|
||||||
runUpdateFriendFlow(ref.id, json.state);
|
runUpdateFriendFlow(ref.id, json.state);
|
||||||
}
|
}
|
||||||
|
previousDisplayName = ref.displayName;
|
||||||
const { hasPropChanged: _hasPropChanged, changedProps: _changedProps } =
|
const { hasPropChanged: _hasPropChanged, changedProps: _changedProps } =
|
||||||
diffObjectProps(ref, json, arraysMatch);
|
diffObjectProps(ref, json, arraysMatch);
|
||||||
for (const prop in json) {
|
for (const prop in json) {
|
||||||
@@ -130,6 +139,7 @@ export function applyUser(json) {
|
|||||||
ref[prop] = json[prop];
|
ref[prop] = json[prop];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
setCachedUser(ref, previousDisplayName);
|
||||||
hasPropChanged = _hasPropChanged;
|
hasPropChanged = _hasPropChanged;
|
||||||
changedProps = _changedProps;
|
changedProps = _changedProps;
|
||||||
}
|
}
|
||||||
@@ -637,7 +647,11 @@ export async function lookupUser(ref) {
|
|||||||
if (!ref.displayName || ref.displayName.substring(0, 3) === 'ID:') {
|
if (!ref.displayName || ref.displayName.substring(0, 3) === 'ID:') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const found = findUserByDisplayName(userStore.cachedUsers, ref.displayName);
|
const found = findUserByDisplayName(
|
||||||
|
userStore.cachedUsers,
|
||||||
|
ref.displayName,
|
||||||
|
userStore.cachedUserIdsByDisplayName
|
||||||
|
);
|
||||||
if (found) {
|
if (found) {
|
||||||
showUserDialog(found.id);
|
showUserDialog(found.id);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export function runFirstLoginFlow(ref, { now = Date.now } = {}) {
|
|||||||
if (gameStore.isGameRunning) {
|
if (gameStore.isGameRunning) {
|
||||||
ref.$previousAvatarSwapTime = now();
|
ref.$previousAvatarSwapTime = now();
|
||||||
}
|
}
|
||||||
userStore.cachedUsers.clear(); // clear before running applyUser
|
userStore.clearCachedUsers(); // clear before running applyUser
|
||||||
userStore.setCurrentUser(ref);
|
userStore.setCurrentUser(ref);
|
||||||
authStore.loginComplete();
|
authStore.loginComplete();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export function clearVRCXCache() {
|
|||||||
!locationStore.lastLocation.playerList.has(ref.id) &&
|
!locationStore.lastLocation.playerList.has(ref.id) &&
|
||||||
id !== userStore.currentUser.id
|
id !== userStore.currentUser.id
|
||||||
) {
|
) {
|
||||||
userStore.cachedUsers.delete(id);
|
userStore.deleteCachedUser(id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
worldStore.cachedWorlds.forEach((ref, id) => {
|
worldStore.cachedWorlds.forEach((ref, id) => {
|
||||||
|
|||||||
@@ -17,6 +17,19 @@ describe('findUserByDisplayName', () => {
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createDisplayNameIndex(entries) {
|
||||||
|
const index = new Map();
|
||||||
|
for (const entry of entries) {
|
||||||
|
let ids = index.get(entry.displayName);
|
||||||
|
if (!ids) {
|
||||||
|
ids = new Set();
|
||||||
|
index.set(entry.displayName, ids);
|
||||||
|
}
|
||||||
|
ids.add(entry.id);
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
test('returns the user matching displayName', () => {
|
test('returns the user matching displayName', () => {
|
||||||
const users = createCachedUsers([
|
const users = createCachedUsers([
|
||||||
{ id: 'usr_1', displayName: 'Alice' },
|
{ id: 'usr_1', displayName: 'Alice' },
|
||||||
@@ -56,4 +69,31 @@ describe('findUserByDisplayName', () => {
|
|||||||
expect(findUserByDisplayName(users, 'alice')).toBeUndefined();
|
expect(findUserByDisplayName(users, 'alice')).toBeUndefined();
|
||||||
expect(findUserByDisplayName(users, 'ALICE')).toBeUndefined();
|
expect(findUserByDisplayName(users, 'ALICE')).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('uses displayName index when provided', () => {
|
||||||
|
const entries = [
|
||||||
|
{ id: 'usr_1', displayName: 'Alice' },
|
||||||
|
{ id: 'usr_2', displayName: 'Bob' }
|
||||||
|
];
|
||||||
|
const users = createCachedUsers(entries);
|
||||||
|
const index = createDisplayNameIndex(entries);
|
||||||
|
|
||||||
|
const result = findUserByDisplayName(users, 'Bob', index);
|
||||||
|
|
||||||
|
expect(result).toEqual({ id: 'usr_2', displayName: 'Bob' });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('indexed lookup falls back to next duplicate when first user is missing', () => {
|
||||||
|
const entries = [
|
||||||
|
{ id: 'usr_1', displayName: 'Alice' },
|
||||||
|
{ id: 'usr_2', displayName: 'Alice' }
|
||||||
|
];
|
||||||
|
const users = createCachedUsers(entries);
|
||||||
|
users.delete('usr_1');
|
||||||
|
const index = createDisplayNameIndex(entries);
|
||||||
|
|
||||||
|
const result = findUserByDisplayName(users, 'Alice', index);
|
||||||
|
|
||||||
|
expect(result).toEqual({ id: 'usr_2', displayName: 'Alice' });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -275,9 +275,23 @@ function userOnlineFor(ref) {
|
|||||||
* Find a user object from cachedUsers by displayName.
|
* Find a user object from cachedUsers by displayName.
|
||||||
* @param {Map} cachedUsers
|
* @param {Map} cachedUsers
|
||||||
* @param {string} displayName
|
* @param {string} displayName
|
||||||
|
* @param {Map<string, Set<string>>} [cachedUserIdsByDisplayName]
|
||||||
* @returns {object|undefined}
|
* @returns {object|undefined}
|
||||||
*/
|
*/
|
||||||
function findUserByDisplayName(cachedUsers, displayName) {
|
function findUserByDisplayName(
|
||||||
|
cachedUsers,
|
||||||
|
displayName,
|
||||||
|
cachedUserIdsByDisplayName
|
||||||
|
) {
|
||||||
|
const indexedUserIds = cachedUserIdsByDisplayName?.get(displayName);
|
||||||
|
if (indexedUserIds) {
|
||||||
|
for (const userId of indexedUserIds) {
|
||||||
|
const ref = cachedUsers.get(userId);
|
||||||
|
if (ref?.displayName === displayName) {
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
for (const ref of cachedUsers.values()) {
|
for (const ref of cachedUsers.values()) {
|
||||||
if (ref.displayName === displayName) {
|
if (ref.displayName === displayName) {
|
||||||
return ref;
|
return ref;
|
||||||
|
|||||||
@@ -193,7 +193,8 @@ export const useGameLogStore = defineStore('GameLog', () => {
|
|||||||
ctx.userId =
|
ctx.userId =
|
||||||
findUserByDisplayName(
|
findUserByDisplayName(
|
||||||
userStore.cachedUsers,
|
userStore.cachedUsers,
|
||||||
ctx.displayName
|
ctx.displayName,
|
||||||
|
userStore.cachedUserIdsByDisplayName
|
||||||
)?.id ?? '';
|
)?.id ?? '';
|
||||||
}
|
}
|
||||||
notificationStore.queueGameLogNoty(ctx);
|
notificationStore.queueGameLogNoty(ctx);
|
||||||
|
|||||||
@@ -139,8 +139,11 @@ export function createMediaParsers({
|
|||||||
let userId = '';
|
let userId = '';
|
||||||
if (displayName) {
|
if (displayName) {
|
||||||
userId =
|
userId =
|
||||||
findUserByDisplayName(userStore.cachedUsers, displayName)?.id ??
|
findUserByDisplayName(
|
||||||
'';
|
userStore.cachedUsers,
|
||||||
|
displayName,
|
||||||
|
userStore.cachedUserIdsByDisplayName
|
||||||
|
)?.id ?? '';
|
||||||
}
|
}
|
||||||
if (videoId === 'YouTube') {
|
if (videoId === 'YouTube') {
|
||||||
const entry1 = {
|
const entry1 = {
|
||||||
@@ -207,8 +210,11 @@ export function createMediaParsers({
|
|||||||
let userId = '';
|
let userId = '';
|
||||||
if (displayName) {
|
if (displayName) {
|
||||||
userId =
|
userId =
|
||||||
findUserByDisplayName(userStore.cachedUsers, displayName)?.id ??
|
findUserByDisplayName(
|
||||||
'';
|
userStore.cachedUsers,
|
||||||
|
displayName,
|
||||||
|
userStore.cachedUserIdsByDisplayName
|
||||||
|
)?.id ?? '';
|
||||||
}
|
}
|
||||||
if (videoId === 'YouTube') {
|
if (videoId === 'YouTube') {
|
||||||
const entry1 = {
|
const entry1 = {
|
||||||
@@ -270,8 +276,11 @@ export function createMediaParsers({
|
|||||||
let userId = '';
|
let userId = '';
|
||||||
if (displayName) {
|
if (displayName) {
|
||||||
userId =
|
userId =
|
||||||
findUserByDisplayName(userStore.cachedUsers, displayName)?.id ??
|
findUserByDisplayName(
|
||||||
'';
|
userStore.cachedUsers,
|
||||||
|
displayName,
|
||||||
|
userStore.cachedUserIdsByDisplayName
|
||||||
|
)?.id ?? '';
|
||||||
}
|
}
|
||||||
if (videoId === 'YouTube') {
|
if (videoId === 'YouTube') {
|
||||||
const entry1 = {
|
const entry1 = {
|
||||||
@@ -327,8 +336,11 @@ export function createMediaParsers({
|
|||||||
let userId = '';
|
let userId = '';
|
||||||
if (displayName) {
|
if (displayName) {
|
||||||
userId =
|
userId =
|
||||||
findUserByDisplayName(userStore.cachedUsers, displayName)?.id ??
|
findUserByDisplayName(
|
||||||
'';
|
userStore.cachedUsers,
|
||||||
|
displayName,
|
||||||
|
userStore.cachedUserIdsByDisplayName
|
||||||
|
)?.id ?? '';
|
||||||
}
|
}
|
||||||
const entry1 = {
|
const entry1 = {
|
||||||
created_at: gameLog.dt,
|
created_at: gameLog.dt,
|
||||||
@@ -384,8 +396,11 @@ export function createMediaParsers({
|
|||||||
let userId = '';
|
let userId = '';
|
||||||
if (displayName) {
|
if (displayName) {
|
||||||
userId =
|
userId =
|
||||||
findUserByDisplayName(userStore.cachedUsers, displayName)?.id ??
|
findUserByDisplayName(
|
||||||
'';
|
userStore.cachedUsers,
|
||||||
|
displayName,
|
||||||
|
userStore.cachedUserIdsByDisplayName
|
||||||
|
)?.id ?? '';
|
||||||
}
|
}
|
||||||
const entry1 = {
|
const entry1 = {
|
||||||
created_at: gameLog.dt,
|
created_at: gameLog.dt,
|
||||||
|
|||||||
@@ -1018,8 +1018,11 @@ export const useNotificationStore = defineStore('Notification', () => {
|
|||||||
if (id) return id;
|
if (id) return id;
|
||||||
if (noty.displayName) {
|
if (noty.displayName) {
|
||||||
return (
|
return (
|
||||||
findUserByDisplayName(userStore.cachedUsers, noty.displayName)
|
findUserByDisplayName(
|
||||||
?.id ?? ''
|
userStore.cachedUsers,
|
||||||
|
noty.displayName,
|
||||||
|
userStore.cachedUserIdsByDisplayName
|
||||||
|
)?.id ?? ''
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
@@ -1086,7 +1089,8 @@ export const useNotificationStore = defineStore('Notification', () => {
|
|||||||
} else if (noty.displayName) {
|
} else if (noty.displayName) {
|
||||||
const ref = findUserByDisplayName(
|
const ref = findUserByDisplayName(
|
||||||
userStore.cachedUsers,
|
userStore.cachedUsers,
|
||||||
noty.displayName
|
noty.displayName,
|
||||||
|
userStore.cachedUserIdsByDisplayName
|
||||||
);
|
);
|
||||||
if (ref) {
|
if (ref) {
|
||||||
noty.isFriend = friendStore.friends.has(ref.id);
|
noty.isFriend = friendStore.friends.has(ref.id);
|
||||||
|
|||||||
@@ -271,6 +271,78 @@ export const useUserStore = defineStore('User', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const cachedUsers = shallowReactive(new Map());
|
const cachedUsers = shallowReactive(new Map());
|
||||||
|
const cachedUserIdsByDisplayName = shallowReactive(new Map());
|
||||||
|
|
||||||
|
function addCachedUserDisplayNameEntry(displayName, userId) {
|
||||||
|
if (!displayName || !userId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let userIds = cachedUserIdsByDisplayName.get(displayName);
|
||||||
|
if (!userIds) {
|
||||||
|
userIds = new Set();
|
||||||
|
cachedUserIdsByDisplayName.set(displayName, userIds);
|
||||||
|
}
|
||||||
|
userIds.add(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeCachedUserDisplayNameEntry(displayName, userId) {
|
||||||
|
if (!displayName || !userId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const userIds = cachedUserIdsByDisplayName.get(displayName);
|
||||||
|
if (!userIds) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
userIds.delete(userId);
|
||||||
|
if (userIds.size === 0) {
|
||||||
|
cachedUserIdsByDisplayName.delete(displayName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncCachedUserDisplayName(ref, previousDisplayName = '') {
|
||||||
|
if (!ref?.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (previousDisplayName && previousDisplayName !== ref.displayName) {
|
||||||
|
removeCachedUserDisplayNameEntry(previousDisplayName, ref.id);
|
||||||
|
}
|
||||||
|
addCachedUserDisplayNameEntry(ref.displayName, ref.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCachedUser(
|
||||||
|
ref,
|
||||||
|
previousDisplayName = '',
|
||||||
|
{ skipIndex = false } = {}
|
||||||
|
) {
|
||||||
|
if (!ref?.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cachedUsers.set(ref.id, ref);
|
||||||
|
if (!skipIndex) {
|
||||||
|
syncCachedUserDisplayName(ref, previousDisplayName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteCachedUser(userId) {
|
||||||
|
const ref = cachedUsers.get(userId);
|
||||||
|
if (!ref) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
removeCachedUserDisplayNameEntry(ref.displayName, userId);
|
||||||
|
return cachedUsers.delete(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearCachedUsers() {
|
||||||
|
cachedUsers.clear();
|
||||||
|
cachedUserIdsByDisplayName.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
function rebuildCachedUserDisplayNameIndex() {
|
||||||
|
cachedUserIdsByDisplayName.clear();
|
||||||
|
for (const ref of cachedUsers.values()) {
|
||||||
|
addCachedUserDisplayNameEntry(ref.displayName, ref.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const isLocalUserVrcPlusSupporter = computed(
|
const isLocalUserVrcPlusSupporter = computed(
|
||||||
() => currentUser.value.$isVRCPlus || AppDebug.debugVrcPlus
|
() => currentUser.value.$isVRCPlus || AppDebug.debugVrcPlus
|
||||||
@@ -730,10 +802,16 @@ export const useUserStore = defineStore('User', () => {
|
|||||||
showUserDialogHistory,
|
showUserDialogHistory,
|
||||||
customUserTags,
|
customUserTags,
|
||||||
cachedUsers,
|
cachedUsers,
|
||||||
|
cachedUserIdsByDisplayName,
|
||||||
isLocalUserVrcPlusSupporter,
|
isLocalUserVrcPlusSupporter,
|
||||||
applyUserLanguage,
|
applyUserLanguage,
|
||||||
applyPresenceLocation,
|
applyPresenceLocation,
|
||||||
applyUserDialogLocation,
|
applyUserDialogLocation,
|
||||||
|
setCachedUser,
|
||||||
|
syncCachedUserDisplayName,
|
||||||
|
deleteCachedUser,
|
||||||
|
clearCachedUsers,
|
||||||
|
rebuildCachedUserDisplayNameIndex,
|
||||||
sortUserDialogAvatars,
|
sortUserDialogAvatars,
|
||||||
initUserNotes,
|
initUserNotes,
|
||||||
showSendBoopDialog,
|
showSendBoopDialog,
|
||||||
|
|||||||
Reference in New Issue
Block a user