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
+518
View File
@@ -0,0 +1,518 @@
import { defineStore } from 'pinia';
import { computed, reactive } from 'vue';
import { $app } from '../../app';
import { t } from '../../plugin';
import configRepository from '../../service/config';
import { sharedFeedFiltersDefaults } from '../../shared/constants';
import { useVrStore } from '../vr';
export const useNotificationsSettingsStore = defineStore(
'NotificationsSettings',
() => {
const vrStore = useVrStore();
const state = reactive({
overlayToast: true,
openVR: false,
overlayNotifications: true,
xsNotifications: true,
ovrtHudNotifications: true,
ovrtWristNotifications: false,
imageNotifications: true,
desktopToast: 'Never',
afkDesktopToast: false,
notificationTTS: 'Never',
notificationTTSNickName: false,
sharedFeedFilters: {
noty: {
Location: 'Off',
OnPlayerJoined: 'VIP',
OnPlayerLeft: 'VIP',
OnPlayerJoining: 'VIP',
Online: 'VIP',
Offline: 'VIP',
GPS: 'Off',
Status: 'Off',
invite: 'Friends',
requestInvite: 'Friends',
inviteResponse: 'Friends',
requestInviteResponse: 'Friends',
friendRequest: 'On',
Friend: 'On',
Unfriend: 'On',
DisplayName: 'VIP',
TrustLevel: 'VIP',
boop: 'Off',
groupChange: 'On',
'group.announcement': 'On',
'group.informative': 'On',
'group.invite': 'On',
'group.joinRequest': 'Off',
'group.transfer': 'On',
'group.queueReady': 'On',
'instance.closed': 'On',
PortalSpawn: 'Everyone',
Event: 'On',
External: 'On',
VideoPlay: 'Off',
BlockedOnPlayerJoined: 'Off',
BlockedOnPlayerLeft: 'Off',
MutedOnPlayerJoined: 'Off',
MutedOnPlayerLeft: 'Off',
AvatarChange: 'Off',
ChatBoxMessage: 'Off',
Blocked: 'Off',
Unblocked: 'Off',
Muted: 'Off',
Unmuted: 'Off'
},
wrist: {
Location: 'On',
OnPlayerJoined: 'Everyone',
OnPlayerLeft: 'Everyone',
OnPlayerJoining: 'Friends',
Online: 'Friends',
Offline: 'Friends',
GPS: 'Friends',
Status: 'Friends',
invite: 'Friends',
requestInvite: 'Friends',
inviteResponse: 'Friends',
requestInviteResponse: 'Friends',
friendRequest: 'On',
Friend: 'On',
Unfriend: 'On',
DisplayName: 'Friends',
TrustLevel: 'Friends',
boop: 'On',
groupChange: 'On',
'group.announcement': 'On',
'group.informative': 'On',
'group.invite': 'On',
'group.joinRequest': 'On',
'group.transfer': 'On',
'group.queueReady': 'On',
'instance.closed': 'On',
PortalSpawn: 'Everyone',
Event: 'On',
External: 'On',
VideoPlay: 'On',
BlockedOnPlayerJoined: 'Off',
BlockedOnPlayerLeft: 'Off',
MutedOnPlayerJoined: 'Off',
MutedOnPlayerLeft: 'Off',
AvatarChange: 'Everyone',
ChatBoxMessage: 'Off',
Blocked: 'On',
Unblocked: 'On',
Muted: 'On',
Unmuted: 'On'
}
},
isTestTTSVisible: false,
notificationTTSVoice: 0,
notificationTTSTest: '',
TTSvoices: [],
notificationPosition: 'topCenter',
notificationTimeout: 3000
});
async function initNotificationsSettings() {
const [
overlayToast,
overlayNotifications,
openVR,
xsNotifications,
ovrtHudNotifications,
ovrtWristNotifications,
imageNotifications,
desktopToast,
afkDesktopToast,
notificationTTS,
notificationTTSNickName,
sharedFeedFilters,
notificationTTSVoice,
notificationPosition,
notificationTimeout
] = await Promise.all([
configRepository.getString('VRCX_overlayToast', 'Game Running'),
configRepository.getBool('VRCX_overlayNotifications', true),
configRepository.getBool('openVR'),
configRepository.getBool('VRCX_xsNotifications', true),
configRepository.getBool('VRCX_ovrtHudNotifications', true),
configRepository.getBool('VRCX_ovrtWristNotifications', false),
configRepository.getBool('VRCX_imageNotifications', true),
configRepository.getString('VRCX_desktopToast', 'Never'),
configRepository.getBool('VRCX_afkDesktopToast', false),
configRepository.getString('VRCX_notificationTTS', 'Never'),
configRepository.getBool('VRCX_notificationTTSNickName', false),
configRepository.getString(
'sharedFeedFilters',
JSON.stringify(sharedFeedFiltersDefaults)
),
configRepository.getString('VRCX_notificationTTSVoice', '0'),
configRepository.getString(
'VRCX_notificationPosition',
'topCenter'
),
configRepository.getString('VRCX_notificationTimeout', '3000')
]);
state.overlayToast = overlayToast;
state.openVR = openVR;
state.overlayNotifications = overlayNotifications;
state.xsNotifications = xsNotifications;
state.ovrtHudNotifications = ovrtHudNotifications;
state.ovrtWristNotifications = ovrtWristNotifications;
state.imageNotifications = imageNotifications;
state.desktopToast = desktopToast;
state.afkDesktopToast = afkDesktopToast;
state.notificationTTS = notificationTTS;
state.notificationTTSNickName = notificationTTSNickName;
state.sharedFeedFilters = JSON.parse(sharedFeedFilters);
state.notificationTTSVoice = notificationTTSVoice;
state.TTSvoices = speechSynthesis.getVoices();
state.notificationPosition = notificationPosition;
state.notificationTimeout = notificationTimeout;
initSharedFeedFilters();
if (LINUX) {
setTimeout(() => {
updateTTSVoices();
}, 5000);
}
}
initNotificationsSettings();
const overlayToast = computed(() => state.overlayToast);
const openVR = computed(() => state.openVR);
const overlayNotifications = computed(() => state.overlayNotifications);
const xsNotifications = computed(() => state.xsNotifications);
const ovrtHudNotifications = computed(() => state.ovrtHudNotifications);
const ovrtWristNotifications = computed(
() => state.ovrtWristNotifications
);
const imageNotifications = computed(() => state.imageNotifications);
const desktopToast = computed(() => state.desktopToast);
const afkDesktopToast = computed(() => state.afkDesktopToast);
const notificationTTS = computed(() => state.notificationTTS);
const notificationTTSNickName = computed(
() => state.notificationTTSNickName
);
const sharedFeedFilters = computed({
get: () => state.sharedFeedFilters,
set: (value) => (state.sharedFeedFilters = value)
});
const isTestTTSVisible = computed({
get: () => state.isTestTTSVisible,
set: (value) => (state.isTestTTSVisible = value)
});
const notificationTTSVoice = computed({
get: () => state.notificationTTSVoice,
set: (value) => (state.notificationTTSVoice = value)
});
const TTSvoices = computed({
get: () => state.TTSvoices,
set: (value) => (state.TTSvoices = value)
});
const notificationTTSTest = computed({
get: () => state.notificationTTSTest,
set: (value) => (state.notificationTTSTest = value)
});
const notificationPosition = computed(() => state.notificationPosition);
const notificationTimeout = computed({
get: () => state.notificationTimeout,
set: (value) => (state.notificationTimeout = value)
});
function setOverlayToast(value) {
state.overlayToast = value;
configRepository.setString('VRCX_overlayToast', value);
}
function setOverlayNotifications() {
state.overlayNotifications = !state.overlayNotifications;
configRepository.setBool(
'VRCX_overlayNotifications',
state.overlayNotifications
);
}
function setOpenVR() {
state.openVR = !state.openVR;
configRepository.setBool('openVR', state.openVR);
}
function setXsNotifications() {
state.xsNotifications = !state.xsNotifications;
configRepository.setBool(
'VRCX_xsNotifications',
state.xsNotifications
);
}
function setOvrtHudNotifications() {
state.ovrtHudNotifications = !state.ovrtHudNotifications;
configRepository.setBool(
'VRCX_ovrtHudNotifications',
state.ovrtHudNotifications
);
}
function setOvrtWristNotifications() {
state.ovrtWristNotifications = !state.ovrtWristNotifications;
configRepository.setBool(
'VRCX_ovrtWristNotifications',
state.ovrtWristNotifications
);
}
function setImageNotifications() {
state.imageNotifications = !state.imageNotifications;
configRepository.setBool(
'VRCX_imageNotifications',
state.imageNotifications
);
}
function changeNotificationPosition(value) {
state.notificationPosition = value;
configRepository.setString(
'VRCX_notificationPosition',
state.notificationPosition
);
vrStore.updateVRConfigVars();
}
/**
* @param {string} value
*/
function setDesktopToast(value) {
state.desktopToast = value;
configRepository.setString('VRCX_desktopToast', value);
}
function setAfkDesktopToast() {
state.afkDesktopToast = !state.afkDesktopToast;
configRepository.setBool(
'VRCX_afkDesktopToast',
state.afkDesktopToast
);
}
/**
* @param {string} value
*/
function setNotificationTTS(value) {
state.notificationTTS = value;
configRepository.setString('VRCX_notificationTTS', value);
}
function setNotificationTTSNickName() {
state.notificationTTSNickName = !state.notificationTTSNickName;
configRepository.setBool(
'VRCX_notificationTTSNickName',
state.notificationTTSNickName
);
}
function initSharedFeedFilters() {
if (!state.sharedFeedFilters.noty.Blocked) {
state.sharedFeedFilters.noty.Blocked = 'Off';
state.sharedFeedFilters.noty.Unblocked = 'Off';
state.sharedFeedFilters.noty.Muted = 'Off';
state.sharedFeedFilters.noty.Unmuted = 'Off';
state.sharedFeedFilters.wrist.Blocked = 'On';
state.sharedFeedFilters.wrist.Unblocked = 'On';
state.sharedFeedFilters.wrist.Muted = 'On';
state.sharedFeedFilters.wrist.Unmuted = 'On';
}
if (!state.sharedFeedFilters.noty['group.announcement']) {
state.sharedFeedFilters.noty['group.announcement'] = 'On';
state.sharedFeedFilters.noty['group.informative'] = 'On';
state.sharedFeedFilters.noty['group.invite'] = 'On';
state.sharedFeedFilters.noty['group.joinRequest'] = 'Off';
state.sharedFeedFilters.wrist['group.announcement'] = 'On';
state.sharedFeedFilters.wrist['group.informative'] = 'On';
state.sharedFeedFilters.wrist['group.invite'] = 'On';
state.sharedFeedFilters.wrist['group.joinRequest'] = 'On';
}
if (!state.sharedFeedFilters.noty['group.queueReady']) {
state.sharedFeedFilters.noty['group.queueReady'] = 'On';
state.sharedFeedFilters.wrist['group.queueReady'] = 'On';
}
if (!state.sharedFeedFilters.noty['instance.closed']) {
state.sharedFeedFilters.noty['instance.closed'] = 'On';
state.sharedFeedFilters.wrist['instance.closed'] = 'On';
}
if (!state.sharedFeedFilters.noty.External) {
state.sharedFeedFilters.noty.External = 'On';
state.sharedFeedFilters.wrist.External = 'On';
}
if (!state.sharedFeedFilters.noty.groupChange) {
state.sharedFeedFilters.noty.groupChange = 'On';
state.sharedFeedFilters.wrist.groupChange = 'On';
}
if (!state.sharedFeedFilters.noty['group.transfer']) {
state.sharedFeedFilters.noty['group.transfer'] = 'On';
state.sharedFeedFilters.wrist['group.transfer'] = 'On';
}
if (!state.sharedFeedFilters.noty.boop) {
state.sharedFeedFilters.noty.boop = 'Off';
state.sharedFeedFilters.wrist.boop = 'On';
}
}
function setNotificationTTSVoice(index) {
state.notificationTTSVoice = index;
configRepository.setString(
'VRCX_notificationTTSVoice',
state.notificationTTSVoice
);
}
function getTTSVoiceName() {
let voices;
if (LINUX) {
voices = state.TTSvoices;
} else {
voices = speechSynthesis.getVoices();
}
if (voices.length === 0) {
return '';
}
if (state.notificationTTSVoice >= voices.length) {
setNotificationTTSVoice(0);
}
return voices[state.notificationTTSVoice].name;
}
async function changeTTSVoice(index) {
setNotificationTTSVoice(index);
let voices;
if (LINUX) {
voices = state.TTSvoices;
} else {
voices = speechSynthesis.getVoices();
}
if (voices.length === 0) {
return;
}
const voiceName = voices[index].name;
speechSynthesis.cancel();
speak(voiceName);
}
function updateTTSVoices() {
state.TTSvoices = speechSynthesis.getVoices();
if (LINUX) {
const voices = speechSynthesis.getVoices();
let uniqueVoices = [];
voices.forEach((voice) => {
if (!uniqueVoices.some((v) => v.lang === voice.lang)) {
uniqueVoices.push(voice);
}
});
uniqueVoices = uniqueVoices.filter((v) =>
v.lang.startsWith('en')
);
state.TTSvoices = uniqueVoices;
}
}
async function saveNotificationTTS(value) {
speechSynthesis.cancel();
if (
(await configRepository.getString('VRCX_notificationTTS')) ===
'Never' &&
value !== 'Never'
) {
speak('Notification text-to-speech enabled');
}
setNotificationTTS(value);
}
function testNotificationTTS() {
speechSynthesis.cancel();
speak(state.notificationTTSTest);
}
function speak(text) {
const tts = new SpeechSynthesisUtterance();
const voices = speechSynthesis.getVoices();
if (voices.length === 0) {
return;
}
let index = 0;
if (state.notificationTTSVoice < voices.length) {
index = state.notificationTTSVoice;
}
tts.voice = voices[index];
tts.text = text;
speechSynthesis.speak(tts);
}
function promptNotificationTimeout() {
$app.$prompt(
t('prompt.notification_timeout.description'),
t('prompt.notification_timeout.header'),
{
distinguishCancelAndClose: true,
confirmButtonText: t('prompt.notification_timeout.ok'),
cancelButtonText: t('prompt.notification_timeout.cancel'),
inputValue: state.notificationTimeout / 1000,
inputPattern: /\d+$/,
inputErrorMessage: t(
'prompt.notification_timeout.input_error'
),
callback: async (action, instance) => {
if (
action === 'confirm' &&
instance.inputValue &&
!isNaN(instance.inputValue)
) {
state.notificationTimeout = Math.trunc(
Number(instance.inputValue) * 1000
);
await configRepository.setString(
'VRCX_notificationTimeout',
state.notificationTimeout
);
vrStore.updateVRConfigVars();
}
}
}
);
}
return {
state,
overlayToast,
openVR,
overlayNotifications,
xsNotifications,
ovrtHudNotifications,
ovrtWristNotifications,
imageNotifications,
desktopToast,
afkDesktopToast,
notificationTTS,
notificationTTSNickName,
sharedFeedFilters,
isTestTTSVisible,
notificationTTSVoice,
TTSvoices,
notificationTTSTest,
notificationPosition,
notificationTimeout,
setOverlayToast,
setOpenVR,
setOverlayNotifications,
setXsNotifications,
setOvrtHudNotifications,
setOvrtWristNotifications,
setImageNotifications,
setDesktopToast,
setAfkDesktopToast,
setNotificationTTS,
setNotificationTTSNickName,
getTTSVoiceName,
changeTTSVoice,
saveNotificationTTS,
testNotificationTTS,
speak,
changeNotificationPosition,
promptNotificationTimeout
};
}
);