refactor store

This commit is contained in:
pa
2026-03-10 17:19:03 +09:00
parent 95c4a1d3e6
commit 1cbad7fb60
39 changed files with 1290 additions and 2366 deletions
+22 -13
View File
@@ -90,29 +90,21 @@ const mockGroupStrictsearch = vi.fn();
vi.mock('../user', () => ({
useUserStore: () => ({
showUserDialog: mockShowUserDialog,
cachedUsers: new Map(),
showUserDialogHistory: new Set(),
currentUser: ref({ id: 'usr_me', homeLocation: '' }),
lookupUser: vi.fn(),
applyUser: vi.fn()
currentUser: ref({ id: 'usr_me', homeLocation: '' })
})
}));
vi.mock('../avatar', () => ({
useAvatarStore: () => ({
showAvatarDialog: mockShowAvatarDialog
})
useAvatarStore: () => ({})
}));
vi.mock('../group', () => ({
useGroupStore: () => ({
showGroupDialog: mockShowGroupDialog
})
useGroupStore: () => ({})
}));
vi.mock('../world', () => ({
useWorldStore: () => ({
showWorldDialog: mockShowWorldDialog
})
useWorldStore: () => ({})
}));
vi.mock('../friend', () => ({
useFriendStore: () => ({
friends: new Map()
@@ -141,12 +133,29 @@ function makeApiMock() {
groupRequest: {
groupStrictsearch: (...args) => mockGroupStrictsearch(...args)
},
queryRequest: {},
miscRequest: {}
};
}
vi.mock('../../api', () => makeApiMock());
vi.mock('../../api/', () => makeApiMock());
vi.mock('../../coordinators/userCoordinator', () => ({
showUserDialog: (...args) => mockShowUserDialog(...args),
lookupUser: vi.fn(),
applyUser: vi.fn()
}));
vi.mock('../../coordinators/avatarCoordinator', () => ({
showAvatarDialog: (...args) => mockShowAvatarDialog(...args),
getAvatarName: vi.fn()
}));
vi.mock('../../coordinators/groupCoordinator', () => ({
showGroupDialog: (...args) => mockShowGroupDialog(...args)
}));
vi.mock('../../coordinators/worldCoordinator', () => ({
showWorldDialog: (...args) => mockShowWorldDialog(...args)
}));
vi.mock('vue-sonner', () => ({
toast: {
success: vi.fn(),
+67 -97
View File
@@ -39,9 +39,7 @@ export const useAuthStore = defineStore('Auth', () => {
const { t } = useI18n();
const state = reactive({
autoLoginAttempts: new Set(),
enableCustomEndpoint: false,
cachedConfig: {}
autoLoginAttempts: new Set()
});
const loginForm = ref({
@@ -106,12 +104,12 @@ export const useAuthStore = defineStore('Auth', () => {
*
*/
async function init() {
const [lastUserLoggedIn, enableCustomEndpoint] = await Promise.all([
const [lastUserLoggedIn, savedEnableCustomEndpoint] = await Promise.all([
configRepository.getString('lastUserLoggedIn', ''),
configRepository.getBool('VRCX_enableCustomEndpoint', false)
]);
loginForm.value.lastUserLoggedIn = lastUserLoggedIn;
state.enableCustomEndpoint = enableCustomEndpoint;
enableCustomEndpoint.value = savedEnableCustomEndpoint;
}
init();
@@ -191,22 +189,17 @@ export const useAuthStore = defineStore('Auth', () => {
await applyAutoLoginDelay();
// login at startup
loginForm.value.loading = true;
authRequest
.getConfig()
.catch((err) => {
loginForm.value.loading = false;
throw err;
})
.then(() => {
getCurrentUser()
.finally(() => {
loginForm.value.loading = false;
})
.catch((err) => {
updateLoopStore.setNextCurrentUserRefresh(60); // 1min
console.error(err);
});
});
try {
await authRequest.getConfig();
try {
await getCurrentUser();
} catch (err) {
updateLoopStore.setNextCurrentUserRefresh(60); // 1min
console.error(err);
}
} finally {
loginForm.value.loading = false;
}
}
}
@@ -435,7 +428,7 @@ export const useAuthStore = defineStore('Auth', () => {
async function toggleCustomEndpoint() {
await configRepository.setBool(
'VRCX_enableCustomEndpoint',
state.enableCustomEndpoint
enableCustomEndpoint.value
);
loginForm.value.endpoint = '';
loginForm.value.websocket = '';
@@ -552,87 +545,64 @@ export const useAuthStore = defineStore('Auth', () => {
AppDebug.endpointDomain = AppDebug.endpointDomainVrchat;
AppDebug.websocketDomain = AppDebug.websocketDomainVrchat;
}
authRequest
.getConfig()
.catch((err) => {
loginForm.value.loading = false;
throw err;
})
.then((args) => {
if (
loginForm.value.saveCredentials &&
advancedSettingsStore.enablePrimaryPassword
) {
modalStore
.prompt({
title: t('prompt.primary_password.header'),
description: t(
'prompt.primary_password.description'
),
inputType: 'password',
pattern: /[\s\S]{1,32}/
})
.then(async ({ ok, value }) => {
if (!ok) return;
const savedCredentials = JSON.parse(
await configRepository.getString(
'savedCredentials'
)
);
const saveCredential =
savedCredentials[
Object.keys(savedCredentials)[0]
];
security
.decrypt(
saveCredential.loginParams.password,
value
)
.then(() => {
security
.encrypt(
loginForm.value.password,
value
)
.then((pwd) => {
authLogin({
username:
loginForm.value
.username,
password:
loginForm.value
.password,
endpoint:
loginForm.value
.endpoint,
websocket:
loginForm.value
.websocket,
saveCredentials:
loginForm.value
.saveCredentials,
cipher: pwd
});
});
});
})
.finally(() => {
loginForm.value.loading = false;
})
.catch(() => {});
return args;
try {
await authRequest.getConfig();
if (
loginForm.value.saveCredentials &&
advancedSettingsStore.enablePrimaryPassword
) {
try {
const { ok, value } = await modalStore.prompt({
title: t('prompt.primary_password.header'),
description: t(
'prompt.primary_password.description'
),
inputType: 'password',
pattern: /[\s\S]{1,32}/
});
if (ok) {
const savedCredentials = JSON.parse(
await configRepository.getString(
'savedCredentials'
)
);
const saveCredential =
savedCredentials[
Object.keys(savedCredentials)[0]
];
await security.decrypt(
saveCredential.loginParams.password,
value
);
const pwd = await security.encrypt(
loginForm.value.password,
value
);
await authLogin({
username: loginForm.value.username,
password: loginForm.value.password,
endpoint: loginForm.value.endpoint,
websocket: loginForm.value.websocket,
saveCredentials:
loginForm.value.saveCredentials,
cipher: pwd
});
}
} catch {
// prompt cancelled or crypto failed
}
authLogin({
} else {
await authLogin({
username: loginForm.value.username,
password: loginForm.value.password,
endpoint: loginForm.value.endpoint,
websocket: loginForm.value.websocket,
saveCredentials: loginForm.value.saveCredentials
}).finally(() => {
loginForm.value.loading = false;
});
return args;
});
}
} finally {
loginForm.value.loading = false;
}
}
}
@@ -876,7 +846,6 @@ export const useAuthStore = defineStore('Auth', () => {
*/
function setCachedConfig(value) {
cachedConfig.value = value;
state.cachedConfig = value;
}
/**
@@ -915,6 +884,7 @@ export const useAuthStore = defineStore('Auth', () => {
handleCurrentUserUpdate,
loginComplete,
getAllSavedCredentials,
getSavedCredentials,
setCachedConfig,
setAttemptingAutoLogin
};
+9 -297
View File
@@ -1,22 +1,17 @@
import { computed, reactive, ref, watch } from 'vue';
import { defineStore } from 'pinia';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import { i18n } from '../plugin/i18n';
import {
compareByCreatedAtAscending,
createRateLimiter,
executeWithBackoff,
getFriendsSortFunction,
getNameColour,
getUserMemo,
isRealInstance
} from '../shared/utils';
import { friendRequest, userRequest } from '../api';
import {
runDeleteFriendshipFlow,
runUpdateFriendshipsFlow
} from '../coordinators/friendRelationshipCoordinator';
import {
runInitFriendsListFlow,
runRefreshFriendsListFlow
@@ -25,6 +20,10 @@ import {
runPendingOfflineTickFlow,
runUpdateFriendFlow
} from '../coordinators/friendPresenceCoordinator';
import {
updateFriendship,
runUpdateFriendshipsFlow
} from '../coordinators/friendRelationshipCoordinator';
import { applyUser } from '../coordinators/userCoordinator';
import { AppDebug } from '../service/appConfig';
import { database } from '../service/database';
@@ -33,10 +32,6 @@ import { useFavoriteStore } from './favorite';
import { useGeneralSettingsStore } from './settings/general';
import { useGroupStore } from './group';
import { useLocationStore } from './location';
import { useModalStore } from './modal';
import { useNotificationStore } from './notification';
import { useSharedFeedStore } from './sharedFeed';
import { useUiStore } from './ui';
import { useUserStore } from './user';
import { watchState } from '../service/watchState';
@@ -48,16 +43,12 @@ export const useFriendStore = defineStore('Friend', () => {
const appearanceSettingsStore = useAppearanceSettingsStore();
const generalSettingsStore = useGeneralSettingsStore();
const userStore = useUserStore();
const notificationStore = useNotificationStore();
const uiStore = useUiStore();
const groupStore = useGroupStore();
const sharedFeedStore = useSharedFeedStore();
const locationStore = useLocationStore();
const favoriteStore = useFavoriteStore();
const modalStore = useModalStore();
const { t } = useI18n();
const router = useRouter();
const t = i18n.global.t;
const state = reactive({
friendNumber: 0
@@ -236,7 +227,7 @@ export const useFriendStore = defineStore('Friend', () => {
onlineFriendCount.value = 0;
pendingOfflineMap.clear();
if (isLoggedIn) {
runInitFriendsListFlow(t);
runInitFriendsListFlow(i18n.global.t);
pendingOfflineWorkerFunction();
} else {
if (pendingOfflineWorker !== null) {
@@ -270,94 +261,7 @@ export const useFriendStore = defineStore('Friend', () => {
init();
/**
*
* @param ref
*/
function updateUserCurrentStatus(ref) {
if (watchState.isFriendsLoaded) {
refreshFriendsStatus(ref);
}
updateOnlineFriendCounter();
if (appearanceSettingsStore.randomUserColours) {
getNameColour(userStore.currentUser.id).then((colour) => {
userStore.setCurrentUserColour(colour);
});
}
}
/**
*
* @param args
*/
function handleFriendStatus(args) {
const D = userStore.userDialog;
if (D.visible === false || D.id !== args.params.userId) {
return;
}
const { json } = args;
D.isFriend = json.isFriend;
D.incomingRequest = json.incomingRequest;
D.outgoingRequest = json.outgoingRequest;
}
/**
*
* @param args
*/
function handleFriendDelete(args) {
const D = userStore.userDialog;
if (D.visible === false || D.id !== args.params.userId) {
return;
}
D.isFriend = false;
runDeleteFriendshipFlow(args.params.userId);
deleteFriend(args.params.userId);
}
/**
*
* @param args
*/
function handleFriendAdd(args) {
addFriendship(args.params.userId);
addFriend(args.params.userId);
}
/**
*
* @param ref
*/
function userOnFriend(ref) {
updateFriendship(ref);
if (
watchState.isFriendsLoaded &&
ref.isFriend &&
!friendLog.has(ref.id) &&
ref.id !== userStore.currentUser.id
) {
addFriendship(ref.id);
}
}
/**
*
* @param {string} userId
* @returns {*|string}
*/
function getFriendRequest(userId) {
const array = notificationStore.notificationTable.data;
for (let i = array.length - 1; i >= 0; i--) {
if (
array[i].type === 'friendRequest' &&
array[i].senderUserId === userId
) {
return array[i].id;
}
}
return '';
}
/**
*
@@ -794,183 +698,13 @@ export const useFriendStore = defineStore('Friend', () => {
*
* @param {string} id
*/
function addFriendship(id) {
if (
!watchState.isFriendsLoaded ||
friendLog.has(id) ||
id === userStore.currentUser.id
) {
return;
}
const ref = userStore.cachedUsers.get(id);
if (typeof ref === 'undefined') {
// deleted account on friends list
return;
}
friendRequest
.getFriendStatus({
userId: id,
currentUserId: userStore.currentUser.id
})
.then((args) => {
if (args.params.currentUserId !== userStore.currentUser.id) {
// safety check for delayed response
return;
}
handleFriendStatus(args);
if (args.json.isFriend && !friendLog.has(id)) {
if (state.friendNumber === 0) {
state.friendNumber = friends.size;
}
ref.$friendNumber = ++state.friendNumber;
configRepository.setInt(
`VRCX_friendNumber_${userStore.currentUser.id}`,
state.friendNumber
);
addFriend(id, ref.state);
const friendLogHistory = {
created_at: new Date().toJSON(),
type: 'Friend',
userId: id,
displayName: ref.displayName,
friendNumber: ref.$friendNumber
};
friendLogTable.value.data.push(friendLogHistory);
database.addFriendLogHistory(friendLogHistory);
notificationStore.queueFriendLogNoty(friendLogHistory);
sharedFeedStore.addEntry(friendLogHistory);
const friendLogCurrent = {
userId: id,
displayName: ref.displayName,
trustLevel: ref.$trustLevel,
friendNumber: ref.$friendNumber
};
friendLog.set(id, friendLogCurrent);
database.setFriendLogCurrent(friendLogCurrent);
uiStore.notifyMenu('friend-log');
deleteFriendRequest(id);
userRequest
.getUser({
userId: id
})
.then(() => {
if (
userStore.userDialog.visible &&
id === userStore.userDialog.id
) {
userStore.applyUserDialogLocation(true);
}
});
}
});
}
/**
*
* @param {string} userId
*/
function deleteFriendRequest(userId) {
const array = notificationStore.notificationTable.data;
for (let i = array.length - 1; i >= 0; i--) {
if (
array[i].type === 'friendRequest' &&
array[i].senderUserId === userId
) {
array.splice(i, 1);
return;
}
}
}
/**
*
* @param {object} ref
*/
function updateFriendship(ref) {
const ctx = friendLog.get(ref.id);
if (!watchState.isFriendsLoaded || typeof ctx === 'undefined') {
return;
}
if (ctx.friendNumber) {
ref.$friendNumber = ctx.friendNumber;
}
if (!ref.$friendNumber) {
ref.$friendNumber = 0; // no null
}
if (ctx.displayName !== ref.displayName) {
if (ctx.displayName) {
const friendLogHistoryDisplayName = {
created_at: new Date().toJSON(),
type: 'DisplayName',
userId: ref.id,
displayName: ref.displayName,
previousDisplayName: ctx.displayName,
friendNumber: ref.$friendNumber
};
friendLogTable.value.data.push(friendLogHistoryDisplayName);
database.addFriendLogHistory(friendLogHistoryDisplayName);
notificationStore.queueFriendLogNoty(
friendLogHistoryDisplayName
);
sharedFeedStore.addEntry(friendLogHistoryDisplayName);
const friendLogCurrent = {
userId: ref.id,
displayName: ref.displayName,
trustLevel: ref.$trustLevel,
friendNumber: ref.$friendNumber
};
friendLog.set(ref.id, friendLogCurrent);
database.setFriendLogCurrent(friendLogCurrent);
ctx.displayName = ref.displayName;
uiStore.notifyMenu('friend-log');
}
}
if (
ref.$trustLevel &&
ctx.trustLevel &&
ctx.trustLevel !== ref.$trustLevel
) {
if (
(ctx.trustLevel === 'Trusted User' &&
ref.$trustLevel === 'Veteran User') ||
(ctx.trustLevel === 'Veteran User' &&
ref.$trustLevel === 'Trusted User')
) {
const friendLogCurrent3 = {
userId: ref.id,
displayName: ref.displayName,
trustLevel: ref.$trustLevel,
friendNumber: ref.$friendNumber
};
friendLog.set(ref.id, friendLogCurrent3);
database.setFriendLogCurrent(friendLogCurrent3);
return;
}
const friendLogHistoryTrustLevel = {
created_at: new Date().toJSON(),
type: 'TrustLevel',
userId: ref.id,
displayName: ref.displayName,
trustLevel: ref.$trustLevel,
previousTrustLevel: ctx.trustLevel,
friendNumber: ref.$friendNumber
};
friendLogTable.value.data.push(friendLogHistoryTrustLevel);
database.addFriendLogHistory(friendLogHistoryTrustLevel);
notificationStore.queueFriendLogNoty(friendLogHistoryTrustLevel);
sharedFeedStore.addEntry(friendLogHistoryTrustLevel);
const friendLogCurrent2 = {
userId: ref.id,
displayName: ref.displayName,
trustLevel: ref.$trustLevel,
friendNumber: ref.$friendNumber
};
friendLog.set(ref.id, friendLogCurrent2);
database.setFriendLogCurrent(friendLogCurrent2);
uiStore.notifyMenu('friend-log');
}
ctx.trustLevel = ref.$trustLevel;
}
/**
*
@@ -1394,21 +1128,7 @@ export const useFriendStore = defineStore('Friend', () => {
*
* @param id
*/
function confirmDeleteFriend(id) {
modalStore
.confirm({
description: t('confirm.unfriend'),
title: t('confirm.title')
})
.then(async ({ ok }) => {
if (!ok) return;
const args = await friendRequest.deleteFriend({
userId: id
});
handleFriendDelete(args);
})
.catch(() => {});
}
/**
* Clears all entries in friendLog.
@@ -1452,14 +1172,6 @@ export const useFriendStore = defineStore('Friend', () => {
initFriendLog,
migrateFriendLog,
getFriendLog,
getFriendRequest,
userOnFriend,
confirmDeleteFriend,
updateUserCurrentStatus,
handleFriendAdd,
handleFriendDelete,
handleFriendStatus,
addFriendship,
tryApplyFriendOrder,
resetFriendLog,
initFriendLogHistoryTable
+14 -577
View File
@@ -1,50 +1,34 @@
import { reactive, ref, shallowRef, watch } from 'vue';
import { defineStore } from 'pinia';
import { toast } from 'vue-sonner';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import dayjs from 'dayjs';
import {
compareGameLogRows,
createJoinLeaveEntry,
createLocationEntry,
createPortalSpawnEntry,
createResourceLoadEntry,
findUserByDisplayName,
formatSeconds,
gameLogSearchFilter,
getGroupName,
parseInventoryFromUrl,
parseLocation,
parsePrintFromUrl,
replaceBioSymbols
getGroupName
} from '../../shared/utils';
import { AppDebug } from '../../service/appConfig';
import { createMediaParsers } from './mediaParsers';
import { database } from '../../service/database';
import { useAdvancedSettingsStore } from '../settings/advanced';
import { useFriendStore } from '../friend';
import { useGalleryStore } from '../gallery';
import { useGameStore } from '../game';
import { useGeneralSettingsStore } from '../settings/general';
import { useInstanceStore } from '../instance';
import { useLocationStore } from '../location';
import { runLastLocationResetFlow, runUpdateCurrentUserLocationFlow } from '../../coordinators/locationCoordinator';
import { useModalStore } from '../modal';
import { useNotificationStore } from '../notification';
import { usePhotonStore } from '../photon';
import { useSharedFeedStore } from '../sharedFeed';
import { useUiStore } from '../ui';
import { useUserStore } from '../user';
import { useVrStore } from '../vr';
import { useVrcxStore } from '../vrcx';
import { userRequest } from '../../api';
import { watchState } from '../../service/watchState';
import { tryLoadPlayerList, addGameLogEvent } from '../../coordinators/gameLogCoordinator';
import configRepository from '../../service/config';
import gameLogService from '../../service/gameLog.js';
import * as workerTimers from 'worker-timers';
@@ -59,14 +43,9 @@ export const useGameLogStore = defineStore('GameLog', () => {
const vrcxStore = useVrcxStore();
const advancedSettingsStore = useAdvancedSettingsStore();
const gameStore = useGameStore();
const generalSettingsStore = useGeneralSettingsStore();
const galleryStore = useGalleryStore();
const photonStore = usePhotonStore();
const sharedFeedStore = useSharedFeedStore();
const modalStore = useModalStore();
const router = useRouter();
const { t } = useI18n();
const state = reactive({
lastLocationAvatarList: new Map()
@@ -304,89 +283,6 @@ export const useGameLogStore = defineStore('GameLog', () => {
workerTimers.setTimeout(() => updateNowPlaying(), 1000);
}
/**
*
*/
async function tryLoadPlayerList() {
// TODO: make this work again
if (!gameStore.isGameRunning) {
return;
}
console.log('Loading player list from game log...');
let ctx;
let i;
const data = await database.getGamelogDatabase();
if (data.length === 0) {
return;
}
let length = 0;
for (i = data.length - 1; i > -1; i--) {
ctx = data[i];
if (ctx.type === 'Location') {
locationStore.setLastLocation({
date: Date.parse(ctx.created_at),
location: ctx.location,
name: ctx.worldName,
playerList: new Map(),
friendList: new Map()
});
length = i;
break;
}
}
if (length > 0) {
for (i = length + 1; i < data.length; i++) {
ctx = data[i];
if (ctx.type === 'OnPlayerJoined') {
if (!ctx.userId) {
ctx.userId =
findUserByDisplayName(
userStore.cachedUsers,
ctx.displayName
)?.id ?? '';
}
const userMap = {
displayName: ctx.displayName,
userId: ctx.userId,
joinTime: Date.parse(ctx.created_at),
lastAvatar: ''
};
locationStore.lastLocation.playerList.set(
ctx.userId,
userMap
);
if (friendStore.friends.has(ctx.userId)) {
locationStore.lastLocation.friendList.set(
ctx.userId,
userMap
);
}
}
if (ctx.type === 'OnPlayerLeft') {
locationStore.lastLocation.playerList.delete(ctx.userId);
locationStore.lastLocation.friendList.delete(ctx.userId);
}
}
locationStore.lastLocation.playerList.forEach((ref1) => {
if (
ref1.userId &&
typeof ref1.userId === 'string' &&
!userStore.cachedUsers.has(ref1.userId)
) {
userRequest.getUser({ userId: ref1.userId });
}
});
runUpdateCurrentUserLocationFlow();
instanceStore.updateCurrentInstanceWorld();
vrStore.updateVRLastLocation();
instanceStore.getCurrentInstanceUserList();
userStore.applyUserDialogLocation();
instanceStore.applyWorldDialogInstances();
instanceStore.applyGroupDialogInstances();
}
}
/**
*
* @param row
@@ -534,472 +430,6 @@ export const useGameLogStore = defineStore('GameLog', () => {
}
}
/**
*
* @param gameLog
* @param location
*/
function addGameLogEntry(gameLog, location) {
let entry = undefined;
if (advancedSettingsStore.gameLogDisabled) {
return;
}
let userId = String(gameLog.userId || '');
if (!userId && gameLog.displayName) {
userId =
findUserByDisplayName(
userStore.cachedUsers,
gameLog.displayName
)?.id ?? '';
}
switch (gameLog.type) {
case 'location-destination':
if (gameStore.isGameRunning) {
// needs to be added before OnPlayerLeft entries from LocationReset
addGameLog({
created_at: gameLog.dt,
type: 'LocationDestination',
location: gameLog.location
});
runLastLocationResetFlow(gameLog.dt);
locationStore.setLastLocationLocation('traveling');
locationStore.setLastLocationDestination(gameLog.location);
locationStore.setLastLocationDestinationTime(
Date.parse(gameLog.dt)
);
state.lastLocationAvatarList.clear();
instanceStore.removeQueuedInstance(gameLog.location);
runUpdateCurrentUserLocationFlow();
clearNowPlaying();
instanceStore.updateCurrentInstanceWorld();
userStore.applyUserDialogLocation();
instanceStore.applyWorldDialogInstances();
instanceStore.applyGroupDialogInstances();
}
break;
case 'location':
instanceStore.addInstanceJoinHistory(
locationStore.lastLocation.location,
gameLog.dt
);
const worldName = replaceBioSymbols(gameLog.worldName);
if (gameStore.isGameRunning) {
runLastLocationResetFlow(gameLog.dt);
clearNowPlaying();
locationStore.setLastLocation({
date: Date.parse(gameLog.dt),
location: gameLog.location,
name: worldName,
playerList: new Map(),
friendList: new Map()
});
instanceStore.removeQueuedInstance(gameLog.location);
runUpdateCurrentUserLocationFlow();
vrStore.updateVRLastLocation();
instanceStore.updateCurrentInstanceWorld();
userStore.applyUserDialogLocation();
instanceStore.applyWorldDialogInstances();
instanceStore.applyGroupDialogInstances();
}
instanceStore.addInstanceJoinHistory(
gameLog.location,
gameLog.dt
);
const L = parseLocation(gameLog.location);
entry = createLocationEntry(
gameLog.dt,
gameLog.location,
L.worldId,
worldName
);
getGroupName(gameLog.location).then((groupName) => {
entry.groupName = groupName;
});
addGamelogLocationToDatabase(entry);
break;
case 'player-joined':
const joinTime = Date.parse(gameLog.dt);
const userMap = {
displayName: gameLog.displayName,
userId,
joinTime,
lastAvatar: ''
};
locationStore.lastLocation.playerList.set(userId, userMap);
const ref = userStore.cachedUsers.get(userId);
if (!userId) {
console.error('Missing userId:', gameLog.displayName);
} else if (userId === userStore.currentUser.id) {
// skip
} else if (
friendStore.friends.has(userId) &&
typeof ref !== 'undefined'
) {
locationStore.lastLocation.friendList.set(userId, userMap);
if (
ref.location !== locationStore.lastLocation.location &&
ref.travelingToLocation !==
locationStore.lastLocation.location
) {
// fix $location_at with private
ref.$location_at = joinTime;
}
} else if (typeof ref !== 'undefined') {
// set $location_at to join time if user isn't a friend
ref.$location_at = joinTime;
} else {
if (AppDebug.debugGameLog || AppDebug.debugWebRequests) {
console.log('Fetching user from gameLog:', userId);
}
userRequest.getUser({ userId });
}
vrStore.updateVRLastLocation();
instanceStore.getCurrentInstanceUserList();
entry = createJoinLeaveEntry(
'OnPlayerJoined',
gameLog.dt,
gameLog.displayName,
location,
userId
);
database.addGamelogJoinLeaveToDatabase(entry);
break;
case 'player-left':
const ref1 = locationStore.lastLocation.playerList.get(userId);
if (typeof ref1 === 'undefined') {
break;
}
const time = dayjs(gameLog.dt) - ref1.joinTime;
locationStore.lastLocation.playerList.delete(userId);
locationStore.lastLocation.friendList.delete(userId);
state.lastLocationAvatarList.delete(gameLog.displayName);
photonStore.photonLobbyAvatars.delete(userId);
vrStore.updateVRLastLocation();
instanceStore.getCurrentInstanceUserList();
entry = createJoinLeaveEntry(
'OnPlayerLeft',
gameLog.dt,
gameLog.displayName,
location,
userId,
time
);
database.addGamelogJoinLeaveToDatabase(entry);
break;
case 'portal-spawn':
if (vrcxStore.ipcEnabled && gameStore.isGameRunning) {
break;
}
entry = createPortalSpawnEntry(gameLog.dt, location);
database.addGamelogPortalSpawnToDatabase(entry);
break;
case 'video-play':
gameLog.videoUrl = decodeURI(gameLog.videoUrl);
if (lastVideoUrl.value === gameLog.videoUrl) {
break;
}
lastVideoUrl.value = gameLog.videoUrl;
addGameLogVideo(gameLog, location, userId);
break;
case 'video-sync':
const timestamp = gameLog.timestamp.replace(/,/g, '');
if (nowPlaying.value.playing) {
nowPlaying.value.offset = parseInt(timestamp, 10);
}
break;
case 'resource-load-string':
case 'resource-load-image':
if (
!generalSettingsStore.logResourceLoad ||
lastResourceloadUrl.value === gameLog.resourceUrl
) {
break;
}
lastResourceloadUrl.value = gameLog.resourceUrl;
entry = createResourceLoadEntry(
gameLog.type,
gameLog.dt,
gameLog.resourceUrl,
location
);
database.addGamelogResourceLoadToDatabase(entry);
break;
case 'screenshot':
// entry = {
// created_at: gameLog.dt,
// type: 'Event',
// data: `Screenshot Processed: ${gameLog.screenshotPath.replace(
// /^.*[\\/]/,
// ''
// )}`
// };
// database.addGamelogEventToDatabase(entry);
vrcxStore.processScreenshot(gameLog.screenshotPath);
break;
case 'api-request':
if (AppDebug.debugWebRequests) {
console.log('API Request:', gameLog.url);
}
// const userId = '';
// try {
// const url = new URL(gameLog.url);
// const urlParams = new URLSearchParams(gameLog.url);
// if (url.pathname.substring(0, 13) === '/api/1/users/') {
// const pathArray = url.pathname.split('/');
// userId = pathArray[4];
// } else if (urlParams.has('userId')) {
// userId = urlParams.get('userId');
// }
// } catch (err) {
// console.error(err);
// }
// if (!userId) {
// break;
// }
if (advancedSettingsStore.saveInstanceEmoji) {
const inv = parseInventoryFromUrl(gameLog.url);
if (inv) {
galleryStore.queueCheckInstanceInventory(
inv.inventoryId,
inv.userId
);
}
}
if (advancedSettingsStore.saveInstancePrints) {
const printId = parsePrintFromUrl(gameLog.url);
if (printId) {
galleryStore.queueSavePrintToFile(printId);
}
}
break;
case 'avatar-change':
if (!gameStore.isGameRunning) {
break;
}
let avatarName = state.lastLocationAvatarList.get(
gameLog.displayName
);
if (
photonStore.photonLoggingEnabled ||
avatarName === gameLog.avatarName
) {
break;
}
if (!avatarName) {
avatarName = gameLog.avatarName;
state.lastLocationAvatarList.set(
gameLog.displayName,
avatarName
);
break;
}
avatarName = gameLog.avatarName;
state.lastLocationAvatarList.set(
gameLog.displayName,
avatarName
);
entry = {
created_at: gameLog.dt,
type: 'AvatarChange',
userId,
name: avatarName,
displayName: gameLog.displayName
};
break;
case 'vrcx':
// VideoPlay(PyPyDance) "https://jd.pypy.moe/api/v1/videos/jr1NX4Jo8GE.mp4",0.1001,239.606,"0905 : [J-POP] 【まなこ】金曜日のおはよう 踊ってみた (vernities)"
const type = gameLog.data.substr(0, gameLog.data.indexOf(' '));
if (type === 'VideoPlay(PyPyDance)') {
addGameLogPyPyDance(gameLog, location);
} else if (type === 'VideoPlay(VRDancing)') {
addGameLogVRDancing(gameLog, location);
} else if (type === 'VideoPlay(ZuwaZuwaDance)') {
addGameLogZuwaZuwaDance(gameLog, location);
} else if (type === 'LSMedia') {
addGameLogLSMedia(gameLog, location);
} else if (type === 'VideoPlay(PopcornPalace)') {
addGameLogPopcornPalace(gameLog, location);
}
break;
case 'photon-id':
if (!gameStore.isGameRunning || !watchState.isFriendsLoaded) {
break;
}
const photonId = parseInt(gameLog.photonId, 10);
const ref2 = photonStore.photonLobby.get(photonId);
if (typeof ref2 === 'undefined') {
const foundUser = findUserByDisplayName(
userStore.cachedUsers,
gameLog.displayName
);
if (foundUser) {
photonStore.photonLobby.set(photonId, foundUser);
photonStore.photonLobbyCurrent.set(photonId, foundUser);
}
const ctx1 = {
displayName: gameLog.displayName
};
photonStore.photonLobby.set(photonId, ctx1);
photonStore.photonLobbyCurrent.set(photonId, ctx1);
instanceStore.getCurrentInstanceUserList();
}
break;
case 'notification':
// entry = {
// created_at: gameLog.dt,
// type: 'Notification',
// data: gameLog.json
// };
break;
case 'event':
entry = {
created_at: gameLog.dt,
type: 'Event',
data: gameLog.event
};
database.addGamelogEventToDatabase(entry);
break;
case 'vrc-quit':
if (!gameStore.isGameRunning) {
break;
}
if (advancedSettingsStore.vrcQuitFix) {
const bias = Date.parse(gameLog.dt) + 3000;
if (bias < Date.now()) {
console.log('QuitFix: Bias too low, not killing VRC');
break;
}
AppApi.QuitGame().then((processCount) => {
if (processCount > 1) {
console.log(
'QuitFix: More than 1 process running, not killing VRC'
);
} else if (processCount === 1) {
console.log('QuitFix: Killed VRC');
} else {
console.log(
'QuitFix: Nothing to kill, no VRC process running'
);
}
});
}
break;
case 'openvr-init':
gameStore.setIsGameNoVR(false);
configRepository.setBool('isGameNoVR', gameStore.isGameNoVR);
vrStore.updateOpenVR();
break;
case 'desktop-mode':
gameStore.setIsGameNoVR(true);
configRepository.setBool('isGameNoVR', gameStore.isGameNoVR);
vrStore.updateOpenVR();
break;
case 'udon-exception':
if (generalSettingsStore.udonExceptionLogging) {
console.log('UdonException', gameLog.data);
}
// entry = {
// created_at: gameLog.dt,
// type: 'Event',
// data: gameLog.data
// };
// database.addGamelogEventToDatabase(entry);
break;
case 'sticker-spawn':
if (!advancedSettingsStore.saveInstanceStickers) {
break;
}
galleryStore.trySaveStickerToFile(
gameLog.displayName,
gameLog.userId,
gameLog.inventoryId
);
break;
}
if (typeof entry !== 'undefined') {
sharedFeedStore.addEntry(entry);
notificationStore.queueGameLogNoty(entry);
addGameLog(entry);
}
}
/**
*
*/
async function getGameLogTable() {
await database.initTables();
const dateTill = await database.getLastDateGameLogDatabase();
updateGameLog(dateTill);
}
/**
*
* @param dateTill
*/
async function updateGameLog(dateTill) {
await gameLogService.setDateTill(dateTill);
await new Promise((resolve) => {
workerTimers.setTimeout(resolve, 10000);
});
let location = '';
for (const gameLog of await gameLogService.getAll()) {
if (gameLog.type === 'location') {
location = gameLog.location;
}
addGameLogEntry(gameLog, location);
}
}
// use in C#
/**
*
* @param json
*/
function addGameLogEvent(json) {
const rawLogs = JSON.parse(json);
const gameLog = gameLogService.parseRawGameLog(
rawLogs[1],
rawLogs[2],
rawLogs.slice(3)
);
if (
AppDebug.debugGameLog &&
gameLog.type !== 'photon-id' &&
gameLog.type !== 'api-request' &&
gameLog.type !== 'udon-exception'
) {
console.log('gameLog:', gameLog);
}
addGameLogEntry(gameLog, locationStore.lastLocation.location);
}
/**
*
*/
async function disableGameLogDialog() {
if (gameStore.isGameRunning) {
toast.error(t('message.gamelog.vrchat_must_be_closed'));
return;
}
if (!advancedSettingsStore.gameLogDisabled) {
modalStore
.confirm({
description: t('confirm.disable_gamelog'),
title: t('confirm.title')
})
.then(({ ok }) => {
if (!ok) return;
advancedSettingsStore.setGameLogDisabled();
})
.catch(() => {});
} else {
advancedSettingsStore.setGameLogDisabled();
}
}
/**
*
*/
@@ -1028,14 +458,21 @@ export const useGameLogStore = defineStore('GameLog', () => {
clearNowPlaying,
resetLastMediaUrls,
tryLoadPlayerList,
gameLogIsFriend,
gameLogIsFavorite,
gameLogTableLookup,
addGameLog,
addGamelogLocationToDatabase,
getGameLogTable,
addGameLogEvent,
disableGameLogDialog
// Media parsers (used by coordinator)
addGameLogVideo,
addGameLogPyPyDance,
addGameLogVRDancing,
addGameLogZuwaZuwaDance,
addGameLogLSMedia,
addGameLogPopcornPalace,
// Re-exported from coordinator (called by C# via window.$pinia)
addGameLogEvent
};
});
+2 -1
View File
@@ -41,6 +41,7 @@ import { useAdvancedSettingsStore } from '../settings/advanced';
import { useAppearanceSettingsStore } from '../settings/appearance';
import { useFavoriteStore } from '../favorite';
import { useFriendStore } from '../friend';
import { handleFriendAdd } from '../../coordinators/friendRelationshipCoordinator';
import { useGameStore } from '../game';
import { useGeneralSettingsStore } from '../settings/general';
import { useGroupStore } from '../group';
@@ -533,7 +534,7 @@ export const useNotificationStore = defineStore('Notification', () => {
notificationId: ref.id
}
});
friendStore.handleFriendAdd({
handleFriendAdd({
params: {
userId: ref.senderUserId
}
+4 -4
View File
@@ -5,11 +5,12 @@ import { database } from '../service/database';
import { groupRequest } from '../api';
import { runRefreshFriendsListFlow } from '../coordinators/friendSyncCoordinator';
import { runUpdateIsGameRunningFlow } from '../coordinators/gameCoordinator';
import { addGameLogEvent } from '../coordinators/gameLogCoordinator';
import { runRefreshPlayerModerationsFlow } from '../coordinators/moderationCoordinator';
import { clearVRCXCache } from '../coordinators/vrcxCoordinator';
import { useAuthStore } from './auth';
import { useDiscordPresenceSettingsStore } from './settings/discordPresence';
import { useFriendStore } from './friend';
import { useGameLogStore } from './gameLog';
import { useGameStore } from './game';
import { useGroupStore } from './group';
import { handleGroupUserInstances } from '../coordinators/groupCoordinator';
@@ -31,7 +32,6 @@ export const useUpdateLoopStore = defineStore('UpdateLoop', () => {
const moderationStore = useModerationStore();
const vrcxStore = useVrcxStore();
const discordPresenceSettingsStore = useDiscordPresenceSettingsStore();
const gameLogStore = useGameLogStore();
const vrcxUpdaterStore = useVRCXUpdaterStore();
const groupStore = useGroupStore();
const vrStore = useVrStore();
@@ -114,7 +114,7 @@ export const useUpdateLoopStore = defineStore('UpdateLoop', () => {
) {
state.nextClearVRCXCacheCheck =
vrcxStore.clearVRCXCacheFrequency / 2;
vrcxStore.clearVRCXCache();
clearVRCXCache();
}
if (--state.nextDiscordUpdate <= 0) {
state.nextDiscordUpdate = 3;
@@ -131,7 +131,7 @@ export const useUpdateLoopStore = defineStore('UpdateLoop', () => {
const logLines = await LogWatcher.GetLogLines();
if (logLines) {
logLines.forEach((logLine) => {
gameLogStore.addGameLogEvent(logLine);
addGameLogEvent(logLine);
});
}
}
+2 -53
View File
@@ -46,6 +46,7 @@ import { useUpdateLoopStore } from './updateLoop';
import { useUserStore } from './user';
import { useVrcStatusStore } from './vrcStatus';
import { useWorldStore } from './world';
import { clearVRCXCache } from '../coordinators/vrcxCoordinator';
import { watchState } from '../service/watchState';
import configRepository from '../service/config';
@@ -277,59 +278,7 @@ export const useVrcxStore = defineStore('Vrcx', () => {
/**
*
*/
function clearVRCXCache() {
console.log('Clearing VRCX cache...');
failedGetRequests.clear();
userStore.cachedUsers.forEach((ref, id) => {
if (
!friendStore.friends.has(id) &&
!locationStore.lastLocation.playerList.has(ref.id) &&
id !== userStore.currentUser.id
) {
userStore.cachedUsers.delete(id);
}
});
worldStore.cachedWorlds.forEach((ref, id) => {
if (
!favoriteStore.getCachedFavoritesByObjectId(id) &&
ref.authorId !== userStore.currentUser.id &&
!favoriteStore.localWorldFavoritesList.includes(id)
) {
worldStore.cachedWorlds.delete(id);
}
});
avatarStore.cachedAvatars.forEach((ref, id) => {
if (
!favoriteStore.getCachedFavoritesByObjectId(id) &&
ref.authorId !== userStore.currentUser.id &&
!favoriteStore.localAvatarFavoritesList.includes(id) &&
!avatarStore.avatarHistory.includes(id)
) {
avatarStore.cachedAvatars.delete(id);
}
});
groupStore.cachedGroups.forEach((ref, id) => {
if (!groupStore.currentUserGroups.has(id)) {
groupStore.cachedGroups.delete(id);
}
});
instanceStore.cachedInstances.forEach((ref, id) => {
if (
[...friendStore.friends.values()].some(
(f) => f.$location?.tag === id
)
) {
return;
}
// delete instances over an hour old
if (Date.parse(ref.$fetchedAt) < Date.now() - 3600000) {
instanceStore.cachedInstances.delete(id);
}
});
avatarStore.cachedAvatarNames.clear();
userStore.customUserTags.clear();
galleryStore.cachedEmoji.clear();
}
/**
*