mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-05-06 06:46:04 +02:00
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:
@@ -0,0 +1,773 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { computed, reactive, watch } from 'vue';
|
||||
import { worldRequest } from '../api';
|
||||
import { $app } from '../app';
|
||||
import configRepository from '../service/config';
|
||||
import { database } from '../service/database';
|
||||
import { AppGlobal } from '../service/appConfig';
|
||||
import { failedGetRequests } from '../service/request';
|
||||
import { watchState } from '../service/watchState';
|
||||
import {
|
||||
debounce,
|
||||
parseLocation,
|
||||
refreshCustomCss,
|
||||
removeFromArray
|
||||
} from '../shared/utils';
|
||||
import { useAvatarStore } from './avatar';
|
||||
import { useAvatarProviderStore } from './avatarProvider';
|
||||
import { useFavoriteStore } from './favorite';
|
||||
import { useFriendStore } from './friend';
|
||||
import { useGameStore } from './game';
|
||||
import { useGameLogStore } from './gameLog';
|
||||
import { useGroupStore } from './group';
|
||||
import { useInstanceStore } from './instance';
|
||||
import { useLocationStore } from './location';
|
||||
import { useNotificationStore } from './notification';
|
||||
import { usePhotonStore } from './photon';
|
||||
import { useSearchStore } from './search';
|
||||
import { useAdvancedSettingsStore } from './settings/advanced';
|
||||
import { useUpdateLoopStore } from './updateLoop';
|
||||
import { useUserStore } from './user';
|
||||
import { useWorldStore } from './world';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
|
||||
export const useVrcxStore = defineStore('Vrcx', () => {
|
||||
const gameStore = useGameStore();
|
||||
const locationStore = useLocationStore();
|
||||
const notificationStore = useNotificationStore();
|
||||
const avatarStore = useAvatarStore();
|
||||
const worldStore = useWorldStore();
|
||||
const instanceStore = useInstanceStore();
|
||||
const friendStore = useFriendStore();
|
||||
const favoriteStore = useFavoriteStore();
|
||||
const groupStore = useGroupStore();
|
||||
const userStore = useUserStore();
|
||||
const photonStore = usePhotonStore();
|
||||
const advancedSettingsStore = useAdvancedSettingsStore();
|
||||
const searchStore = useSearchStore();
|
||||
const avatarProviderStore = useAvatarProviderStore();
|
||||
const gameLogStore = useGameLogStore();
|
||||
const updateLoopStore = useUpdateLoopStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
const state = reactive({
|
||||
isRunningUnderWine: false,
|
||||
databaseVersion: 0,
|
||||
clearVRCXCacheFrequency: 172800,
|
||||
proxyServer: '',
|
||||
locationX: 0,
|
||||
locationY: 0,
|
||||
sizeWidth: 800,
|
||||
sizeHeight: 600,
|
||||
windowState: '',
|
||||
maxTableSize: 1000,
|
||||
ipcEnabled: false,
|
||||
externalNotifierVersion: 0,
|
||||
currentlyDroppingFile: null,
|
||||
isRegistryBackupDialogVisible: false
|
||||
});
|
||||
|
||||
async function init() {
|
||||
if (LINUX) {
|
||||
window.electron.onWindowPositionChanged((event, position) => {
|
||||
state.locationX = position.x;
|
||||
state.locationY = position.y;
|
||||
debounce(saveVRCXWindowOption(), 300);
|
||||
});
|
||||
|
||||
window.electron.onWindowSizeChanged((event, size) => {
|
||||
state.sizeWidth = size.width;
|
||||
state.sizeHeight = size.height;
|
||||
debounce(saveVRCXWindowOption(), 300);
|
||||
});
|
||||
|
||||
window.electron.onWindowStateChange((event, state) => {
|
||||
state.windowState = state;
|
||||
debounce(saveVRCXWindowOption(), 300);
|
||||
});
|
||||
|
||||
// window.electron.onWindowClosed((event) => {
|
||||
// window.$app.saveVRCXWindowOption();
|
||||
// });
|
||||
}
|
||||
|
||||
state.databaseVersion = await configRepository.getInt(
|
||||
'VRCX_databaseVersion',
|
||||
0
|
||||
);
|
||||
updateDatabaseVersion();
|
||||
|
||||
state.clearVRCXCacheFrequency = await configRepository.getInt(
|
||||
'VRCX_clearVRCXCacheFrequency',
|
||||
172800
|
||||
);
|
||||
|
||||
if (!(await VRCXStorage.Get('VRCX_DatabaseLocation'))) {
|
||||
await VRCXStorage.Set('VRCX_DatabaseLocation', '');
|
||||
}
|
||||
if (!(await VRCXStorage.Get('VRCX_ProxyServer'))) {
|
||||
await VRCXStorage.Set('VRCX_ProxyServer', '');
|
||||
}
|
||||
if ((await VRCXStorage.Get('VRCX_DisableGpuAcceleration')) === '') {
|
||||
await VRCXStorage.Set('VRCX_DisableGpuAcceleration', 'false');
|
||||
}
|
||||
if (
|
||||
(await VRCXStorage.Get('VRCX_DisableVrOverlayGpuAcceleration')) ===
|
||||
''
|
||||
) {
|
||||
await VRCXStorage.Set(
|
||||
'VRCX_DisableVrOverlayGpuAcceleration',
|
||||
'false'
|
||||
);
|
||||
}
|
||||
state.proxyServer = await VRCXStorage.Get('VRCX_ProxyServer');
|
||||
state.locationX = await VRCXStorage.Get('VRCX_LocationX');
|
||||
state.locationY = await VRCXStorage.Get('VRCX_LocationY');
|
||||
state.sizeWidth = await VRCXStorage.Get('VRCX_SizeWidth');
|
||||
state.sizeHeight = await VRCXStorage.Get('VRCX_SizeHeight');
|
||||
state.windowState = await VRCXStorage.Get('VRCX_WindowState');
|
||||
|
||||
state.maxTableSize = await configRepository.getInt(
|
||||
'VRCX_maxTableSize',
|
||||
1000
|
||||
);
|
||||
if (state.maxTableSize > 10000) {
|
||||
state.maxTableSize = 1000;
|
||||
}
|
||||
database.setMaxTableSize(state.maxTableSize);
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
const currentlyDroppingFile = computed({
|
||||
get: () => state.currentlyDroppingFile,
|
||||
set: (value) => {
|
||||
state.currentlyDroppingFile = value;
|
||||
}
|
||||
});
|
||||
|
||||
const isRegistryBackupDialogVisible = computed({
|
||||
get: () => state.isRegistryBackupDialogVisible,
|
||||
set: (value) => {
|
||||
state.isRegistryBackupDialogVisible = value;
|
||||
}
|
||||
});
|
||||
|
||||
const ipcEnabled = computed({
|
||||
get: () => state.ipcEnabled,
|
||||
set: (value) => {
|
||||
state.ipcEnabled = value;
|
||||
}
|
||||
});
|
||||
|
||||
const clearVRCXCacheFrequency = computed({
|
||||
get: () => state.clearVRCXCacheFrequency,
|
||||
set: (value) => {
|
||||
state.clearVRCXCacheFrequency = value;
|
||||
}
|
||||
});
|
||||
|
||||
const maxTableSize = computed({
|
||||
get: () => state.maxTableSize,
|
||||
set: (value) => {
|
||||
state.maxTableSize = value;
|
||||
}
|
||||
});
|
||||
|
||||
// Make sure file drops outside of the screenshot manager don't navigate to the file path dropped.
|
||||
// This issue persists on prompts created with prompt(), unfortunately. Not sure how to fix that.
|
||||
document.body.addEventListener('drop', function (e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
document.addEventListener('keyup', function (e) {
|
||||
if (e.ctrlKey) {
|
||||
if (e.key === 'I') {
|
||||
showConsole();
|
||||
} else if (e.key === 'r') {
|
||||
location.reload();
|
||||
}
|
||||
} else if (e.altKey && e.key === 'R') {
|
||||
refreshCustomCss();
|
||||
}
|
||||
});
|
||||
|
||||
const isRunningUnderWine = computed({
|
||||
get: () => state.isRunningUnderWine,
|
||||
set: (value) => {
|
||||
state.isRunningUnderWine = value;
|
||||
}
|
||||
});
|
||||
|
||||
async function applyWineEmojis() {
|
||||
if (document.contains(document.getElementById('app-emoji-font'))) {
|
||||
document.getElementById('app-emoji-font').remove();
|
||||
}
|
||||
if (gameStore.isRunningUnderWine) {
|
||||
const $appEmojiFont = document.createElement('link');
|
||||
$appEmojiFont.setAttribute('id', 'app-emoji-font');
|
||||
$appEmojiFont.rel = 'stylesheet';
|
||||
$appEmojiFont.href = 'emoji.font.css';
|
||||
document.head.appendChild($appEmojiFont);
|
||||
}
|
||||
}
|
||||
|
||||
function showConsole() {
|
||||
AppApi.ShowDevTools();
|
||||
if (
|
||||
AppGlobal.debug ||
|
||||
AppGlobal.debugWebRequests ||
|
||||
AppGlobal.debugWebSocket ||
|
||||
AppGlobal.debugUserDiff
|
||||
) {
|
||||
return;
|
||||
}
|
||||
console.log(
|
||||
'%cCareful! This might not do what you think.',
|
||||
'background-color: red; color: yellow; font-size: 32px; font-weight: bold'
|
||||
);
|
||||
console.log(
|
||||
'%cIf someone told you to copy-paste something here, it can give them access to your account.',
|
||||
'font-size: 20px;'
|
||||
);
|
||||
}
|
||||
|
||||
async function updateDatabaseVersion() {
|
||||
const databaseVersion = 12;
|
||||
let msgBox;
|
||||
if (state.databaseVersion < databaseVersion) {
|
||||
if (state.databaseVersion) {
|
||||
msgBox = $app.$message({
|
||||
message:
|
||||
'DO NOT CLOSE VRCX, database upgrade in progress...',
|
||||
type: 'warning',
|
||||
duration: 0
|
||||
});
|
||||
}
|
||||
console.log(
|
||||
`Updating database from ${state.databaseVersion} to ${databaseVersion}...`
|
||||
);
|
||||
try {
|
||||
await database.cleanLegendFromFriendLog(); // fix friendLog spammed with crap
|
||||
await database.fixGameLogTraveling(); // fix bug with gameLog location being set as traveling
|
||||
await database.fixNegativeGPS(); // fix GPS being a negative value due to VRCX bug with traveling
|
||||
await database.fixBrokenLeaveEntries(); // fix user instance timer being higher than current user location timer
|
||||
await database.fixBrokenGroupInvites(); // fix notification v2 in wrong table
|
||||
await database.fixBrokenNotifications(); // fix notifications being null
|
||||
await database.fixBrokenGroupChange(); // fix spam group left & name change
|
||||
await database.fixCancelFriendRequestTypo(); // fix CancelFriendRequst typo
|
||||
await database.fixBrokenGameLogDisplayNames(); // fix gameLog display names "DisplayName (userId)"
|
||||
await database.upgradeDatabaseVersion(); // update database version
|
||||
await database.vacuum(); // succ
|
||||
await database.optimize();
|
||||
await configRepository.setInt(
|
||||
'VRCX_databaseVersion',
|
||||
databaseVersion
|
||||
);
|
||||
console.log('Database update complete.');
|
||||
msgBox?.close();
|
||||
if (state.databaseVersion) {
|
||||
// only display when database exists
|
||||
$app.$message({
|
||||
message: 'Database upgrade complete',
|
||||
type: 'success'
|
||||
});
|
||||
}
|
||||
state.databaseVersion = databaseVersion;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
msgBox?.close();
|
||||
$app.$message({
|
||||
message:
|
||||
'Database upgrade failed, check console for details',
|
||||
type: 'error',
|
||||
duration: 120000
|
||||
});
|
||||
AppApi.ShowDevTools();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clearVRCXCache() {
|
||||
failedGetRequests.clear();
|
||||
userStore.cachedUsers.forEach((ref, id) => {
|
||||
if (
|
||||
!friendStore.friends.has(id) &&
|
||||
!locationStore.lastLocation.playerList.has(ref.id) &&
|
||||
id !== userStore.currentUser.id
|
||||
) {
|
||||
userStore.cachedUsers.delete(id);
|
||||
}
|
||||
});
|
||||
worldStore.cachedWorlds.forEach((ref, id) => {
|
||||
if (
|
||||
!favoriteStore.cachedFavoritesByObjectId.has(id) &&
|
||||
ref.authorId !== userStore.currentUser.id &&
|
||||
!favoriteStore.localWorldFavoritesList.includes(id)
|
||||
) {
|
||||
worldStore.cachedWorlds.delete(id);
|
||||
}
|
||||
});
|
||||
avatarStore.cachedAvatars.forEach((ref, id) => {
|
||||
if (
|
||||
!favoriteStore.cachedFavoritesByObjectId.has(id) &&
|
||||
ref.authorId !== userStore.currentUser.id &&
|
||||
!favoriteStore.localAvatarFavoritesList.includes(id) &&
|
||||
!avatarStore.avatarHistory.has(id)
|
||||
) {
|
||||
avatarStore.cachedAvatars.delete(id);
|
||||
}
|
||||
});
|
||||
groupStore.cachedGroups.forEach((ref, id) => {
|
||||
if (!groupStore.currentUserGroups.has(id)) {
|
||||
groupStore.cachedGroups.delete(id);
|
||||
}
|
||||
});
|
||||
instanceStore.cachedInstances.forEach((ref, id) => {
|
||||
// delete instances over an hour old
|
||||
if (Date.parse(ref.$fetchedAt) < Date.now() - 3600000) {
|
||||
instanceStore.cachedInstances.delete(id);
|
||||
}
|
||||
});
|
||||
avatarStore.cachedAvatarNames = new Map();
|
||||
userStore.customUserTags = new Map();
|
||||
}
|
||||
|
||||
function eventVrcxMessage(data) {
|
||||
let entry;
|
||||
switch (data.MsgType) {
|
||||
case 'CustomTag':
|
||||
userStore.addCustomTag(data);
|
||||
break;
|
||||
case 'ClearCustomTags':
|
||||
userStore.customUserTags.forEach((value, key) => {
|
||||
userStore.customUserTags.delete(key);
|
||||
const ref = userStore.cachedUsers.get(key);
|
||||
if (typeof ref !== 'undefined') {
|
||||
ref.$customTag = '';
|
||||
ref.$customTagColour = '';
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'Noty':
|
||||
if (
|
||||
photonStore.photonLoggingEnabled ||
|
||||
(state.externalNotifierVersion &&
|
||||
state.externalNotifierVersion > 21)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
entry = {
|
||||
created_at: new Date().toJSON(),
|
||||
type: 'Event',
|
||||
data: data.Data
|
||||
};
|
||||
database.addGamelogEventToDatabase(entry);
|
||||
notificationStore.queueGameLogNoty(entry);
|
||||
gameLogStore.addGameLog(entry);
|
||||
break;
|
||||
case 'External': {
|
||||
const displayName = data.DisplayName ?? '';
|
||||
entry = {
|
||||
created_at: new Date().toJSON(),
|
||||
type: 'External',
|
||||
message: data.Data,
|
||||
displayName,
|
||||
userId: data.UserId,
|
||||
location: locationStore.lastLocation.location
|
||||
};
|
||||
database.addGamelogExternalToDatabase(entry);
|
||||
notificationStore.queueGameLogNoty(entry);
|
||||
gameLogStore.addGameLog(entry);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
console.log('VRCXMessage:', data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async function saveVRCXWindowOption() {
|
||||
if (LINUX) {
|
||||
VRCXStorage.Set('VRCX_LocationX', state.locationX);
|
||||
VRCXStorage.Set('VRCX_LocationY', state.locationY);
|
||||
VRCXStorage.Set('VRCX_SizeWidth', state.sizeWidth);
|
||||
VRCXStorage.Set('VRCX_SizeHeight', state.sizeHeight);
|
||||
VRCXStorage.Set('VRCX_WindowState', state.windowState);
|
||||
VRCXStorage.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
async function processScreenshot(path) {
|
||||
let newPath = path;
|
||||
if (advancedSettingsStore.screenshotHelper) {
|
||||
const location = parseLocation(locationStore.lastLocation.location);
|
||||
const metadata = {
|
||||
application: 'VRCX',
|
||||
version: 1,
|
||||
author: {
|
||||
id: userStore.currentUser.id,
|
||||
displayName: userStore.currentUser.displayName
|
||||
},
|
||||
world: {
|
||||
name: locationStore.lastLocation.name,
|
||||
id: location.worldId,
|
||||
instanceId: locationStore.lastLocation.location
|
||||
},
|
||||
players: []
|
||||
};
|
||||
for (const user of locationStore.lastLocation.playerList.values()) {
|
||||
metadata.players.push({
|
||||
id: user.userId,
|
||||
displayName: user.displayName
|
||||
});
|
||||
}
|
||||
newPath = await AppApi.AddScreenshotMetadata(
|
||||
path,
|
||||
JSON.stringify(metadata),
|
||||
location.worldId,
|
||||
advancedSettingsStore.screenshotHelperModifyFilename
|
||||
);
|
||||
console.log('Screenshot metadata added', newPath);
|
||||
}
|
||||
if (advancedSettingsStore.screenshotHelperCopyToClipboard) {
|
||||
await AppApi.CopyImageToClipboard(newPath);
|
||||
console.log('Screenshot copied to clipboard', newPath);
|
||||
}
|
||||
}
|
||||
|
||||
// use in C# side
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function ipcEvent(json) {
|
||||
if (!watchState.isLoggedIn) {
|
||||
return;
|
||||
}
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(json);
|
||||
} catch {
|
||||
console.log(`IPC invalid JSON, ${json}`);
|
||||
return;
|
||||
}
|
||||
switch (data.type) {
|
||||
case 'OnEvent':
|
||||
if (!gameStore.isGameRunning) {
|
||||
console.log('Game closed, skipped event', data);
|
||||
return;
|
||||
}
|
||||
if (AppGlobal.debugPhotonLogging) {
|
||||
console.log(
|
||||
'OnEvent',
|
||||
data.OnEventData.Code,
|
||||
data.OnEventData
|
||||
);
|
||||
}
|
||||
photonStore.parsePhotonEvent(data.OnEventData, data.dt);
|
||||
photonStore.photonEventPulse();
|
||||
break;
|
||||
case 'OnOperationResponse':
|
||||
if (!gameStore.isGameRunning) {
|
||||
console.log('Game closed, skipped event', data);
|
||||
return;
|
||||
}
|
||||
if (AppGlobal.debugPhotonLogging) {
|
||||
console.log(
|
||||
'OnOperationResponse',
|
||||
data.OnOperationResponseData.OperationCode,
|
||||
data.OnOperationResponseData
|
||||
);
|
||||
}
|
||||
photonStore.parseOperationResponse(
|
||||
data.OnOperationResponseData,
|
||||
data.dt
|
||||
);
|
||||
photonStore.photonEventPulse();
|
||||
break;
|
||||
case 'OnOperationRequest':
|
||||
if (!gameStore.isGameRunning) {
|
||||
console.log('Game closed, skipped event', data);
|
||||
return;
|
||||
}
|
||||
if (AppGlobal.debugPhotonLogging) {
|
||||
console.log(
|
||||
'OnOperationRequest',
|
||||
data.OnOperationRequestData.OperationCode,
|
||||
data.OnOperationRequestData
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'VRCEvent':
|
||||
if (!gameStore.isGameRunning) {
|
||||
console.log('Game closed, skipped event', data);
|
||||
return;
|
||||
}
|
||||
photonStore.parseVRCEvent(data);
|
||||
photonStore.photonEventPulse();
|
||||
break;
|
||||
case 'Event7List':
|
||||
photonStore.photonEvent7List.clear();
|
||||
for (const [id, dt] of Object.entries(data.Event7List)) {
|
||||
photonStore.photonEvent7List.set(parseInt(id, 10), dt);
|
||||
}
|
||||
photonStore.photonLastEvent7List = Date.parse(data.dt);
|
||||
break;
|
||||
case 'VrcxMessage':
|
||||
if (AppGlobal.debugPhotonLogging) {
|
||||
console.log('VrcxMessage:', data);
|
||||
}
|
||||
eventVrcxMessage(data);
|
||||
break;
|
||||
case 'Ping':
|
||||
if (!photonStore.photonLoggingEnabled) {
|
||||
photonStore.setPhotonLoggingEnabled();
|
||||
}
|
||||
state.ipcEnabled = true;
|
||||
updateLoopStore.ipcTimeout = 60; // 30secs
|
||||
break;
|
||||
case 'MsgPing':
|
||||
state.externalNotifierVersion = data.version;
|
||||
break;
|
||||
case 'LaunchCommand':
|
||||
eventLaunchCommand(data.command);
|
||||
break;
|
||||
case 'VRCXLaunch':
|
||||
console.log('VRCXLaunch:', data);
|
||||
break;
|
||||
default:
|
||||
console.log('IPC:', data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called by .NET(CefCustomDragHandler#CefCustomDragHandler) when a file is dragged over a drop zone in the app window.
|
||||
* @param {string} filePath - The full path to the file being dragged into the window
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function dragEnterCef(filePath) {
|
||||
state.currentlyDroppingFile = filePath;
|
||||
}
|
||||
|
||||
watch(
|
||||
() => watchState.isLoggedIn,
|
||||
(isLoggedIn) => {
|
||||
state.isRegistryBackupDialogVisible = false;
|
||||
if (isLoggedIn) {
|
||||
startupLaunchCommand();
|
||||
}
|
||||
},
|
||||
{ flush: 'sync' }
|
||||
);
|
||||
|
||||
async function startupLaunchCommand() {
|
||||
const command = await AppApi.GetLaunchCommand();
|
||||
if (command) {
|
||||
eventLaunchCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
function eventLaunchCommand(input) {
|
||||
if (!watchState.isLoggedIn) {
|
||||
return;
|
||||
}
|
||||
console.log('LaunchCommand:', input);
|
||||
const args = input.split('/');
|
||||
const command = args[0];
|
||||
const commandArg = args[1]?.trim();
|
||||
let shouldFocusWindow = true;
|
||||
switch (command) {
|
||||
case 'world':
|
||||
searchStore.directAccessWorld(input.replace('world/', ''));
|
||||
break;
|
||||
case 'avatar':
|
||||
avatarStore.showAvatarDialog(commandArg);
|
||||
break;
|
||||
case 'user':
|
||||
userStore.showUserDialog(commandArg);
|
||||
break;
|
||||
case 'group':
|
||||
groupStore.showGroupDialog(commandArg);
|
||||
break;
|
||||
case 'local-favorite-world':
|
||||
console.log('local-favorite-world', commandArg);
|
||||
const [id, group] = commandArg.split(':');
|
||||
worldRequest.getCachedWorld({ worldId: id }).then((args1) => {
|
||||
searchStore.directAccessWorld(id);
|
||||
favoriteStore.addLocalWorldFavorite(id, group);
|
||||
return args1;
|
||||
});
|
||||
break;
|
||||
case 'addavatardb':
|
||||
avatarProviderStore.addAvatarProvider(
|
||||
input.replace('addavatardb/', '')
|
||||
);
|
||||
break;
|
||||
case 'switchavatar':
|
||||
const avatarId = commandArg;
|
||||
const regexAvatarId =
|
||||
/avtr_[0-9A-Fa-f]{8}-([0-9A-Fa-f]{4}-){3}[0-9A-Fa-f]{12}/g;
|
||||
if (!avatarId.match(regexAvatarId) || avatarId.length !== 41) {
|
||||
$app.$message({
|
||||
message: 'Invalid Avatar ID',
|
||||
type: 'error'
|
||||
});
|
||||
break;
|
||||
}
|
||||
if (advancedSettingsStore.showConfirmationOnSwitchAvatar) {
|
||||
avatarStore.selectAvatarWithConfirmation(avatarId);
|
||||
// Makes sure the window is focused
|
||||
shouldFocusWindow = true;
|
||||
} else {
|
||||
$app.selectAvatarWithoutConfirmation(avatarId);
|
||||
shouldFocusWindow = false;
|
||||
}
|
||||
break;
|
||||
case 'import':
|
||||
const type = args[1];
|
||||
if (!type) break;
|
||||
const data = input.replace(`import/${type}/`, '');
|
||||
if (type === 'avatar') {
|
||||
favoriteStore.avatarImportDialogInput = data;
|
||||
favoriteStore.showAvatarImportDialog();
|
||||
} else if (type === 'world') {
|
||||
favoriteStore.worldImportDialogInput = data;
|
||||
favoriteStore.showWorldImportDialog();
|
||||
} else if (type === 'friend') {
|
||||
favoriteStore.friendImportDialogInput = data;
|
||||
favoriteStore.showFriendImportDialog();
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (shouldFocusWindow) {
|
||||
AppApi.FocusWindow();
|
||||
}
|
||||
}
|
||||
|
||||
async function backupVrcRegistry(name) {
|
||||
let regJson;
|
||||
if (LINUX) {
|
||||
regJson = await AppApi.GetVRChatRegistryJson();
|
||||
regJson = JSON.parse(regJson);
|
||||
} else {
|
||||
regJson = await AppApi.GetVRChatRegistry();
|
||||
}
|
||||
const newBackup = {
|
||||
name,
|
||||
date: new Date().toJSON(),
|
||||
data: regJson
|
||||
};
|
||||
let backupsJson = await configRepository.getString(
|
||||
'VRCX_VRChatRegistryBackups'
|
||||
);
|
||||
if (!backupsJson) {
|
||||
backupsJson = JSON.stringify([]);
|
||||
}
|
||||
const backups = JSON.parse(backupsJson);
|
||||
backups.push(newBackup);
|
||||
await configRepository.setString(
|
||||
'VRCX_VRChatRegistryBackups',
|
||||
JSON.stringify(backups)
|
||||
);
|
||||
// await this.updateRegistryBackupDialog();
|
||||
}
|
||||
|
||||
async function checkAutoBackupRestoreVrcRegistry() {
|
||||
if (!advancedSettingsStore.vrcRegistryAutoBackup) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check for auto restore
|
||||
const hasVRChatRegistryFolder = await AppApi.HasVRChatRegistryFolder();
|
||||
if (!hasVRChatRegistryFolder) {
|
||||
const lastBackupDate = await configRepository.getString(
|
||||
'VRCX_VRChatRegistryLastBackupDate'
|
||||
);
|
||||
const lastRestoreCheck = await configRepository.getString(
|
||||
'VRCX_VRChatRegistryLastRestoreCheck'
|
||||
);
|
||||
if (
|
||||
!lastBackupDate ||
|
||||
(lastRestoreCheck &&
|
||||
lastBackupDate &&
|
||||
lastRestoreCheck === lastBackupDate)
|
||||
) {
|
||||
// only ask to restore once and when backup is present
|
||||
return;
|
||||
}
|
||||
// popup message about auto restore
|
||||
$app.$alert(
|
||||
t('dialog.registry_backup.restore_prompt'),
|
||||
t('dialog.registry_backup.header')
|
||||
);
|
||||
showRegistryBackupDialog();
|
||||
await AppApi.FocusWindow();
|
||||
await configRepository.setString(
|
||||
'VRCX_VRChatRegistryLastRestoreCheck',
|
||||
lastBackupDate
|
||||
);
|
||||
} else {
|
||||
await autoBackupVrcRegistry();
|
||||
}
|
||||
}
|
||||
|
||||
function showRegistryBackupDialog() {
|
||||
state.isRegistryBackupDialogVisible = true;
|
||||
}
|
||||
|
||||
async function autoBackupVrcRegistry() {
|
||||
const date = new Date();
|
||||
const lastBackupDate = await configRepository.getString(
|
||||
'VRCX_VRChatRegistryLastBackupDate'
|
||||
);
|
||||
if (lastBackupDate) {
|
||||
const lastBackup = new Date(lastBackupDate);
|
||||
const diff = date.getTime() - lastBackup.getTime();
|
||||
const diffDays = Math.floor(diff / (1000 * 60 * 60 * 24));
|
||||
if (diffDays < 7) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
let backupsJson = await configRepository.getString(
|
||||
'VRCX_VRChatRegistryBackups'
|
||||
);
|
||||
if (!backupsJson) {
|
||||
backupsJson = JSON.stringify([]);
|
||||
}
|
||||
const backups = JSON.parse(backupsJson);
|
||||
backups.forEach((backup) => {
|
||||
if (backup.name === 'Auto Backup') {
|
||||
// remove old auto backup
|
||||
removeFromArray(backups, backup);
|
||||
}
|
||||
});
|
||||
await configRepository.setString(
|
||||
'VRCX_VRChatRegistryBackups',
|
||||
JSON.stringify(backups)
|
||||
);
|
||||
backupVrcRegistry('Auto Backup');
|
||||
await configRepository.setString(
|
||||
'VRCX_VRChatRegistryLastBackupDate',
|
||||
date.toJSON()
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
state,
|
||||
isRunningUnderWine,
|
||||
currentlyDroppingFile,
|
||||
isRegistryBackupDialogVisible,
|
||||
ipcEnabled,
|
||||
clearVRCXCacheFrequency,
|
||||
maxTableSize,
|
||||
showConsole,
|
||||
applyWineEmojis,
|
||||
clearVRCXCache,
|
||||
startupLaunchCommand,
|
||||
eventVrcxMessage,
|
||||
showRegistryBackupDialog,
|
||||
checkAutoBackupRestoreVrcRegistry,
|
||||
processScreenshot,
|
||||
ipcEvent,
|
||||
dragEnterCef,
|
||||
backupVrcRegistry
|
||||
};
|
||||
});
|
||||
Reference in New Issue
Block a user