diff --git a/src/api/auth.js b/src/api/auth.js
index af3460c3..cca0dc91 100644
--- a/src/api/auth.js
+++ b/src/api/auth.js
@@ -1,5 +1,5 @@
import { request } from '../service/request';
-import { useUserStore } from '../stores';
+import { handleConfig } from '../coordinators/userCoordinator';
const loginReq = {
/**
@@ -63,7 +63,7 @@ const loginReq = {
const args = {
json
};
- useUserStore().handleConfig(args);
+ handleConfig(args);
return args;
});
}
diff --git a/src/api/avatar.js b/src/api/avatar.js
index cf4a3146..e60b2926 100644
--- a/src/api/avatar.js
+++ b/src/api/avatar.js
@@ -1,6 +1,7 @@
import { patchAndRefetchActiveQuery, queryKeys } from '../queries';
import { request } from '../service/request';
import { useUserStore } from '../stores';
+import { applyCurrentUser } from '../coordinators/userCoordinator';
const avatarReq = {
/**
@@ -65,7 +66,7 @@ const avatarReq = {
* @returns {Promise<{json: any, params}>}
*/
selectAvatar(params) {
- const userStore = useUserStore();
+
return request(`avatars/${params.avatarId}/select`, {
method: 'PUT',
params
@@ -74,7 +75,7 @@ const avatarReq = {
json,
params
};
- const ref = userStore.applyCurrentUser(json);
+ const ref = applyCurrentUser(json);
patchAndRefetchActiveQuery({
queryKey: queryKeys.user(ref.id),
nextData: {
@@ -97,7 +98,7 @@ const avatarReq = {
* @returns { Promise<{json: any, params}> }
*/
selectFallbackAvatar(params) {
- const userStore = useUserStore();
+
return request(`avatars/${params.avatarId}/selectfallback`, {
method: 'PUT',
params
@@ -106,7 +107,7 @@ const avatarReq = {
json,
params
};
- const ref = userStore.applyCurrentUser(json);
+ const ref = applyCurrentUser(json);
patchAndRefetchActiveQuery({
queryKey: queryKeys.user(ref.id),
nextData: {
diff --git a/src/api/friend.js b/src/api/friend.js
index ff317b2f..7f9750be 100644
--- a/src/api/friend.js
+++ b/src/api/friend.js
@@ -1,6 +1,7 @@
import { queryClient } from '../queries';
import { request } from '../service/request';
import { useUserStore } from '../stores/user';
+import { applyUser } from '../coordinators/userCoordinator';
/**
*
@@ -22,7 +23,7 @@ const friendReq = {
* @type {import('../types/api/friend').GetFriends}
*/
getFriends(params) {
- const userStore = useUserStore();
+
return request('auth/user/friends', {
method: 'GET',
params
@@ -36,7 +37,7 @@ const friendReq = {
console.error('/friends gave us garbage', user);
continue;
}
- userStore.applyUser(user);
+ applyUser(user);
}
return args;
});
diff --git a/src/api/group.js b/src/api/group.js
index fdbdead5..8e03725c 100644
--- a/src/api/group.js
+++ b/src/api/group.js
@@ -1,4 +1,5 @@
-import { useGroupStore, useUserStore } from '../stores';
+import { useUserStore } from '../stores';
+import { applyGroup } from '../coordinators/groupCoordinator';
import { queryClient } from '../queries';
import { request } from '../service/request';
@@ -89,12 +90,11 @@ const groupReq = {
includeRoles: params.includeRoles || false
}
}).then((json) => {
- const groupStore = useGroupStore();
const args = {
json,
params
};
- args.ref = groupStore.applyGroup(json);
+ args.ref = applyGroup(json);
return args;
});
},
diff --git a/src/api/image.js b/src/api/image.js
index 24321f4b..4d435f34 100644
--- a/src/api/image.js
+++ b/src/api/image.js
@@ -1,4 +1,5 @@
import { useAvatarStore, useWorldStore } from '../stores';
+import { applyWorld } from '../coordinators/worldCoordinator';
import { request } from '../service/request';
const imageReq = {
@@ -267,7 +268,7 @@ const imageReq = {
json,
params
};
- args.ref = worldStore.applyWorld(json);
+ args.ref = applyWorld(json);
return args;
});
},
diff --git a/src/api/user.js b/src/api/user.js
index b59ac367..44bb0a50 100644
--- a/src/api/user.js
+++ b/src/api/user.js
@@ -1,6 +1,7 @@
import { patchAndRefetchActiveQuery, queryKeys } from '../queries';
import { request } from '../service/request';
import { useUserStore } from '../stores';
+import { applyUser, applyCurrentUser } from '../coordinators/userCoordinator';
/**
* @returns {string}
@@ -16,7 +17,7 @@ const userReq = {
* @type {import('../types/api/user').GetUser}
*/
getUser(params) {
- const userStore = useUserStore();
+
return request(`users/${params.userId}`, {
method: 'GET'
}).then((json) => {
@@ -29,7 +30,7 @@ const userReq = {
const args = {
json,
params,
- ref: userStore.applyUser(json)
+ ref: applyUser(json)
};
return args;
});
@@ -56,7 +57,7 @@ const userReq = {
* @returns {Promise<{json: any, params: {tags: string[]}}>}
*/
addUserTags(params) {
- const userStore = useUserStore();
+
return request(`users/${getCurrentUserId()}/addTags`, {
method: 'POST',
params
@@ -65,7 +66,7 @@ const userReq = {
json,
params
};
- userStore.applyCurrentUser(json);
+ applyCurrentUser(json);
return args;
});
},
@@ -75,7 +76,7 @@ const userReq = {
* @returns {Promise<{json: any, params: {tags: string[]}}>}
*/
removeUserTags(params) {
- const userStore = useUserStore();
+
return request(`users/${getCurrentUserId()}/removeTags`, {
method: 'POST',
params
@@ -84,7 +85,7 @@ const userReq = {
json,
params
};
- userStore.applyCurrentUser(json);
+ applyCurrentUser(json);
return args;
});
},
@@ -113,7 +114,7 @@ const userReq = {
* @type {import('../types/api/user').GetCurrentUser}
*/
saveCurrentUser(params) {
- const userStore = useUserStore();
+
return request(`users/${getCurrentUserId()}`, {
method: 'PUT',
params
@@ -121,7 +122,7 @@ const userReq = {
const args = {
json,
params,
- ref: userStore.applyCurrentUser(json)
+ ref: applyCurrentUser(json)
};
patchAndRefetchActiveQuery({
queryKey: queryKeys.user(args.ref.id),
diff --git a/src/api/world.js b/src/api/world.js
index 89189595..4d159ee5 100644
--- a/src/api/world.js
+++ b/src/api/world.js
@@ -1,13 +1,12 @@
import { patchAndRefetchActiveQuery, queryKeys } from '../queries';
import { request } from '../service/request';
-import { useWorldStore } from '../stores';
+import { applyWorld } from '../coordinators/worldCoordinator';
const worldReq = {
/**
* @type {import('../types/api/world').GetWorld}
*/
getWorld(params) {
- const worldStore = useWorldStore();
return request(`worlds/${params.worldId}`, {
method: 'GET'
}).then((json) => {
@@ -15,7 +14,7 @@ const worldReq = {
json,
params
};
- args.ref = worldStore.applyWorld(json);
+ args.ref = applyWorld(json);
return args;
});
},
@@ -24,7 +23,6 @@ const worldReq = {
* @type {import('../types/api/world').GetWorlds}
*/
getWorlds(params, option) {
- const worldStore = useWorldStore();
let endpoint = 'worlds';
if (typeof option !== 'undefined') {
endpoint = `worlds/${option}`;
@@ -39,7 +37,7 @@ const worldReq = {
option
};
for (const json of args.json) {
- worldStore.applyWorld(json);
+ applyWorld(json);
}
return args;
});
@@ -64,7 +62,6 @@ const worldReq = {
* @type {import('../types/api/world').SaveWorld}
*/
saveWorld(params) {
- const worldStore = useWorldStore();
return request(`worlds/${params.id}`, {
method: 'PUT',
params
@@ -73,7 +70,7 @@ const worldReq = {
json,
params
};
- args.ref = worldStore.applyWorld(json);
+ args.ref = applyWorld(json);
patchAndRefetchActiveQuery({
queryKey: queryKeys.world(args.ref.id),
nextData: args
@@ -92,7 +89,6 @@ const worldReq = {
* @returns {Promise<{json: any, params}>}
*/
publishWorld(params) {
- const worldStore = useWorldStore();
return request(`worlds/${params.worldId}/publish`, {
method: 'PUT',
params
@@ -101,7 +97,7 @@ const worldReq = {
json,
params
};
- args.ref = worldStore.applyWorld(json);
+ args.ref = applyWorld(json);
patchAndRefetchActiveQuery({
queryKey: queryKeys.world(args.ref.id),
nextData: args
@@ -120,7 +116,6 @@ const worldReq = {
* @returns {Promise<{json: any, params}>}
*/
unpublishWorld(params) {
- const worldStore = useWorldStore();
return request(`worlds/${params.worldId}/publish`, {
method: 'DELETE',
params
@@ -129,7 +124,7 @@ const worldReq = {
json,
params
};
- args.ref = worldStore.applyWorld(json);
+ args.ref = applyWorld(json);
patchAndRefetchActiveQuery({
queryKey: queryKeys.world(args.ref.id),
nextData: args
diff --git a/src/components/AvatarInfo.vue b/src/components/AvatarInfo.vue
index fe78bc05..02596387 100644
--- a/src/components/AvatarInfo.vue
+++ b/src/components/AvatarInfo.vue
@@ -22,6 +22,7 @@
import { TooltipWrapper } from './ui/tooltip';
import { useAvatarStore } from '../stores';
+ import { getAvatarName, showAvatarAuthorDialog } from '../coordinators/avatarCoordinator';
const { t } = useI18n();
const avatarStore = useAvatarStore();
@@ -54,7 +55,7 @@
ownerId = props.hintownerid;
} else {
try {
- const info = await avatarStore.getAvatarName(props.imageurl);
+ const info = await getAvatarName(props.imageurl);
avatarName.value = info.avatarName;
ownerId = info.ownerId;
} catch {
@@ -77,7 +78,7 @@
const confirm = () => {
if (!props.imageurl) return;
- avatarStore.showAvatarAuthorDialog(props.userid, ownerId, props.imageurl);
+ showAvatarAuthorDialog(props.userid, ownerId, props.imageurl);
};
watch([() => props.imageurl, () => props.userid, () => props.avatartags], parse, { immediate: true });
diff --git a/src/components/DisplayName.vue b/src/components/DisplayName.vue
index 2eea3781..31d9a9bb 100644
--- a/src/components/DisplayName.vue
+++ b/src/components/DisplayName.vue
@@ -1,14 +1,12 @@
- {{ username }}
+ {{ username }}
+
diff --git a/src/components/InstanceActionBar.vue b/src/components/InstanceActionBar.vue
index e6fb259d..4fdb3f00 100644
--- a/src/components/InstanceActionBar.vue
+++ b/src/components/InstanceActionBar.vue
@@ -161,6 +161,7 @@
} from '../stores';
import { checkCanInviteSelf, formatDateFilter, hasGroupPermission, parseLocation } from '../shared/utils';
import { instanceRequest, miscRequest } from '../api';
+ import { showUserDialog } from '../coordinators/userCoordinator';
defineOptions({
inheritAttrs: false
@@ -352,9 +353,7 @@
}
};
- const showUserDialog = (userId) => {
- userStore.showUserDialog(userId);
- };
+
const closeInstance = (location) => {
modalStore
diff --git a/src/components/Location.vue b/src/components/Location.vue
index 6c9363ae..2bac7065 100644
--- a/src/components/Location.vue
+++ b/src/components/Location.vue
@@ -50,12 +50,13 @@
useSearchStore,
useWorldStore
} from '../stores';
+ import { showWorldDialog } from '../coordinators/worldCoordinator';
import { Spinner } from './ui/spinner';
import { accessTypeLocaleKeyMap } from '../shared/constants';
const { t } = useI18n();
- const { cachedWorlds, showWorldDialog } = useWorldStore();
+ const { cachedWorlds } = useWorldStore();
const { showGroupDialog } = useGroupStore();
const { showPreviousInstancesInfoDialog } = useInstanceStore();
const { verifyShortName } = useSearchStore();
diff --git a/src/components/LocationWorld.vue b/src/components/LocationWorld.vue
index 86721799..e1ff62a9 100644
--- a/src/components/LocationWorld.vue
+++ b/src/components/LocationWorld.vue
@@ -5,7 +5,7 @@
{{ accessTypeName }} #{{ instanceName }}
- ({{ groupName }})
+ ({{ groupName }})
@@ -20,6 +20,7 @@
import { useI18n } from 'vue-i18n';
import { useGroupStore, useInstanceStore, useLaunchStore } from '../stores';
+ import { showGroupDialog } from '../coordinators/groupCoordinator';
import { getGroupName, parseLocation } from '../shared/utils';
import { accessTypeLocaleKeyMap } from '../shared/constants';
@@ -135,10 +136,10 @@
/**
*
*/
- function showGroupDialog() {
+ function openLocationGroupDialog() {
if (!location.value) return;
const L = parseLocation(location.value);
if (!L.groupId) return;
- groupStore.showGroupDialog(L.groupId);
+ showGroupDialog(L.groupId);
}
diff --git a/src/components/dialogs/AvatarDialog/AvatarDialog.vue b/src/components/dialogs/AvatarDialog/AvatarDialog.vue
index 65ce0f76..fb735c44 100644
--- a/src/components/dialogs/AvatarDialog/AvatarDialog.vue
+++ b/src/components/dialogs/AvatarDialog/AvatarDialog.vue
@@ -601,20 +601,22 @@
import { formatJsonVars } from '../../../shared/utils/base/ui';
import { handleImageUploadInput } from '../../../shared/utils/imageUpload';
import { runDeleteVRChatCacheFlow as deleteVRChatCache } from '../../../coordinators/gameCoordinator';
+ import { showAvatarDialog, applyAvatar, selectAvatarWithoutConfirmation } from '../../../coordinators/avatarCoordinator';
import { useAvatarDialogCommands } from './useAvatarDialogCommands';
import DialogJsonTab from '../DialogJsonTab.vue';
import ImageCropDialog from '../ImageCropDialog.vue';
+import { showUserDialog } from '../../../coordinators/userCoordinator';
const SetAvatarStylesDialog = defineAsyncComponent(() => import('./SetAvatarStylesDialog.vue'));
const SetAvatarTagsDialog = defineAsyncComponent(() => import('./SetAvatarTagsDialog.vue'));
- const { showUserDialog, sortUserDialogAvatars } = useUserStore();
+ const { sortUserDialogAvatars } = useUserStore();
const { userDialog, currentUser } = storeToRefs(useUserStore());
const avatarStore = useAvatarStore();
const { cachedAvatarModerations, cachedAvatars } = avatarStore;
const { avatarDialog } = storeToRefs(avatarStore);
- const { showAvatarDialog, getAvatarGallery, applyAvatarModeration, applyAvatar, selectAvatarWithoutConfirmation } =
+ const { getAvatarGallery, applyAvatarModeration } =
avatarStore;
const { showFavoriteDialog } = useFavoriteStore();
const { isGameRunning } = storeToRefs(useGameStore());
diff --git a/src/components/dialogs/AvatarDialog/SetAvatarStylesDialog.vue b/src/components/dialogs/AvatarDialog/SetAvatarStylesDialog.vue
index 5f100ff6..dfb91e1e 100644
--- a/src/components/dialogs/AvatarDialog/SetAvatarStylesDialog.vue
+++ b/src/components/dialogs/AvatarDialog/SetAvatarStylesDialog.vue
@@ -93,6 +93,7 @@
import { avatarRequest, queryRequest } from '../../../api';
import { arraysMatch } from '../../../shared/utils';
import { useAvatarStore } from '../../../stores';
+ import { applyAvatar } from '../../../coordinators/avatarCoordinator';
const props = defineProps({
setAvatarStylesDialog: {
@@ -104,7 +105,6 @@
const emit = defineEmits(['update:setAvatarStylesDialog']);
const { t } = useI18n();
- const { applyAvatar } = useAvatarStore();
const SELECT_CLEAR_VALUE = '__clear__';
diff --git a/src/components/dialogs/AvatarDialog/SetAvatarTagsDialog.vue b/src/components/dialogs/AvatarDialog/SetAvatarTagsDialog.vue
index de667867..46aca514 100644
--- a/src/components/dialogs/AvatarDialog/SetAvatarTagsDialog.vue
+++ b/src/components/dialogs/AvatarDialog/SetAvatarTagsDialog.vue
@@ -128,8 +128,8 @@
import { avatarRequest } from '../../../api';
import { removeFromArray } from '../../../shared/utils';
import { useAvatarStore } from '../../../stores';
+ import { showAvatarDialog, applyAvatar } from '../../../coordinators/avatarCoordinator';
- const { showAvatarDialog, applyAvatar } = useAvatarStore();
const { cachedAvatars } = useAvatarStore();
const { t } = useI18n();
diff --git a/src/components/dialogs/GroupDialog/GroupDialog.vue b/src/components/dialogs/GroupDialog/GroupDialog.vue
index f3f7025b..0e1a752c 100644
--- a/src/components/dialogs/GroupDialog/GroupDialog.vue
+++ b/src/components/dialogs/GroupDialog/GroupDialog.vue
@@ -407,6 +407,13 @@
removeFromArray
} from '../../../shared/utils';
import { useGalleryStore, useGroupStore, useModalStore, useUserStore } from '../../../stores';
+ import {
+ getGroupDialogGroup,
+ showGroupDialog,
+ leaveGroupPrompt,
+ setGroupVisibility,
+ setGroupSubscription
+ } from '../../../coordinators/groupCoordinator';
import { groupRequest, queryRequest } from '../../../api';
import { queryKeys, refetchActiveEntityQuery } from '../../../queries';
import { Badge } from '../../ui/badge';
@@ -419,6 +426,7 @@
import GroupDialogPhotosTab from './GroupDialogPhotosTab.vue';
import GroupDialogPostsTab from './GroupDialogPostsTab.vue';
import GroupPostEditDialog from './GroupPostEditDialog.vue';
+import { showUserDialog } from '../../../coordinators/userCoordinator';
const { t } = useI18n();
const groupDialogTabs = computed(() => [
@@ -431,16 +439,11 @@
const modalStore = useModalStore();
- const { showUserDialog } = useUserStore();
+
const { currentUser } = storeToRefs(useUserStore());
const { groupDialog, inviteGroupDialog } = storeToRefs(useGroupStore());
const {
- getGroupDialogGroup,
updateGroupPostSearch,
- showGroupDialog,
- leaveGroupPrompt,
- setGroupVisibility,
- setGroupSubscription,
showGroupMemberModerationDialog
} = useGroupStore();
diff --git a/src/components/dialogs/GroupDialog/GroupDialogInfoTab.vue b/src/components/dialogs/GroupDialog/GroupDialogInfoTab.vue
index 86d44928..14ebb74e 100644
--- a/src/components/dialogs/GroupDialog/GroupDialogInfoTab.vue
+++ b/src/components/dialogs/GroupDialog/GroupDialogInfoTab.vue
@@ -361,6 +361,7 @@
import GroupCalendarEventCard from '../../../views/Tools/components/GroupCalendarEventCard.vue';
import InstanceActionBar from '../../InstanceActionBar.vue';
+import { showUserDialog } from '../../../coordinators/userCoordinator';
const props = defineProps({
showGroupPostEditDialog: {
@@ -375,7 +376,7 @@
const { t } = useI18n();
- const { showUserDialog } = useUserStore();
+
const { groupDialog } = storeToRefs(useGroupStore());
const { lastLocation } = storeToRefs(useLocationStore());
const { showFullscreenImageDialog } = useGalleryStore();
diff --git a/src/components/dialogs/GroupDialog/GroupDialogMembersTab.vue b/src/components/dialogs/GroupDialog/GroupDialogMembersTab.vue
index 0022ffc5..15dd038d 100644
--- a/src/components/dialogs/GroupDialog/GroupDialogMembersTab.vue
+++ b/src/components/dialogs/GroupDialog/GroupDialogMembersTab.vue
@@ -212,15 +212,16 @@
import { downloadAndSaveJson, hasGroupPermission, userImage } from '../../../shared/utils';
import { useGroupStore, useUserStore } from '../../../stores';
+ import { applyGroupMember, handleGroupMember } from '../../../coordinators/groupCoordinator';
import { groupDialogSortingOptions } from '../../../shared/constants';
import { useGroupMembers } from './useGroupMembers';
+import { showUserDialog } from '../../../coordinators/userCoordinator';
const { t } = useI18n();
- const { showUserDialog } = useUserStore();
+
const { currentUser } = storeToRefs(useUserStore());
const { groupDialog } = storeToRefs(useGroupStore());
- const { applyGroupMember, handleGroupMember } = useGroupStore();
const {
isGroupMembersDone,
diff --git a/src/components/dialogs/GroupDialog/GroupMemberModerationDialog.vue b/src/components/dialogs/GroupDialog/GroupMemberModerationDialog.vue
index 1245da20..b21be5e5 100644
--- a/src/components/dialogs/GroupDialog/GroupMemberModerationDialog.vue
+++ b/src/components/dialogs/GroupDialog/GroupMemberModerationDialog.vue
@@ -122,6 +122,7 @@
import { useI18n } from 'vue-i18n';
import { useAppearanceSettingsStore, useGalleryStore, useGroupStore, useUserStore } from '../../../stores';
+ import { applyGroupMember, handleGroupMember, handleGroupMemberProps } from '../../../coordinators/groupCoordinator';
import { hasGroupPermission, userImage, userImageFull } from '../../../shared/utils';
import { groupDialogFilterOptions, groupDialogSortingOptions } from '../../../shared/constants';
import { groupRequest, userRequest } from '../../../api';
@@ -138,14 +139,14 @@
import GroupModerationInvitesTab from './GroupModerationInvitesTab.vue';
import GroupModerationLogsTab from './GroupModerationLogsTab.vue';
import GroupModerationMembersTab from './GroupModerationMembersTab.vue';
+import { showUserDialog } from '../../../coordinators/userCoordinator';
// ── Stores ───────────────────────────────────────────────────
const appearanceSettingsStore = useAppearanceSettingsStore();
const { randomUserColours } = storeToRefs(appearanceSettingsStore);
- const { showUserDialog } = useUserStore();
+
const { currentUser } = storeToRefs(useUserStore());
const { groupDialog, groupMemberModeration } = storeToRefs(useGroupStore());
- const { applyGroupMember, handleGroupMember, handleGroupMemberProps } = useGroupStore();
const { showFullscreenImageDialog } = useGalleryStore();
const { t } = useI18n();
diff --git a/src/components/dialogs/PreviousInstancesDialog/PreviousInstancesInfoDialog.vue b/src/components/dialogs/PreviousInstancesDialog/PreviousInstancesInfoDialog.vue
index 68691b63..44438bc6 100644
--- a/src/components/dialogs/PreviousInstancesDialog/PreviousInstancesInfoDialog.vue
+++ b/src/components/dialogs/PreviousInstancesDialog/PreviousInstancesInfoDialog.vue
@@ -43,8 +43,9 @@
import { createColumns } from './previousInstancesInfoColumns.jsx';
import { database } from '../../../service/database';
import { useVrcxVueTable } from '../../../lib/table/useVrcxVueTable';
+import { lookupUser } from '../../../coordinators/userCoordinator';
- const { lookupUser } = useUserStore();
+
const { previousInstancesInfoDialog, previousInstancesInfoState } = storeToRefs(useInstanceStore());
const { gameLogIsFriend, gameLogIsFavorite } = useGameLogStore();
const { t } = useI18n();
diff --git a/src/components/dialogs/UserDialog/UserDialog.vue b/src/components/dialogs/UserDialog/UserDialog.vue
index 428fb404..c1e15513 100644
--- a/src/components/dialogs/UserDialog/UserDialog.vue
+++ b/src/components/dialogs/UserDialog/UserDialog.vue
@@ -91,6 +91,7 @@
useNotificationStore,
useUserStore
} from '../../../stores';
+ import { showGroupDialog } from '../../../coordinators/groupCoordinator';
import { copyToClipboard } from '../../../shared/utils';
import { formatJsonVars } from '../../../shared/utils/base/ui';
import { miscRequest } from '../../../api';
@@ -139,11 +140,12 @@
const instanceStore = useInstanceStore();
const { userDialog, languageDialog, currentUser, isLocalUserVrcPlusSupporter } = storeToRefs(useUserStore());
- const { cachedUsers, showUserDialog, refreshUserDialogAvatars, showSendBoopDialog } = useUserStore();
+ const { cachedUsers, showSendBoopDialog } = useUserStore();
const { showFavoriteDialog } = useFavoriteStore();
- const { showAvatarDialog, showAvatarAuthorDialog } = useAvatarStore();
+ import { showAvatarDialog, showAvatarAuthorDialog } from '../../../coordinators/avatarCoordinator';
+import { showUserDialog, refreshUserDialogAvatars } from '../../../coordinators/userCoordinator';
- const { showGroupDialog, showModerateGroupDialog } = useGroupStore();
+ const { showModerateGroupDialog } = useGroupStore();
const { inviteGroupDialog } = storeToRefs(useGroupStore());
const { lastLocation, lastLocationDestination } = storeToRefs(useLocationStore());
const { refreshInviteMessageTableData } = useInviteStore();
diff --git a/src/components/dialogs/UserDialog/UserDialogAvatarsTab.vue b/src/components/dialogs/UserDialog/UserDialogAvatarsTab.vue
index da798bd6..7fe1d471 100644
--- a/src/components/dialogs/UserDialog/UserDialogAvatarsTab.vue
+++ b/src/components/dialogs/UserDialog/UserDialogAvatarsTab.vue
@@ -119,7 +119,7 @@
const { userDialog, currentUser } = storeToRefs(userStore);
const { sortUserDialogAvatars, refreshUserDialogAvatars } = userStore;
- const { showAvatarDialog, lookupAvatars } = useAvatarStore();
+ import { showAvatarDialog, lookupAvatars } from '../../../coordinators/avatarCoordinator';
const { cachedAvatars } = useAvatarStore();
const { avatarRemoteDatabase } = storeToRefs(useAdvancedSettingsStore());
diff --git a/src/components/dialogs/UserDialog/UserDialogFavoriteWorldsTab.vue b/src/components/dialogs/UserDialog/UserDialogFavoriteWorldsTab.vue
index 5cd3c16a..6ea82202 100644
--- a/src/components/dialogs/UserDialog/UserDialogFavoriteWorldsTab.vue
+++ b/src/components/dialogs/UserDialog/UserDialogFavoriteWorldsTab.vue
@@ -76,6 +76,7 @@
import DeprecationAlert from '@/components/DeprecationAlert.vue';
import { useFavoriteStore, useUserStore, useWorldStore } from '../../../stores';
+ import { showWorldDialog } from '../../../coordinators/worldCoordinator';
import { handleFavoriteWorldList } from '../../../coordinators/favoriteCoordinator';
import { favoriteRequest } from '../../../api';
@@ -83,7 +84,7 @@
const { userDialog, currentUser } = storeToRefs(useUserStore());
const { favoriteLimits } = storeToRefs(useFavoriteStore());
- const { showWorldDialog } = useWorldStore();
+
const favoriteWorldsTab = ref('0');
const userDialogFavoriteWorldsRequestId = ref(0);
diff --git a/src/components/dialogs/UserDialog/UserDialogGroupsTab.vue b/src/components/dialogs/UserDialog/UserDialogGroupsTab.vue
index c2f584dd..209714b1 100644
--- a/src/components/dialogs/UserDialog/UserDialogGroupsTab.vue
+++ b/src/components/dialogs/UserDialog/UserDialogGroupsTab.vue
@@ -372,15 +372,7 @@
import { useI18n } from 'vue-i18n';
import { useAuthStore, useGroupStore, useUiStore, useUserStore } from '../../../stores';
- import { compareByMemberCount, compareByName } from '../../../shared/utils';
- import { groupRequest } from '../../../api';
- import { useOptionKeySelect } from '../../../composables/useOptionKeySelect';
- import { userDialogGroupSortingOptions } from '../../../shared/constants';
-
- const { t } = useI18n();
-
- const { userDialog, currentUser, isLocalUserVrcPlusSupporter } = storeToRefs(useUserStore());
- const {
+ import {
showGroupDialog,
applyGroup,
saveCurrentUserGroups,
@@ -389,7 +381,15 @@
leaveGroupPrompt,
setGroupVisibility,
handleGroupList
- } = useGroupStore();
+ } from '../../../coordinators/groupCoordinator';
+ import { compareByMemberCount, compareByName } from '../../../shared/utils';
+ import { groupRequest } from '../../../api';
+ import { useOptionKeySelect } from '../../../composables/useOptionKeySelect';
+ import { userDialogGroupSortingOptions } from '../../../shared/constants';
+
+ const { t } = useI18n();
+
+ const { userDialog, currentUser, isLocalUserVrcPlusSupporter } = storeToRefs(useUserStore());
const { currentUserGroups, inGameGroupOrder } = storeToRefs(useGroupStore());
const { cachedConfig } = storeToRefs(useAuthStore());
const { shiftHeld } = storeToRefs(useUiStore());
diff --git a/src/components/dialogs/UserDialog/UserDialogInfoTab.vue b/src/components/dialogs/UserDialog/UserDialogInfoTab.vue
index 3b707881..2043307a 100644
--- a/src/components/dialogs/UserDialog/UserDialogInfoTab.vue
+++ b/src/components/dialogs/UserDialog/UserDialogInfoTab.vue
@@ -501,9 +501,11 @@
useUserStore,
useWorldStore
} from '../../../stores';
+ import { showWorldDialog } from '../../../coordinators/worldCoordinator';
import { queryRequest, userRequest } from '../../../api';
import InstanceActionBar from '../../InstanceActionBar.vue';
+import { showUserDialog } from '../../../coordinators/userCoordinator';
const EditNoteAndMemoDialog = defineAsyncComponent(() => import('./EditNoteAndMemoDialog.vue'));
@@ -518,8 +520,8 @@
const { bioLanguage, translationApi, translationApiType } = storeToRefs(useAdvancedSettingsStore());
const { translateText } = useAdvancedSettingsStore();
const { userDialog, currentUser } = storeToRefs(useUserStore());
- const { showUserDialog, toggleSharedConnectionsOptOut, toggleDiscordFriendsOptOut } = useUserStore();
- const { showWorldDialog } = useWorldStore();
+ const { toggleSharedConnectionsOptOut, toggleDiscordFriendsOptOut } = useUserStore();
+
const { showGroupDialog } = useGroupStore();
const { lastLocation } = storeToRefs(useLocationStore());
const { showFullscreenImageDialog } = useGalleryStore();
diff --git a/src/components/dialogs/UserDialog/UserDialogMutualFriendsTab.vue b/src/components/dialogs/UserDialog/UserDialogMutualFriendsTab.vue
index f6dec69e..852d15a8 100644
--- a/src/components/dialogs/UserDialog/UserDialogMutualFriendsTab.vue
+++ b/src/components/dialogs/UserDialog/UserDialogMutualFriendsTab.vue
@@ -73,12 +73,13 @@
import { useUserStore } from '../../../stores';
import { userDialogMutualFriendSortingOptions } from '../../../shared/constants';
import { userRequest } from '../../../api';
+import { showUserDialog } from '../../../coordinators/userCoordinator';
const { t } = useI18n();
const userStore = useUserStore();
const { userDialog, currentUser } = storeToRefs(userStore);
- const { cachedUsers, showUserDialog } = userStore;
+ const { cachedUsers } = userStore;
const { selectedKey: userDialogMutualFriendSortingKey, selectByKey: setUserDialogMutualFriendSortingByKey } =
useOptionKeySelect(
diff --git a/src/components/dialogs/UserDialog/UserDialogWorldsTab.vue b/src/components/dialogs/UserDialog/UserDialogWorldsTab.vue
index 46b5c4aa..9b655d2f 100644
--- a/src/components/dialogs/UserDialog/UserDialogWorldsTab.vue
+++ b/src/components/dialogs/UserDialog/UserDialogWorldsTab.vue
@@ -86,6 +86,7 @@
import { useI18n } from 'vue-i18n';
import { useUserStore, useWorldStore } from '../../../stores';
+ import { showWorldDialog } from '../../../coordinators/worldCoordinator';
import { userDialogWorldOrderOptions, userDialogWorldSortingOptions } from '../../../shared/constants/';
import { queryRequest } from '../../../api';
import { useOptionKeySelect } from '../../../composables/useOptionKeySelect';
@@ -94,7 +95,7 @@
const userStore = useUserStore();
const { userDialog, currentUser } = storeToRefs(userStore);
- const { cachedWorlds, showWorldDialog } = useWorldStore();
+ const { cachedWorlds } = useWorldStore();
const userDialogWorldsRequestId = ref(0);
diff --git a/src/components/dialogs/WorldDialog/SetWorldTagsDialog.vue b/src/components/dialogs/WorldDialog/SetWorldTagsDialog.vue
index 06dcdb7d..f899b0fe 100644
--- a/src/components/dialogs/WorldDialog/SetWorldTagsDialog.vue
+++ b/src/components/dialogs/WorldDialog/SetWorldTagsDialog.vue
@@ -110,6 +110,7 @@
import { useI18n } from 'vue-i18n';
import { useWorldStore } from '../../../stores';
+ import { showWorldDialog } from '../../../coordinators/worldCoordinator';
import { worldRequest } from '../../../api';
const props = defineProps({
@@ -133,7 +134,7 @@
const emit = defineEmits(['update:isSetWorldTagsDialogVisible']);
- const { showWorldDialog } = useWorldStore();
+
const { t } = useI18n();
diff --git a/src/components/dialogs/WorldDialog/WorldDialog.vue b/src/components/dialogs/WorldDialog/WorldDialog.vue
index 1c3cbabf..168b57cb 100644
--- a/src/components/dialogs/WorldDialog/WorldDialog.vue
+++ b/src/components/dialogs/WorldDialog/WorldDialog.vue
@@ -395,6 +395,7 @@
useUserStore,
useWorldStore
} from '../../../stores';
+ import { showWorldDialog } from '../../../coordinators/worldCoordinator';
import {
DropdownMenu,
DropdownMenuContent,
@@ -412,15 +413,16 @@
import ImageCropDialog from '../ImageCropDialog.vue';
import WorldDialogInfoTab from './WorldDialogInfoTab.vue';
import WorldDialogInstancesTab from './WorldDialogInstancesTab.vue';
+import { showUserDialog } from '../../../coordinators/userCoordinator';
const SetWorldTagsDialog = defineAsyncComponent(() => import('./SetWorldTagsDialog.vue'));
const WorldAllowedDomainsDialog = defineAsyncComponent(() => import('./WorldAllowedDomainsDialog.vue'));
const NewInstanceDialog = defineAsyncComponent(() => import('../NewInstanceDialog.vue'));
- const { showUserDialog } = useUserStore();
+
const { currentUser, userDialog } = storeToRefs(useUserStore());
const { worldDialog } = storeToRefs(useWorldStore());
- const { cachedWorlds, showWorldDialog } = useWorldStore();
+ const { cachedWorlds } = useWorldStore();
const { lastLocation } = storeToRefs(useLocationStore());
const { canOpenInstanceInGame } = useInviteStore();
const { showFavoriteDialog } = useFavoriteStore();
diff --git a/src/components/dialogs/WorldDialog/WorldDialogInstancesTab.vue b/src/components/dialogs/WorldDialog/WorldDialogInstancesTab.vue
index a304f94f..426cc337 100644
--- a/src/components/dialogs/WorldDialog/WorldDialogInstancesTab.vue
+++ b/src/components/dialogs/WorldDialog/WorldDialogInstancesTab.vue
@@ -118,11 +118,12 @@
} from '../../../stores';
import InstanceActionBar from '../../InstanceActionBar.vue';
+import { showUserDialog } from '../../../coordinators/userCoordinator';
const { t } = useI18n();
const { isAgeGatedInstancesVisible } = storeToRefs(useAppearanceSettingsStore());
- const { showUserDialog } = useUserStore();
+
const { currentUser } = storeToRefs(useUserStore());
const { worldDialog } = storeToRefs(useWorldStore());
const { lastLocation } = storeToRefs(useLocationStore());
diff --git a/src/coordinators/authAutoLoginCoordinator.js b/src/coordinators/authAutoLoginCoordinator.js
index b97acfa9..af866948 100644
--- a/src/coordinators/authAutoLoginCoordinator.js
+++ b/src/coordinators/authAutoLoginCoordinator.js
@@ -1,5 +1,5 @@
import { toast } from 'vue-sonner';
-import { useI18n } from 'vue-i18n';
+import { i18n } from '../plugin/i18n';
import { AppDebug } from '../service/appConfig';
import { useAdvancedSettingsStore } from '../stores/settings/advanced';
@@ -17,7 +17,7 @@ export async function runHandleAutoLoginFlow({
} = {}) {
const authStore = useAuthStore();
const advancedSettingsStore = useAdvancedSettingsStore();
- const { t } = useI18n();
+ const t = i18n.global.t;
if (authStore.attemptingAutoLogin) {
return;
diff --git a/src/coordinators/authCoordinator.js b/src/coordinators/authCoordinator.js
index b229902c..285dedf9 100644
--- a/src/coordinators/authCoordinator.js
+++ b/src/coordinators/authCoordinator.js
@@ -1,4 +1,4 @@
-import { useI18n } from 'vue-i18n';
+import { i18n } from '../plugin/i18n';
import Noty from 'noty';
@@ -9,6 +9,7 @@ import { useAuthStore } from '../stores/auth';
import { useNotificationStore } from '../stores/notification';
import { useUpdateLoopStore } from '../stores/updateLoop';
import { useUserStore } from '../stores/user';
+import { applyCurrentUser } from './userCoordinator';
import { watchState } from '../service/watchState';
import configRepository from '../service/config';
@@ -21,7 +22,7 @@ export async function runLogoutFlow() {
const authStore = useAuthStore();
const userStore = useUserStore();
const notificationStore = useNotificationStore();
- const { t } = useI18n();
+ const t = i18n.global.t;
if (watchState.isLoggedIn) {
new Noty({
@@ -56,6 +57,6 @@ export function runLoginSuccessFlow(json) {
const userStore = useUserStore();
updateLoopStore.setNextCurrentUserRefresh(420); // 7mins
- userStore.applyCurrentUser(json);
+ applyCurrentUser(json);
initWebsocket();
}
diff --git a/src/coordinators/avatarCoordinator.js b/src/coordinators/avatarCoordinator.js
new file mode 100644
index 00000000..77f48203
--- /dev/null
+++ b/src/coordinators/avatarCoordinator.js
@@ -0,0 +1,649 @@
+import { nextTick } from 'vue';
+import { toast } from 'vue-sonner';
+import { i18n } from '../plugin/i18n';
+
+import {
+ createDefaultAvatarRef,
+ extractFileId,
+ getAvailablePlatforms,
+ getBundleDateSize,
+ getPlatformInfo,
+ replaceBioSymbols,
+ sanitizeEntityJson,
+ storeAvatarImage
+} from '../shared/utils';
+import { avatarRequest, miscRequest, queryRequest } from '../api';
+import { AppDebug } from '../service/appConfig';
+import { database } from '../service/database';
+import { patchAvatarFromEvent } from '../queries';
+import { processBulk } from '../service/request';
+import { applyFavorite } from './favoriteCoordinator';
+import { refreshUserDialogAvatars, showUserDialog } from './userCoordinator';
+import { useAdvancedSettingsStore } from '../stores/settings/advanced';
+import { useAvatarProviderStore } from '../stores/avatarProvider';
+import { useAvatarStore } from '../stores/avatar';
+import { useFavoriteStore } from '../stores/favorite';
+import { useModalStore } from '../stores/modal';
+import { useUiStore } from '../stores/ui';
+import { useUserStore } from '../stores/user';
+import { useVRCXUpdaterStore } from '../stores/vrcxUpdater';
+
+import webApiService from '../service/webapi';
+
+/**
+ * @param {object} json
+ * @returns {object} ref
+ */
+export function applyAvatar(json) {
+ const avatarStore = useAvatarStore();
+ const favoriteStore = useFavoriteStore();
+
+ sanitizeEntityJson(json, ['name', 'description']);
+ let ref = avatarStore.cachedAvatars.get(json.id);
+ if (typeof ref === 'undefined') {
+ ref = createDefaultAvatarRef(json);
+ avatarStore.cachedAvatars.set(ref.id, ref);
+ } else {
+ const { unityPackages } = ref;
+ Object.assign(ref, json);
+ if (
+ json.unityPackages?.length > 0 &&
+ unityPackages.length > 0 &&
+ !json.unityPackages[0].assetUrl
+ ) {
+ ref.unityPackages = unityPackages;
+ }
+ }
+ for (const listing of ref.publishedListings) {
+ listing.displayName = replaceBioSymbols(listing.displayName);
+ listing.description = replaceBioSymbols(listing.description);
+ }
+ applyFavorite('avatar', ref.id);
+ if (favoriteStore.localAvatarFavoritesList.includes(ref.id)) {
+ const avatarRef = ref;
+ favoriteStore.syncLocalAvatarFavoriteRef(avatarRef);
+
+ // update db cache
+ database.addAvatarToCache(avatarRef);
+ }
+ patchAvatarFromEvent(ref);
+ return ref;
+}
+
+/**
+ *
+ * @param {string} avatarId
+ * @param options
+ * @returns
+ */
+export function showAvatarDialog(avatarId, options = {}) {
+ const avatarStore = useAvatarStore();
+ const uiStore = useUiStore();
+ const favoriteStore = useFavoriteStore();
+ const userStore = useUserStore();
+ const t = i18n.global.t;
+
+ const D = avatarStore.avatarDialog;
+ const forceRefresh = Boolean(options?.forceRefresh);
+ const isMainDialogOpen = uiStore.openDialog({
+ type: 'avatar',
+ id: avatarId
+ });
+ D.visible = true;
+ if (isMainDialogOpen && D.id === avatarId && !forceRefresh) {
+ uiStore.setDialogCrumbLabel('avatar', D.id, D.ref?.name || D.id);
+ nextTick(() => (D.loading = false));
+ return;
+ }
+ D.loading = true;
+ D.id = avatarId;
+ D.inCache = false;
+ D.cacheSize = '';
+ D.cacheLocked = false;
+ D.cachePath = '';
+ D.fileAnalysis = {};
+ D.isQuestFallback = false;
+ D.isPC = false;
+ D.isQuest = false;
+ D.isIos = false;
+ D.hasImposter = false;
+ D.imposterVersion = '';
+ D.platformInfo = {};
+ D.galleryImages = [];
+ D.galleryLoading = true;
+ D.isFavorite =
+ favoriteStore.getCachedFavoritesByObjectId(avatarId) ||
+ (userStore.isLocalUserVrcPlusSupporter &&
+ favoriteStore.localAvatarFavoritesList.includes(avatarId));
+ D.isBlocked = avatarStore.cachedAvatarModerations.has(avatarId);
+ const ref2 = avatarStore.cachedAvatars.get(avatarId);
+ if (typeof ref2 !== 'undefined') {
+ D.ref = ref2;
+ uiStore.setDialogCrumbLabel('avatar', D.id, D.ref?.name || D.id);
+ nextTick(() => (D.loading = false));
+ }
+ const loadAvatarRequest = forceRefresh
+ ? avatarRequest.getAvatar({ avatarId })
+ : queryRequest.fetch('avatar', { avatarId });
+ loadAvatarRequest
+ .then((args) => {
+ const ref = applyAvatar(args.json);
+ D.ref = ref;
+ uiStore.setDialogCrumbLabel(
+ 'avatar',
+ D.id,
+ D.ref?.name || D.id
+ );
+ avatarStore.getAvatarGallery(avatarId);
+ avatarStore.updateVRChatAvatarCache();
+ if (/quest/.test(ref.tags)) {
+ D.isQuestFallback = true;
+ }
+ const { isPC, isQuest, isIos } = getAvailablePlatforms(
+ ref.unityPackages
+ );
+ D.isPC = isPC;
+ D.isQuest = isQuest;
+ D.isIos = isIos;
+ D.platformInfo = getPlatformInfo(ref.unityPackages);
+ for (let i = ref.unityPackages.length - 1; i > -1; i--) {
+ const unityPackage = ref.unityPackages[i];
+ if (unityPackage.variant === 'impostor') {
+ D.hasImposter = true;
+ D.imposterVersion = unityPackage.impostorizerVersion;
+ break;
+ }
+ }
+ if (Object.keys(D.fileAnalysis).length === 0) {
+ getBundleDateSize(ref);
+ }
+ })
+ .catch((err) => {
+ D.loading = false;
+ D.id = null;
+ D.visible = false;
+ uiStore.jumpBackDialogCrumb();
+ toast.error(t('message.api_handler.avatar_private_or_deleted'));
+ throw err;
+ })
+ .finally(() => {
+ nextTick(() => (D.loading = false));
+ });
+}
+
+/**
+ *
+ * @returns {Promise}
+ */
+export async function getAvatarHistory() {
+ const avatarStore = useAvatarStore();
+ const userStore = useUserStore();
+
+ const historyArray = await database.getAvatarHistory(
+ userStore.currentUser.id
+ );
+ for (let i = 0; i < historyArray.length; i++) {
+ const avatar = historyArray[i];
+ if (avatar.authorId === userStore.currentUser.id) {
+ continue;
+ }
+ applyAvatar(avatar);
+ }
+ avatarStore.avatarHistory = historyArray;
+}
+
+/**
+ * @param {string} avatarId
+ */
+export function addAvatarToHistory(avatarId) {
+ const avatarStore = useAvatarStore();
+ const userStore = useUserStore();
+
+ avatarRequest
+ .getAvatar({ avatarId })
+ .then((args) => {
+ const ref = applyAvatar(args.json);
+
+ database.addAvatarToCache(ref);
+ database.addAvatarToHistory(ref.id);
+
+ if (ref.authorId === userStore.currentUser.id) {
+ return;
+ }
+
+ const historyArray = avatarStore.avatarHistory;
+ for (let i = 0; i < historyArray.length; ++i) {
+ if (historyArray[i].id === ref.id) {
+ historyArray.splice(i, 1);
+ }
+ }
+
+ avatarStore.avatarHistory.unshift(ref);
+ })
+ .catch((err) => {
+ console.error('Failed to add avatar to history:', err);
+ });
+}
+
+/**
+ *
+ */
+export function promptClearAvatarHistory() {
+ const avatarStore = useAvatarStore();
+ const modalStore = useModalStore();
+ const t = i18n.global.t;
+
+ modalStore
+ .confirm({
+ description: t('confirm.clear_avatar_history'),
+ title: 'Confirm'
+ })
+ .then(({ ok }) => {
+ if (!ok) return;
+ avatarStore.clearAvatarHistory();
+ })
+ .catch(() => {});
+}
+
+/**
+ *
+ * @param {string} imageUrl
+ * @returns {Promise