refactor: app.js (#1291)

* refactor: frontend

* Fix avatar gallery sort

* Update .NET dependencies

* Update npm dependencies

electron v37.1.0

* bulkRefreshFriends

* fix dark theme

* Remove crowdin

* Fix config.json dialog not updating

* VRCX log file fixes & add Cef log

* Remove SharedVariable, fix startup

* Revert init theme change

* Logging date not working? Fix WinformThemer designer error

* Add Cef request hander, no more escaping main page

* clean

* fix

* fix

* clean

* uh

* Apply thememode at startup, fixes random user colours

* Split database into files

* Instance info remove empty lines

* Open external VRC links with VRCX

* Electron fixes

* fix userdialog style

* ohhhh

* fix store

* fix store

* fix: load all group members after kicking a user

* fix: world dialog favorite button style

* fix: Clear VRCX Cache Timer input value

* clean

* Fix VR overlay

* Fix VR overlay 2

* Fix Discord discord rich presence for RPC worlds

* Clean up age verified user tags

* Fix playerList being occupied after program reload

* no `this`

* Fix login stuck loading

* writable: false

* Hide dialogs on logout

* add flush sync option

* rm LOGIN event

* rm LOGOUT event

* remove duplicate event listeners

* remove duplicate event listeners

* clean

* remove duplicate event listeners

* clean

* fix theme style

* fix t

* clearable

* clean

* fix ipcEvent

* Small changes

* Popcorn Palace support

* Remove checkActiveFriends

* Clean up

* Fix dragEnterCef

* Block API requests when not logged in

* Clear state on login & logout

* Fix worldDialog instances not updating

* use <script setup>

* Fix avatar change event, CheckGameRunning at startup

* Fix image dragging

* fix

* Remove PWI

* fix updateLoop

* add webpack-dev-server to dev environment

* rm unnecessary chunks

* use <script setup>

* webpack-dev-server changes

* use <script setup>

* use <script setup>

* Fix UGC text size

* Split login event

* t

* use <script setup>

* fix

* Update .gitignore and enable checkJs in jsconfig

* fix i18n t

* use <script setup>

* use <script setup>

* clean

* global types

* fix

* use checkJs for debugging

* Add watchState for login watchers

* fix .vue template

* type fixes

* rm Vue.filter

* Cef v138.0.170, VC++ 2022

* Settings fixes

* Remove 'USER:CURRENT'

* clean up 2FA callbacks

* remove userApply

* rm i18n import

* notification handling to use notification store methods

* refactor favorite handling to use favorite store methods and clean up event emissions

* refactor moderation handling to use dedicated functions for player moderation events

* refactor friend handling to use dedicated functions for friend events

* Fix program startup, move lang init

* Fix friend state

* Fix status change error

* Fix user notes diff

* fix

* rm group event

* rm auth event

* rm avatar event

* clean

* clean

* getUser

* getFriends

* getFavoriteWorlds, getFavoriteAvatars

* AvatarGalleryUpload btn style & package.json update

* Fix friend requests

* Apply user

* Apply world

* Fix note diff

* Fix VR overlay

* Fixes

* Update build scripts

* Apply avatar

* Apply instance

* Apply group

* update hidden VRC+ badge

* Fix sameInstance "private"

* fix 502/504 API errors

* fix 502/504 API errors

* clean

* Fix friend in same instance on orange showing twice in friends list

* Add back in broken friend state repair methods

* add types

---------

Co-authored-by: Natsumi <cmcooper123@hotmail.com>
This commit is contained in:
pa
2025-07-14 12:00:08 +09:00
committed by GitHub
parent 952fd77ed5
commit f4f78bb5ec
323 changed files with 47745 additions and 43326 deletions

55
src/types/avatar.d.ts vendored Normal file
View File

@@ -0,0 +1,55 @@
export type getAvatar = (params: { avatarId: string }) => Promise<{
json: getAvatarResponse;
params: { avatarId: string };
}>;
interface getAvatarResponse {
acknowledgements: string | null;
authorId: string;
authorName: string;
created_at: string;
description: string;
featured: boolean;
id: string;
imageUrl: string;
name: string;
pendingUpload: boolean;
performance: Performance;
releaseStatus: string;
searchable: boolean;
styles: Styles;
tags: string[];
thumbnailImageUrl: string;
unityPackageUrl: string;
unityPackageUrlObject: UnityPackageUrlObject;
unityPackages: UnityPackage[];
updated_at: string;
version: number;
}
interface Performance {
standalonewindows: string;
'standalonewindows-sort': number;
}
interface Styles {
primary: string | null;
secondary: string | null;
}
interface UnityPackageUrlObject {
unityPackageUrl: string;
}
interface UnityPackage {
assetUrl: string;
assetVersion: number;
created_at: string;
id: string;
performanceRating: string;
platform: string;
scanStatus: string;
unitySortNumber: number;
unityVersion: string;
variant: string;
}

111
src/types/favorite.d.ts vendored Normal file
View File

@@ -0,0 +1,111 @@
export type getFavorites = (params: { n: number; offset: number }) => Promise<{
json: getFavoritesResponseList;
params: { n: number; offset: number };
}>;
interface getFavoritesResponseItem {
favoriteId: string;
id: string;
tags: string[];
type: 'world' | 'friend' | 'avatar';
}
type getFavoritesResponseList = getFavoritesResponseItem[] | undefined;
export type getFavoriteAvatars = (params: {
n: number;
offset: number;
tag: string;
}) => Promise<{
json: getFavoriteAvatarsResponseList;
params: { n: number; offset: number; tag: string };
}>;
interface UnityPackage {
assetVersion: number;
created_at: string;
id: string;
performanceRating?: string;
platform: string;
scanStatus?: string;
unityVersion: string;
variant: string;
impostorizerVersion?: string;
assetUrl: string;
unitySortNumber?: number;
worldSignature?: string;
[key: string]: any;
}
interface Performance {
[platform: string]: string | number;
}
interface Styles {
primary: null;
secondary: null;
}
interface AvatarFavoriteItem {
acknowledgements?: null | string;
authorId: string;
authorName: string;
created_at: string;
description: string;
favoriteGroup: string;
favoriteId: string;
featured: boolean;
id: string;
imageUrl: string;
name: string;
performance: Performance;
releaseStatus: string;
searchable: boolean;
styles: Styles;
tags: any[];
thumbnailImageUrl: string;
unityPackageUrl: string;
unityPackageUrlObject: Record<string, any>;
unityPackages: UnityPackage[];
updated_at: string;
version: number;
}
type getFavoriteAvatarsResponseList = getFavoriteAvatarsResponseItem[];
export type getFavoriteWorlds = (params: {
n: number;
offset: number;
}) => Promise<{
json: getFavoriteWorldsResponseList;
params: { n: number; offset: number };
}>;
interface getFavoriteWorldsResponseItem {
id: string;
name: string;
authorId: string;
authorName: string;
description: string;
capacity: number;
recommendedCapacity?: number;
occupants?: number;
favorites: number;
visits: number;
heat: number;
popularity: number;
created_at: string;
updated_at: string;
publicationDate?: string;
releaseStatus: string;
version: number;
tags: string[];
imageUrl: string;
thumbnailImageUrl: string;
urlList: string[];
defaultContentSettings: Record<string, any>;
unityPackages: UnityPackage[];
[key: string]: any;
}
type getFavoriteWorldsResponseList = getFavoriteWorldsResponseItem[];

40
src/types/friend.d.ts vendored Normal file
View File

@@ -0,0 +1,40 @@
export type getFriends = (params: {
n: number;
offline: boolean;
offset: number;
}) => Promise<{
json: getFriendsResponseList;
params: {
n: number;
offline: boolean;
offset: number;
};
}>;
type getFriendsResponseList = getFriendsResponseItem[] | undefined;
interface getFriendsResponseItem {
bio: string;
bioLinks: string[];
currentAvatarImageUrl: string;
currentAvatarTags: string[];
currentAvatarThumbnailImageUrl: string;
developerType: string;
displayName: string;
friendKey: string;
id: string;
imageUrl: string;
isFriend: boolean;
last_activity: string;
last_login: string;
last_mobile: string | null;
last_platform: string;
location: string;
platform: string;
profilePicOverride: string;
profilePicOverrideThumbnail: string;
status: string;
statusDescription: string;
tags: string[];
userIcon: string;
}

415
src/types/globals.d.ts vendored Normal file
View File

@@ -0,0 +1,415 @@
/// <reference types="node" />
declare global {
const WINDOWS: boolean;
const LINUX: boolean;
interface Window {
$app: any;
API: API;
AppApi: AppApi;
WebApi: WebApi;
VRCXStorage: VRCXStorage;
SQLite: SQLite;
LogWatcher: LogWatcher;
Discord: Discord;
AssetBundleManager: AssetBundleManager;
webApiService: webApiService;
request: {};
configRepository: any;
datebase: any;
gameLogService: any;
crypto: any;
sqliteService: any;
interopApi: {
callDotNetMethod: (
className: any,
methodName: any,
args: any
) => Promise<any>;
};
electron: {
openFileDialog: () => Promise<string>;
openDirectoryDialog: () => Promise<string>;
desktopNotification: (
displayName: string,
body?: string,
image?: string
) => Promise<void>;
onWindowPositionChanged: (
Function: (
event: any,
position: { x: number; y: number }
) => void
) => void;
onWindowSizeChanged: (
Function: (
event: any,
size: { width: number; height: number }
) => void
) => void;
onWindowStateChange: (
Function: (event: any, state: { windowState: any }) => void
) => void;
restartApp: () => Promise<void>;
};
__APP_GLOBALS__: {
debug: boolean;
debugWebSocket: boolean;
debugUserDiff: boolean;
debugPhotonLogging: boolean;
debugGameLog: boolean;
debugWebRequests: boolean;
debugFriendState: boolean;
errorNoty: any;
dontLogMeOut: boolean;
endpointDomain: string;
endpointDomainVrchat: string;
websocketDomain: string;
websocketDomainVrchat: string;
};
}
declare const API: {
// HTTP request methods
$bulk: (options: any, args?: any) => Promise<any>;
bulk: (options: any) => Promise<any>;
// Event system
$emit: (event: string, ...args: any[]) => void;
$off: (event: string, handler?: Function) => void;
$on: (event: string, handler: Function) => void;
// Debug functions
debug: boolean | ((message: any) => void);
debugCurrentUserDiff: boolean | ((data: any) => void);
debugFriendState: boolean | ((data: any) => void);
debugGameLog: boolean | ((data: any) => void);
debugPhotonLogging: boolean | ((data: any) => void);
debugUserDiff: boolean | ((data: any) => void);
debugWebRequests: boolean | ((data: any) => void);
debugWebSocket: boolean | ((data: any) => void);
// Configuration
dontLogMeOut: boolean;
endpointDomain: string;
endpointDomainVrchat: string;
websocketDomain: string;
websocketDomainVrchat: string;
// Error handling
errorNoty: (error: any) => void;
};
const CefSharp: {
PostMessage: (message: any) => void;
BindObjectAsync: (...args: string[]) => Promise<any>;
BindObject: (name: string) => any;
ExecuteScriptAsync: (script: string) => Promise<any>;
ExecuteScript: (script: string) => any;
RemoveObjectFromCache?: (name: string) => void;
DeleteBoundObject?: (name: string) => void;
};
const VRCXStorage: {
Get(key: string): Promise<string>;
Set(key: string, value: string): Promise<void>;
Remove(key: string): Promise<void>;
GetAll(): Promise<string>;
Flush(): Promise<void>;
Save(): Promise<void>;
Load(): Promise<void>;
GetArray(key: string): Promise<any[]>;
SetArray(key: string, value: any[]): Promise<void>;
GetObject(key: string): Promise<object>;
SetObject(key: string, value: object): Promise<void>;
};
const SQLite: {
Execute: (
sql: string,
args: string
) => Promise<{ Item1: any; Item2: any[] }>;
ExecuteJson: (sql: string, args: string) => Promise<string>;
ExecuteNonQuery: (sql: string, args: string) => Promise<void>;
};
const LogWatcher: {
Get(): Promise<Array<[string, string, string, ...any[]]>>;
SetDateTill(date: string): Promise<void>;
GetLogLines(): Array<any>;
Reset(): Promise<void>;
};
const Discord: {
SetTimestamps(startTimestamp: number, endTimestamp: number): void;
SetAssets(
bigIcon: string,
bigIconText: string,
smallIcon: string,
smallIconText: string,
partyId: string,
partySize: number,
partyMaxSize: number,
buttonText: string,
buttonUrl: string,
appId: string,
activityType: number
): void;
SetText(details: string, state: string): void;
SetActive(active: boolean): Promise<boolean>;
};
const AppApi: {
// Basic App Functions
ShowDevTools(): Promise<void>;
SetVR(
active: boolean,
hmdOverlay: boolean,
wristOverlay: boolean,
menuButton: boolean,
overlayHand: number
): Promise<void>;
RefreshVR(): Promise<void>;
RestartVR(): Promise<void>;
SetZoom(zoomLevel: number): Promise<void>;
GetZoom(): Promise<number>;
DesktopNotification(
boldText: string,
text?: string,
image?: string
): Promise<void>;
RestartApplication(isUpgrade: boolean): Promise<void>;
CheckForUpdateExe(): Promise<boolean>;
ExecuteAppFunction(key: string, json: string): Promise<void>;
ExecuteVrFeedFunction(key: string, json: string): Promise<void>;
ExecuteVrOverlayFunction(key: string, json: string): Promise<void>;
FocusWindow(): Promise<void>;
ChangeTheme(value: number): Promise<void>;
DoFunny(): Promise<void>;
GetClipboard(): Promise<string>;
SetStartup(enabled: boolean): Promise<void>;
CopyImageToClipboard(path: string): Promise<void>;
FlashWindow(): Promise<void>;
SetUserAgent(): Promise<void>;
IsRunningUnderWine(): Promise<boolean>;
// Common Functions
MD5File(blob: string): Promise<string>;
GetColourFromUserID(userId: string): Promise<number>;
SignFile(blob: string): Promise<string>;
FileLength(blob: string): Promise<string>;
OpenLink(url: string): Promise<void>;
GetLaunchCommand(): Promise<string>;
IPCAnnounceStart(): Promise<void>;
SendIpc(type: string, data: string): Promise<void>;
CustomCssPath(): Promise<string>;
CustomScriptPath(): Promise<string>;
CurrentCulture(): Promise<string>;
CurrentLanguage(): Promise<string>;
GetVersion(): Promise<string>;
VrcClosedGracefully(): Promise<boolean>;
GetColourBulk(userIds: string[]): Promise<Record<string, number>>;
SetAppLauncherSettings(
enabled: boolean,
killOnExit: boolean
): Promise<void>;
GetFileBase64(path: string): Promise<string | null>;
// Folders
GetVRChatAppDataLocation(): Promise<string>;
GetVRChatPhotosLocation(): Promise<string>;
GetUGCPhotoLocation(path?: string): Promise<string>;
GetVRChatScreenshotsLocation(): Promise<string>;
GetVRChatCacheLocation(): Promise<string>;
OpenVrcxAppDataFolder(): Promise<boolean>;
OpenVrcAppDataFolder(): Promise<boolean>;
OpenVrcPhotosFolder(): Promise<boolean>;
OpenUGCPhotosFolder(ugcPath?: string): Promise<boolean>;
OpenVrcScreenshotsFolder(): Promise<boolean>;
OpenCrashVrcCrashDumps(): Promise<boolean>;
OpenShortcutFolder(): Promise<void>;
OpenFolderAndSelectItem(
path: string,
isFolder?: boolean
): Promise<void>;
OpenFolderSelectorDialog(defaultPath?: string): Promise<string>;
OpenFileSelectorDialog(
defaultPath?: string,
defaultExt?: string,
defaultFilter?: string
): Promise<string>;
// Game Handler
OnProcessStateChanged(monitoredProcess: any): Promise<void>;
CheckGameRunning(): Promise<void>;
IsGameRunning(): Promise<boolean>;
IsSteamVRRunning(): Promise<boolean>;
QuitGame(): Promise<number>;
StartGame(arguments: string): Promise<boolean>;
StartGameFromPath(path: string, arguments: string): Promise<boolean>;
// Registry
GetVRChatRegistryKey(key: string): Promise<any>;
GetVRChatRegistryKeyString(key: string): Promise<string>;
SetVRChatRegistryKey(
key: string,
value: any,
typeInt: number
): Promise<boolean>;
GetVRChatRegistry(): Promise<Record<string, Record<string, any>>>;
SetVRChatRegistry(json: string): Promise<void>;
HasVRChatRegistryFolder(): Promise<boolean>;
DeleteVRChatRegistryFolder(): Promise<void>;
ReadVrcRegJsonFile(filepath: string): Promise<string>;
GetVRChatRegistryJson: () => Promise<string>;
// Image Functions
PopulateImageHosts(json: string): Promise<void>;
GetImage(url: string, fileId: string, version: string): Promise<string>;
ResizeImageToFitLimits(base64data: string): Promise<string>;
CropAllPrints(ugcFolderPath: string): Promise<void>;
CropPrintImage(path: string): Promise<boolean>;
SavePrintToFile(
url: string,
ugcFolderPath: string,
monthFolder: string,
fileName: string
): Promise<string>;
SaveStickerToFile(
url: string,
ugcFolderPath: string,
monthFolder: string,
fileName: string
): Promise<string>;
SaveEmojiToFile(
url: string,
ugcFolderPath: string,
monthFolder: string,
fileName: string
): Promise<string>;
// Screenshot
AddScreenshotMetadata(
path: string,
metadataString: string,
worldId: string,
changeFilename?: boolean
): Promise<string>;
GetExtraScreenshotData(
path: string,
carouselCache: boolean
): Promise<string>;
GetScreenshotMetadata(path: string): Promise<string>;
FindScreenshotsBySearch(
searchQuery: string,
searchType?: number
): Promise<string>;
GetLastScreenshot(): Promise<string>;
// Moderations
GetVRChatModerations(
currentUserId: string
): Promise<Record<string, number> | null>;
GetVRChatUserModeration(
currentUserId: string,
userId: string
): Promise<number>;
SetVRChatUserModeration(
currentUserId: string,
userId: string,
type: number
): Promise<boolean>;
// VRC Config
ReadConfigFile(): Promise<string>;
ReadConfigFileSafe(): Promise<string>;
WriteConfigFile(json: string): Promise<void>;
// Update
DownloadUpdate(
fileUrl: string,
fileName: string,
hashUrl: string,
downloadSize: number
): Promise<void>;
CancelUpdate(): Promise<void>;
CheckUpdateProgress(): Promise<number>;
// Notifications
XSNotification(
title: string,
content: string,
timeout: number,
opacity: number,
image?: string
): Promise<void>;
OVRTNotification(
hudNotification: boolean,
wristNotification: boolean,
title: string,
body: string,
timeout: number,
opacity: number,
image?: string
): Promise<void>;
};
const AppApiVr: {
Init(): void;
VrInit(): void;
ToggleSystemMonitor(enabled: boolean): void;
CpuUsage(): number;
GetVRDevices(): string[][];
GetUptime(): number;
CurrentCulture(): string;
CustomVrScriptPath(): string;
IsRunningUnderWine(): boolean;
};
const WebApi: {
ClearCookies(): void;
GetCookies(): string;
SetCookies(cookie: string): void;
Execute(options: any): Promise<{ Item1: number; Item2: string }>;
ExecuteJson(requestJson: string): Promise<string>;
};
const AssetBundleManager: {
SweepCache(): Promise<string>;
GetCacheSize(): Promise<number>;
GetVRChatCacheFullLocation(
fileId: string,
fileVersion: number,
variant: string,
variantVersion: number
): Promise<string>;
CheckVRChatCache(
fileId: string,
fileVersion: number,
variant: string,
variantVersion: number
): Promise<{ Item1: number; Item2: boolean; Item3: string }>;
DeleteCache(
fileId: string,
fileVersion: number,
variant: string,
variantVersion: number
): Promise<void>;
DeleteAllCache: () => Promise<void>;
};
const webApiService: {
clearCookies(): void;
getCookies(): string;
setCookies(cookie: string): void;
execute(options: {
url: string;
method: string;
headers?: Record<string, string>;
data?: any;
}): Promise<{ status: number; data: string }>;
};
}
export {};

34
src/types/group.d.ts vendored Normal file
View File

@@ -0,0 +1,34 @@
export type getGroup = (params: {
groupId: string;
includeRoles: boolean;
}) => Promise<{
json: getGroupResponse;
params: { groupId: string; includeRoles: boolean };
}>;
interface Group {
badges: any[];
bannerId: string;
bannerUrl: string;
createdAt: string;
description: string;
discriminator: string;
galleries: any[];
iconId: string;
iconUrl: string;
id: string;
isVerified: boolean;
joinState: string;
languages: string[];
links: any[];
memberCount: number;
memberCountSyncedAt: string;
membershipStatus: string;
name: string;
onlineMemberCount: number;
ownerId: string;
privacy: string;
rules: string;
shortCode: string;
tags: string[];
}

93
src/types/instance.d.ts vendored Normal file
View File

@@ -0,0 +1,93 @@
export type getInstance = (params: {
worldId: string;
instanceId: string;
}) => Promise<{
json: getInstanceResponse;
params: { worldId: string; instanceId: string };
}>;
interface getInstanceResponse {
active: boolean;
ageGate: boolean;
canRequestInvite: boolean;
capacity: number;
clientNumber: string;
closedAt: string | null;
contentSettings: Record<string, any>;
displayName: string | null;
full: boolean;
gameServerVersion: number;
hardClose: string | null;
hasCapacityForYou: boolean;
hidden: string;
id: string;
instanceId: string;
instancePersistenceEnabled: boolean | null;
location: string;
n_users: number;
name: string;
ownerId: string;
permanent: boolean;
photonRegion: string;
platforms: Platforms;
playerPersistenceEnabled: boolean;
queueEnabled: boolean;
queueSize: number;
recommendedCapacity: number;
region: string;
secureName: string;
shortName: string | null;
strict: boolean;
tags: string[];
type: string;
userCount: number;
world: World;
worldId: string;
}
interface Platforms {
android: number;
ios: number;
standalonewindows: number;
}
interface World {
authorId: string;
authorName: string;
capacity: number;
created_at: string;
defaultContentSettings: Record<string, any>;
description: string;
favorites: number;
featured: boolean;
heat: number;
id: string;
imageUrl: string;
labsPublicationDate: string;
name: string;
organization: string;
popularity: number;
previewYoutubeId: string | null;
publicationDate: string;
recommendedCapacity: number;
releaseStatus: string;
tags: string[];
thumbnailImageUrl: string;
udonProducts: any[];
unityPackages: WorldUnityPackage[];
updated_at: string;
urlList: any[];
version: number;
visits: number;
}
interface WorldUnityPackage {
assetUrl: string;
assetVersion: number;
created_at: string;
id: string;
platform: string;
unitySortNumber: number;
unityVersion: string;
worldSignature: string;
}

49
src/types/user.d.ts vendored Normal file
View File

@@ -0,0 +1,49 @@
export type getUser = (params: { userId: string }) => Promise<{
json: getUserResponse;
params: { userId: string };
}>;
interface getUserResponse {
ageVerificationStatus: string;
ageVerified: boolean;
allowAvatarCopying: boolean;
badges: {
badgeDescription: string;
badgeId: string;
badgeImageUrl: string;
badgeName: string;
showcased: boolean;
}[];
bio: string;
bioLinks: string[];
currentAvatarImageUrl: string;
currentAvatarTags: string[];
currentAvatarThumbnailImageUrl: string;
date_joined: string;
developerType: string;
displayName: string;
friendKey: string;
friendRequestStatus: string;
id: string;
instanceId: string;
isFriend: boolean;
last_activity: string;
last_login: string;
last_mobile: string | null;
last_platform: string;
location: string;
note: string;
platform: string;
profilePicOverride: string;
profilePicOverrideThumbnail: string;
pronouns: string;
state: string;
status: string;
statusDescription: string;
tags: string[];
travelingToInstance: string;
travelingToLocation: string;
travelingToWorld: string;
userIcon: string;
worldId: string;
}