refactor coordinators

This commit is contained in:
pa
2026-03-10 11:54:09 +09:00
parent 648fcde085
commit 2fffadfbcf
16 changed files with 1068 additions and 1257 deletions
+9 -67
View File
@@ -5,19 +5,20 @@ import { useI18n } from 'vue-i18n';
import Noty from 'noty';
import { closeWebSocket, initWebsocket } from '../service/websocket';
import {
runLoginSuccessFlow,
runLogoutFlow
} from '../coordinators/authCoordinator';
import { AppDebug } from '../service/appConfig';
import { authRequest } from '../api';
import { createAuthAutoLoginCoordinator } from '../coordinators/authAutoLoginCoordinator';
import { createAuthCoordinator } from '../coordinators/authCoordinator';
import { database } from '../service/database';
import { escapeTag } from '../shared/utils';
import { queryClient } from '../queries';
import { initWebsocket } from '../service/websocket';
import { request } from '../service/request';
import { runHandleAutoLoginFlow } from '../coordinators/authAutoLoginCoordinator';
import { useAdvancedSettingsStore } from './settings/advanced';
import { useGeneralSettingsStore } from './settings/general';
import { useModalStore } from './modal';
import { useNotificationStore } from './notification';
import { useUpdateLoopStore } from './updateLoop';
import { useUserStore } from './user';
import { watchState } from '../service/watchState';
@@ -31,7 +32,6 @@ import * as workerTimers from 'worker-timers';
export const useAuthStore = defineStore('Auth', () => {
const advancedSettingsStore = useAdvancedSettingsStore();
const generalSettingsStore = useGeneralSettingsStore();
const notificationStore = useNotificationStore();
const userStore = useUserStore();
const updateLoopStore = useUpdateLoopStore();
const modalStore = useModalStore();
@@ -168,15 +168,7 @@ export const useAuthStore = defineStore('Auth', () => {
*
*/
async function handleLogoutEvent() {
if (watchState.isLoggedIn) {
new Noty({
type: 'success',
text: t('message.auth.logout_greeting', {
name: `<strong>${escapeTag(userStore.currentUser.displayName)}</strong>`
})
}).show();
}
await authCoordinator.runLogoutFlow();
await runLogoutFlow();
}
/**
@@ -828,7 +820,7 @@ export const useAuthStore = defineStore('Auth', () => {
} else if (json.requiresTwoFactorAuth) {
promptTOTP();
} else {
authCoordinator.runLoginSuccessFlow(json);
runLoginSuccessFlow(json);
}
}
@@ -836,7 +828,7 @@ export const useAuthStore = defineStore('Auth', () => {
*
*/
async function handleAutoLogin() {
await authAutoLoginCoordinator.runHandleAutoLoginFlow();
await runHandleAutoLoginFlow();
}
/**
@@ -894,56 +886,6 @@ export const useAuthStore = defineStore('Auth', () => {
attemptingAutoLogin.value = value;
}
const authAutoLoginCoordinator = createAuthAutoLoginCoordinator({
getIsAttemptingAutoLogin: () => attemptingAutoLogin.value,
setAttemptingAutoLogin,
getLastUserLoggedIn: () => loginForm.value.lastUserLoggedIn,
getSavedCredentials,
isPrimaryPasswordEnabled: () =>
advancedSettingsStore.enablePrimaryPassword,
handleLogoutEvent,
autoLoginAttempts: state.autoLoginAttempts,
relogin,
notifyAutoLoginSuccess: () => {
if (AppDebug.errorNoty) {
toast.dismiss(AppDebug.errorNoty);
}
AppDebug.errorNoty = toast.success(
t('message.auth.auto_login_success')
);
},
notifyAutoLoginFailed: () => {
if (AppDebug.errorNoty) {
toast.dismiss(AppDebug.errorNoty);
}
AppDebug.errorNoty = toast.error(
t('message.auth.auto_login_failed')
);
},
notifyOffline: () => {
AppDebug.errorNoty = toast.error(t('message.auth.offline'));
},
flashWindow: () => AppApi.FlashWindow(),
isOnline: () => navigator.onLine,
now: () => Date.now()
});
const authCoordinator = createAuthCoordinator({
userStore,
notificationStore,
updateLoopStore,
initWebsocket,
updateStoredUser,
webApiService,
loginForm,
configRepository,
setAttemptingAutoLogin,
autoLoginAttempts: state.autoLoginAttempts,
closeWebSocket,
queryClient,
watchState
});
return {
state,
+3 -2
View File
@@ -13,6 +13,7 @@ import {
import { avatarRequest, favoriteRequest, queryRequest } from '../api';
import { database } from '../service/database';
import { processBulk } from '../service/request';
import { runUpdateFriendFlow } from '../coordinators/friendPresenceCoordinator';
import { useAppearanceSettingsStore } from './settings/appearance';
import { useAvatarStore } from './avatar';
import { useFriendStore } from './friend';
@@ -339,7 +340,7 @@ export const useFavoriteStore = defineStore('Favorite', () => {
function handleFavorite(args) {
args.ref = applyFavoriteCached(args.json);
applyFavorite(args.ref.type, args.ref.favoriteId);
friendStore.updateFriend(args.ref.favoriteId);
runUpdateFriendFlow(args.ref.favoriteId);
const { ref } = args;
const userDialog = userStore.userDialog;
if (userDialog.visible && ref.favoriteId === userDialog.id) {
@@ -437,7 +438,7 @@ export const useFavoriteStore = defineStore('Favorite', () => {
(id) => id !== ref.favoriteId
);
friendStore.updateFriend(ref.favoriteId);
runUpdateFriendFlow(ref.favoriteId);
friendStore.updateSidebarFavorites();
const userDialog = userStore.userDialog;
if (userDialog.visible && userDialog.id === ref.favoriteId) {
+31 -137
View File
@@ -1,6 +1,5 @@
import { computed, reactive, ref, watch } from 'vue';
import { defineStore } from 'pinia';
import { toast } from 'vue-sonner';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
@@ -9,24 +8,27 @@ import {
createRateLimiter,
executeWithBackoff,
getFriendsSortFunction,
getGroupName,
getNameColour,
getUserMemo,
getWorldName,
isRealInstance,
migrateMemos
isRealInstance
} from '../shared/utils';
import { friendRequest, userRequest } from '../api';
import {
runDeleteFriendshipFlow,
runUpdateFriendshipsFlow
} from '../coordinators/friendRelationshipCoordinator';
import {
runInitFriendsListFlow,
runRefreshFriendsListFlow
} from '../coordinators/friendSyncCoordinator';
import {
runPendingOfflineTickFlow,
runUpdateFriendFlow
} from '../coordinators/friendPresenceCoordinator';
import { AppDebug } from '../service/appConfig';
import { createFriendPresenceCoordinator } from '../coordinators/friendPresenceCoordinator';
import { createFriendRelationshipCoordinator } from '../coordinators/friendRelationshipCoordinator';
import { createFriendSyncCoordinator } from '../coordinators/friendSyncCoordinator';
import { database } from '../service/database';
import { reconnectWebSocket } from '../service/websocket';
import { useAppearanceSettingsStore } from './settings/appearance';
import { useAuthStore } from './auth';
import { useFavoriteStore } from './favorite';
import { useFeedStore } from './feed';
import { useGeneralSettingsStore } from './settings/general';
import { useGroupStore } from './group';
import { useLocationStore } from './location';
@@ -34,7 +36,6 @@ import { useModalStore } from './modal';
import { useNotificationStore } from './notification';
import { useSharedFeedStore } from './sharedFeed';
import { useUiStore } from './ui';
import { useUpdateLoopStore } from './updateLoop';
import { useUserStore } from './user';
import { watchState } from '../service/watchState';
@@ -47,12 +48,9 @@ export const useFriendStore = defineStore('Friend', () => {
const generalSettingsStore = useGeneralSettingsStore();
const userStore = useUserStore();
const notificationStore = useNotificationStore();
const feedStore = useFeedStore();
const uiStore = useUiStore();
const groupStore = useGroupStore();
const sharedFeedStore = useSharedFeedStore();
const updateLoopStore = useUpdateLoopStore();
const authStore = useAuthStore();
const locationStore = useLocationStore();
const favoriteStore = useFavoriteStore();
const modalStore = useModalStore();
@@ -64,7 +62,7 @@ export const useFriendStore = defineStore('Friend', () => {
friendNumber: 0
});
let friendLog = new Map();
const friendLog = new Map();
const friends = reactive(new Map());
@@ -237,7 +235,7 @@ export const useFriendStore = defineStore('Friend', () => {
onlineFriendCount.value = 0;
pendingOfflineMap.clear();
if (isLoggedIn) {
initFriendsList();
runInitFriendsListFlow(t);
pendingOfflineWorkerFunction();
} else {
if (pendingOfflineWorker !== null) {
@@ -313,7 +311,7 @@ export const useFriendStore = defineStore('Friend', () => {
return;
}
D.isFriend = false;
deleteFriendship(args.params.userId);
runDeleteFriendshipFlow(args.params.userId);
deleteFriend(args.params.userId);
}
@@ -404,20 +402,12 @@ export const useFriendStore = defineStore('Friend', () => {
}
}
/**
* @param {string} id
* @param {string?} stateInput
*/
function updateFriend(id, stateInput = undefined) {
friendPresenceCoordinator.runUpdateFriendFlow(id, stateInput);
}
/**
*
*/
async function pendingOfflineWorkerFunction() {
pendingOfflineWorker = workerTimers.setInterval(() => {
friendPresenceCoordinator.runPendingOfflineTickFlow();
runPendingOfflineTickFlow();
}, 1000);
}
@@ -454,7 +444,7 @@ export const useFriendStore = defineStore('Friend', () => {
for (const friend of map) {
const [id, state_input] = friend;
if (friends.has(id)) {
updateFriend(id, state_input);
runUpdateFriendFlow(id, state_input);
} else {
addFriend(id, state_input);
}
@@ -698,10 +688,6 @@ export const useFriendStore = defineStore('Friend', () => {
/**
* @returns {Promise<void>}
*/
async function refreshFriendsList() {
await friendSyncCoordinator.runRefreshFriendsListFlow();
}
/**
*
* @param forceUpdate
@@ -895,22 +881,6 @@ export const useFriendStore = defineStore('Friend', () => {
}
}
/**
*
* @param {string} id
*/
function deleteFriendship(id) {
friendRelationshipCoordinator.runDeleteFriendshipFlow(id);
}
/**
*
* @param {object} ref
*/
function updateFriendships(ref) {
friendRelationshipCoordinator.runUpdateFriendshipsFlow(ref);
}
/**
*
* @param {object} ref
@@ -1075,7 +1045,7 @@ export const useFriendStore = defineStore('Friend', () => {
}
}
if (typeof currentUser.friends !== 'undefined') {
updateFriendships(currentUser);
runUpdateFriendshipsFlow(currentUser);
}
}
@@ -1440,91 +1410,14 @@ export const useFriendStore = defineStore('Friend', () => {
}
/**
*
* Clears all entries in friendLog.
* Uses .clear() instead of reassignment to keep the same Map reference,
* so that coordinators reading friendStore.friendLog stay in sync.
*/
async function initFriendsList() {
await friendSyncCoordinator.runInitFriendsListFlow();
function resetFriendLog() {
friendLog.clear();
}
/**
* @param {boolean} value
*/
function setRefreshFriendsLoading(value) {
isRefreshFriendsLoading.value = value;
}
const friendPresenceCoordinator = createFriendPresenceCoordinator({
friends,
localFavoriteFriends,
pendingOfflineMap,
pendingOfflineDelay,
watchState,
appDebug: AppDebug,
getCachedUsers: () => userStore.cachedUsers,
isRealInstance,
requestUser: (userId) =>
userRequest.getUser({
userId
}),
getWorldName,
getGroupName,
feedStore,
database,
updateOnlineFriendCounter,
now: () => Date.now(),
nowIso: () => new Date().toJSON()
});
const friendRelationshipCoordinator = createFriendRelationshipCoordinator({
friendLog,
friendLogTable,
getCurrentUserId: () => userStore.currentUser.id,
requestFriendStatus: (params) => friendRequest.getFriendStatus(params),
handleFriendStatus,
addFriendship,
deleteFriend,
database,
notificationStore,
sharedFeedStore,
favoriteStore,
uiStore,
shouldNotifyUnfriend: () => !appearanceSettingsStore.hideUnfriends,
nowIso: () => new Date().toJSON()
});
const friendSyncCoordinator = createFriendSyncCoordinator({
getNextCurrentUserRefresh: () => updateLoopStore.nextCurrentUserRefresh,
getCurrentUser: () => userStore.getCurrentUser(),
refreshFriends,
reconnectWebSocket,
getCurrentUserId: () => userStore.currentUser.id,
getCurrentUserRef: () => userStore.currentUser,
setRefreshFriendsLoading: (value) => {
isRefreshFriendsLoading.value = value;
},
setFriendsLoaded: (value) => {
watchState.isFriendsLoaded = value;
},
resetFriendLog: () => {
friendLog = new Map();
},
isFriendLogInitialized: (userId) =>
configRepository.getBool(`friendLogInit_${userId}`),
getFriendLog,
initFriendLog,
isDontLogMeOut: () => AppDebug.dontLogMeOut,
showLoadFailedToast: () => toast.error(t('message.friend.load_failed')),
handleLogoutEvent: () => authStore.handleLogoutEvent(),
tryApplyFriendOrder,
getAllUserStats,
hasLegacyFriendLogData: (userId) =>
VRCXStorage.Get(`${userId}_friendLogUpdatedAt`),
removeLegacyFeedTable: (userId) =>
VRCXStorage.Remove(`${userId}_feedTable`),
migrateMemos,
migrateFriendLog
});
return {
state,
@@ -1543,16 +1436,15 @@ export const useFriendStore = defineStore('Friend', () => {
onlineFriendCount,
friendLog,
friendLogTable,
pendingOfflineMap,
pendingOfflineDelay,
initFriendsList,
updateLocalFavoriteFriends,
updateSidebarFavorites,
updateFriend,
deleteFriend,
refreshFriendsStatus,
addFriend,
refreshFriends,
refreshFriendsList,
updateOnlineFriendCounter,
getAllUserStats,
getAllUserMutualCount,
@@ -1562,11 +1454,13 @@ export const useFriendStore = defineStore('Friend', () => {
getFriendRequest,
userOnFriend,
confirmDeleteFriend,
updateFriendships,
updateUserCurrentStatus,
handleFriendAdd,
handleFriendDelete,
initFriendLogHistoryTable,
setRefreshFriendsLoading
handleFriendStatus,
addFriendship,
tryApplyFriendOrder,
resetFriendLog,
initFriendLogHistoryTable
};
});
+4 -24
View File
@@ -6,18 +6,15 @@ import {
deleteVRChatCache as _deleteVRChatCache,
isRealInstance
} from '../shared/utils';
import { createGameCoordinator } from '../coordinators/gameCoordinator';
import { database } from '../service/database';
import { runGameRunningChangedFlow } from '../coordinators/gameCoordinator';
import { useAdvancedSettingsStore } from './settings/advanced';
import { useAvatarStore } from './avatar';
import { useGameLogStore } from './gameLog';
import { useInstanceStore } from './instance';
import { useLaunchStore } from './launch';
import { useLocationStore } from './location';
import { useModalStore } from './modal';
import { useNotificationStore } from './notification';
import { useUpdateLoopStore } from './updateLoop';
import { useUserStore } from './user';
import { useVrStore } from './vr';
import { useWorldStore } from './world';
@@ -32,11 +29,8 @@ export const useGameStore = defineStore('Game', () => {
const avatarStore = useAvatarStore();
const launchStore = useLaunchStore();
const worldStore = useWorldStore();
const instanceStore = useInstanceStore();
const gameLogStore = useGameLogStore();
const vrStore = useVrStore();
const userStore = useUserStore();
const updateLoopStore = useUpdateLoopStore();
const modalStore = useModalStore();
const state = reactive({
@@ -168,22 +162,6 @@ export const useGameStore = defineStore('Game', () => {
VRChatCacheSizeLoading.value = false;
}
const gameCoordinator = createGameCoordinator({
userStore,
instanceStore,
updateLoopStore,
locationStore,
gameLogStore,
vrStore,
avatarStore,
configRepository,
workerTimers,
checkVRChatDebugLogging,
autoVRChatCacheManagement,
checkIfGameCrashed,
getIsGameNoVR: () => isGameNoVR.value
});
// use in C#
/**
* @param {boolean} isGameRunningArg Game running flag from IPC.
@@ -195,7 +173,7 @@ export const useGameStore = defineStore('Game', () => {
}
if (isGameRunningArg !== isGameRunning.value) {
isGameRunning.value = isGameRunningArg;
await gameCoordinator.runGameRunningChangedFlow(isGameRunningArg);
await runGameRunningChangedFlow(isGameRunningArg);
console.log(new Date(), 'isGameRunning', isGameRunningArg);
}
@@ -300,6 +278,8 @@ export const useGameStore = defineStore('Game', () => {
setIsGameNoVR,
getVRChatRegistryKey,
checkVRChatDebugLogging,
autoVRChatCacheManagement,
checkIfGameCrashed,
updateIsHmdAfk
};
});
+26 -2
View File
@@ -3,6 +3,7 @@ import { watch } from 'vue';
import { database } from '../service/database';
import { groupRequest } from '../api';
import { runRefreshFriendsListFlow } from '../coordinators/friendSyncCoordinator';
import { useAuthStore } from './auth';
import { useDiscordPresenceSettingsStore } from './settings/discordPresence';
import { useFriendStore } from './friend';
@@ -62,6 +63,9 @@ export const useUpdateLoopStore = defineStore('UpdateLoop', () => {
const ipcTimeout = state.ipcTimeout;
/**
*
*/
async function updateLoop() {
try {
if (watchState.isLoggedIn) {
@@ -71,7 +75,7 @@ export const useUpdateLoopStore = defineStore('UpdateLoop', () => {
}
if (--state.nextFriendsRefresh <= 0) {
state.nextFriendsRefresh = 3600; // 1hour
friendStore.refreshFriendsList();
runRefreshFriendsListFlow();
authStore.updateStoredUser(userStore.currentUser);
if (
userStore.currentUser.last_activity &&
@@ -141,28 +145,48 @@ export const useUpdateLoopStore = defineStore('UpdateLoop', () => {
}
}
} catch (err) {
friendStore.setRefreshFriendsLoading(false);
friendStore.isRefreshFriendsLoading = false;
console.error(err);
}
workerTimers.setTimeout(() => updateLoop(), 1000);
}
/**
*
* @param value
*/
function setNextClearVRCXCacheCheck(value) {
state.nextClearVRCXCacheCheck = value;
}
/**
*
* @param value
*/
function setNextGroupInstanceRefresh(value) {
state.nextGroupInstanceRefresh = value;
}
/**
*
* @param value
*/
function setNextDiscordUpdate(value) {
state.nextDiscordUpdate = value;
}
/**
*
* @param value
*/
function setIpcTimeout(value) {
state.ipcTimeout = value;
}
/**
*
* @param value
*/
function setNextCurrentUserRefresh(value) {
state.nextCurrentUserRefresh = value;
}
+16 -52
View File
@@ -17,7 +17,6 @@ import {
extractFileId,
findUserByDisplayName,
getAllUserMemos,
getGroupName,
getUserMemo,
getWorldName,
isRealInstance,
@@ -27,22 +26,26 @@ import {
} from '../shared/utils';
import {
avatarRequest,
groupRequest,
instanceRequest,
queryRequest,
userRequest
} from '../api';
import {
runAvatarSwapFlow,
runFirstLoginFlow,
runHomeLocationSyncFlow,
runPostApplySyncFlow
} from '../coordinators/userSessionCoordinator';
import { processBulk, request } from '../service/request';
import { AppDebug } from '../service/appConfig';
import { createUserEventCoordinator } from '../coordinators/userEventCoordinator';
import { createUserSessionCoordinator } from '../coordinators/userSessionCoordinator';
import { database } from '../service/database';
import { patchUserFromEvent } from '../queries';
import { runHandleUserUpdateFlow } from '../coordinators/userEventCoordinator';
import { runUpdateFriendFlow } from '../coordinators/friendPresenceCoordinator';
import { useAppearanceSettingsStore } from './settings/appearance';
import { useAuthStore } from './auth';
import { useAvatarStore } from './avatar';
import { useFavoriteStore } from './favorite';
import { useFeedStore } from './feed';
import { useFriendStore } from './friend';
import { useGameStore } from './game';
import { useGeneralSettingsStore } from './settings/general';
@@ -55,7 +58,6 @@ import { usePhotonStore } from './photon';
import { useSearchStore } from './search';
import { useSharedFeedStore } from './sharedFeed';
import { useUiStore } from './ui';
import { useWorldStore } from './world';
import { watchState } from '../service/watchState';
import * as workerTimers from 'worker-timers';
@@ -73,8 +75,6 @@ export const useUserStore = defineStore('User', () => {
const notificationStore = useNotificationStore();
const authStore = useAuthStore();
const groupStore = useGroupStore();
const feedStore = useFeedStore();
const worldStore = useWorldStore();
const uiStore = useUiStore();
const moderationStore = useModerationStore();
const photonStore = usePhotonStore();
@@ -472,11 +472,11 @@ export const useUserStore = defineStore('User', () => {
{ logLabel: 'User cache cleanup' }
);
cachedUsers.set(ref.id, ref);
friendStore.updateFriend(ref.id);
runUpdateFriendFlow(ref.id);
} else {
if (json.state !== 'online') {
// offline event before GPS to offline location
friendStore.updateFriend(ref.id, json.state);
runUpdateFriendFlow(ref.id, json.state);
}
const {
hasPropChanged: _hasPropChanged,
@@ -584,7 +584,7 @@ export const useUserStore = defineStore('User', () => {
instanceStore.getCurrentInstanceUserList();
}
if (ref.state === 'online') {
friendStore.updateFriend(ref.id, ref.state); // online/offline
runUpdateFriendFlow(ref.id, ref.state); // online/offline
}
favoriteStore.applyFavorite('friend', ref.id);
friendStore.userOnFriend(ref);
@@ -1145,7 +1145,7 @@ export const useUserStore = defineStore('User', () => {
* @returns {Promise<void>}
*/
async function handleUserUpdate(ref, props) {
await userEventCoordinator.runHandleUserUpdateFlow(ref, props);
await runHandleUserUpdateFlow(ref, props);
}
/**
@@ -1421,7 +1421,7 @@ export const useUserStore = defineStore('User', () => {
function applyCurrentUser(json) {
authStore.setAttemptingAutoLogin(false);
let ref = currentUser.value;
userSessionCoordinator.runAvatarSwapFlow({
runAvatarSwapFlow({
json,
ref,
isLoggedIn: watchState.isLoggedIn
@@ -1545,15 +1545,15 @@ export const useUserStore = defineStore('User', () => {
$travelingToLocation: '',
...json
};
userSessionCoordinator.runFirstLoginFlow(ref);
runFirstLoginFlow(ref);
}
ref.$isVRCPlus = ref.tags.includes('system_supporter');
appearanceSettingsStore.applyUserTrustLevel(ref);
applyUserLanguage(ref);
applyPresenceLocation(ref);
userSessionCoordinator.runPostApplySyncFlow(ref);
userSessionCoordinator.runHomeLocationSyncFlow(ref);
runPostApplySyncFlow(ref);
runHomeLocationSyncFlow(ref);
// when isGameRunning use gameLog instead of API
const $location = parseLocation(locationStore.lastLocation.location);
@@ -1744,42 +1744,6 @@ export const useUserStore = defineStore('User', () => {
});
}
const userSessionCoordinator = createUserSessionCoordinator({
avatarStore,
gameStore,
groupStore,
instanceStore,
friendStore,
authStore,
cachedUsers,
currentUser,
userDialog,
getWorldName,
parseLocation,
now: () => Date.now()
});
const userEventCoordinator = createUserEventCoordinator({
friendStore,
state,
parseLocation,
userDialog,
applyUserDialogLocation,
worldStore,
groupStore,
instanceStore,
appDebug: AppDebug,
getWorldName,
getGroupName,
feedStore,
database,
avatarStore,
generalSettingsStore,
checkNote,
now: () => Date.now(),
nowIso: () => new Date().toJSON()
});
return {
state,