Files
VRCX/src/coordinators/userCoordinator.js
2026-03-13 20:04:36 +09:00

1058 lines
35 KiB
JavaScript

import { reactive } from 'vue';
import { toast } from 'vue-sonner';
import { i18n } from '../plugins/i18n';
import {
arraysMatch,
computeUserPlatform,
createDefaultUserRef,
diffObjectProps,
evictMapCache,
extractFileId,
findUserByDisplayName,
getWorldName,
isRealInstance,
parseLocation,
sanitizeUserJson
} from '../shared/utils';
import { getUserMemo } from './memoCoordinator';
import {
avatarRequest,
instanceRequest,
queryRequest,
userRequest
} from '../api';
import { processBulk, request } from '../services/request';
import { AppDebug } from '../services/appConfig';
import { database } from '../services/database';
import { patchUserFromEvent } from '../queries';
import { watchState } from '../services/watchState';
import { applyAvatar, showAvatarDialog } from './avatarCoordinator';
import { applyFavorite } from './favoriteCoordinator';
import {
runAvatarSwapFlow,
runFirstLoginFlow,
runHomeLocationSyncFlow,
runPostApplySyncFlow
} from './userSessionCoordinator';
import { runHandleUserUpdateFlow } from './userEventCoordinator';
import { runUpdateCurrentUserLocationFlow } from './locationCoordinator';
import { runUpdateFriendFlow } from './friendPresenceCoordinator';
import { userOnFriend } from './friendRelationshipCoordinator';
import { handleGroupRepresented } from './groupCoordinator';
import { useAppearanceSettingsStore } from '../stores/settings/appearance';
import { useAuthStore } from '../stores/auth';
import { useAvatarStore } from '../stores/avatar';
import { useFavoriteStore } from '../stores/favorite';
import { useFriendStore } from '../stores/friend';
import { useGameStore } from '../stores/game';
import { useGeneralSettingsStore } from '../stores/settings/general';
import { useInstanceStore } from '../stores/instance';
import { useLocationStore } from '../stores/location';
import { useModerationStore } from '../stores/moderation';
import { useNotificationStore } from '../stores/notification';
import { usePhotonStore } from '../stores/photon';
import { useSearchStore } from '../stores/search';
import { useSharedFeedStore } from '../stores/sharedFeed';
import { useUiStore } from '../stores/ui';
import { useUserStore } from '../stores/user';
const getRobotUrl = () =>
`${AppDebug.endpointDomain}/file/file_0e8c4e32-7444-44ea-ade4-313c010d4bae/1/file`;
/**
* @param {import('../types/api/user').GetUserResponse} json
* @returns {import('../types/api/user').VrcxUser}
*/
export function applyUser(json) {
const userStore = useUserStore();
const appearanceSettingsStore = useAppearanceSettingsStore();
const friendStore = useFriendStore();
const locationStore = useLocationStore();
const instanceStore = useInstanceStore();
const moderationStore = useModerationStore();
const photonStore = usePhotonStore();
const {
currentUser,
cachedUsers,
currentTravelers,
customUserTags,
state,
userDialog
} = userStore;
let ref = cachedUsers.get(json.id);
let hasPropChanged = false;
let changedProps = {};
sanitizeUserJson(json, getRobotUrl());
if (typeof ref === 'undefined') {
ref = reactive(createDefaultUserRef(json));
if (locationStore.lastLocation.playerList.has(json.id)) {
const player = locationStore.lastLocation.playerList.get(json.id);
ref.$location_at = player.joinTime;
ref.$online_for = player.joinTime;
}
if (ref.isFriend || ref.id === currentUser.id) {
let newCount = state.instancePlayerCount.get(ref.location);
if (typeof newCount === 'undefined') {
newCount = 0;
}
newCount++;
state.instancePlayerCount.set(ref.location, newCount);
}
const tag = customUserTags.get(json.id);
if (tag) {
ref.$customTag = tag.tag;
ref.$customTagColour = tag.colour;
} else if (ref.$customTag) {
ref.$customTag = '';
ref.$customTagColour = '';
}
evictMapCache(
cachedUsers,
friendStore.friends.size + 300,
(_value, key) => friendStore.friends.has(key),
{ logLabel: 'User cache cleanup' }
);
cachedUsers.set(ref.id, ref);
runUpdateFriendFlow(ref.id);
} else {
if (json.state !== 'online') {
runUpdateFriendFlow(ref.id, json.state);
}
const { hasPropChanged: _hasPropChanged, changedProps: _changedProps } =
diffObjectProps(ref, json, arraysMatch);
for (const prop in json) {
if (typeof json[prop] !== 'undefined') {
ref[prop] = json[prop];
}
}
hasPropChanged = _hasPropChanged;
changedProps = _changedProps;
}
ref.$moderations = moderationStore.getUserModerations(ref.id);
ref.$isVRCPlus = ref.tags.includes('system_supporter');
appearanceSettingsStore.applyUserTrustLevel(ref);
userStore.applyUserLanguage(ref);
ref.$platform = computeUserPlatform(ref.platform, ref.last_platform);
// traveling
if (ref.location === 'traveling') {
ref.$location = parseLocation(ref.travelingToLocation);
if (!currentTravelers.has(ref.id) && ref.travelingToLocation) {
const travelRef = reactive({
created_at: new Date().toJSON(),
...ref
});
currentTravelers.set(ref.id, travelRef);
onPlayerTraveling(travelRef);
}
} else {
ref.$location = parseLocation(ref.location);
currentTravelers.delete(ref.id);
}
if (
!instanceStore.cachedInstances.has(ref.$location.tag) &&
isRealInstance(ref.location)
) {
instanceRequest.getInstance({
worldId: ref.$location.worldId,
instanceId: ref.$location.instanceId
});
}
if (
ref.$isVRCPlus &&
ref.badges &&
ref.badges.every(
(x) => x.badgeId !== 'bdg_754f9935-0f97-49d8-b857-95afb9b673fa'
)
) {
ref.badges.unshift({
badgeId: 'bdg_754f9935-0f97-49d8-b857-95afb9b673fa',
badgeName: 'Supporter',
badgeDescription: 'Supports VRChat through VRC+',
badgeImageUrl:
'https://assets.vrchat.com/badges/fa/bdgai_583f6b13-91ab-4e1b-974e-ab91600b06cb.png',
hidden: true,
showcased: false
});
}
const friendCtx = friendStore.friends.get(ref.id);
if (friendCtx) {
friendCtx.ref = ref;
friendCtx.name = ref.displayName;
}
if (ref.id === currentUser.id) {
if (ref.status) {
currentUser.status = ref.status;
}
runUpdateCurrentUserLocationFlow();
}
// add user ref to playerList, friendList, photonLobby, photonLobbyCurrent
const playerListRef = locationStore.lastLocation.playerList.get(ref.id);
if (playerListRef) {
if (
!locationStore.lastLocation.friendList.has(ref.id) &&
friendStore.friends.has(ref.id)
) {
const userMap = {
displayName: ref.displayName,
userId: ref.id,
joinTime: playerListRef.joinTime
};
locationStore.lastLocation.friendList.set(ref.id, userMap);
}
if (
locationStore.lastLocation.friendList.has(ref.id) &&
!friendStore.friends.has(ref.id)
) {
locationStore.lastLocation.friendList.delete(ref.id);
}
photonStore.photonLobby.forEach((ref1, id) => {
if (
typeof ref1 !== 'undefined' &&
ref1.displayName === ref.displayName &&
ref1 !== ref
) {
photonStore.photonLobby.set(id, ref);
if (photonStore.photonLobbyCurrent.has(id)) {
photonStore.photonLobbyCurrent.set(id, ref);
}
}
});
instanceStore.getCurrentInstanceUserList();
}
if (ref.state === 'online') {
runUpdateFriendFlow(ref.id, ref.state);
}
applyFavorite('friend', ref.id);
userOnFriend(ref);
const D = userDialog;
if (D.visible && D.id === ref.id) {
D.ref = ref;
D.note = String(ref.note || '');
D.incomingRequest = false;
D.outgoingRequest = false;
if (D.ref.friendRequestStatus === 'incoming') {
D.incomingRequest = true;
} else if (D.ref.friendRequestStatus === 'outgoing') {
D.outgoingRequest = true;
}
}
if (hasPropChanged) {
if (changedProps.location && changedProps.location[0] !== 'traveling') {
const ts = Date.now();
changedProps.location.push(ts - ref.$location_at);
ref.$location_at = ts;
}
handleUserUpdate(ref, changedProps);
if (AppDebug.debugUserDiff) {
delete changedProps.last_login;
delete changedProps.last_activity;
if (Object.keys(changedProps).length !== 0) {
console.log('>', ref.displayName, changedProps);
}
}
}
patchUserFromEvent(ref);
return ref;
}
/**
* @param {string} userId
*/
export function showUserDialog(userId) {
if (
!userId ||
typeof userId !== 'string' ||
userId === 'usr_00000000-0000-0000-0000-000000000000'
) {
return;
}
const userStore = useUserStore();
const uiStore = useUiStore();
const friendStore = useFriendStore();
const moderationStore = useModerationStore();
const favoriteStore = useFavoriteStore();
const locationStore = useLocationStore();
const searchStore = useSearchStore();
const appearanceSettingsStore = useAppearanceSettingsStore();
const t = i18n.global.t;
const { currentUser, userDialog, showUserDialogHistory } = userStore;
const isMainDialogOpen = uiStore.openDialog({
type: 'user',
id: userId
});
const D = userDialog;
D.visible = true;
if (isMainDialogOpen && D.id === userId) {
uiStore.setDialogCrumbLabel('user', D.id, D.ref?.displayName || D.id);
userStore.applyUserDialogLocation(true);
return;
}
D.id = userId;
D.memo = '';
D.note = '';
getUserMemo(userId).then((memo) => {
if (memo.userId === userId) {
D.memo = memo.memo;
const ref = friendStore.friends.get(userId);
if (ref) {
ref.memo = String(memo.memo || '');
if (memo.memo) {
ref.$nickName = memo.memo.split('\n')[0];
} else {
ref.$nickName = '';
}
}
}
});
D.loading = true;
D.avatars = [];
D.worlds = [];
D.instance = {
id: '',
tag: '',
$location: {},
friendCount: 0,
users: [],
shortName: '',
ref: {}
};
D.isRepresentedGroupLoading = true;
D.representedGroup = {
bannerId: '',
bannerUrl: '',
description: '',
discriminator: '',
groupId: '',
id: '',
iconUrl: '',
isRepresenting: false,
memberCount: 0,
memberVisibility: '',
name: '',
ownerId: '',
privacy: '',
shortCode: '',
$thumbnailUrl: '',
$memberId: ''
};
D.lastSeen = '';
D.joinCount = 0;
D.timeSpent = 0;
D.avatarModeration = 0;
D.isHideAvatar = false;
D.isShowAvatar = false;
D.previousDisplayNames = [];
D.dateFriended = '';
D.unFriended = false;
D.dateFriendedInfo = [];
D.mutualFriendCount = 0;
D.mutualGroupCount = 0;
if (userId === currentUser.id) {
getWorldName(currentUser.homeLocation).then((worldName) => {
D.$homeLocationName = worldName;
});
}
AppApi.SendIpc('ShowUserDialog', userId);
queryRequest
.fetch('user', {
userId
})
.catch((err) => {
D.loading = false;
D.id = null;
D.visible = false;
uiStore.jumpBackDialogCrumb();
toast.error(t('message.user.load_failed'));
throw err;
})
.then((args) => {
if (args.ref.id === D.id) {
D.loading = false;
D.ref = args.ref;
uiStore.setDialogCrumbLabel(
'user',
D.id,
D.ref?.displayName || D.id
);
D.friend = friendStore.friends.get(D.id);
D.isFriend = Boolean(D.friend);
D.note = String(D.ref.note || '');
D.incomingRequest = false;
D.outgoingRequest = false;
D.isBlock = false;
D.isMute = false;
D.isInteractOff = false;
D.isMuteChat = false;
for (const ref of moderationStore.cachedPlayerModerations.values()) {
if (
ref.targetUserId === D.id &&
ref.sourceUserId === currentUser.id
) {
if (ref.type === 'block') {
D.isBlock = true;
} else if (ref.type === 'mute') {
D.isMute = true;
} else if (ref.type === 'interactOff') {
D.isInteractOff = true;
} else if (ref.type === 'muteChat') {
D.isMuteChat = true;
}
}
}
D.isFavorite =
favoriteStore.getCachedFavoritesByObjectId(D.id) ||
favoriteStore.isInAnyLocalFriendGroup(D.id);
if (D.ref.friendRequestStatus === 'incoming') {
D.incomingRequest = true;
} else if (D.ref.friendRequestStatus === 'outgoing') {
D.outgoingRequest = true;
}
let inCurrentWorld = false;
if (locationStore.lastLocation.playerList.has(D.ref.id)) {
inCurrentWorld = true;
}
if (userId !== currentUser.id) {
database
.getUserStats(D.ref, inCurrentWorld)
.then(async (ref1) => {
if (ref1.userId === D.id) {
D.lastSeen = ref1.lastSeen;
D.joinCount = ref1.joinCount;
D.timeSpent = ref1.timeSpent;
}
const displayNameMap = ref1.previousDisplayNames;
const userNotifications =
await database.getFriendLogHistoryForUserId(
D.id,
['DisplayName', 'Friend', 'Unfriend']
);
const dateFriendedInfo = [];
for (const notification of userNotifications) {
if (notification.userId !== D.id) {
continue;
}
if (notification.type === 'DisplayName') {
displayNameMap.set(
notification.previousDisplayName,
notification.created_at
);
}
if (
notification.type === 'Friend' ||
(notification.type === 'Unfriend' &&
!appearanceSettingsStore.hideUnfriends)
) {
dateFriendedInfo.unshift(notification);
}
}
D.dateFriendedInfo = dateFriendedInfo;
if (dateFriendedInfo.length > 0) {
const latestFriendedInfo = dateFriendedInfo[0];
D.unFriended =
latestFriendedInfo.type === 'Unfriend';
D.dateFriended = latestFriendedInfo.created_at;
}
displayNameMap.forEach(
(updated_at, displayName) => {
D.previousDisplayNames.push({
displayName,
updated_at
});
}
);
});
AppApi.GetVRChatUserModeration(currentUser.id, userId).then(
(result) => {
D.avatarModeration = result;
if (result === 4) {
D.isHideAvatar = true;
} else if (result === 5) {
D.isShowAvatar = true;
}
}
);
if (!currentUser.hasSharedConnectionsOptOut) {
try {
queryRequest
.fetch('mutualCounts', { userId })
.then((args) => {
if (args.params.userId === D.id) {
D.mutualFriendCount = args.json.friends;
D.mutualGroupCount = args.json.groups;
}
});
} catch (error) {
console.error(error);
}
}
} else {
D.previousDisplayNames = currentUser.pastDisplayNames;
database
.getUserStats(D.ref, inCurrentWorld)
.then((ref1) => {
if (ref1.userId === D.id) {
D.lastSeen = ref1.lastSeen;
D.joinCount = ref1.joinCount;
D.timeSpent = ref1.timeSpent;
}
});
}
queryRequest
.fetch('representedGroup', { userId })
.then((args1) => {
handleGroupRepresented(args1);
});
D.visible = true;
userStore.applyUserDialogLocation(true);
}
});
showUserDialogHistory.delete(userId);
showUserDialogHistory.add(userId);
searchStore.setQuickSearchItems(searchStore.quickSearchUserHistory());
}
/**
* @param {object} ref
*/
function onPlayerTraveling(ref) {
const userStore = useUserStore();
const gameStore = useGameStore();
const locationStore = useLocationStore();
const notificationStore = useNotificationStore();
if (
!gameStore.isGameRunning ||
!locationStore.lastLocation.location ||
locationStore.lastLocation.location !== ref.travelingToLocation ||
ref.id === userStore.currentUser.id ||
locationStore.lastLocation.playerList.has(ref.id)
) {
return;
}
const onPlayerJoining = {
created_at: new Date(ref.created_at).toJSON(),
userId: ref.id,
displayName: ref.displayName,
type: 'OnPlayerJoining'
};
notificationStore.queueFeedNoty(onPlayerJoining);
}
/**
* @param {object} ref
* @param {object} props
*/
async function handleUserUpdate(ref, props) {
await runHandleUserUpdateFlow(ref, props);
}
/**
* @param fileId
*/
export async function refreshUserDialogAvatars(fileId) {
const userStore = useUserStore();
const avatarStore = useAvatarStore();
const t = i18n.global.t;
const D = userStore.userDialog;
const userId = D.id;
if (D.isAvatarsLoading) {
return;
}
D.isAvatarsLoading = true;
if (fileId) {
D.loading = true;
}
D.avatarSorting = 'update';
D.avatarReleaseStatus = 'all';
const params = {
n: 50,
offset: 0,
sort: 'updated',
order: 'descending',
releaseStatus: 'all',
user: 'me'
};
for (const ref of avatarStore.cachedAvatars.values()) {
if (ref.authorId === D.id) {
avatarStore.cachedAvatars.delete(ref.id);
}
}
const map = new Map();
await processBulk({
fn: avatarRequest.getAvatars,
N: -1,
params,
handle: (args) => {
for (const json of args.json) {
const ref = applyAvatar(json);
map.set(ref.id, ref);
}
},
done: () => {
const array = Array.from(map.values());
if (userId === D.id) {
userStore.sortUserDialogAvatars(array);
}
D.isAvatarsLoading = false;
if (fileId) {
D.loading = false;
for (const ref of array) {
if (extractFileId(ref.imageUrl) === fileId) {
showAvatarDialog(ref.id);
return;
}
}
toast.error('Own avatar not found');
}
}
});
}
/**
* @param ref
*/
export async function lookupUser(ref) {
const userStore = useUserStore();
const searchStore = useSearchStore();
let ctx;
if (ref.userId) {
showUserDialog(ref.userId);
return;
}
if (!ref.displayName || ref.displayName.substring(0, 3) === 'ID:') {
return;
}
const found = findUserByDisplayName(userStore.cachedUsers, ref.displayName);
if (found) {
showUserDialog(found.id);
return;
}
searchStore.setSearchText(ref.displayName);
await searchStore.searchUserByDisplayName(ref.displayName);
for (ctx of searchStore.searchUserResults) {
if (ctx.displayName === ref.displayName) {
searchStore.setSearchText('');
searchStore.clearSearch();
showUserDialog(ctx.id);
return;
}
}
}
/**
* @param {object} args
*/
export function handleConfig(args) {
const authStore = useAuthStore();
const userStore = useUserStore();
const ref = {
...args.json
};
args.ref = ref;
authStore.setCachedConfig(ref);
if (typeof args.ref?.whiteListedAssetUrls !== 'object') {
console.error('Invalid config whiteListedAssetUrls');
}
AppApi.PopulateImageHosts(JSON.stringify(args.ref.whiteListedAssetUrls));
const languages = args.ref?.constants?.LANGUAGE?.SPOKEN_LANGUAGE_OPTIONS;
if (!languages) {
return;
}
userStore.subsetOfLanguages = languages;
const data = [];
for (const key in languages) {
const value = languages[key];
data.push({
key,
value
});
}
userStore.languageDialog.languages = data;
}
/**
* @param {import('../types/api/user').GetCurrentUserResponse} json
* @returns {import('../types/api/user').GetCurrentUserResponse}
*/
export function applyCurrentUser(json) {
const userStore = useUserStore();
const appearanceSettingsStore = useAppearanceSettingsStore();
const authStore = useAuthStore();
const gameStore = useGameStore();
const locationStore = useLocationStore();
authStore.setAttemptingAutoLogin(false);
let ref = userStore.currentUser;
runAvatarSwapFlow({
json,
ref,
isLoggedIn: watchState.isLoggedIn
});
if (watchState.isLoggedIn) {
for (const prop in json) {
if (typeof json[prop] !== 'undefined') {
ref[prop] = json[prop];
}
}
} else {
ref = {
acceptedPrivacyVersion: 0,
acceptedTOSVersion: 0,
accountDeletionDate: null,
accountDeletionLog: null,
activeFriends: [],
ageVerificationStatus: '',
ageVerified: false,
allowAvatarCopying: false,
badges: [],
bio: '',
bioLinks: [],
currentAvatar: '',
currentAvatarImageUrl: '',
currentAvatarTags: [],
currentAvatarThumbnailImageUrl: '',
date_joined: '',
developerType: '',
discordDetails: {
global_name: '',
id: ''
},
discordId: '',
displayName: '',
emailVerified: false,
fallbackAvatar: '',
friendGroupNames: [],
friendKey: '',
friends: [],
googleId: '',
hasBirthday: false,
hasDiscordFriendsOptOut: false,
hasEmail: false,
hasLoggedInFromClient: false,
hasPendingEmail: false,
hasSharedConnectionsOptOut: false,
hideContentFilterSettings: false,
homeLocation: '',
id: '',
isAdult: true,
isBoopingEnabled: false,
isFriend: false,
last_activity: '',
last_login: '',
last_mobile: null,
last_platform: '',
obfuscatedEmail: '',
obfuscatedPendingEmail: '',
oculusId: '',
offlineFriends: [],
onlineFriends: [],
pastDisplayNames: [],
picoId: '',
presence: {
avatarThumbnail: '',
currentAvatarTags: '',
debugflag: '',
displayName: '',
groups: [],
id: '',
instance: '',
instanceType: '',
platform: '',
profilePicOverride: '',
status: '',
travelingToInstance: '',
travelingToWorld: '',
userIcon: '',
world: '',
...json.presence
},
profilePicOverride: '',
profilePicOverrideThumbnail: '',
pronouns: '',
queuedInstance: '',
state: '',
status: '',
statusDescription: '',
statusFirstTime: false,
statusHistory: [],
steamDetails: {},
steamId: '',
tags: [],
twoFactorAuthEnabled: false,
twoFactorAuthEnabledDate: null,
unsubscribe: false,
updated_at: '',
userIcon: '',
userLanguage: '',
userLanguageCode: '',
username: '',
viveId: '',
// VRCX
$online_for: null,
$offline_for: null,
$location_at: Date.now(),
$travelingToTime: Date.now(),
$previousAvatarSwapTime: null,
$homeLocation: {},
$isVRCPlus: false,
$isModerator: false,
$isTroll: false,
$isProbableTroll: false,
$trustLevel: 'Visitor',
$trustClass: 'x-tag-untrusted',
$userColour: '',
$trustSortNum: 1,
$languages: [],
$locationTag: '',
$travelingToLocation: '',
...json
};
runFirstLoginFlow(ref);
}
ref.$isVRCPlus = ref.tags.includes('system_supporter');
appearanceSettingsStore.applyUserTrustLevel(ref);
userStore.applyUserLanguage(ref);
userStore.applyPresenceLocation(ref);
runPostApplySyncFlow(ref);
runHomeLocationSyncFlow(ref);
// when isGameRunning use gameLog instead of API
const $location = parseLocation(locationStore.lastLocation.location);
const $travelingLocation = parseLocation(
locationStore.lastLocationDestination
);
let location = locationStore.lastLocation.location;
let instanceId = $location.instanceId;
let worldId = $location.worldId;
let travelingToLocation = locationStore.lastLocationDestination;
let travelingToWorld = $travelingLocation.worldId;
let travelingToInstance = $travelingLocation.instanceId;
if (!gameStore.isGameRunning && json.presence) {
if (isRealInstance(json.presence.world)) {
location = `${json.presence.world}:${json.presence.instance}`;
} else {
location = json.presence.world;
}
if (isRealInstance(json.presence.travelingToWorld)) {
travelingToLocation = `${json.presence.travelingToWorld}:${json.presence.travelingToInstance}`;
} else {
travelingToLocation = json.presence.travelingToWorld;
}
instanceId = json.presence.instance;
worldId = json.presence.world;
travelingToInstance = json.presence.travelingToInstance;
travelingToWorld = json.presence.travelingToWorld;
}
const userRef = applyUser({
ageVerificationStatus: json.ageVerificationStatus,
ageVerified: json.ageVerified,
allowAvatarCopying: json.allowAvatarCopying,
badges: json.badges,
bio: json.bio,
bioLinks: json.bioLinks,
currentAvatarImageUrl: json.currentAvatarImageUrl,
currentAvatarTags: json.currentAvatarTags,
currentAvatarThumbnailImageUrl: json.currentAvatarThumbnailImageUrl,
date_joined: json.date_joined,
developerType: json.developerType,
discordId: json.discordId,
displayName: json.displayName,
friendKey: json.friendKey,
id: json.id,
isFriend: json.isFriend,
last_activity: json.last_activity,
last_login: json.last_login,
last_mobile: json.last_mobile,
last_platform: json.last_platform,
profilePicOverride: json.profilePicOverride,
profilePicOverrideThumbnail: json.profilePicOverrideThumbnail,
pronouns: json.pronouns,
state: json.state,
status: json.status,
statusDescription: json.statusDescription,
tags: json.tags,
userIcon: json.userIcon,
location,
instanceId,
worldId,
travelingToLocation,
travelingToInstance,
travelingToWorld
});
// set VRCX online/offline timers
userRef.$online_for = userStore.currentUser.$online_for;
userRef.$offline_for = userStore.currentUser.$offline_for;
userRef.$location_at = userStore.currentUser.$location_at;
userRef.$travelingToTime = userStore.currentUser.$travelingToTime;
if (json.presence?.platform) {
userRef.platform = json.presence.platform;
}
return ref;
}
/**
*/
export function getCurrentUser() {
const authStore = useAuthStore();
return request('auth/user', {
method: 'GET'
}).then((json) => {
const args = {
json
};
authStore.handleCurrentUserUpdate(json);
return args;
});
}
/**
* @param data
*/
export function addCustomTag(data) {
const userStore = useUserStore();
const sharedFeedStore = useSharedFeedStore();
if (data.Tag) {
userStore.customUserTags.set(data.UserId, {
tag: data.Tag,
colour: data.TagColour
});
} else {
userStore.customUserTags.delete(data.UserId);
}
const feedUpdate = {
userId: data.UserId,
colour: data.TagColour
};
AppApi.ExecuteVrOverlayFunction(
'updateHudFeedTag',
JSON.stringify(feedUpdate)
);
const ref = userStore.cachedUsers.get(data.UserId);
if (typeof ref !== 'undefined') {
ref.$customTag = data.Tag;
ref.$customTagColour = data.TagColour;
}
sharedFeedStore.addTag(data.UserId, data.TagColour);
}
/**
*/
export function updateAutoStateChange() {
const userStore = useUserStore();
const generalSettingsStore = useGeneralSettingsStore();
const gameStore = useGameStore();
const locationStore = useLocationStore();
const favoriteStore = useFavoriteStore();
if (
!generalSettingsStore.autoStateChangeEnabled ||
!gameStore.isGameRunning ||
!locationStore.lastLocation.playerList.size ||
locationStore.lastLocation.location === '' ||
locationStore.lastLocation.location === 'traveling'
) {
return;
}
const $location = parseLocation(locationStore.lastLocation.location);
let instanceType = $location.accessType;
if (instanceType === 'group') {
if ($location.groupAccessType === 'members') {
instanceType = 'groupOnly';
} else if ($location.groupAccessType === 'plus') {
instanceType = 'groupPlus';
} else {
instanceType = 'groupPublic';
}
}
if (
generalSettingsStore.autoStateChangeInstanceTypes.length > 0 &&
!generalSettingsStore.autoStateChangeInstanceTypes.includes(
instanceType
)
) {
return;
}
let withCompany = locationStore.lastLocation.playerList.size > 1;
if (generalSettingsStore.autoStateChangeNoFriends) {
const selectedGroups = generalSettingsStore.autoStateChangeGroups;
if (selectedGroups.length > 0) {
const groupFriendIds = new Set();
for (const ref of favoriteStore.cachedFavorites.values()) {
if (
ref.type === 'friend' &&
selectedGroups.includes(ref.$groupKey)
) {
groupFriendIds.add(ref.favoriteId);
}
}
for (const selectedKey of selectedGroups) {
if (selectedKey.startsWith('local:')) {
const groupName = selectedKey.slice(6);
const userIds =
favoriteStore.localFriendFavorites[groupName];
if (userIds) {
for (let i = 0; i < userIds.length; ++i) {
groupFriendIds.add(userIds[i]);
}
}
}
}
withCompany = false;
for (const friendId of locationStore.lastLocation.friendList.keys()) {
if (groupFriendIds.has(friendId)) {
withCompany = true;
break;
}
}
} else {
withCompany = locationStore.lastLocation.friendList.size >= 1;
}
}
const currentStatus = userStore.currentUser.status;
const newStatus = withCompany
? generalSettingsStore.autoStateChangeCompanyStatus
: generalSettingsStore.autoStateChangeAloneStatus;
if (currentStatus === newStatus) {
return;
}
const params = { status: newStatus };
if (withCompany && generalSettingsStore.autoStateChangeCompanyDescEnabled) {
params.statusDescription =
generalSettingsStore.autoStateChangeCompanyDesc;
} else if (
!withCompany &&
generalSettingsStore.autoStateChangeAloneDescEnabled
) {
params.statusDescription =
generalSettingsStore.autoStateChangeAloneDesc;
}
userRequest.saveCurrentUser(params).then(() => {
const text = `Status automatically changed to ${newStatus}`;
if (AppDebug.errorNoty) {
toast.dismiss(AppDebug.errorNoty);
}
AppDebug.errorNoty = toast.info(text);
console.log(text);
});
}