mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-14 20:33:52 +02:00
refactor store
This commit is contained in:
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
},
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
},
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
<template>
|
||||
<span @click="showUserDialog" class="cursor-pointer">{{ username }}</span>
|
||||
<span @click="openUserDialog" class="cursor-pointer">{{ username }}</span>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
import { queryRequest } from '../api';
|
||||
import { useUserStore } from '../stores';
|
||||
|
||||
const userStore = useUserStore();
|
||||
import { showUserDialog } from '../coordinators/userCoordinator';
|
||||
|
||||
const props = defineProps({
|
||||
userid: String,
|
||||
@@ -40,9 +38,10 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function showUserDialog() {
|
||||
userStore.showUserDialog(props.userid);
|
||||
function openUserDialog() {
|
||||
showUserDialog(props.userid);
|
||||
}
|
||||
|
||||
watch([() => props.userid, () => props.location, () => props.forceUpdateKey], parse, { immediate: true });
|
||||
</script>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<Unlock v-if="isUnlocked" :class="['inline-block', 'mr-1.25']" />
|
||||
<span> {{ accessTypeName }} #{{ instanceName }}</span>
|
||||
</span>
|
||||
<span v-if="groupName" @click="showGroupDialog" class="cursor-pointer">({{ groupName }})</span>
|
||||
<span v-if="groupName" @click="openLocationGroupDialog" class="cursor-pointer">({{ groupName }})</span>
|
||||
<TooltipWrapper v-if="isClosed" :content="t('dialog.user.info.instance_closed')">
|
||||
<AlertTriangle :class="['inline-block', 'ml-1']" style="color: lightcoral" />
|
||||
</TooltipWrapper>
|
||||
@@ -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);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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__';
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
649
src/coordinators/avatarCoordinator.js
Normal file
649
src/coordinators/avatarCoordinator.js
Normal file
@@ -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<void>}
|
||||
*/
|
||||
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<object>}
|
||||
*/
|
||||
export async function getAvatarName(imageUrl) {
|
||||
const avatarStore = useAvatarStore();
|
||||
|
||||
const fileId = extractFileId(imageUrl);
|
||||
if (!fileId) {
|
||||
return {
|
||||
ownerId: '',
|
||||
avatarName: '-'
|
||||
};
|
||||
}
|
||||
if (avatarStore.cachedAvatarNames.has(fileId)) {
|
||||
return avatarStore.cachedAvatarNames.get(fileId);
|
||||
}
|
||||
try {
|
||||
const args = await miscRequest.getFile({ fileId });
|
||||
return storeAvatarImage(args, avatarStore.cachedAvatarNames);
|
||||
} catch (error) {
|
||||
console.error('Failed to get avatar images:', error);
|
||||
return {
|
||||
ownerId: '',
|
||||
avatarName: '-'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param type
|
||||
* @param search
|
||||
*/
|
||||
export async function lookupAvatars(type, search) {
|
||||
const avatarProviderStore = useAvatarProviderStore();
|
||||
const vrcxUpdaterStore = useVRCXUpdaterStore();
|
||||
|
||||
const avatars = new Map();
|
||||
if (type === 'search') {
|
||||
try {
|
||||
const url = `${
|
||||
avatarProviderStore.avatarRemoteDatabaseProvider
|
||||
}?${type}=${encodeURIComponent(search)}&n=5000`;
|
||||
const response = await webApiService.execute({
|
||||
url,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Referer: 'https://vrcx.app',
|
||||
'VRCX-ID': vrcxUpdaterStore.vrcxId
|
||||
}
|
||||
});
|
||||
const json = JSON.parse(response.data);
|
||||
if (AppDebug.debugWebRequests) {
|
||||
console.log(url, json, response);
|
||||
}
|
||||
if (response.status === 200 && typeof json === 'object') {
|
||||
json.forEach((avatar) => {
|
||||
if (!avatars.has(avatar.Id)) {
|
||||
const ref = {
|
||||
authorId: '',
|
||||
authorName: '',
|
||||
name: '',
|
||||
description: '',
|
||||
id: '',
|
||||
imageUrl: '',
|
||||
thumbnailImageUrl: '',
|
||||
created_at: '0001-01-01T00:00:00.0000000Z',
|
||||
updated_at: '0001-01-01T00:00:00.0000000Z',
|
||||
releaseStatus: 'public',
|
||||
...avatar
|
||||
};
|
||||
avatars.set(ref.id, ref);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw new Error(`Error: ${response.data}`);
|
||||
}
|
||||
} catch (err) {
|
||||
const msg = `Avatar search failed for ${search} with ${avatarProviderStore.avatarRemoteDatabaseProvider}\n${err}`;
|
||||
console.error(msg);
|
||||
toast.error(msg);
|
||||
}
|
||||
} else if (type === 'authorId') {
|
||||
const length =
|
||||
avatarProviderStore.avatarRemoteDatabaseProviderList.length;
|
||||
for (let i = 0; i < length; ++i) {
|
||||
const url =
|
||||
avatarProviderStore.avatarRemoteDatabaseProviderList[i];
|
||||
const avatarArray = await lookupAvatarsByAuthor(url, search);
|
||||
avatarArray.forEach((avatar) => {
|
||||
if (!avatars.has(avatar.id)) {
|
||||
avatars.set(avatar.id, avatar);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return avatars;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param authorId
|
||||
* @param fileId
|
||||
*/
|
||||
export async function lookupAvatarByImageFileId(authorId, fileId) {
|
||||
const avatarProviderStore = useAvatarProviderStore();
|
||||
|
||||
for (const providerUrl of avatarProviderStore.avatarRemoteDatabaseProviderList) {
|
||||
const avatar = await lookupAvatarByFileId(providerUrl, fileId);
|
||||
if (avatar?.id) {
|
||||
return avatar.id;
|
||||
}
|
||||
}
|
||||
|
||||
for (const providerUrl of avatarProviderStore.avatarRemoteDatabaseProviderList) {
|
||||
const avatarArray = await lookupAvatarsByAuthor(
|
||||
providerUrl,
|
||||
authorId
|
||||
);
|
||||
for (const avatar of avatarArray) {
|
||||
if (extractFileId(avatar.imageUrl) === fileId) {
|
||||
return avatar.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param providerUrl
|
||||
* @param fileId
|
||||
*/
|
||||
async function lookupAvatarByFileId(providerUrl, fileId) {
|
||||
const vrcxUpdaterStore = useVRCXUpdaterStore();
|
||||
|
||||
try {
|
||||
const url = `${providerUrl}?fileId=${encodeURIComponent(fileId)}`;
|
||||
const response = await webApiService.execute({
|
||||
url,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Referer: 'https://vrcx.app',
|
||||
'VRCX-ID': vrcxUpdaterStore.vrcxId
|
||||
}
|
||||
});
|
||||
const json = JSON.parse(response.data);
|
||||
if (AppDebug.debugWebRequests) {
|
||||
console.log(url, json, response);
|
||||
}
|
||||
if (response.status === 200 && typeof json === 'object') {
|
||||
const ref = {
|
||||
authorId: '',
|
||||
authorName: '',
|
||||
name: '',
|
||||
description: '',
|
||||
id: '',
|
||||
imageUrl: '',
|
||||
thumbnailImageUrl: '',
|
||||
created_at: '0001-01-01T00:00:00.0000000Z',
|
||||
updated_at: '0001-01-01T00:00:00.0000000Z',
|
||||
releaseStatus: 'public',
|
||||
...json
|
||||
};
|
||||
return ref;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (err) {
|
||||
// ignore errors for now, not all providers support this lookup type
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param providerUrl
|
||||
* @param authorId
|
||||
*/
|
||||
async function lookupAvatarsByAuthor(providerUrl, authorId) {
|
||||
const vrcxUpdaterStore = useVRCXUpdaterStore();
|
||||
|
||||
const avatars = [];
|
||||
if (!providerUrl || !authorId) {
|
||||
return avatars;
|
||||
}
|
||||
const url = `${providerUrl}?authorId=${encodeURIComponent(authorId)}`;
|
||||
try {
|
||||
const response = await webApiService.execute({
|
||||
url,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Referer: 'https://vrcx.app',
|
||||
'VRCX-ID': vrcxUpdaterStore.vrcxId
|
||||
}
|
||||
});
|
||||
const json = JSON.parse(response.data);
|
||||
if (AppDebug.debugWebRequests) {
|
||||
console.log(url, json, response);
|
||||
}
|
||||
if (response.status === 200 && typeof json === 'object') {
|
||||
json.forEach((avatar) => {
|
||||
const ref = {
|
||||
authorId: '',
|
||||
authorName: '',
|
||||
name: '',
|
||||
description: '',
|
||||
id: '',
|
||||
imageUrl: '',
|
||||
thumbnailImageUrl: '',
|
||||
created_at: '0001-01-01T00:00:00.0000000Z',
|
||||
updated_at: '0001-01-01T00:00:00.0000000Z',
|
||||
releaseStatus: 'public',
|
||||
...avatar
|
||||
};
|
||||
avatars.push(ref);
|
||||
});
|
||||
} else {
|
||||
throw new Error(`Error: ${response.data}`);
|
||||
}
|
||||
} catch (err) {
|
||||
const msg = `Avatar lookup failed for ${authorId} with ${url}\n${err}`;
|
||||
console.error(msg);
|
||||
toast.error(msg);
|
||||
}
|
||||
return avatars;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param id
|
||||
*/
|
||||
export function selectAvatarWithConfirmation(id) {
|
||||
const modalStore = useModalStore();
|
||||
const t = i18n.global.t;
|
||||
|
||||
modalStore
|
||||
.confirm({
|
||||
description: t('confirm.select_avatar'),
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
selectAvatarWithoutConfirmation(id);
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param id
|
||||
*/
|
||||
export async function selectAvatarWithoutConfirmation(id) {
|
||||
const userStore = useUserStore();
|
||||
|
||||
if (userStore.currentUser.currentAvatar === id) {
|
||||
toast.info('Avatar already selected');
|
||||
return;
|
||||
}
|
||||
return avatarRequest
|
||||
.selectAvatar({
|
||||
avatarId: id
|
||||
})
|
||||
.then(() => {
|
||||
toast.success('Avatar changed');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param fileId
|
||||
*/
|
||||
export function checkAvatarCache(fileId) {
|
||||
const avatarStore = useAvatarStore();
|
||||
|
||||
let avatarId = '';
|
||||
for (let ref of avatarStore.cachedAvatars.values()) {
|
||||
if (extractFileId(ref.imageUrl) === fileId) {
|
||||
avatarId = ref.id;
|
||||
}
|
||||
}
|
||||
return avatarId;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param fileId
|
||||
* @param ownerUserId
|
||||
*/
|
||||
export async function checkAvatarCacheRemote(fileId, ownerUserId) {
|
||||
const advancedSettingsStore = useAdvancedSettingsStore();
|
||||
const avatarStore = useAvatarStore();
|
||||
const t = i18n.global.t;
|
||||
|
||||
if (advancedSettingsStore.avatarRemoteDatabase) {
|
||||
try {
|
||||
toast.dismiss(avatarStore.loadingToastId);
|
||||
avatarStore.loadingToastId = toast.loading(
|
||||
t('message.avatar_lookup.loading')
|
||||
);
|
||||
const avatarId = await lookupAvatarByImageFileId(
|
||||
ownerUserId,
|
||||
fileId
|
||||
);
|
||||
return avatarId;
|
||||
} catch (err) {
|
||||
console.error('Failed to lookup avatar by image file id:', err);
|
||||
} finally {
|
||||
toast.dismiss(avatarStore.loadingToastId);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param refUserId
|
||||
* @param ownerUserId
|
||||
* @param currentAvatarImageUrl
|
||||
*/
|
||||
export async function showAvatarAuthorDialog(
|
||||
refUserId,
|
||||
ownerUserId,
|
||||
currentAvatarImageUrl
|
||||
) {
|
||||
const userStore = useUserStore();
|
||||
const t = i18n.global.t;
|
||||
|
||||
const fileId = extractFileId(currentAvatarImageUrl);
|
||||
if (!fileId) {
|
||||
toast.error(t('message.avatar_lookup.failed'));
|
||||
} else if (refUserId === userStore.currentUser.id) {
|
||||
showAvatarDialog(userStore.currentUser.currentAvatar);
|
||||
} else {
|
||||
let avatarId = checkAvatarCache(fileId);
|
||||
let avatarInfo;
|
||||
if (!avatarId) {
|
||||
avatarInfo = await getAvatarName(currentAvatarImageUrl);
|
||||
if (avatarInfo.ownerId === userStore.currentUser.id) {
|
||||
await refreshUserDialogAvatars(fileId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!avatarId) {
|
||||
avatarId = await checkAvatarCacheRemote(fileId, ownerUserId);
|
||||
}
|
||||
if (!avatarId) {
|
||||
if (ownerUserId === refUserId) {
|
||||
toast.warning(
|
||||
t('message.avatar_lookup.private_or_not_found')
|
||||
);
|
||||
} else {
|
||||
toast.warning(t('message.avatar_lookup.not_found'));
|
||||
showUserDialog(avatarInfo.ownerId);
|
||||
}
|
||||
}
|
||||
if (avatarId) {
|
||||
showAvatarDialog(avatarId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param avatarId
|
||||
*/
|
||||
export function addAvatarWearTime(avatarId) {
|
||||
const userStore = useUserStore();
|
||||
|
||||
if (!userStore.currentUser.$previousAvatarSwapTime || !avatarId) {
|
||||
return;
|
||||
}
|
||||
const timeSpent =
|
||||
Date.now() - userStore.currentUser.$previousAvatarSwapTime;
|
||||
database.addAvatarTimeSpent(avatarId, timeSpent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Preload all own avatars into cache at startup for global search.
|
||||
*/
|
||||
export async function preloadOwnAvatars() {
|
||||
const params = {
|
||||
n: 50,
|
||||
offset: 0,
|
||||
sort: 'updated',
|
||||
order: 'descending',
|
||||
releaseStatus: 'all',
|
||||
user: 'me'
|
||||
};
|
||||
await processBulk({
|
||||
fn: avatarRequest.getAvatars,
|
||||
N: -1,
|
||||
params,
|
||||
handle: (args) => {
|
||||
for (const json of args.json) {
|
||||
applyAvatar(json);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2,10 +2,12 @@ import { toast } from 'vue-sonner';
|
||||
import { useFavoriteStore } from '../stores/favorite';
|
||||
import { useAppearanceSettingsStore } from '../stores/settings/appearance';
|
||||
import { useAvatarStore } from '../stores/avatar';
|
||||
import { applyAvatar } from './avatarCoordinator';
|
||||
import { useFriendStore } from '../stores/friend';
|
||||
import { useGeneralSettingsStore } from '../stores/settings/general';
|
||||
import { useUserStore } from '../stores/user';
|
||||
import { useWorldStore } from '../stores/world';
|
||||
import { applyWorld } from './worldCoordinator';
|
||||
import { runUpdateFriendFlow } from './friendPresenceCoordinator';
|
||||
import { avatarRequest, favoriteRequest, queryRequest } from '../api';
|
||||
import { database } from '../service/database';
|
||||
@@ -177,7 +179,7 @@ export function handleFavoriteWorldList(args) {
|
||||
if (json.id === '???') {
|
||||
continue;
|
||||
}
|
||||
worldStore.applyWorld(json);
|
||||
applyWorld(json);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +193,7 @@ export function handleFavoriteAvatarList(args) {
|
||||
if (json.releaseStatus === 'hidden') {
|
||||
continue;
|
||||
}
|
||||
avatarStore.applyAvatar(json);
|
||||
applyAvatar(json);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -652,7 +654,7 @@ export async function getLocalWorldFavorites() {
|
||||
for (let i = 0; i < worldCache.length; ++i) {
|
||||
const ref = worldCache[i];
|
||||
if (!worldStore.cachedWorlds.has(ref.id)) {
|
||||
worldStore.applyWorld(ref);
|
||||
applyWorld(ref);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -909,7 +911,7 @@ export async function getLocalAvatarFavorites() {
|
||||
for (let i = 0; i < avatarCache.length; ++i) {
|
||||
const ref = avatarCache[i];
|
||||
if (!avatarStore.cachedAvatars.has(ref.id)) {
|
||||
avatarStore.applyAvatar(ref);
|
||||
applyAvatar(ref);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useAuthStore } from '../stores/auth';
|
||||
import { useFriendStore } from '../stores/friend';
|
||||
import { useUpdateLoopStore } from '../stores/updateLoop';
|
||||
import { useUserStore } from '../stores/user';
|
||||
import { getCurrentUser } from './userCoordinator';
|
||||
import { watchState } from '../service/watchState';
|
||||
|
||||
import configRepository from '../service/config';
|
||||
@@ -21,7 +22,7 @@ export async function runRefreshFriendsListFlow() {
|
||||
|
||||
// If we just got user less then 2 min before code call, don't call it again
|
||||
if (updateLoopStore.nextCurrentUserRefresh < 300) {
|
||||
await userStore.getCurrentUser();
|
||||
await getCurrentUser();
|
||||
}
|
||||
await friendStore.refreshFriends();
|
||||
reconnectWebSocket();
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
import { database } from '../service/database';
|
||||
import { useAdvancedSettingsStore } from '../stores/settings/advanced';
|
||||
import { useAvatarStore } from '../stores/avatar';
|
||||
import { addAvatarWearTime } from './avatarCoordinator';
|
||||
import { useGameLogStore } from '../stores/gameLog';
|
||||
import { useGameStore } from '../stores/game';
|
||||
import { useInstanceStore } from '../stores/instance';
|
||||
@@ -47,7 +48,7 @@ export async function runGameRunningChangedFlow(isGameRunning) {
|
||||
runAutoVRChatCacheManagementFlow();
|
||||
runCheckIfGameCrashedFlow();
|
||||
updateLoopStore.setIpcTimeout(0);
|
||||
avatarStore.addAvatarWearTime(userStore.currentUser.currentAvatar);
|
||||
addAvatarWearTime(userStore.currentUser.currentAvatar);
|
||||
}
|
||||
|
||||
runLastLocationResetFlow();
|
||||
|
||||
957
src/coordinators/groupCoordinator.js
Normal file
957
src/coordinators/groupCoordinator.js
Normal file
@@ -0,0 +1,957 @@
|
||||
import { nextTick } from 'vue';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { i18n } from '../plugin/i18n';
|
||||
|
||||
import {
|
||||
convertFileUrlToImageUrl,
|
||||
createDefaultGroupRef,
|
||||
sanitizeEntityJson,
|
||||
replaceBioSymbols
|
||||
} from '../shared/utils';
|
||||
import { groupRequest, instanceRequest, queryRequest } from '../api';
|
||||
import { database } from '../service/database';
|
||||
import { groupDialogFilterOptions } from '../shared/constants/';
|
||||
import { patchGroupFromEvent } from '../queries';
|
||||
import { useGameStore } from '../stores/game';
|
||||
import { useInstanceStore } from '../stores/instance';
|
||||
import { useModalStore } from '../stores/modal';
|
||||
import { useNotificationStore } from '../stores/notification';
|
||||
import { useUiStore } from '../stores/ui';
|
||||
import { useUserStore } from '../stores/user';
|
||||
import { useGroupStore } from '../stores/group';
|
||||
import { watchState } from '../service/watchState';
|
||||
|
||||
import configRepository from '../service/config';
|
||||
|
||||
import * as workerTimers from 'worker-timers';
|
||||
|
||||
// ─── Internal helpers (not exported) ─────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* @param ref
|
||||
*/
|
||||
function applyGroupLanguage(ref) {
|
||||
const userStore = useUserStore();
|
||||
ref.$languages = [];
|
||||
const { languages } = ref;
|
||||
if (!languages) {
|
||||
return;
|
||||
}
|
||||
for (const language of languages) {
|
||||
const value = userStore.subsetOfLanguages[language];
|
||||
if (typeof value === 'undefined') {
|
||||
continue;
|
||||
}
|
||||
ref.$languages.push({
|
||||
key: language,
|
||||
value
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Core entity application ─────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object} json
|
||||
* @returns {object} ref
|
||||
*/
|
||||
export function applyGroup(json) {
|
||||
const groupStore = useGroupStore();
|
||||
const userStore = useUserStore();
|
||||
let ref = groupStore.cachedGroups.get(json.id);
|
||||
sanitizeEntityJson(json, ['rules', 'name', 'description']);
|
||||
if (typeof ref === 'undefined') {
|
||||
ref = createDefaultGroupRef(json);
|
||||
groupStore.cachedGroups.set(ref.id, ref);
|
||||
} else {
|
||||
if (groupStore.currentUserGroups.has(ref.id)) {
|
||||
// compare group props
|
||||
if (
|
||||
ref.ownerId &&
|
||||
json.ownerId &&
|
||||
ref.ownerId !== json.ownerId
|
||||
) {
|
||||
// owner changed
|
||||
groupOwnerChange(json, ref.ownerId, json.ownerId);
|
||||
}
|
||||
if (ref.name && json.name && ref.name !== json.name) {
|
||||
// name changed
|
||||
groupChange(
|
||||
json,
|
||||
`Name changed from ${ref.name} to ${json.name}`
|
||||
);
|
||||
}
|
||||
if (ref.myMember?.roleIds && json.myMember?.roleIds) {
|
||||
const oldRoleIds = ref.myMember.roleIds;
|
||||
const newRoleIds = json.myMember.roleIds;
|
||||
if (
|
||||
oldRoleIds.length !== newRoleIds.length ||
|
||||
!oldRoleIds.every(
|
||||
(value, index) => value === newRoleIds[index]
|
||||
)
|
||||
) {
|
||||
// roleIds changed
|
||||
groupRoleChange(
|
||||
json,
|
||||
ref.roles,
|
||||
json.roles,
|
||||
oldRoleIds,
|
||||
newRoleIds
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (json.myMember) {
|
||||
if (typeof json.myMember.roleIds === 'undefined') {
|
||||
// keep roleIds
|
||||
json.myMember.roleIds = ref.myMember.roleIds;
|
||||
}
|
||||
Object.assign(ref.myMember, json.myMember);
|
||||
}
|
||||
Object.assign(ref, json);
|
||||
}
|
||||
// update myMember without fetching member
|
||||
if (typeof json.memberVisibility !== 'undefined') {
|
||||
ref.myMember.visibility = json.memberVisibility;
|
||||
}
|
||||
if (typeof json.isRepresenting !== 'undefined') {
|
||||
ref.myMember.isRepresenting = json.isRepresenting;
|
||||
}
|
||||
if (typeof json.membershipStatus !== 'undefined') {
|
||||
ref.myMember.membershipStatus = json.membershipStatus;
|
||||
}
|
||||
if (typeof json.roleIds !== 'undefined') {
|
||||
ref.myMember.roleIds = json.roleIds;
|
||||
}
|
||||
ref.$url = `https://vrc.group/${ref.shortCode}.${ref.discriminator}`;
|
||||
applyGroupLanguage(ref);
|
||||
|
||||
const currentUserGroupRef = groupStore.currentUserGroups.get(ref.id);
|
||||
if (currentUserGroupRef) {
|
||||
groupStore.currentUserGroups.set(ref.id, ref);
|
||||
}
|
||||
|
||||
const D = groupStore.groupDialog;
|
||||
if (D.visible && D.id === ref.id) {
|
||||
D.inGroup = ref.membershipStatus === 'member';
|
||||
D.ref = ref;
|
||||
}
|
||||
patchGroupFromEvent(ref);
|
||||
return ref;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object} json
|
||||
* @returns {*}
|
||||
*/
|
||||
export function applyGroupMember(json) {
|
||||
const userStore = useUserStore();
|
||||
const groupStore = useGroupStore();
|
||||
let ref;
|
||||
if (typeof json?.user !== 'undefined') {
|
||||
if (json.userId === userStore.currentUser.id) {
|
||||
json.user = userStore.currentUser;
|
||||
json.$displayName = userStore.currentUser.displayName;
|
||||
} else {
|
||||
ref = userStore.cachedUsers.get(json.user.id);
|
||||
if (typeof ref !== 'undefined') {
|
||||
json.user = ref;
|
||||
json.$displayName = ref.displayName;
|
||||
} else {
|
||||
json.$displayName = json.user?.displayName;
|
||||
}
|
||||
}
|
||||
}
|
||||
// update myMember without fetching member
|
||||
if (json?.userId === userStore.currentUser.id) {
|
||||
ref = groupStore.cachedGroups.get(json.groupId);
|
||||
if (typeof ref !== 'undefined') {
|
||||
const newJson = {
|
||||
id: json.groupId,
|
||||
memberVisibility: json.visibility,
|
||||
isRepresenting: json.isRepresenting,
|
||||
isSubscribedToAnnouncements:
|
||||
json.isSubscribedToAnnouncements,
|
||||
joinedAt: json.joinedAt,
|
||||
roleIds: json.roleIds,
|
||||
membershipStatus: json.membershipStatus
|
||||
};
|
||||
applyGroup(newJson);
|
||||
}
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
// ─── Group change notifications ──────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
*
|
||||
* @param ref
|
||||
* @param message
|
||||
*/
|
||||
function groupChange(ref, message) {
|
||||
const groupStore = useGroupStore();
|
||||
const notificationStore = useNotificationStore();
|
||||
if (!groupStore.currentUserGroupsInit) {
|
||||
return;
|
||||
}
|
||||
// oh the level of cursed for compibility
|
||||
const json = {
|
||||
id: Math.random().toString(36),
|
||||
type: 'groupChange',
|
||||
senderUserId: ref.id,
|
||||
senderUsername: ref.name,
|
||||
imageUrl: ref.iconUrl,
|
||||
details: {
|
||||
imageUrl: ref.iconUrl
|
||||
},
|
||||
message,
|
||||
created_at: new Date().toJSON()
|
||||
};
|
||||
notificationStore.handleNotification({
|
||||
json,
|
||||
params: {
|
||||
notificationId: json.id
|
||||
}
|
||||
});
|
||||
|
||||
// delay to wait for json to be assigned to ref
|
||||
workerTimers.setTimeout(() => saveCurrentUserGroups(), 100);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object }ref
|
||||
* @param {string} oldUserId
|
||||
* @param {string} newUserId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function groupOwnerChange(ref, oldUserId, newUserId) {
|
||||
const oldUser = await queryRequest.fetch('user', {
|
||||
userId: oldUserId
|
||||
});
|
||||
const newUser = await queryRequest.fetch('user', {
|
||||
userId: newUserId
|
||||
});
|
||||
const oldDisplayName = oldUser?.ref?.displayName;
|
||||
const newDisplayName = newUser?.ref?.displayName;
|
||||
|
||||
groupChange(
|
||||
ref,
|
||||
`Owner changed from ${oldDisplayName} to ${newDisplayName}`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object} ref
|
||||
* @param {Array} oldRoles
|
||||
* @param {Array} newRoles
|
||||
* @param {Array} oldRoleIds
|
||||
* @param {Array} newRoleIds
|
||||
*/
|
||||
function groupRoleChange(ref, oldRoles, newRoles, oldRoleIds, newRoleIds) {
|
||||
// check for removed/added roleIds
|
||||
for (const roleId of oldRoleIds) {
|
||||
if (!newRoleIds.includes(roleId)) {
|
||||
let roleName = '';
|
||||
const role = oldRoles.find(
|
||||
(fineRole) => fineRole.id === roleId
|
||||
);
|
||||
if (role) {
|
||||
roleName = role.name;
|
||||
}
|
||||
groupChange(ref, `Role ${roleName} removed`);
|
||||
}
|
||||
}
|
||||
if (typeof newRoles !== 'undefined') {
|
||||
for (const roleId of newRoleIds) {
|
||||
if (!oldRoleIds.includes(roleId)) {
|
||||
let roleName = '';
|
||||
const role = newRoles.find(
|
||||
(fineRole) => fineRole.id === roleId
|
||||
);
|
||||
if (role) {
|
||||
roleName = role.name;
|
||||
}
|
||||
groupChange(ref, `Role ${roleName} added`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Dialog flows ────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
*
|
||||
* @param groupId
|
||||
* @param options
|
||||
*/
|
||||
export function showGroupDialog(groupId, options = {}) {
|
||||
const t = i18n.global.t;
|
||||
const groupStore = useGroupStore();
|
||||
const uiStore = useUiStore();
|
||||
const instanceStore = useInstanceStore();
|
||||
if (!groupId) {
|
||||
return;
|
||||
}
|
||||
const forceRefresh = Boolean(options?.forceRefresh);
|
||||
const isMainDialogOpen = uiStore.openDialog({
|
||||
type: 'group',
|
||||
id: groupId
|
||||
});
|
||||
const D = groupStore.groupDialog;
|
||||
D.visible = true;
|
||||
if (isMainDialogOpen && D.id === groupId && !forceRefresh) {
|
||||
uiStore.setDialogCrumbLabel('group', D.id, D.ref?.name || D.id);
|
||||
instanceStore.applyGroupDialogInstances();
|
||||
D.loading = false;
|
||||
return;
|
||||
}
|
||||
D.loading = true;
|
||||
D.id = groupId;
|
||||
D.inGroup = false;
|
||||
D.ownerDisplayName = '';
|
||||
D.announcement = {};
|
||||
D.posts = [];
|
||||
D.postsFiltered = [];
|
||||
D.instances = [];
|
||||
D.memberRoles = [];
|
||||
D.lastVisit = '';
|
||||
D.memberSearch = '';
|
||||
D.memberSearchResults = [];
|
||||
D.galleries = {};
|
||||
D.members = [];
|
||||
D.memberFilter = groupDialogFilterOptions.everyone;
|
||||
D.calendar = [];
|
||||
const loadGroupRequest = groupRequest.getGroup({
|
||||
groupId,
|
||||
includeRoles: true
|
||||
});
|
||||
|
||||
loadGroupRequest
|
||||
.catch((err) => {
|
||||
D.loading = false;
|
||||
D.id = null;
|
||||
D.visible = false;
|
||||
uiStore.jumpBackDialogCrumb();
|
||||
toast.error(t('message.group.load_failed'));
|
||||
throw err;
|
||||
})
|
||||
.then((args) => {
|
||||
const ref = args.ref || applyGroup(args.json);
|
||||
if (groupId === ref.id) {
|
||||
D.ref = ref;
|
||||
uiStore.setDialogCrumbLabel(
|
||||
'group',
|
||||
D.id,
|
||||
D.ref?.name || D.id
|
||||
);
|
||||
D.inGroup = ref.membershipStatus === 'member';
|
||||
D.ownerDisplayName = ref.ownerId;
|
||||
D.visible = true;
|
||||
D.loading = false;
|
||||
queryRequest
|
||||
.fetch('user', {
|
||||
userId: ref.ownerId
|
||||
})
|
||||
.then((args1) => {
|
||||
D.ownerDisplayName = args1.ref.displayName;
|
||||
});
|
||||
database.getLastGroupVisit(D.ref.name).then((r) => {
|
||||
if (D.id === ref.id) {
|
||||
D.lastVisit = r.created_at;
|
||||
}
|
||||
});
|
||||
instanceStore.applyGroupDialogInstances();
|
||||
getGroupDialogGroup(groupId, ref);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param groupId
|
||||
* @param {object} [existingRef]
|
||||
* @returns { Promise<object> }
|
||||
*/
|
||||
export function getGroupDialogGroup(groupId, existingRef) {
|
||||
const groupStore = useGroupStore();
|
||||
const instanceStore = useInstanceStore();
|
||||
const D = groupStore.groupDialog;
|
||||
D.isGetGroupDialogGroupLoading = false;
|
||||
|
||||
const refPromise = existingRef
|
||||
? Promise.resolve({ ref: existingRef })
|
||||
: queryRequest
|
||||
.fetch('group', { groupId, includeRoles: true })
|
||||
.then((args) => ({ ref: applyGroup(args.json), args }));
|
||||
|
||||
return refPromise
|
||||
.catch((err) => {
|
||||
throw err;
|
||||
})
|
||||
.then((result) => {
|
||||
const ref = result.ref;
|
||||
if (D.id === ref.id) {
|
||||
D.loading = false;
|
||||
D.ref = ref;
|
||||
D.inGroup = ref.membershipStatus === 'member';
|
||||
D.memberRoles = [];
|
||||
for (const role of ref.roles) {
|
||||
if (
|
||||
D.ref &&
|
||||
D.ref.myMember &&
|
||||
Array.isArray(D.ref.myMember.roleIds) &&
|
||||
D.ref.myMember.roleIds.includes(role.id)
|
||||
) {
|
||||
D.memberRoles.push(role);
|
||||
}
|
||||
}
|
||||
groupStore.getAllGroupPosts({
|
||||
groupId
|
||||
});
|
||||
D.isGetGroupDialogGroupLoading = true;
|
||||
groupRequest
|
||||
.getGroupInstances({
|
||||
groupId
|
||||
})
|
||||
.then((args) => {
|
||||
if (groupStore.groupDialog.id === args.params.groupId) {
|
||||
instanceStore.applyGroupDialogInstances(
|
||||
args.json.instances
|
||||
);
|
||||
}
|
||||
for (const json of args.json.instances) {
|
||||
instanceStore.applyInstance(json);
|
||||
queryRequest
|
||||
.fetch('world', {
|
||||
worldId: json.world.id
|
||||
})
|
||||
.then((args1) => {
|
||||
json.world = args1.ref;
|
||||
});
|
||||
// get queue size etc
|
||||
instanceRequest.getInstance({
|
||||
worldId: json.worldId,
|
||||
instanceId: json.instanceId
|
||||
});
|
||||
}
|
||||
});
|
||||
queryRequest
|
||||
.fetch('groupCalendar', { groupId })
|
||||
.then((args) => {
|
||||
if (groupStore.groupDialog.id === args.params.groupId) {
|
||||
D.calendar = args.json.results;
|
||||
for (const event of D.calendar) {
|
||||
Object.assign(event, groupStore.applyGroupEvent(event));
|
||||
// fetch again for isFollowing
|
||||
queryRequest
|
||||
.fetch('groupCalendarEvent', {
|
||||
groupId,
|
||||
eventId: event.id
|
||||
})
|
||||
.then((args) => {
|
||||
Object.assign(
|
||||
event,
|
||||
groupStore.applyGroupEvent(args.json)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
nextTick(() => (D.isGetGroupDialogGroupLoading = false));
|
||||
return result.args || result;
|
||||
});
|
||||
}
|
||||
|
||||
// ─── Group lifecycle flows ───────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object} ref
|
||||
*/
|
||||
export function applyPresenceGroups(ref) {
|
||||
const groupStore = useGroupStore();
|
||||
if (!groupStore.currentUserGroupsInit) {
|
||||
// wait for init before diffing
|
||||
return;
|
||||
}
|
||||
const groups = ref.presence?.groups;
|
||||
if (!groups) {
|
||||
console.error('applyPresenceGroups: invalid groups', ref);
|
||||
return;
|
||||
}
|
||||
if (groups.length === 0) {
|
||||
// as it turns out, this is not the most trust worthly source of info
|
||||
return;
|
||||
}
|
||||
|
||||
// update group list
|
||||
for (const groupId of groups) {
|
||||
if (!groupStore.currentUserGroups.has(groupId)) {
|
||||
onGroupJoined(groupId);
|
||||
}
|
||||
}
|
||||
for (const groupId of groupStore.currentUserGroups.keys()) {
|
||||
if (!groups.includes(groupId)) {
|
||||
onGroupLeft(groupId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} groupId
|
||||
*/
|
||||
export function onGroupJoined(groupId) {
|
||||
const groupStore = useGroupStore();
|
||||
if (!groupStore.currentUserGroups.has(groupId)) {
|
||||
groupStore.currentUserGroups.set(groupId, {
|
||||
id: groupId,
|
||||
name: '',
|
||||
iconUrl: ''
|
||||
});
|
||||
groupRequest
|
||||
.getGroup({ groupId, includeRoles: true })
|
||||
.then((args) => {
|
||||
applyGroup(args.json);
|
||||
saveCurrentUserGroups();
|
||||
return args;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} groupId
|
||||
*/
|
||||
export async function onGroupLeft(groupId) {
|
||||
const groupStore = useGroupStore();
|
||||
const args = await groupRequest.getGroup({ groupId });
|
||||
const ref = applyGroup(args.json);
|
||||
if (ref.membershipStatus === 'member') {
|
||||
// wtf, not trusting presence
|
||||
console.error(
|
||||
`onGroupLeft: presence lied, still a member of ${groupId}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (groupStore.groupDialog.visible && groupStore.groupDialog.id === groupId) {
|
||||
showGroupDialog(groupId);
|
||||
}
|
||||
if (groupStore.currentUserGroups.has(groupId)) {
|
||||
groupStore.currentUserGroups.delete(groupId);
|
||||
groupChange(ref, 'Left group');
|
||||
|
||||
// delay to wait for json to be assigned to ref
|
||||
workerTimers.setTimeout(() => saveCurrentUserGroups(), 100);
|
||||
}
|
||||
}
|
||||
|
||||
// ─── User group management ───────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export function saveCurrentUserGroups() {
|
||||
const groupStore = useGroupStore();
|
||||
const userStore = useUserStore();
|
||||
if (!groupStore.currentUserGroupsInit) {
|
||||
return;
|
||||
}
|
||||
const groups = [];
|
||||
for (const ref of groupStore.currentUserGroups.values()) {
|
||||
groups.push({
|
||||
id: ref.id,
|
||||
name: ref.name,
|
||||
ownerId: ref.ownerId,
|
||||
iconUrl: ref.iconUrl,
|
||||
roles: ref.roles,
|
||||
roleIds: ref.myMember?.roleIds
|
||||
});
|
||||
}
|
||||
configRepository.setString(
|
||||
`VRCX_currentUserGroups_${userStore.currentUser.id}`,
|
||||
JSON.stringify(groups)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param userId
|
||||
* @param groups
|
||||
*/
|
||||
export async function loadCurrentUserGroups(userId, groups) {
|
||||
const groupStore = useGroupStore();
|
||||
const savedGroups = JSON.parse(
|
||||
await configRepository.getString(
|
||||
`VRCX_currentUserGroups_${userId}`,
|
||||
'[]'
|
||||
)
|
||||
);
|
||||
groupStore.cachedGroups.clear();
|
||||
groupStore.currentUserGroups.clear();
|
||||
for (const group of savedGroups) {
|
||||
const json = {
|
||||
id: group.id,
|
||||
name: group.name,
|
||||
iconUrl: group.iconUrl,
|
||||
ownerId: group.ownerId,
|
||||
roles: group.roles,
|
||||
myMember: {
|
||||
roleIds: group.roleIds
|
||||
}
|
||||
};
|
||||
const ref = applyGroup(json);
|
||||
groupStore.currentUserGroups.set(group.id, ref);
|
||||
}
|
||||
|
||||
if (groups) {
|
||||
const promises = groups.map(async (groupId) => {
|
||||
const groupRef = groupStore.cachedGroups.get(groupId);
|
||||
|
||||
if (
|
||||
typeof groupRef !== 'undefined' &&
|
||||
groupRef.roles?.length > 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log(`Fetching group with missing roles ${groupId}`);
|
||||
const args = await groupRequest.getGroup({
|
||||
groupId,
|
||||
includeRoles: true
|
||||
});
|
||||
const ref = applyGroup(args.json);
|
||||
groupStore.currentUserGroups.set(groupId, ref);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.allSettled(promises);
|
||||
}
|
||||
|
||||
groupStore.currentUserGroupsInit = true;
|
||||
getCurrentUserGroups();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export async function getCurrentUserGroups() {
|
||||
const groupStore = useGroupStore();
|
||||
const userStore = useUserStore();
|
||||
const args = await groupRequest.getGroups({
|
||||
userId: userStore.currentUser.id
|
||||
});
|
||||
handleGroupList(args);
|
||||
groupStore.currentUserGroups.clear();
|
||||
for (const group of args.json) {
|
||||
const ref = applyGroup(group);
|
||||
if (!groupStore.currentUserGroups.has(group.id)) {
|
||||
groupStore.currentUserGroups.set(group.id, ref);
|
||||
}
|
||||
}
|
||||
const args1 = await groupRequest.getGroupPermissions({
|
||||
userId: userStore.currentUser.id
|
||||
});
|
||||
handleGroupPermissions(args1);
|
||||
saveCurrentUserGroups();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export function getCurrentUserRepresentedGroup() {
|
||||
const userStore = useUserStore();
|
||||
return groupRequest
|
||||
.getRepresentedGroup({
|
||||
userId: userStore.currentUser.id
|
||||
})
|
||||
.then((args) => {
|
||||
handleGroupRepresented(args);
|
||||
return args;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export async function initUserGroups() {
|
||||
const userStore = useUserStore();
|
||||
updateInGameGroupOrder();
|
||||
loadCurrentUserGroups(
|
||||
userStore.currentUser.id,
|
||||
userStore.currentUser?.presence?.groups
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export async function updateInGameGroupOrder() {
|
||||
const groupStore = useGroupStore();
|
||||
const gameStore = useGameStore();
|
||||
const userStore = useUserStore();
|
||||
groupStore.inGameGroupOrder = [];
|
||||
try {
|
||||
const json = await gameStore.getVRChatRegistryKey(
|
||||
`VRC_GROUP_ORDER_${userStore.currentUser.id}`
|
||||
);
|
||||
if (!json) {
|
||||
return;
|
||||
}
|
||||
groupStore.inGameGroupOrder = JSON.parse(json);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Group actions ───────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
*
|
||||
* @param groupId
|
||||
*/
|
||||
export function leaveGroup(groupId) {
|
||||
const groupStore = useGroupStore();
|
||||
const userStore = useUserStore();
|
||||
groupRequest
|
||||
.leaveGroup({
|
||||
groupId
|
||||
})
|
||||
.then((args) => {
|
||||
const groupId = args.params.groupId;
|
||||
if (
|
||||
groupStore.groupDialog.visible &&
|
||||
groupStore.groupDialog.id === groupId
|
||||
) {
|
||||
groupStore.groupDialog.inGroup = false;
|
||||
getGroupDialogGroup(groupId);
|
||||
}
|
||||
if (
|
||||
userStore.userDialog.visible &&
|
||||
userStore.userDialog.id === userStore.currentUser.id &&
|
||||
userStore.userDialog.representedGroup.id === groupId
|
||||
) {
|
||||
getCurrentUserRepresentedGroup();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param groupId
|
||||
*/
|
||||
export function leaveGroupPrompt(groupId) {
|
||||
const t = i18n.global.t;
|
||||
const modalStore = useModalStore();
|
||||
modalStore
|
||||
.confirm({
|
||||
description: t('confirm.leave_group'),
|
||||
title: t('confirm.title')
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
leaveGroup(groupId);
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param groupId
|
||||
* @param visibility
|
||||
*/
|
||||
export function setGroupVisibility(groupId, visibility) {
|
||||
const t = i18n.global.t;
|
||||
const userStore = useUserStore();
|
||||
return groupRequest
|
||||
.setGroupMemberProps(userStore.currentUser.id, groupId, {
|
||||
visibility
|
||||
})
|
||||
.then((args) => {
|
||||
handleGroupMemberProps(args);
|
||||
toast.success(t('message.group.visibility_updated'));
|
||||
return args;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param groupId
|
||||
* @param subscribe
|
||||
*/
|
||||
export function setGroupSubscription(groupId, subscribe) {
|
||||
const t = i18n.global.t;
|
||||
const userStore = useUserStore();
|
||||
return groupRequest
|
||||
.setGroupMemberProps(userStore.currentUser.id, groupId, {
|
||||
isSubscribedToAnnouncements: subscribe
|
||||
})
|
||||
.then((args) => {
|
||||
handleGroupMemberProps(args);
|
||||
toast.success(t('message.group.subscription_updated'));
|
||||
return args;
|
||||
});
|
||||
}
|
||||
|
||||
// ─── Event handlers ──────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
*
|
||||
* @param args
|
||||
*/
|
||||
export function handleGroupRepresented(args) {
|
||||
const userStore = useUserStore();
|
||||
const D = userStore.userDialog;
|
||||
const json = args.json;
|
||||
D.representedGroup = json;
|
||||
D.representedGroup.$thumbnailUrl = convertFileUrlToImageUrl(
|
||||
json.iconUrl
|
||||
);
|
||||
if (!json || !json.isRepresenting) {
|
||||
D.isRepresentedGroupLoading = false;
|
||||
}
|
||||
if (!json.groupId) {
|
||||
// no group
|
||||
return;
|
||||
}
|
||||
if (args.params.userId !== userStore.currentUser.id) {
|
||||
// not current user, don't apply someone elses myMember
|
||||
return;
|
||||
}
|
||||
json.$memberId = json.id;
|
||||
json.id = json.groupId;
|
||||
applyGroup(json);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param args
|
||||
*/
|
||||
export function handleGroupList(args) {
|
||||
for (const json of args.json) {
|
||||
json.$memberId = json.id;
|
||||
json.id = json.groupId;
|
||||
applyGroup(json);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param args
|
||||
*/
|
||||
export function handleGroupMemberProps(args) {
|
||||
const groupStore = useGroupStore();
|
||||
const userStore = useUserStore();
|
||||
if (args.userId === userStore.currentUser.id) {
|
||||
const json = args.json;
|
||||
json.$memberId = json.id;
|
||||
json.id = json.groupId;
|
||||
if (
|
||||
groupStore.groupDialog.visible &&
|
||||
groupStore.groupDialog.id === json.groupId
|
||||
) {
|
||||
groupStore.groupDialog.ref.myMember.visibility = json.visibility;
|
||||
groupStore.groupDialog.ref.myMember.isSubscribedToAnnouncements =
|
||||
json.isSubscribedToAnnouncements;
|
||||
}
|
||||
if (
|
||||
userStore.userDialog.visible &&
|
||||
userStore.userDialog.id === userStore.currentUser.id
|
||||
) {
|
||||
getCurrentUserRepresentedGroup();
|
||||
}
|
||||
handleGroupMember({
|
||||
json,
|
||||
params: {
|
||||
groupId: json.groupId
|
||||
}
|
||||
});
|
||||
}
|
||||
let member;
|
||||
if (groupStore.groupDialog.id === args.json.groupId) {
|
||||
let i;
|
||||
for (i = 0; i < groupStore.groupDialog.members.length; ++i) {
|
||||
member = groupStore.groupDialog.members[i];
|
||||
if (member.userId === args.json.userId) {
|
||||
Object.assign(member, applyGroupMember(args.json));
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < groupStore.groupDialog.memberSearchResults.length; ++i) {
|
||||
member = groupStore.groupDialog.memberSearchResults[i];
|
||||
if (member.userId === args.json.userId) {
|
||||
Object.assign(member, applyGroupMember(args.json));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param args
|
||||
*/
|
||||
export function handleGroupPermissions(args) {
|
||||
const groupStore = useGroupStore();
|
||||
const userStore = useUserStore();
|
||||
if (args.params.userId !== userStore.currentUser.id) {
|
||||
return;
|
||||
}
|
||||
const json = args.json;
|
||||
for (const groupId in json) {
|
||||
const permissions = json[groupId];
|
||||
const group = groupStore.cachedGroups.get(groupId);
|
||||
if (group) {
|
||||
group.myMember.permissions = permissions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param args
|
||||
*/
|
||||
export function handleGroupMember(args) {
|
||||
args.ref = applyGroupMember(args.json);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param args
|
||||
*/
|
||||
export async function handleGroupUserInstances(args) {
|
||||
const groupStore = useGroupStore();
|
||||
const instanceStore = useInstanceStore();
|
||||
groupStore.groupInstances = [];
|
||||
for (const json of args.json.instances) {
|
||||
if (args.json.fetchedAt) {
|
||||
// tack on fetchedAt
|
||||
json.$fetchedAt = args.json.fetchedAt;
|
||||
}
|
||||
const instanceRef = instanceStore.applyInstance(json);
|
||||
const groupRef = groupStore.cachedGroups.get(json.ownerId);
|
||||
if (typeof groupRef === 'undefined') {
|
||||
if (watchState.isFriendsLoaded) {
|
||||
const args = await groupRequest.getGroup({
|
||||
groupId: json.ownerId
|
||||
});
|
||||
applyGroup(args.json);
|
||||
}
|
||||
return;
|
||||
}
|
||||
groupStore.groupInstances.push({
|
||||
group: groupRef,
|
||||
instance: instanceRef
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { i18n } from '../plugin/i18n';
|
||||
|
||||
import { instanceRequest } from '../api';
|
||||
import { parseLocation } from '../shared/utils';
|
||||
@@ -16,7 +16,7 @@ export function runNewInstanceSelfInviteFlow(worldId) {
|
||||
const instanceStore = useInstanceStore();
|
||||
const launchStore = useLaunchStore();
|
||||
const inviteStore = useInviteStore();
|
||||
const { t } = useI18n();
|
||||
const t = i18n.global.t;
|
||||
|
||||
instanceStore.createNewInstance(worldId).then((args) => {
|
||||
const location = args?.json?.location;
|
||||
|
||||
@@ -19,9 +19,6 @@ export async function runRefreshPlayerModerationsFlow() {
|
||||
playerModerationRequest.getPlayerModerations(),
|
||||
avatarModerationRequest.getAvatarModerations()
|
||||
])
|
||||
.finally(() => {
|
||||
moderationStore.playerModerationTable.loading = false;
|
||||
})
|
||||
.then((res) => {
|
||||
avatarStore.resetCachedAvatarModerations();
|
||||
if (res[1]?.json) {
|
||||
@@ -41,5 +38,8 @@ export async function runRefreshPlayerModerationsFlow() {
|
||||
'Failed to load player/avatar moderations:',
|
||||
error
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
moderationStore.playerModerationTable.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
1070
src/coordinators/userCoordinator.js
Normal file
1070
src/coordinators/userCoordinator.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@ import { getGroupName, getWorldName, parseLocation } from '../shared/utils';
|
||||
import { AppDebug } from '../service/appConfig';
|
||||
import { database } from '../service/database';
|
||||
import { useAvatarStore } from '../stores/avatar';
|
||||
import { getAvatarName } from './avatarCoordinator';
|
||||
import { useFeedStore } from '../stores/feed';
|
||||
import { useFriendStore } from '../stores/friend';
|
||||
import { useGeneralSettingsStore } from '../stores/settings/general';
|
||||
@@ -222,7 +223,7 @@ export async function runHandleUserUpdateFlow(
|
||||
avatarName: ''
|
||||
};
|
||||
try {
|
||||
avatarInfo = await avatarStore.getAvatarName(
|
||||
avatarInfo = await getAvatarName(
|
||||
currentAvatarImageUrl
|
||||
);
|
||||
} catch (err) {
|
||||
@@ -233,7 +234,7 @@ export async function runHandleUserUpdateFlow(
|
||||
avatarName: ''
|
||||
};
|
||||
try {
|
||||
previousAvatarInfo = await avatarStore.getAvatarName(
|
||||
previousAvatarInfo = await getAvatarName(
|
||||
previousCurrentAvatarImageUrl
|
||||
);
|
||||
} catch (err) {
|
||||
|
||||
@@ -2,9 +2,11 @@ import { getWorldName, parseLocation } from '../shared/utils';
|
||||
import { runUpdateFriendshipsFlow } from './friendRelationshipCoordinator';
|
||||
import { useAuthStore } from '../stores/auth';
|
||||
import { useAvatarStore } from '../stores/avatar';
|
||||
import { addAvatarToHistory, addAvatarWearTime } from './avatarCoordinator';
|
||||
import { useFriendStore } from '../stores/friend';
|
||||
import { useGameStore } from '../stores/game';
|
||||
import { useGroupStore } from '../stores/group';
|
||||
import { applyPresenceGroups } from './groupCoordinator';
|
||||
import { useInstanceStore } from '../stores/instance';
|
||||
import { useUserStore } from '../stores/user';
|
||||
|
||||
@@ -28,9 +30,9 @@ export function runAvatarSwapFlow(
|
||||
return;
|
||||
}
|
||||
if (json.currentAvatar !== ref.currentAvatar) {
|
||||
avatarStore.addAvatarToHistory(json.currentAvatar);
|
||||
addAvatarToHistory(json.currentAvatar);
|
||||
if (gameStore.isGameRunning) {
|
||||
avatarStore.addAvatarWearTime(ref.currentAvatar);
|
||||
addAvatarWearTime(ref.currentAvatar);
|
||||
ref.$previousAvatarSwapTime = now();
|
||||
}
|
||||
}
|
||||
@@ -64,7 +66,7 @@ export function runPostApplySyncFlow(ref) {
|
||||
const instanceStore = useInstanceStore();
|
||||
const friendStore = useFriendStore();
|
||||
|
||||
groupStore.applyPresenceGroups(ref);
|
||||
applyPresenceGroups(ref);
|
||||
instanceStore.applyQueuedInstance(ref.queuedInstance);
|
||||
friendStore.updateUserCurrentStatus(ref);
|
||||
if (typeof ref.friends !== 'undefined') {
|
||||
|
||||
246
src/coordinators/worldCoordinator.js
Normal file
246
src/coordinators/worldCoordinator.js
Normal file
@@ -0,0 +1,246 @@
|
||||
import { nextTick } from 'vue';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { i18n } from '../plugin/i18n';
|
||||
|
||||
import {
|
||||
checkVRChatCache,
|
||||
createDefaultWorldRef,
|
||||
evictMapCache,
|
||||
getAvailablePlatforms,
|
||||
getBundleDateSize,
|
||||
getWorldMemo,
|
||||
isRealInstance,
|
||||
parseLocation,
|
||||
sanitizeEntityJson
|
||||
} from '../shared/utils';
|
||||
import { instanceRequest, queryRequest, worldRequest } from '../api';
|
||||
import { database } from '../service/database';
|
||||
import { patchWorldFromEvent } from '../queries';
|
||||
import { processBulk } from '../service/request';
|
||||
import { applyFavorite } from './favoriteCoordinator';
|
||||
import { useFavoriteStore } from '../stores/favorite';
|
||||
import { useInstanceStore } from '../stores/instance';
|
||||
import { useLocationStore } from '../stores/location';
|
||||
import { useUiStore } from '../stores/ui';
|
||||
import { useUserStore } from '../stores/user';
|
||||
import { useWorldStore } from '../stores/world';
|
||||
|
||||
/**
|
||||
* @param {string} tag
|
||||
* @param {string} shortName
|
||||
* @param options
|
||||
*/
|
||||
export function showWorldDialog(tag, shortName = null, options = {}) {
|
||||
const worldStore = useWorldStore();
|
||||
const uiStore = useUiStore();
|
||||
const instanceStore = useInstanceStore();
|
||||
const favoriteStore = useFavoriteStore();
|
||||
const locationStore = useLocationStore();
|
||||
const t = i18n.global.t;
|
||||
|
||||
const D = worldStore.worldDialog;
|
||||
const forceRefresh = Boolean(options?.forceRefresh);
|
||||
const L = parseLocation(tag);
|
||||
if (L.worldId === '') {
|
||||
return;
|
||||
}
|
||||
const isMainDialogOpen = uiStore.openDialog({
|
||||
type: 'world',
|
||||
id: L.worldId,
|
||||
tag,
|
||||
shortName
|
||||
});
|
||||
D.visible = true;
|
||||
if (isMainDialogOpen && D.id === L.worldId && !forceRefresh) {
|
||||
uiStore.setDialogCrumbLabel('world', D.id, D.ref?.name || D.id);
|
||||
instanceStore.applyWorldDialogInstances();
|
||||
nextTick(() => (D.loading = false));
|
||||
return;
|
||||
}
|
||||
L.shortName = shortName;
|
||||
D.id = L.worldId;
|
||||
D.$location = L;
|
||||
D.loading = true;
|
||||
D.inCache = false;
|
||||
D.cacheSize = '';
|
||||
D.cacheLocked = false;
|
||||
D.cachePath = '';
|
||||
D.fileAnalysis = {};
|
||||
D.rooms = [];
|
||||
D.lastVisit = '';
|
||||
D.visitCount = 0;
|
||||
D.timeSpent = 0;
|
||||
D.isFavorite = false;
|
||||
D.avatarScalingDisabled = false;
|
||||
D.focusViewDisabled = false;
|
||||
D.isPC = false;
|
||||
D.isQuest = false;
|
||||
D.isIos = false;
|
||||
D.hasPersistData = false;
|
||||
D.memo = '';
|
||||
const LL = parseLocation(locationStore.lastLocation.location);
|
||||
let currentWorldMatch = false;
|
||||
if (LL.worldId === D.id) {
|
||||
currentWorldMatch = true;
|
||||
}
|
||||
getWorldMemo(D.id).then((memo) => {
|
||||
if (memo.worldId === D.id) {
|
||||
D.memo = memo.memo;
|
||||
}
|
||||
});
|
||||
database.getLastVisit(D.id, currentWorldMatch).then((ref) => {
|
||||
if (ref.worldId === D.id) {
|
||||
D.lastVisit = ref.created_at;
|
||||
}
|
||||
});
|
||||
database.getVisitCount(D.id).then((ref) => {
|
||||
if (ref.worldId === D.id) {
|
||||
D.visitCount = ref.visitCount;
|
||||
}
|
||||
});
|
||||
database.getTimeSpentInWorld(D.id).then((ref) => {
|
||||
if (ref.worldId === D.id) {
|
||||
D.timeSpent = ref.timeSpent;
|
||||
}
|
||||
});
|
||||
const loadWorldRequest = worldRequest.getWorld({
|
||||
worldId: L.worldId
|
||||
});
|
||||
loadWorldRequest
|
||||
.catch((err) => {
|
||||
nextTick(() => (D.loading = false));
|
||||
D.id = null;
|
||||
D.visible = false;
|
||||
uiStore.jumpBackDialogCrumb();
|
||||
toast.error(t('message.world.load_failed'));
|
||||
throw err;
|
||||
})
|
||||
.then((args) => {
|
||||
if (D.id === args.ref.id) {
|
||||
D.ref = args.ref;
|
||||
uiStore.setDialogCrumbLabel(
|
||||
'world',
|
||||
D.id,
|
||||
D.ref?.name || D.id
|
||||
);
|
||||
D.visible = true;
|
||||
D.loading = false;
|
||||
D.isFavorite = favoriteStore.getCachedFavoritesByObjectId(
|
||||
D.id
|
||||
);
|
||||
if (!D.isFavorite) {
|
||||
D.isFavorite =
|
||||
favoriteStore.localWorldFavoritesList.includes(
|
||||
D.id
|
||||
);
|
||||
}
|
||||
let { isPC, isQuest, isIos } = getAvailablePlatforms(
|
||||
args.ref.unityPackages
|
||||
);
|
||||
D.avatarScalingDisabled = args.ref?.tags.includes(
|
||||
'feature_avatar_scaling_disabled'
|
||||
);
|
||||
D.focusViewDisabled = args.ref?.tags.includes(
|
||||
'feature_focus_view_disabled'
|
||||
);
|
||||
D.isPC = isPC;
|
||||
D.isQuest = isQuest;
|
||||
D.isIos = isIos;
|
||||
worldStore.updateVRChatWorldCache();
|
||||
queryRequest
|
||||
.fetch('worldPersistData', {
|
||||
worldId: D.id
|
||||
})
|
||||
.then((args) => {
|
||||
if (
|
||||
args.params.worldId === worldStore.worldDialog.id &&
|
||||
worldStore.worldDialog.visible
|
||||
) {
|
||||
worldStore.worldDialog.hasPersistData =
|
||||
args.json !== false;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} json
|
||||
* @returns {object} ref
|
||||
*/
|
||||
export function applyWorld(json) {
|
||||
const worldStore = useWorldStore();
|
||||
const favoriteStore = useFavoriteStore();
|
||||
const instanceStore = useInstanceStore();
|
||||
const userStore = useUserStore();
|
||||
|
||||
sanitizeEntityJson(json, ['name', 'description']);
|
||||
let ref = worldStore.cachedWorlds.get(json.id);
|
||||
if (typeof ref === 'undefined') {
|
||||
ref = createDefaultWorldRef(json);
|
||||
evictMapCache(worldStore.cachedWorlds, 10000, () => false, {
|
||||
logLabel: 'World cache cleanup'
|
||||
});
|
||||
worldStore.cachedWorlds.set(ref.id, ref);
|
||||
} else {
|
||||
Object.assign(ref, json);
|
||||
}
|
||||
ref.$isLabs = ref.tags.includes('system_labs');
|
||||
applyFavorite('world', ref.id);
|
||||
const userDialog = userStore.userDialog;
|
||||
if (userDialog.visible && userDialog.$location.worldId === ref.id) {
|
||||
userStore.applyUserDialogLocation();
|
||||
}
|
||||
const worldDialog = worldStore.worldDialog;
|
||||
if (worldDialog.visible && worldDialog.id === ref.id) {
|
||||
worldDialog.ref = ref;
|
||||
worldDialog.avatarScalingDisabled = ref.tags?.includes(
|
||||
'feature_avatar_scaling_disabled'
|
||||
);
|
||||
worldDialog.focusViewDisabled = ref.tags?.includes(
|
||||
'feature_focus_view_disabled'
|
||||
);
|
||||
instanceStore.applyWorldDialogInstances();
|
||||
for (const room of worldDialog.rooms) {
|
||||
if (isRealInstance(room.tag)) {
|
||||
instanceRequest.getInstance({
|
||||
worldId: worldDialog.id,
|
||||
instanceId: room.id
|
||||
});
|
||||
}
|
||||
}
|
||||
if (Object.keys(worldDialog.fileAnalysis).length === 0) {
|
||||
getBundleDateSize(ref);
|
||||
}
|
||||
}
|
||||
if (favoriteStore.localWorldFavoritesList.includes(ref.id)) {
|
||||
// update db cache
|
||||
database.addWorldToCache(ref);
|
||||
}
|
||||
patchWorldFromEvent(ref);
|
||||
return ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Preload all own worlds into cache at startup for global search.
|
||||
*/
|
||||
export async function preloadOwnWorlds() {
|
||||
const params = {
|
||||
n: 50,
|
||||
offset: 0,
|
||||
sort: 'updated',
|
||||
order: 'descending',
|
||||
releaseStatus: 'all',
|
||||
user: 'me'
|
||||
};
|
||||
await processBulk({
|
||||
fn: (p) => worldRequest.getWorlds(p),
|
||||
N: -1,
|
||||
params,
|
||||
handle: (args) => {
|
||||
for (const json of args.json) {
|
||||
applyWorld(json);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
useUpdateLoopStore,
|
||||
useUserStore
|
||||
} from '../stores';
|
||||
import { getCurrentUser } from '../coordinators/userCoordinator';
|
||||
import { AppDebug } from './appConfig.js';
|
||||
import { escapeTag } from '../shared/utils';
|
||||
import { i18n } from '../plugin/i18n';
|
||||
@@ -207,7 +208,7 @@ export function request(endpoint, options) {
|
||||
) {
|
||||
// trigger 2FA dialog }
|
||||
if (!authStore.twoFactorAuthDialogVisible) {
|
||||
userStore.getCurrentUser();
|
||||
getCurrentUser();
|
||||
}
|
||||
$throw(401, t('api.status_code.401'), endpoint);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,13 @@ import {
|
||||
useUiStore,
|
||||
useUserStore
|
||||
} from '../stores';
|
||||
import { applyUser, applyCurrentUser } from '../coordinators/userCoordinator';
|
||||
import {
|
||||
onGroupLeft,
|
||||
applyGroup,
|
||||
getGroupDialogGroup,
|
||||
handleGroupMember
|
||||
} from '../coordinators/groupCoordinator';
|
||||
import { escapeTag, parseLocation } from '../shared/utils';
|
||||
import { AppDebug } from './appConfig';
|
||||
import { groupRequest } from '../api';
|
||||
@@ -260,7 +267,7 @@ function handlePipeline(args) {
|
||||
break;
|
||||
|
||||
case 'friend-add':
|
||||
userStore.applyUser(content.user);
|
||||
applyUser(content.user);
|
||||
friendStore.handleFriendAdd({
|
||||
params: {
|
||||
userId: content.userId
|
||||
@@ -298,7 +305,7 @@ function handlePipeline(args) {
|
||||
|
||||
...content.user
|
||||
};
|
||||
userStore.applyUser(onlineJson);
|
||||
applyUser(onlineJson);
|
||||
} else {
|
||||
console.error('friend-online missing user id', content);
|
||||
runUpdateFriendFlow(content.userId, 'online');
|
||||
@@ -321,7 +328,7 @@ function handlePipeline(args) {
|
||||
|
||||
...content.user
|
||||
};
|
||||
userStore.applyUser(activeJson);
|
||||
applyUser(activeJson);
|
||||
} else {
|
||||
console.error('friend-active missing user id', content);
|
||||
runUpdateFriendFlow(content.userId, 'active');
|
||||
@@ -342,11 +349,11 @@ function handlePipeline(args) {
|
||||
travelingToWorld: 'offline',
|
||||
travelingToInstance: 'offline'
|
||||
};
|
||||
userStore.applyUser(offlineJson);
|
||||
applyUser(offlineJson);
|
||||
break;
|
||||
|
||||
case 'friend-update':
|
||||
userStore.applyUser(content.user);
|
||||
applyUser(content.user);
|
||||
break;
|
||||
|
||||
case 'friend-location':
|
||||
@@ -365,7 +372,7 @@ function handlePipeline(args) {
|
||||
travelingToWorld: $travelingToLocation1.worldId,
|
||||
travelingToInstance: $travelingToLocation1.instanceId
|
||||
};
|
||||
userStore.applyUser(jankLocationJson);
|
||||
applyUser(jankLocationJson);
|
||||
break;
|
||||
}
|
||||
const locationJson = {
|
||||
@@ -378,12 +385,12 @@ function handlePipeline(args) {
|
||||
...content.user,
|
||||
state: 'online' // JANK
|
||||
};
|
||||
userStore.applyUser(locationJson);
|
||||
applyUser(locationJson);
|
||||
|
||||
break;
|
||||
|
||||
case 'user-update':
|
||||
userStore.applyCurrentUser(content.user);
|
||||
applyCurrentUser(content.user);
|
||||
break;
|
||||
|
||||
case 'user-location':
|
||||
@@ -410,14 +417,14 @@ function handlePipeline(args) {
|
||||
break;
|
||||
|
||||
case 'group-left':
|
||||
groupStore.onGroupLeft(content.groupId);
|
||||
onGroupLeft(content.groupId);
|
||||
break;
|
||||
|
||||
case 'group-role-updated':
|
||||
const groupId = content.role.groupId;
|
||||
groupRequest
|
||||
.getGroup({ groupId, includeRoles: true })
|
||||
.then((args) => groupStore.applyGroup(args.json));
|
||||
.then((args) => applyGroup(args.json));
|
||||
console.log('group-role-updated', content);
|
||||
|
||||
// content {
|
||||
@@ -446,9 +453,9 @@ function handlePipeline(args) {
|
||||
groupStore.groupDialog.visible &&
|
||||
groupStore.groupDialog.id === groupId1
|
||||
) {
|
||||
groupStore.getGroupDialogGroup(groupId1);
|
||||
getGroupDialogGroup(groupId1);
|
||||
}
|
||||
groupStore.handleGroupMember({
|
||||
handleGroupMember({
|
||||
json: member,
|
||||
params: {
|
||||
groupId: groupId1
|
||||
|
||||
@@ -16,6 +16,7 @@ import { escapeTag } from '../shared/utils';
|
||||
import { initWebsocket } from '../service/websocket';
|
||||
import { request } from '../service/request';
|
||||
import { runHandleAutoLoginFlow } from '../coordinators/authAutoLoginCoordinator';
|
||||
import { getCurrentUser } from '../coordinators/userCoordinator';
|
||||
import { useAdvancedSettingsStore } from './settings/advanced';
|
||||
import { useGeneralSettingsStore } from './settings/general';
|
||||
import { useModalStore } from './modal';
|
||||
@@ -197,8 +198,7 @@ export const useAuthStore = defineStore('Auth', () => {
|
||||
throw err;
|
||||
})
|
||||
.then(() => {
|
||||
userStore
|
||||
.getCurrentUser()
|
||||
getCurrentUser()
|
||||
.finally(() => {
|
||||
loginForm.value.loading = false;
|
||||
})
|
||||
@@ -671,7 +671,7 @@ export const useAuthStore = defineStore('Auth', () => {
|
||||
clearCookiesTryLogin();
|
||||
})
|
||||
.then(() => {
|
||||
userStore.getCurrentUser();
|
||||
getCurrentUser();
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
@@ -713,7 +713,7 @@ export const useAuthStore = defineStore('Auth', () => {
|
||||
clearCookiesTryLogin();
|
||||
})
|
||||
.then(() => {
|
||||
userStore.getCurrentUser();
|
||||
getCurrentUser();
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
@@ -756,7 +756,7 @@ export const useAuthStore = defineStore('Auth', () => {
|
||||
promptEmailOTP();
|
||||
})
|
||||
.then(() => {
|
||||
userStore.getCurrentUser();
|
||||
getCurrentUser();
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
@@ -1,46 +1,13 @@
|
||||
import { nextTick, ref, watch } from 'vue';
|
||||
import { ref, watch } from 'vue';
|
||||
import { defineStore } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import {
|
||||
checkVRChatCache,
|
||||
createDefaultAvatarRef,
|
||||
extractFileId,
|
||||
getAvailablePlatforms,
|
||||
getBundleDateSize,
|
||||
getPlatformInfo,
|
||||
replaceBioSymbols,
|
||||
sanitizeEntityJson,
|
||||
storeAvatarImage
|
||||
checkVRChatCache
|
||||
} 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 '../coordinators/favoriteCoordinator';
|
||||
import { useAdvancedSettingsStore } from './settings/advanced';
|
||||
import { useAvatarProviderStore } from './avatarProvider';
|
||||
import { useFavoriteStore } from './favorite';
|
||||
import { useModalStore } from './modal';
|
||||
import { useUiStore } from './ui';
|
||||
import { useUserStore } from './user';
|
||||
import { useVRCXUpdaterStore } from './vrcxUpdater';
|
||||
import { watchState } from '../service/watchState';
|
||||
|
||||
import webApiService from '../service/webapi';
|
||||
|
||||
export const useAvatarStore = defineStore('Avatar', () => {
|
||||
const favoriteStore = useFavoriteStore();
|
||||
const avatarProviderStore = useAvatarProviderStore();
|
||||
const vrcxUpdaterStore = useVRCXUpdaterStore();
|
||||
const advancedSettingsStore = useAdvancedSettingsStore();
|
||||
const userStore = useUserStore();
|
||||
const modalStore = useModalStore();
|
||||
const uiStore = useUiStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
let cachedAvatarModerations = new Map();
|
||||
let cachedAvatars = new Map();
|
||||
let cachedAvatarNames = new Map();
|
||||
@@ -83,145 +50,17 @@ export const useAvatarStore = defineStore('Avatar', () => {
|
||||
cachedAvatarModerations.clear();
|
||||
avatarHistory.value = [];
|
||||
if (isLoggedIn) {
|
||||
getAvatarHistory();
|
||||
preloadOwnAvatars();
|
||||
import('../coordinators/avatarCoordinator').then(
|
||||
({ getAvatarHistory, preloadOwnAvatars }) => {
|
||||
getAvatarHistory();
|
||||
preloadOwnAvatars();
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
{ flush: 'sync' }
|
||||
);
|
||||
|
||||
/**
|
||||
* @param {object} json
|
||||
* @returns {object} ref
|
||||
*/
|
||||
function applyAvatar(json) {
|
||||
sanitizeEntityJson(json, ['name', 'description']);
|
||||
let ref = cachedAvatars.get(json.id);
|
||||
if (typeof ref === 'undefined') {
|
||||
ref = createDefaultAvatarRef(json);
|
||||
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
|
||||
*/
|
||||
function showAvatarDialog(avatarId, options = {}) {
|
||||
const D = avatarDialog.value;
|
||||
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 = cachedAvatarModerations.has(avatarId);
|
||||
const ref2 = 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
|
||||
);
|
||||
getAvatarGallery(avatarId);
|
||||
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));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {boolean} value
|
||||
*/
|
||||
@@ -249,6 +88,7 @@ export const useAvatarStore = defineStore('Avatar', () => {
|
||||
* @returns {Promise<string[]>}
|
||||
*/
|
||||
async function getAvatarGallery(avatarId) {
|
||||
const { queryRequest } = await import('../api');
|
||||
const D = avatarDialog.value;
|
||||
const args = await queryRequest
|
||||
.fetch('avatarGallery', { avatarId })
|
||||
@@ -341,54 +181,6 @@ export const useAvatarStore = defineStore('Avatar', () => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function getAvatarHistory() {
|
||||
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);
|
||||
}
|
||||
avatarHistory.value = historyArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} avatarId
|
||||
*/
|
||||
function addAvatarToHistory(avatarId) {
|
||||
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 = avatarHistory.value;
|
||||
for (let i = 0; i < historyArray.length; ++i) {
|
||||
if (historyArray[i].id === ref.id) {
|
||||
historyArray.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
avatarHistory.value.unshift(ref);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Failed to add avatar to history:', err);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -397,423 +189,21 @@ export const useAvatarStore = defineStore('Avatar', () => {
|
||||
database.clearAvatarHistory();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function promptClearAvatarHistory() {
|
||||
modalStore
|
||||
.confirm({
|
||||
description: t('confirm.clear_avatar_history'),
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
clearAvatarHistory();
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} imageUrl
|
||||
* @returns {Promise<object>}
|
||||
*/
|
||||
async function getAvatarName(imageUrl) {
|
||||
const fileId = extractFileId(imageUrl);
|
||||
if (!fileId) {
|
||||
return {
|
||||
ownerId: '',
|
||||
avatarName: '-'
|
||||
};
|
||||
}
|
||||
if (cachedAvatarNames.has(fileId)) {
|
||||
return cachedAvatarNames.get(fileId);
|
||||
}
|
||||
try {
|
||||
const args = await miscRequest.getFile({ fileId });
|
||||
return storeAvatarImage(args, cachedAvatarNames);
|
||||
} catch (error) {
|
||||
console.error('Failed to get avatar images:', error);
|
||||
return {
|
||||
ownerId: '',
|
||||
avatarName: '-'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param type
|
||||
* @param search
|
||||
*/
|
||||
async function lookupAvatars(type, search) {
|
||||
const avatars = new Map();
|
||||
if (type === 'search') {
|
||||
try {
|
||||
const url = `${
|
||||
avatarProviderStore.avatarRemoteDatabaseProvider
|
||||
}?${type}=${encodeURIComponent(search)}&n=5000`;
|
||||
const response = await webApiService.execute({
|
||||
url,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Referer: 'https://vrcx.app',
|
||||
'VRCX-ID': vrcxUpdaterStore.vrcxId
|
||||
}
|
||||
});
|
||||
const json = JSON.parse(response.data);
|
||||
if (AppDebug.debugWebRequests) {
|
||||
console.log(url, json, response);
|
||||
}
|
||||
if (response.status === 200 && typeof json === 'object') {
|
||||
json.forEach((avatar) => {
|
||||
if (!avatars.has(avatar.Id)) {
|
||||
const ref = {
|
||||
authorId: '',
|
||||
authorName: '',
|
||||
name: '',
|
||||
description: '',
|
||||
id: '',
|
||||
imageUrl: '',
|
||||
thumbnailImageUrl: '',
|
||||
created_at: '0001-01-01T00:00:00.0000000Z',
|
||||
updated_at: '0001-01-01T00:00:00.0000000Z',
|
||||
releaseStatus: 'public',
|
||||
...avatar
|
||||
};
|
||||
avatars.set(ref.id, ref);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw new Error(`Error: ${response.data}`);
|
||||
}
|
||||
} catch (err) {
|
||||
const msg = `Avatar search failed for ${search} with ${avatarProviderStore.avatarRemoteDatabaseProvider}\n${err}`;
|
||||
console.error(msg);
|
||||
toast.error(msg);
|
||||
}
|
||||
} else if (type === 'authorId') {
|
||||
const length =
|
||||
avatarProviderStore.avatarRemoteDatabaseProviderList.length;
|
||||
for (let i = 0; i < length; ++i) {
|
||||
const url =
|
||||
avatarProviderStore.avatarRemoteDatabaseProviderList[i];
|
||||
const avatarArray = await lookupAvatarsByAuthor(url, search);
|
||||
avatarArray.forEach((avatar) => {
|
||||
if (!avatars.has(avatar.id)) {
|
||||
avatars.set(avatar.id, avatar);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return avatars;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param authorId
|
||||
* @param fileId
|
||||
*/
|
||||
async function lookupAvatarByImageFileId(authorId, fileId) {
|
||||
for (const providerUrl of avatarProviderStore.avatarRemoteDatabaseProviderList) {
|
||||
const avatar = await lookupAvatarByFileId(providerUrl, fileId);
|
||||
if (avatar?.id) {
|
||||
return avatar.id;
|
||||
}
|
||||
}
|
||||
|
||||
for (const providerUrl of avatarProviderStore.avatarRemoteDatabaseProviderList) {
|
||||
const avatarArray = await lookupAvatarsByAuthor(
|
||||
providerUrl,
|
||||
authorId
|
||||
);
|
||||
for (const avatar of avatarArray) {
|
||||
if (extractFileId(avatar.imageUrl) === fileId) {
|
||||
return avatar.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param providerUrl
|
||||
* @param fileId
|
||||
*/
|
||||
async function lookupAvatarByFileId(providerUrl, fileId) {
|
||||
try {
|
||||
const url = `${providerUrl}?fileId=${encodeURIComponent(fileId)}`;
|
||||
const response = await webApiService.execute({
|
||||
url,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Referer: 'https://vrcx.app',
|
||||
'VRCX-ID': vrcxUpdaterStore.vrcxId
|
||||
}
|
||||
});
|
||||
const json = JSON.parse(response.data);
|
||||
if (AppDebug.debugWebRequests) {
|
||||
console.log(url, json, response);
|
||||
}
|
||||
if (response.status === 200 && typeof json === 'object') {
|
||||
const ref = {
|
||||
authorId: '',
|
||||
authorName: '',
|
||||
name: '',
|
||||
description: '',
|
||||
id: '',
|
||||
imageUrl: '',
|
||||
thumbnailImageUrl: '',
|
||||
created_at: '0001-01-01T00:00:00.0000000Z',
|
||||
updated_at: '0001-01-01T00:00:00.0000000Z',
|
||||
releaseStatus: 'public',
|
||||
...json
|
||||
};
|
||||
return ref;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (err) {
|
||||
// ignore errors for now, not all providers support this lookup type
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param providerUrl
|
||||
* @param authorId
|
||||
*/
|
||||
async function lookupAvatarsByAuthor(providerUrl, authorId) {
|
||||
const avatars = [];
|
||||
if (!providerUrl || !authorId) {
|
||||
return avatars;
|
||||
}
|
||||
const url = `${providerUrl}?authorId=${encodeURIComponent(authorId)}`;
|
||||
try {
|
||||
const response = await webApiService.execute({
|
||||
url,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Referer: 'https://vrcx.app',
|
||||
'VRCX-ID': vrcxUpdaterStore.vrcxId
|
||||
}
|
||||
});
|
||||
const json = JSON.parse(response.data);
|
||||
if (AppDebug.debugWebRequests) {
|
||||
console.log(url, json, response);
|
||||
}
|
||||
if (response.status === 200 && typeof json === 'object') {
|
||||
json.forEach((avatar) => {
|
||||
const ref = {
|
||||
authorId: '',
|
||||
authorName: '',
|
||||
name: '',
|
||||
description: '',
|
||||
id: '',
|
||||
imageUrl: '',
|
||||
thumbnailImageUrl: '',
|
||||
created_at: '0001-01-01T00:00:00.0000000Z',
|
||||
updated_at: '0001-01-01T00:00:00.0000000Z',
|
||||
releaseStatus: 'public',
|
||||
...avatar
|
||||
};
|
||||
avatars.push(ref);
|
||||
});
|
||||
} else {
|
||||
throw new Error(`Error: ${response.data}`);
|
||||
}
|
||||
} catch (err) {
|
||||
const msg = `Avatar lookup failed for ${authorId} with ${url}\n${err}`;
|
||||
console.error(msg);
|
||||
toast.error(msg);
|
||||
}
|
||||
return avatars;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param id
|
||||
*/
|
||||
function selectAvatarWithConfirmation(id) {
|
||||
modalStore
|
||||
.confirm({
|
||||
description: t('confirm.select_avatar'),
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
selectAvatarWithoutConfirmation(id);
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param id
|
||||
*/
|
||||
async function selectAvatarWithoutConfirmation(id) {
|
||||
if (userStore.currentUser.currentAvatar === id) {
|
||||
toast.info('Avatar already selected');
|
||||
return;
|
||||
}
|
||||
return avatarRequest
|
||||
.selectAvatar({
|
||||
avatarId: id
|
||||
})
|
||||
.then(() => {
|
||||
toast.success('Avatar changed');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param fileId
|
||||
*/
|
||||
function checkAvatarCache(fileId) {
|
||||
let avatarId = '';
|
||||
for (let ref of cachedAvatars.values()) {
|
||||
if (extractFileId(ref.imageUrl) === fileId) {
|
||||
avatarId = ref.id;
|
||||
}
|
||||
}
|
||||
return avatarId;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param fileId
|
||||
* @param ownerUserId
|
||||
*/
|
||||
async function checkAvatarCacheRemote(fileId, ownerUserId) {
|
||||
if (advancedSettingsStore.avatarRemoteDatabase) {
|
||||
try {
|
||||
toast.dismiss(loadingToastId.value);
|
||||
loadingToastId.value = toast.loading(
|
||||
t('message.avatar_lookup.loading')
|
||||
);
|
||||
const avatarId = await lookupAvatarByImageFileId(
|
||||
ownerUserId,
|
||||
fileId
|
||||
);
|
||||
return avatarId;
|
||||
} catch (err) {
|
||||
console.error('Failed to lookup avatar by image file id:', err);
|
||||
} finally {
|
||||
toast.dismiss(loadingToastId.value);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param refUserId
|
||||
* @param ownerUserId
|
||||
* @param currentAvatarImageUrl
|
||||
*/
|
||||
async function showAvatarAuthorDialog(
|
||||
refUserId,
|
||||
ownerUserId,
|
||||
currentAvatarImageUrl
|
||||
) {
|
||||
const fileId = extractFileId(currentAvatarImageUrl);
|
||||
if (!fileId) {
|
||||
toast.error(t('message.avatar_lookup.failed'));
|
||||
} else if (refUserId === userStore.currentUser.id) {
|
||||
showAvatarDialog(userStore.currentUser.currentAvatar);
|
||||
} else {
|
||||
let avatarId = checkAvatarCache(fileId);
|
||||
let avatarInfo;
|
||||
if (!avatarId) {
|
||||
avatarInfo = await getAvatarName(currentAvatarImageUrl);
|
||||
if (avatarInfo.ownerId === userStore.currentUser.id) {
|
||||
await userStore.refreshUserDialogAvatars(fileId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!avatarId) {
|
||||
avatarId = await checkAvatarCacheRemote(fileId, ownerUserId);
|
||||
}
|
||||
if (!avatarId) {
|
||||
if (ownerUserId === refUserId) {
|
||||
toast.warning(
|
||||
t('message.avatar_lookup.private_or_not_found')
|
||||
);
|
||||
} else {
|
||||
toast.warning(t('message.avatar_lookup.not_found'));
|
||||
userStore.showUserDialog(avatarInfo.ownerId);
|
||||
}
|
||||
}
|
||||
if (avatarId) {
|
||||
showAvatarDialog(avatarId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param avatarId
|
||||
*/
|
||||
function addAvatarWearTime(avatarId) {
|
||||
if (!userStore.currentUser.$previousAvatarSwapTime || !avatarId) {
|
||||
return;
|
||||
}
|
||||
const timeSpent =
|
||||
Date.now() - userStore.currentUser.$previousAvatarSwapTime;
|
||||
database.addAvatarTimeSpent(avatarId, timeSpent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Preload all own avatars into cache at startup for global search.
|
||||
*/
|
||||
async function preloadOwnAvatars() {
|
||||
const params = {
|
||||
n: 50,
|
||||
offset: 0,
|
||||
sort: 'updated',
|
||||
order: 'descending',
|
||||
releaseStatus: 'all',
|
||||
user: 'me'
|
||||
};
|
||||
await processBulk({
|
||||
fn: avatarRequest.getAvatars,
|
||||
N: -1,
|
||||
params,
|
||||
handle: (args) => {
|
||||
for (const json of args.json) {
|
||||
applyAvatar(json);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
avatarDialog,
|
||||
avatarHistory,
|
||||
loadingToastId,
|
||||
cachedAvatarModerations,
|
||||
cachedAvatars,
|
||||
cachedAvatarNames,
|
||||
|
||||
showAvatarDialog,
|
||||
applyAvatarModeration,
|
||||
resetCachedAvatarModerations,
|
||||
getAvatarGallery,
|
||||
updateVRChatAvatarCache,
|
||||
getAvatarHistory,
|
||||
addAvatarToHistory,
|
||||
applyAvatar,
|
||||
promptClearAvatarHistory,
|
||||
getAvatarName,
|
||||
lookupAvatars,
|
||||
selectAvatarWithConfirmation,
|
||||
selectAvatarWithoutConfirmation,
|
||||
clearAvatarHistory,
|
||||
setAvatarDialogVisible,
|
||||
setAvatarDialogIsFavorite,
|
||||
setAvatarDialogLoading,
|
||||
showAvatarAuthorDialog,
|
||||
addAvatarWearTime,
|
||||
preloadOwnAvatars
|
||||
setAvatarDialogLoading
|
||||
};
|
||||
});
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
runPendingOfflineTickFlow,
|
||||
runUpdateFriendFlow
|
||||
} from '../coordinators/friendPresenceCoordinator';
|
||||
import { applyUser } from '../coordinators/userCoordinator';
|
||||
import { AppDebug } from '../service/appConfig';
|
||||
import { database } from '../service/database';
|
||||
import { useAppearanceSettingsStore } from './settings/appearance';
|
||||
@@ -981,7 +982,7 @@ export const useFriendStore = defineStore('Friend', () => {
|
||||
const sqlValues = [];
|
||||
const friends = await refreshFriends();
|
||||
for (const friend of friends) {
|
||||
const ref = userStore.applyUser(friend);
|
||||
const ref = applyUser(friend);
|
||||
const row = {
|
||||
userId: ref.id,
|
||||
displayName: ref.displayName,
|
||||
|
||||
@@ -13,6 +13,10 @@ import { useAvatarStore } from './avatar';
|
||||
import { useFavoriteStore } from './favorite';
|
||||
import { useFriendStore } from './friend';
|
||||
import { useGroupStore } from './group';
|
||||
import { showGroupDialog } from '../coordinators/groupCoordinator';
|
||||
import { showWorldDialog } from '../coordinators/worldCoordinator';
|
||||
import { showAvatarDialog } from '../coordinators/avatarCoordinator';
|
||||
import { showUserDialog } from '../coordinators/userCoordinator';
|
||||
import { useUserStore } from './user';
|
||||
import { useWorldStore } from './world';
|
||||
|
||||
@@ -166,16 +170,16 @@ export const useGlobalSearchStore = defineStore('GlobalSearch', () => {
|
||||
|
||||
switch (item.type) {
|
||||
case 'friend':
|
||||
userStore.showUserDialog(item.id);
|
||||
showUserDialog(item.id);
|
||||
break;
|
||||
case 'avatar':
|
||||
avatarStore.showAvatarDialog(item.id);
|
||||
showAvatarDialog(item.id);
|
||||
break;
|
||||
case 'world':
|
||||
worldStore.showWorldDialog(item.id);
|
||||
showWorldDialog(item.id);
|
||||
break;
|
||||
case 'group':
|
||||
groupStore.showGroupDialog(item.id);
|
||||
showGroupDialog(item.id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -44,6 +44,8 @@ import { useFriendStore } from '../friend';
|
||||
import { useGameStore } from '../game';
|
||||
import { useGeneralSettingsStore } from '../settings/general';
|
||||
import { useGroupStore } from '../group';
|
||||
import { showGroupDialog } from '../../coordinators/groupCoordinator';
|
||||
import { showUserDialog } from '../../coordinators/userCoordinator';
|
||||
import { useInstanceStore } from '../instance';
|
||||
import { useLocationStore } from '../location';
|
||||
import { useModalStore } from '../modal';
|
||||
@@ -1415,10 +1417,10 @@ export const useNotificationStore = defineStore('Notification', () => {
|
||||
}
|
||||
switch (data[0]) {
|
||||
case 'group':
|
||||
groupStore.showGroupDialog(data[1]);
|
||||
showGroupDialog(data[1]);
|
||||
break;
|
||||
case 'user':
|
||||
userStore.showUserDialog(data[1]);
|
||||
showUserDialog(data[1]);
|
||||
break;
|
||||
case 'event':
|
||||
const ids = data[1].split(',');
|
||||
@@ -1427,7 +1429,7 @@ export const useNotificationStore = defineStore('Notification', () => {
|
||||
return;
|
||||
}
|
||||
|
||||
groupStore.showGroupDialog(ids[0]);
|
||||
showGroupDialog(ids[0]);
|
||||
// ids[1] cal_ is the event id
|
||||
break;
|
||||
case 'openNotificationLink':
|
||||
|
||||
@@ -20,6 +20,8 @@ import { photonEmojis, photonEventType } from '../shared/constants/photon';
|
||||
import { AppDebug } from '../service/appConfig';
|
||||
import { database } from '../service/database';
|
||||
import { useAvatarStore } from './avatar';
|
||||
import { applyAvatar } from '../coordinators/avatarCoordinator';
|
||||
import { showUserDialog, lookupUser, applyUser } from '../coordinators/userCoordinator';
|
||||
import { useFavoriteStore } from './favorite';
|
||||
import { useFriendStore } from './friend';
|
||||
import { useGameLogStore } from './gameLog';
|
||||
@@ -424,9 +426,9 @@ export const usePhotonStore = defineStore('Photon', () => {
|
||||
const ref = photonLobby.value.get(photonId);
|
||||
if (typeof ref !== 'undefined') {
|
||||
if (typeof ref.id !== 'undefined') {
|
||||
userStore.showUserDialog(ref.id);
|
||||
showUserDialog(ref.id);
|
||||
} else if (typeof ref.displayName !== 'undefined') {
|
||||
userStore.lookupUser(ref);
|
||||
lookupUser(ref);
|
||||
}
|
||||
} else {
|
||||
toast.error('No user info available');
|
||||
@@ -1475,7 +1477,7 @@ export const usePhotonStore = defineStore('Photon', () => {
|
||||
typeof ref.id !== 'undefined' &&
|
||||
ref.currentAvatarImageUrl !== user.currentAvatarImageUrl
|
||||
) {
|
||||
userStore.applyUser({
|
||||
applyUser({
|
||||
...ref,
|
||||
currentAvatarImageUrl: user.currentAvatarImageUrl,
|
||||
currentAvatarThumbnailImageUrl:
|
||||
@@ -1803,7 +1805,7 @@ export const usePhotonStore = defineStore('Photon', () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
avatarStore.applyAvatar({
|
||||
applyAvatar({
|
||||
id: avatar.id,
|
||||
authorId: avatar.authorId,
|
||||
authorName: avatar.authorName,
|
||||
|
||||
@@ -12,6 +12,10 @@ import { useAppearanceSettingsStore } from './settings/appearance';
|
||||
import { useAvatarStore } from './avatar';
|
||||
import { useFriendStore } from './friend';
|
||||
import { useGroupStore } from './group';
|
||||
import { showGroupDialog } from '../coordinators/groupCoordinator';
|
||||
import { showWorldDialog } from '../coordinators/worldCoordinator';
|
||||
import { showAvatarDialog } from '../coordinators/avatarCoordinator';
|
||||
import { applyUser, showUserDialog, lookupUser } from '../coordinators/userCoordinator';
|
||||
import { useModalStore } from './modal';
|
||||
import { useUserStore } from './user';
|
||||
import { useWorldStore } from './world';
|
||||
@@ -93,7 +97,7 @@ export const useSearchStore = defineStore('Search', () => {
|
||||
console.error('getUsers gave us garbage', json);
|
||||
continue;
|
||||
}
|
||||
userStore.applyUser(json);
|
||||
applyUser(json);
|
||||
}
|
||||
|
||||
const map = new Map();
|
||||
@@ -210,10 +214,10 @@ export const useSearchStore = defineStore('Search', () => {
|
||||
} else {
|
||||
router.push({ name: 'search' });
|
||||
searchText.value = searchTerm;
|
||||
userStore.lookupUser({ displayName: searchTerm });
|
||||
lookupUser({ displayName: searchTerm });
|
||||
}
|
||||
} else {
|
||||
userStore.showUserDialog(value);
|
||||
showUserDialog(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,15 +277,15 @@ export const useSearchStore = defineStore('Search', () => {
|
||||
const type = urlPathSplit[2];
|
||||
if (type === 'user') {
|
||||
const userId = urlPathSplit[3];
|
||||
userStore.showUserDialog(userId);
|
||||
showUserDialog(userId);
|
||||
return true;
|
||||
} else if (type === 'avatar') {
|
||||
const avatarId = urlPathSplit[3];
|
||||
avatarStore.showAvatarDialog(avatarId);
|
||||
showAvatarDialog(avatarId);
|
||||
return true;
|
||||
} else if (type === 'group') {
|
||||
const groupId = urlPathSplit[3];
|
||||
groupStore.showGroupDialog(groupId);
|
||||
showGroupDialog(groupId);
|
||||
return true;
|
||||
}
|
||||
} else if (input.startsWith('https://vrc.group/')) {
|
||||
@@ -295,16 +299,16 @@ export const useSearchStore = defineStore('Search', () => {
|
||||
input.substring(0, 4) === 'usr_' ||
|
||||
/^[A-Za-z0-9]{10}$/g.test(input)
|
||||
) {
|
||||
userStore.showUserDialog(input);
|
||||
showUserDialog(input);
|
||||
return true;
|
||||
} else if (
|
||||
input.substring(0, 5) === 'avtr_' ||
|
||||
input.substring(0, 2) === 'b_'
|
||||
) {
|
||||
avatarStore.showAvatarDialog(input);
|
||||
showAvatarDialog(input);
|
||||
return true;
|
||||
} else if (input.substring(0, 4) === 'grp_') {
|
||||
groupStore.showGroupDialog(input);
|
||||
showGroupDialog(input);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -331,7 +335,7 @@ export const useSearchStore = defineStore('Search', () => {
|
||||
const urlPathSplit = urlPath.split('/');
|
||||
if (urlPathSplit.length >= 4 && urlPathSplit[2] === 'world') {
|
||||
worldId = urlPathSplit[3];
|
||||
worldStore.showWorldDialog(worldId);
|
||||
showWorldDialog(worldId);
|
||||
return true;
|
||||
} else if (urlPath.substring(5, 12) === '/launch') {
|
||||
const urlParams = new URLSearchParams(url.search);
|
||||
@@ -343,10 +347,10 @@ export const useSearchStore = defineStore('Search', () => {
|
||||
if (shortName) {
|
||||
return verifyShortName(location, shortName);
|
||||
}
|
||||
worldStore.showWorldDialog(location);
|
||||
showWorldDialog(location);
|
||||
return true;
|
||||
} else if (worldId) {
|
||||
worldStore.showWorldDialog(worldId);
|
||||
showWorldDialog(worldId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -360,7 +364,7 @@ export const useSearchStore = defineStore('Search', () => {
|
||||
input = `https://vrchat.com/home/launch?worldId=${input}`;
|
||||
return directAccessWorld(input);
|
||||
}
|
||||
worldStore.showWorldDialog(input.trim());
|
||||
showWorldDialog(input.trim());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -399,7 +403,7 @@ export const useSearchStore = defineStore('Search', () => {
|
||||
groupRequest.groupStrictsearch({ query: shortCode }).then((args) => {
|
||||
for (const group of args.json) {
|
||||
if (`${group.shortCode}.${group.discriminator}` === shortCode) {
|
||||
groupStore.showGroupDialog(group.id);
|
||||
showGroupDialog(group.id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -414,11 +418,11 @@ export const useSearchStore = defineStore('Search', () => {
|
||||
const newLocation = args.json.location;
|
||||
const newShortName = args.json.shortName;
|
||||
if (newShortName) {
|
||||
worldStore.showWorldDialog(newLocation, newShortName);
|
||||
showWorldDialog(newLocation, newShortName);
|
||||
} else if (newLocation) {
|
||||
worldStore.showWorldDialog(newLocation);
|
||||
showWorldDialog(newLocation);
|
||||
} else {
|
||||
worldStore.showWorldDialog(location);
|
||||
showWorldDialog(location);
|
||||
}
|
||||
return args;
|
||||
});
|
||||
|
||||
@@ -10,6 +10,10 @@ import { updateLocalizedStrings } from '../plugin/i18n';
|
||||
import { useAppearanceSettingsStore } from './settings/appearance';
|
||||
import { useAvatarStore } from './avatar';
|
||||
import { useGroupStore } from './group';
|
||||
import { showGroupDialog } from '../coordinators/groupCoordinator';
|
||||
import { showWorldDialog } from '../coordinators/worldCoordinator';
|
||||
import { showAvatarDialog } from '../coordinators/avatarCoordinator';
|
||||
import { showUserDialog } from '../coordinators/userCoordinator';
|
||||
import { useInstanceStore } from './instance';
|
||||
import { useNotificationStore } from './notification';
|
||||
import { useSearchStore } from './search';
|
||||
@@ -142,19 +146,19 @@ export const useUiStore = defineStore('Ui', () => {
|
||||
}
|
||||
jumpDialogCrumb(index);
|
||||
if (item.type === 'user') {
|
||||
userStore.showUserDialog(item.id);
|
||||
showUserDialog(item.id);
|
||||
return;
|
||||
}
|
||||
if (item.type === 'world') {
|
||||
worldStore.showWorldDialog(item.tag, item.shortName);
|
||||
showWorldDialog(item.tag, item.shortName);
|
||||
return;
|
||||
}
|
||||
if (item.type === 'avatar') {
|
||||
avatarStore.showAvatarDialog(item.id);
|
||||
showAvatarDialog(item.id);
|
||||
return;
|
||||
}
|
||||
if (item.type === 'group') {
|
||||
groupStore.showGroupDialog(item.id);
|
||||
showGroupDialog(item.id);
|
||||
return;
|
||||
}
|
||||
if (item.type === 'previous-instances-user') {
|
||||
|
||||
@@ -12,6 +12,8 @@ import { useFriendStore } from './friend';
|
||||
import { useGameLogStore } from './gameLog';
|
||||
import { useGameStore } from './game';
|
||||
import { useGroupStore } from './group';
|
||||
import { handleGroupUserInstances } from '../coordinators/groupCoordinator';
|
||||
import { getCurrentUser, updateAutoStateChange } from '../coordinators/userCoordinator';
|
||||
import { useModerationStore } from './moderation';
|
||||
import { useUserStore } from './user';
|
||||
import { useVRCXUpdaterStore } from './vrcxUpdater';
|
||||
@@ -73,7 +75,7 @@ export const useUpdateLoopStore = defineStore('UpdateLoop', () => {
|
||||
if (watchState.isLoggedIn) {
|
||||
if (--state.nextCurrentUserRefresh <= 0) {
|
||||
state.nextCurrentUserRefresh = 300; // 5min
|
||||
userStore.getCurrentUser();
|
||||
getCurrentUser();
|
||||
}
|
||||
if (--state.nextFriendsRefresh <= 0) {
|
||||
state.nextFriendsRefresh = 3600; // 1hour
|
||||
@@ -92,7 +94,7 @@ export const useUpdateLoopStore = defineStore('UpdateLoop', () => {
|
||||
state.nextGroupInstanceRefresh = 300; // 5min
|
||||
const args =
|
||||
await groupRequest.getUsersGroupInstances();
|
||||
groupStore.handleGroupUserInstances(args);
|
||||
handleGroupUserInstances(args);
|
||||
}
|
||||
AppApi.CheckGameRunning();
|
||||
}
|
||||
@@ -122,7 +124,7 @@ export const useUpdateLoopStore = defineStore('UpdateLoop', () => {
|
||||
}
|
||||
if (--state.nextAutoStateChange <= 0) {
|
||||
state.nextAutoStateChange = 3;
|
||||
userStore.updateAutoStateChange();
|
||||
updateAutoStateChange();
|
||||
}
|
||||
if (LINUX && --state.nextGetLogCheck <= 0) {
|
||||
state.nextGetLogCheck = 0.5;
|
||||
|
||||
1069
src/stores/user.js
1069
src/stores/user.js
File diff suppressed because it is too large
Load Diff
@@ -32,6 +32,10 @@ import { useGalleryStore } from './gallery';
|
||||
import { useGameLogStore } from './gameLog';
|
||||
import { useGameStore } from './game';
|
||||
import { useGroupStore } from './group';
|
||||
import { showGroupDialog } from '../coordinators/groupCoordinator';
|
||||
import { showWorldDialog } from '../coordinators/worldCoordinator';
|
||||
import { showAvatarDialog, selectAvatarWithConfirmation, selectAvatarWithoutConfirmation } from '../coordinators/avatarCoordinator';
|
||||
import { showUserDialog, addCustomTag } from '../coordinators/userCoordinator';
|
||||
import { useInstanceStore } from './instance';
|
||||
import { useLocationStore } from './location';
|
||||
import { useModalStore } from './modal';
|
||||
@@ -335,7 +339,7 @@ export const useVrcxStore = defineStore('Vrcx', () => {
|
||||
let entry;
|
||||
switch (data.MsgType) {
|
||||
case 'CustomTag':
|
||||
userStore.addCustomTag(data);
|
||||
addCustomTag(data);
|
||||
break;
|
||||
case 'ClearCustomTags':
|
||||
userStore.customUserTags.forEach((value, key) => {
|
||||
@@ -669,17 +673,17 @@ export const useVrcxStore = defineStore('Vrcx', () => {
|
||||
!searchStore.directAccessWorld(input.replace('world/', ''))
|
||||
) {
|
||||
// fallback for mangled world ids
|
||||
worldStore.showWorldDialog(commandArg);
|
||||
showWorldDialog(commandArg);
|
||||
}
|
||||
break;
|
||||
case 'avatar':
|
||||
avatarStore.showAvatarDialog(commandArg);
|
||||
showAvatarDialog(commandArg);
|
||||
break;
|
||||
case 'user':
|
||||
userStore.showUserDialog(commandArg);
|
||||
showUserDialog(commandArg);
|
||||
break;
|
||||
case 'group':
|
||||
groupStore.showGroupDialog(commandArg);
|
||||
showGroupDialog(commandArg);
|
||||
break;
|
||||
case 'local-favorite-world':
|
||||
console.log('local-favorite-world', commandArg);
|
||||
@@ -701,7 +705,7 @@ export const useVrcxStore = defineStore('Vrcx', () => {
|
||||
break;
|
||||
}
|
||||
avatarRequest.getAvatar({ avatarId: avatarIdFav }).then(() => {
|
||||
avatarStore.showAvatarDialog(avatarIdFav);
|
||||
showAvatarDialog(avatarIdFav);
|
||||
addLocalAvatarFavorite(
|
||||
avatarIdFav,
|
||||
avatarGroup
|
||||
@@ -722,12 +726,11 @@ export const useVrcxStore = defineStore('Vrcx', () => {
|
||||
break;
|
||||
}
|
||||
if (advancedSettingsStore.showConfirmationOnSwitchAvatar) {
|
||||
avatarStore.selectAvatarWithConfirmation(avatarId);
|
||||
selectAvatarWithConfirmation(avatarId);
|
||||
// Makes sure the window is focused
|
||||
shouldFocusWindow = true;
|
||||
} else {
|
||||
avatarStore
|
||||
.selectAvatarWithoutConfirmation(avatarId)
|
||||
selectAvatarWithoutConfirmation(avatarId)
|
||||
.then(() => {
|
||||
toast.success('Avatar changed via launch command');
|
||||
});
|
||||
|
||||
@@ -1,39 +1,12 @@
|
||||
import { nextTick, reactive, shallowReactive, watch } from 'vue';
|
||||
import { reactive, shallowReactive, watch } from 'vue';
|
||||
import { defineStore } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import {
|
||||
checkVRChatCache,
|
||||
createDefaultWorldRef,
|
||||
evictMapCache,
|
||||
getAvailablePlatforms,
|
||||
getBundleDateSize,
|
||||
getWorldMemo,
|
||||
isRealInstance,
|
||||
parseLocation,
|
||||
sanitizeEntityJson
|
||||
checkVRChatCache
|
||||
} from '../shared/utils';
|
||||
import { instanceRequest, queryRequest, worldRequest } from '../api';
|
||||
import { database } from '../service/database';
|
||||
import { patchWorldFromEvent } from '../queries';
|
||||
import { processBulk } from '../service/request';
|
||||
import { applyFavorite } from '../coordinators/favoriteCoordinator';
|
||||
import { useFavoriteStore } from './favorite';
|
||||
import { useInstanceStore } from './instance';
|
||||
import { useLocationStore } from './location';
|
||||
import { useUiStore } from './ui';
|
||||
import { useUserStore } from './user';
|
||||
import { watchState } from '../service/watchState';
|
||||
|
||||
export const useWorldStore = defineStore('World', () => {
|
||||
const locationStore = useLocationStore();
|
||||
const favoriteStore = useFavoriteStore();
|
||||
const instanceStore = useInstanceStore();
|
||||
const userStore = useUserStore();
|
||||
const uiStore = useUiStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
const worldDialog = reactive({
|
||||
visible: false,
|
||||
loading: false,
|
||||
@@ -69,145 +42,14 @@ export const useWorldStore = defineStore('World', () => {
|
||||
worldDialog.visible = false;
|
||||
cachedWorlds.clear();
|
||||
if (isLoggedIn) {
|
||||
preloadOwnWorlds();
|
||||
import('../coordinators/worldCoordinator').then(
|
||||
({ preloadOwnWorlds }) => preloadOwnWorlds()
|
||||
);
|
||||
}
|
||||
},
|
||||
{ flush: 'sync' }
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} tag
|
||||
* @param {string} shortName
|
||||
* @param options
|
||||
*/
|
||||
function showWorldDialog(tag, shortName = null, options = {}) {
|
||||
const D = worldDialog;
|
||||
const forceRefresh = Boolean(options?.forceRefresh);
|
||||
const L = parseLocation(tag);
|
||||
if (L.worldId === '') {
|
||||
return;
|
||||
}
|
||||
const isMainDialogOpen = uiStore.openDialog({
|
||||
type: 'world',
|
||||
id: L.worldId,
|
||||
tag,
|
||||
shortName
|
||||
});
|
||||
D.visible = true;
|
||||
if (isMainDialogOpen && D.id === L.worldId && !forceRefresh) {
|
||||
uiStore.setDialogCrumbLabel('world', D.id, D.ref?.name || D.id);
|
||||
instanceStore.applyWorldDialogInstances();
|
||||
nextTick(() => (D.loading = false));
|
||||
return;
|
||||
}
|
||||
L.shortName = shortName;
|
||||
D.id = L.worldId;
|
||||
D.$location = L;
|
||||
D.loading = true;
|
||||
D.inCache = false;
|
||||
D.cacheSize = '';
|
||||
D.cacheLocked = false;
|
||||
D.cachePath = '';
|
||||
D.fileAnalysis = {};
|
||||
D.rooms = [];
|
||||
D.lastVisit = '';
|
||||
D.visitCount = 0;
|
||||
D.timeSpent = 0;
|
||||
D.isFavorite = false;
|
||||
D.avatarScalingDisabled = false;
|
||||
D.focusViewDisabled = false;
|
||||
D.isPC = false;
|
||||
D.isQuest = false;
|
||||
D.isIos = false;
|
||||
D.hasPersistData = false;
|
||||
D.memo = '';
|
||||
const LL = parseLocation(locationStore.lastLocation.location);
|
||||
let currentWorldMatch = false;
|
||||
if (LL.worldId === D.id) {
|
||||
currentWorldMatch = true;
|
||||
}
|
||||
getWorldMemo(D.id).then((memo) => {
|
||||
if (memo.worldId === D.id) {
|
||||
D.memo = memo.memo;
|
||||
}
|
||||
});
|
||||
database.getLastVisit(D.id, currentWorldMatch).then((ref) => {
|
||||
if (ref.worldId === D.id) {
|
||||
D.lastVisit = ref.created_at;
|
||||
}
|
||||
});
|
||||
database.getVisitCount(D.id).then((ref) => {
|
||||
if (ref.worldId === D.id) {
|
||||
D.visitCount = ref.visitCount;
|
||||
}
|
||||
});
|
||||
database.getTimeSpentInWorld(D.id).then((ref) => {
|
||||
if (ref.worldId === D.id) {
|
||||
D.timeSpent = ref.timeSpent;
|
||||
}
|
||||
});
|
||||
const loadWorldRequest = worldRequest.getWorld({
|
||||
worldId: L.worldId
|
||||
});
|
||||
loadWorldRequest
|
||||
.catch((err) => {
|
||||
nextTick(() => (D.loading = false));
|
||||
D.id = null;
|
||||
D.visible = false;
|
||||
uiStore.jumpBackDialogCrumb();
|
||||
toast.error(t('message.world.load_failed'));
|
||||
throw err;
|
||||
})
|
||||
.then((args) => {
|
||||
if (D.id === args.ref.id) {
|
||||
D.ref = args.ref;
|
||||
uiStore.setDialogCrumbLabel(
|
||||
'world',
|
||||
D.id,
|
||||
D.ref?.name || D.id
|
||||
);
|
||||
D.visible = true;
|
||||
D.loading = false;
|
||||
D.isFavorite = favoriteStore.getCachedFavoritesByObjectId(
|
||||
D.id
|
||||
);
|
||||
if (!D.isFavorite) {
|
||||
D.isFavorite =
|
||||
favoriteStore.localWorldFavoritesList.includes(
|
||||
D.id
|
||||
);
|
||||
}
|
||||
let { isPC, isQuest, isIos } = getAvailablePlatforms(
|
||||
args.ref.unityPackages
|
||||
);
|
||||
D.avatarScalingDisabled = args.ref?.tags.includes(
|
||||
'feature_avatar_scaling_disabled'
|
||||
);
|
||||
D.focusViewDisabled = args.ref?.tags.includes(
|
||||
'feature_focus_view_disabled'
|
||||
);
|
||||
D.isPC = isPC;
|
||||
D.isQuest = isQuest;
|
||||
D.isIos = isIos;
|
||||
updateVRChatWorldCache();
|
||||
queryRequest
|
||||
.fetch('worldPersistData', {
|
||||
worldId: D.id
|
||||
})
|
||||
.then((args) => {
|
||||
if (
|
||||
args.params.worldId === worldDialog.id &&
|
||||
worldDialog.visible
|
||||
) {
|
||||
worldDialog.hasPersistData =
|
||||
args.json !== false;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {boolean} value
|
||||
*/
|
||||
@@ -250,99 +92,12 @@ export const useWorldStore = defineStore('World', () => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param WorldCache
|
||||
*/
|
||||
function cleanupWorldCache(WorldCache) {
|
||||
evictMapCache(WorldCache, 10000, () => false, {
|
||||
logLabel: 'World cache cleanup'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object} json
|
||||
* @returns {object} ref
|
||||
*/
|
||||
function applyWorld(json) {
|
||||
sanitizeEntityJson(json, ['name', 'description']);
|
||||
let ref = cachedWorlds.get(json.id);
|
||||
if (typeof ref === 'undefined') {
|
||||
ref = createDefaultWorldRef(json);
|
||||
cleanupWorldCache(cachedWorlds);
|
||||
cachedWorlds.set(ref.id, ref);
|
||||
} else {
|
||||
Object.assign(ref, json);
|
||||
}
|
||||
ref.$isLabs = ref.tags.includes('system_labs');
|
||||
applyFavorite('world', ref.id);
|
||||
const userDialog = userStore.userDialog;
|
||||
if (userDialog.visible && userDialog.$location.worldId === ref.id) {
|
||||
userStore.applyUserDialogLocation();
|
||||
}
|
||||
if (worldDialog.visible && worldDialog.id === ref.id) {
|
||||
worldDialog.ref = ref;
|
||||
worldDialog.avatarScalingDisabled = ref.tags?.includes(
|
||||
'feature_avatar_scaling_disabled'
|
||||
);
|
||||
worldDialog.focusViewDisabled = ref.tags?.includes(
|
||||
'feature_focus_view_disabled'
|
||||
);
|
||||
instanceStore.applyWorldDialogInstances();
|
||||
for (const room of worldDialog.rooms) {
|
||||
if (isRealInstance(room.tag)) {
|
||||
instanceRequest.getInstance({
|
||||
worldId: worldDialog.id,
|
||||
instanceId: room.id
|
||||
});
|
||||
}
|
||||
}
|
||||
if (Object.keys(worldDialog.fileAnalysis).length === 0) {
|
||||
getBundleDateSize(ref);
|
||||
}
|
||||
}
|
||||
if (favoriteStore.localWorldFavoritesList.includes(ref.id)) {
|
||||
// update db cache
|
||||
database.addWorldToCache(ref);
|
||||
}
|
||||
patchWorldFromEvent(ref);
|
||||
return ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Preload all own worlds into cache at startup for global search.
|
||||
*/
|
||||
async function preloadOwnWorlds() {
|
||||
const params = {
|
||||
n: 50,
|
||||
offset: 0,
|
||||
sort: 'updated',
|
||||
order: 'descending',
|
||||
releaseStatus: 'all',
|
||||
user: 'me'
|
||||
};
|
||||
await processBulk({
|
||||
fn: (p) => worldRequest.getWorlds(p),
|
||||
N: -1,
|
||||
params,
|
||||
handle: (args) => {
|
||||
for (const json of args.json) {
|
||||
applyWorld(json);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
worldDialog,
|
||||
cachedWorlds,
|
||||
showWorldDialog,
|
||||
setWorldDialogVisible,
|
||||
setWorldDialogIsFavorite,
|
||||
setWorldDialogLoading,
|
||||
updateVRChatWorldCache,
|
||||
applyWorld,
|
||||
preloadOwnWorlds
|
||||
updateVRChatWorldCache
|
||||
};
|
||||
});
|
||||
|
||||
@@ -28,9 +28,10 @@
|
||||
import { timeToText } from '../../../shared/utils';
|
||||
|
||||
import * as echarts from 'echarts';
|
||||
import { showUserDialog } from '../../../coordinators/userCoordinator';
|
||||
|
||||
const { isDarkMode, dtHour12 } = storeToRefs(useAppearanceSettingsStore());
|
||||
const { showUserDialog } = useUserStore();
|
||||
|
||||
const { currentUser } = storeToRefs(useUserStore());
|
||||
|
||||
const props = defineProps({
|
||||
|
||||
@@ -275,6 +275,7 @@
|
||||
useUserStore
|
||||
} from '../../../stores';
|
||||
import { userImage, userStatusClass } from '../../../shared/utils';
|
||||
import { showUserDialog } from '../../../coordinators/userCoordinator';
|
||||
import { database } from '../../../service/database';
|
||||
import { watchState } from '../../../service/watchState';
|
||||
|
||||
@@ -290,7 +291,7 @@
|
||||
const { currentUser } = storeToRefs(userStore);
|
||||
const { isDarkMode } = storeToRefs(appearanceStore);
|
||||
const cachedUsers = userStore.cachedUsers;
|
||||
const showUserDialog = (userId) => userStore.showUserDialog(userId);
|
||||
|
||||
|
||||
const fetchState = chartsStore.mutualGraphFetchState;
|
||||
const status = chartsStore.mutualGraphStatus;
|
||||
|
||||
@@ -519,7 +519,7 @@
|
||||
handleFavoriteGroup
|
||||
} = favoriteStore;
|
||||
const { avatarHistory } = storeToRefs(useAvatarStore());
|
||||
const { promptClearAvatarHistory, showAvatarDialog, applyAvatar } = useAvatarStore();
|
||||
import { promptClearAvatarHistory, showAvatarDialog, applyAvatar } from '../../coordinators/avatarCoordinator';
|
||||
const { isLocalUserVrcPlusSupporter } = storeToRefs(useUserStore());
|
||||
const { t } = useI18n();
|
||||
|
||||
|
||||
@@ -388,6 +388,7 @@
|
||||
DropdownMenuTrigger
|
||||
} from '../../components/ui/dropdown-menu';
|
||||
import { useAppearanceSettingsStore, useFavoriteStore, useModalStore, useWorldStore } from '../../stores';
|
||||
import { showWorldDialog } from '../../coordinators/worldCoordinator';
|
||||
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '../../components/ui/resizable';
|
||||
import { favoriteRequest, worldRequest } from '../../api';
|
||||
import { debounce } from '../../shared/utils';
|
||||
@@ -436,7 +437,7 @@
|
||||
handleFavoriteGroup,
|
||||
localWorldFavoritesList
|
||||
} = favoriteStore;
|
||||
const { showWorldDialog } = useWorldStore();
|
||||
|
||||
|
||||
const {
|
||||
cardScale: worldCardScale,
|
||||
|
||||
@@ -170,7 +170,7 @@
|
||||
|
||||
const { favoriteAvatarGroups } = storeToRefs(useFavoriteStore());
|
||||
const { showFavoriteDialog } = useFavoriteStore();
|
||||
const { selectAvatarWithConfirmation } = useAvatarStore();
|
||||
import { selectAvatarWithConfirmation } from '../../../coordinators/avatarCoordinator';
|
||||
const { shiftHeld } = storeToRefs(useUiStore());
|
||||
const { currentUser } = storeToRefs(useUserStore());
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
const { t } = useI18n();
|
||||
|
||||
const { showFavoriteDialog, getCachedFavoritesByObjectId } = useFavoriteStore();
|
||||
const { selectAvatarWithConfirmation } = useAvatarStore();
|
||||
import { selectAvatarWithConfirmation } from '../../../coordinators/avatarCoordinator';
|
||||
const { currentUser } = storeToRefs(useUserStore());
|
||||
|
||||
const props = defineProps({
|
||||
|
||||
@@ -131,11 +131,12 @@
|
||||
|
||||
const emit = defineEmits(['update:avatarImportDialogInput']);
|
||||
const { t } = useI18n();
|
||||
const { showUserDialog } = useUserStore();
|
||||
|
||||
const { favoriteAvatarGroups, avatarImportDialogInput, avatarImportDialogVisible, localAvatarFavoriteGroups } =
|
||||
storeToRefs(useFavoriteStore());
|
||||
const { localAvatarFavGroupLength, getCachedFavoritesByObjectId } = useFavoriteStore();
|
||||
const { showAvatarDialog, applyAvatar } = useAvatarStore();
|
||||
import { showAvatarDialog, applyAvatar } from '../../../coordinators/avatarCoordinator';
|
||||
import { showUserDialog } from '../../../coordinators/userCoordinator';
|
||||
const { showFullscreenImageDialog } = useGalleryStore();
|
||||
|
||||
const avatarImportDialog = ref({
|
||||
|
||||
@@ -131,12 +131,13 @@
|
||||
import { favoriteRequest, userRequest } from '../../../api';
|
||||
import { createColumns } from './friendImportColumns.jsx';
|
||||
import { useVrcxVueTable } from '../../../lib/table/useVrcxVueTable';
|
||||
import { showUserDialog } from '../../../coordinators/userCoordinator';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const emit = defineEmits(['update:friendImportDialogInput']);
|
||||
|
||||
const { showUserDialog } = useUserStore();
|
||||
|
||||
const { favoriteFriendGroups, friendImportDialogInput, friendImportDialogVisible, localFriendFavoriteGroups } =
|
||||
storeToRefs(useFavoriteStore());
|
||||
const { showFullscreenImageDialog } = useGalleryStore();
|
||||
|
||||
@@ -127,17 +127,19 @@
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { useFavoriteStore, useGalleryStore, useUserStore, useWorldStore } from '../../../stores';
|
||||
import { showWorldDialog } from '../../../coordinators/worldCoordinator';
|
||||
import { addLocalWorldFavorite } from '../../../coordinators/favoriteCoordinator';
|
||||
import { favoriteRequest, worldRequest } from '../../../api';
|
||||
import { createColumns } from './worldImportColumns.jsx';
|
||||
import { removeFromArray } from '../../../shared/utils';
|
||||
import { useVrcxVueTable } from '../../../lib/table/useVrcxVueTable';
|
||||
import { showUserDialog } from '../../../coordinators/userCoordinator';
|
||||
|
||||
const { showUserDialog } = useUserStore();
|
||||
|
||||
const { favoriteWorldGroups, worldImportDialogInput, worldImportDialogVisible, localWorldFavoriteGroups } =
|
||||
storeToRefs(useFavoriteStore());
|
||||
const { localWorldFavGroupLength, getCachedFavoritesByObjectId } = useFavoriteStore();
|
||||
const { showWorldDialog } = useWorldStore();
|
||||
|
||||
const { showFullscreenImageDialog } = useGalleryStore();
|
||||
|
||||
const emit = defineEmits(['update:worldImportDialogInput']);
|
||||
|
||||
@@ -16,7 +16,8 @@ import {
|
||||
} from 'lucide-vue-next';
|
||||
import { formatDateFilter, statusClass, timeToText } from '../../shared/utils';
|
||||
import { i18n } from '../../plugin';
|
||||
import { useUserStore, useGalleryStore } from '../../stores';
|
||||
import { useGalleryStore } from '../../stores';
|
||||
import { showUserDialog } from '../../coordinators/userCoordinator';
|
||||
|
||||
const { t } = i18n.global;
|
||||
|
||||
@@ -276,7 +277,7 @@ export const columns = [
|
||||
header: () => t('table.feed.user'),
|
||||
meta: { label: () => t('table.feed.user') },
|
||||
cell: ({ row }) => {
|
||||
const { showUserDialog } = useUserStore();
|
||||
|
||||
const original = row.original;
|
||||
return (
|
||||
<span
|
||||
|
||||
@@ -148,6 +148,7 @@
|
||||
import { router } from '../../plugin/router';
|
||||
import { useDataTableScrollHeight } from '../../composables/useDataTableScrollHeight';
|
||||
import { useVrcxVueTable } from '../../lib/table/useVrcxVueTable';
|
||||
import { showUserDialog } from '../../coordinators/userCoordinator';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -158,7 +159,7 @@
|
||||
const { getAllUserStats, getAllUserMutualCount, confirmDeleteFriend, handleFriendDelete } = useFriendStore();
|
||||
const appearanceSettingsStore = useAppearanceSettingsStore();
|
||||
const { randomUserColours } = storeToRefs(appearanceSettingsStore);
|
||||
const { showUserDialog } = useUserStore();
|
||||
|
||||
const { stringComparer, friendsListSearch } = storeToRefs(useSearchStore());
|
||||
|
||||
const friendsListSearchFilters = ref([]);
|
||||
|
||||
@@ -10,12 +10,13 @@ import { storeToRefs } from 'pinia';
|
||||
|
||||
import { formatDateFilter } from '../../shared/utils';
|
||||
import { i18n } from '../../plugin';
|
||||
import { useUiStore, useUserStore } from '../../stores';
|
||||
import { useUiStore } from '../../stores';
|
||||
import { showUserDialog } from '../../coordinators/userCoordinator';
|
||||
|
||||
const { t } = i18n.global;
|
||||
|
||||
export const createColumns = ({ onDelete, onDeletePrompt }) => {
|
||||
const { showUserDialog } = useUserStore();
|
||||
|
||||
const { shiftHeld } = storeToRefs(useUiStore());
|
||||
|
||||
return [
|
||||
|
||||
@@ -81,9 +81,10 @@
|
||||
import { checkCanInvite, checkCanInviteSelf } from '../../../shared/utils/invite.js';
|
||||
|
||||
import Location from '../../../components/Location.vue';
|
||||
import { showUserDialog } from '../../../coordinators/userCoordinator';
|
||||
|
||||
const { t } = useI18n();
|
||||
const { showUserDialog, showSendBoopDialog } = useUserStore();
|
||||
const { showSendBoopDialog } = useUserStore();
|
||||
const launchStore = useLaunchStore();
|
||||
const { lastLocation, lastLocationDestination } = storeToRefs(useLocationStore());
|
||||
const { isGameRunning } = storeToRefs(useGameStore());
|
||||
|
||||
@@ -9,10 +9,10 @@ import { formatDateFilter, openExternalLink } from '../../shared/utils';
|
||||
import { i18n } from '../../plugin';
|
||||
import {
|
||||
useInstanceStore,
|
||||
useUiStore,
|
||||
useUserStore,
|
||||
useWorldStore
|
||||
useUiStore
|
||||
} from '../../stores';
|
||||
import { lookupUser } from '../../coordinators/userCoordinator';
|
||||
import { showWorldDialog } from '../../coordinators/worldCoordinator';
|
||||
|
||||
const { t } = i18n.global;
|
||||
|
||||
@@ -24,8 +24,9 @@ const UNACTIONABLE_TYPES = new Set([
|
||||
]);
|
||||
|
||||
export const createColumns = ({ getCreatedAt, onDelete, onDeletePrompt }) => {
|
||||
const { showWorldDialog } = useWorldStore();
|
||||
const { lookupUser } = useUserStore();
|
||||
|
||||
|
||||
|
||||
const { showPreviousInstancesInfoDialog } = useInstanceStore();
|
||||
const { shiftHeld } = storeToRefs(useUiStore());
|
||||
|
||||
|
||||
@@ -11,11 +11,12 @@ import { storeToRefs } from 'pinia';
|
||||
import { formatDateFilter } from '../../shared/utils';
|
||||
import { i18n } from '../../plugin';
|
||||
import { useUiStore, useUserStore } from '../../stores';
|
||||
import { showUserDialog } from '../../coordinators/userCoordinator';
|
||||
|
||||
const { t, te } = i18n.global;
|
||||
|
||||
export const createColumns = ({ onDelete, onDeletePrompt }) => {
|
||||
const { showUserDialog } = useUserStore();
|
||||
|
||||
const { shiftHeld } = storeToRefs(useUiStore());
|
||||
const { currentUser } = storeToRefs(useUserStore());
|
||||
|
||||
|
||||
@@ -347,12 +347,13 @@
|
||||
import ManageTagsDialog from './ManageTagsDialog.vue';
|
||||
import MyAvatarCard from './components/MyAvatarCard.vue';
|
||||
import configRepository from '../../service/config.js';
|
||||
import { showAvatarDialog, selectAvatarWithoutConfirmation, applyAvatar } from '../../coordinators/avatarCoordinator';
|
||||
|
||||
const { t } = useI18n();
|
||||
const appearanceSettingsStore = useAppearanceSettingsStore();
|
||||
const avatarStore = useAvatarStore();
|
||||
const modalStore = useModalStore();
|
||||
const { showAvatarDialog, selectAvatarWithoutConfirmation, applyAvatar } = avatarStore;
|
||||
|
||||
const { currentUser } = storeToRefs(useUserStore());
|
||||
|
||||
const pageSizes = computed(() => appearanceSettingsStore.tablePageSizes);
|
||||
|
||||
@@ -31,13 +31,14 @@ import { checkCanInvite, formatDateFilter } from '../../shared/utils';
|
||||
import { i18n } from '../../plugin';
|
||||
import {
|
||||
useGameStore,
|
||||
useGroupStore,
|
||||
useLocationStore,
|
||||
useUiStore,
|
||||
useUserStore,
|
||||
useWorldStore,
|
||||
useNotificationStore
|
||||
} from '../../stores';
|
||||
import { showUserDialog } from '../../coordinators/userCoordinator';
|
||||
import { showWorldDialog } from '../../coordinators/worldCoordinator';
|
||||
import { showGroupDialog } from '../../coordinators/groupCoordinator';
|
||||
|
||||
import Emoji from '../../components/Emoji.vue';
|
||||
|
||||
@@ -61,9 +62,8 @@ export const createColumns = ({
|
||||
deleteNotificationLog,
|
||||
deleteNotificationLogPrompt
|
||||
}) => {
|
||||
const { showUserDialog, showSendBoopDialog } = useUserStore();
|
||||
const { showWorldDialog } = useWorldStore();
|
||||
const { showGroupDialog } = useGroupStore();
|
||||
const { showSendBoopDialog } = useUserStore();
|
||||
|
||||
const { shiftHeld } = storeToRefs(useUiStore());
|
||||
const { currentUser } = storeToRefs(useUserStore());
|
||||
const { lastLocation } = storeToRefs(useLocationStore());
|
||||
|
||||
@@ -196,6 +196,7 @@
|
||||
|
||||
import ChatboxBlacklistDialog from './dialogs/ChatboxBlacklistDialog.vue';
|
||||
import Timer from '../../components/Timer.vue';
|
||||
import { showUserDialog, lookupUser } from '../../coordinators/userCoordinator';
|
||||
|
||||
const PhotonEventTable = defineAsyncComponent(() => import('./components/PhotonEventTable.vue'));
|
||||
|
||||
@@ -203,7 +204,7 @@
|
||||
const photonStore = usePhotonStore();
|
||||
const { photonLoggingEnabled, chatboxUserBlacklist } = storeToRefs(photonStore);
|
||||
const { saveChatboxUserBlacklist } = photonStore;
|
||||
const { showUserDialog, lookupUser } = useUserStore();
|
||||
|
||||
const { showWorldDialog } = useWorldStore();
|
||||
const { lastLocation } = storeToRefs(useLocationStore());
|
||||
const { currentInstanceLocation, currentInstanceWorld, currentInstanceUsersData } = storeToRefs(useInstanceStore());
|
||||
|
||||
@@ -83,9 +83,11 @@
|
||||
useVrcxStore,
|
||||
useWorldStore
|
||||
} from '../../../stores';
|
||||
import { showWorldDialog } from '../../../coordinators/worldCoordinator';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../../components/ui/select';
|
||||
import { createColumns } from './photonEventColumns.jsx';
|
||||
import { photonEventTableTypeFilterList } from '../../../shared/constants/photon';
|
||||
import { lookupUser } from '../../../coordinators/userCoordinator';
|
||||
|
||||
const emit = defineEmits(['show-chatbox-blacklist']);
|
||||
const { t } = useI18n();
|
||||
@@ -106,9 +108,9 @@
|
||||
|
||||
const { stringComparer } = storeToRefs(useSearchStore());
|
||||
|
||||
const { lookupUser } = useUserStore();
|
||||
|
||||
const { showAvatarDialog } = useAvatarStore();
|
||||
const { showWorldDialog } = useWorldStore();
|
||||
|
||||
const { showGroupDialog } = useGroupStore();
|
||||
const { showFullscreenImageDialog } = useGalleryStore();
|
||||
const { ipcEnabled } = storeToRefs(useVrcxStore());
|
||||
|
||||
@@ -402,13 +402,14 @@
|
||||
userImage
|
||||
} from '../../shared/utils';
|
||||
import { groupRequest, worldRequest } from '../../api';
|
||||
import { showUserDialog, refreshUserDialogAvatars } from '../../coordinators/userCoordinator';
|
||||
|
||||
const { randomUserColours } = storeToRefs(useAppearanceSettingsStore());
|
||||
const { avatarRemoteDatabase } = storeToRefs(useAdvancedSettingsStore());
|
||||
const { avatarRemoteDatabaseProviderList, avatarRemoteDatabaseProvider } = storeToRefs(useAvatarProviderStore());
|
||||
const { setAvatarProvider } = useAvatarProviderStore();
|
||||
const { userDialog } = storeToRefs(useUserStore());
|
||||
const { showUserDialog, refreshUserDialogAvatars } = useUserStore();
|
||||
|
||||
const { showAvatarDialog, lookupAvatars, cachedAvatars } = useAvatarStore();
|
||||
const { cachedWorlds, showWorldDialog } = useWorldStore();
|
||||
const { showGroupDialog } = useGroupStore();
|
||||
|
||||
@@ -80,6 +80,7 @@
|
||||
import { userImage, userStatusClass } from '../../../shared/utils';
|
||||
|
||||
import '@/styles/status-icon.css';
|
||||
import { showUserDialog } from '../../../coordinators/userCoordinator';
|
||||
|
||||
const props = defineProps({
|
||||
friend: { type: Object, required: true },
|
||||
@@ -89,7 +90,7 @@
|
||||
const { hideNicknames } = storeToRefs(useAppearanceSettingsStore());
|
||||
const { isRefreshFriendsLoading, allFavoriteFriendIds } = storeToRefs(useFriendStore());
|
||||
const { confirmDeleteFriend } = useFriendStore();
|
||||
const { showUserDialog } = useUserStore();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const isFriendTraveling = computed(() => props.friend.ref?.location === 'traveling');
|
||||
|
||||
@@ -216,6 +216,7 @@
|
||||
import configRepository from '../../../service/config';
|
||||
|
||||
import '@/styles/status-icon.css';
|
||||
import { showUserDialog } from '../../../coordinators/userCoordinator';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -238,7 +239,7 @@
|
||||
sidebarSortMethods
|
||||
} = storeToRefs(appearanceSettingsStore);
|
||||
const { gameLogDisabled } = storeToRefs(useAdvancedSettingsStore());
|
||||
const { showUserDialog, showSendBoopDialog } = useUserStore();
|
||||
const { showSendBoopDialog } = useUserStore();
|
||||
const launchStore = useLaunchStore();
|
||||
const { favoriteFriendGroups, groupedByGroupKeyFavoriteFriends, localFriendFavorites } =
|
||||
storeToRefs(useFavoriteStore());
|
||||
|
||||
@@ -255,6 +255,8 @@
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import { useGameStore, useGroupStore, useLocationStore, useNotificationStore, useUserStore } from '../../../stores';
|
||||
import { showGroupDialog } from '../../../coordinators/groupCoordinator';
|
||||
import { showUserDialog } from '../../../coordinators/userCoordinator';
|
||||
import { checkCanInvite, userImage } from '../../../shared/utils';
|
||||
|
||||
import Location from '../../../components/Location.vue';
|
||||
@@ -461,13 +463,13 @@
|
||||
if (userId.startsWith('grp_') || n.type?.startsWith('group.') || n.type === 'groupChange') {
|
||||
const groupId = userId.startsWith('grp_') ? userId : n.data?.groupId || n.details?.groupId || '';
|
||||
if (groupId) {
|
||||
groupStore.showGroupDialog(groupId);
|
||||
showGroupDialog(groupId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (userId) {
|
||||
userStore.showUserDialog(userId);
|
||||
showUserDialog(userId);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -66,11 +66,12 @@
|
||||
import { useVrcxVueTable } from '../../../lib/table/useVrcxVueTable';
|
||||
|
||||
import * as workerTimers from 'worker-timers';
|
||||
import { showUserDialog } from '../../../coordinators/userCoordinator';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const { friends } = storeToRefs(useFriendStore());
|
||||
const { showUserDialog } = useUserStore();
|
||||
|
||||
const { showFullscreenImageDialog } = useGalleryStore();
|
||||
|
||||
const props = defineProps({
|
||||
|
||||
Reference in New Issue
Block a user