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
+652
View File
@@ -0,0 +1,652 @@
import { defineStore } from 'pinia';
import { computed, reactive, watch } from 'vue';
import { $app } from '../../app';
import { t } from '../../plugin';
import configRepository from '../../service/config';
import { database } from '../../service/database';
import webApiService from '../../service/webapi';
import { watchState } from '../../service/watchState';
import { useGameStore } from '../game';
import { useVrcxStore } from '../vrcx';
import { AppGlobal } from '../../service/appConfig';
export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
const gameStore = useGameStore();
const vrcxStore = useVrcxStore();
const state = reactive({
enablePrimaryPassword: false,
relaunchVRChatAfterCrash: false,
vrcQuitFix: true,
autoSweepVRChatCache: false,
saveInstancePrints: false,
cropInstancePrints: false,
saveInstanceStickers: false,
avatarRemoteDatabase: true,
enableAppLauncher: true,
enableAppLauncherAutoClose: true,
screenshotHelper: true,
screenshotHelperModifyFilename: false,
screenshotHelperCopyToClipboard: false,
youTubeApi: false,
youTubeApiKey: '',
progressPie: false,
progressPieFilter: true,
showConfirmationOnSwitchAvatar: false,
gameLogDisabled: false,
sqliteTableSizes: {},
ugcFolderPath: '',
currentUserInventory: new Map(),
autoDeleteOldPrints: false,
notificationOpacity: 100,
folderSelectorDialogVisible: false,
isVRChatConfigDialogVisible: false,
saveInstanceEmoji: false,
vrcRegistryAutoBackup: true
});
async function initAdvancedSettings() {
const [
enablePrimaryPassword,
relaunchVRChatAfterCrash,
vrcQuitFix,
autoSweepVRChatCache,
saveInstancePrints,
cropInstancePrints,
saveInstanceStickers,
avatarRemoteDatabase,
enableAppLauncher,
enableAppLauncherAutoClose,
screenshotHelper,
screenshotHelperModifyFilename,
screenshotHelperCopyToClipboard,
youTubeApi,
youTubeApiKey,
progressPie,
progressPieFilter,
showConfirmationOnSwitchAvatar,
gameLogDisabled,
ugcFolderPath,
autoDeleteOldPrints,
notificationOpacity,
saveInstanceEmoji,
vrcRegistryAutoBackup
] = await Promise.all([
configRepository.getBool('enablePrimaryPassword', false),
configRepository.getBool('VRCX_relaunchVRChatAfterCrash', false),
configRepository.getBool('VRCX_vrcQuitFix', true),
configRepository.getBool('VRCX_autoSweepVRChatCache', false),
configRepository.getBool('VRCX_saveInstancePrints', false),
configRepository.getBool('VRCX_cropInstancePrints', false),
configRepository.getBool('VRCX_saveInstanceStickers', false),
configRepository.getBool('VRCX_avatarRemoteDatabase', true),
configRepository.getBool('VRCX_enableAppLauncher', true),
configRepository.getBool('VRCX_enableAppLauncherAutoClose', true),
configRepository.getBool('VRCX_screenshotHelper', true),
configRepository.getBool(
'VRCX_screenshotHelperModifyFilename',
false
),
configRepository.getBool(
'VRCX_screenshotHelperCopyToClipboard',
false
),
configRepository.getBool('VRCX_youtubeAPI', false),
configRepository.getString('VRCX_youtubeAPIKey', ''),
configRepository.getBool('VRCX_progressPie', false),
configRepository.getBool('VRCX_progressPieFilter', true),
configRepository.getBool(
'VRCX_showConfirmationOnSwitchAvatar',
false
),
configRepository.getBool('VRCX_gameLogDisabled', false),
configRepository.getString('VRCX_userGeneratedContentPath', ''),
configRepository.getBool('VRCX_autoDeleteOldPrints', false),
configRepository.getFloat('VRCX_notificationOpacity', 100),
configRepository.getBool('VRCX_saveInstanceEmoji', false),
configRepository.getBool('VRCX_vrcRegistryAutoBackup', true)
]);
state.enablePrimaryPassword = enablePrimaryPassword;
state.relaunchVRChatAfterCrash = relaunchVRChatAfterCrash;
state.vrcQuitFix = vrcQuitFix;
state.autoSweepVRChatCache = autoSweepVRChatCache;
state.saveInstancePrints = saveInstancePrints;
state.cropInstancePrints = cropInstancePrints;
state.saveInstanceStickers = saveInstanceStickers;
state.avatarRemoteDatabase = avatarRemoteDatabase;
state.enableAppLauncher = enableAppLauncher;
state.enableAppLauncherAutoClose = enableAppLauncherAutoClose;
state.screenshotHelper = screenshotHelper;
state.screenshotHelperModifyFilename = screenshotHelperModifyFilename;
state.screenshotHelperCopyToClipboard = screenshotHelperCopyToClipboard;
state.youTubeApi = youTubeApi;
state.youTubeApiKey = youTubeApiKey;
state.progressPie = progressPie;
state.progressPieFilter = progressPieFilter;
state.showConfirmationOnSwitchAvatar = showConfirmationOnSwitchAvatar;
state.gameLogDisabled = gameLogDisabled === 'true';
state.ugcFolderPath = ugcFolderPath;
state.autoDeleteOldPrints = autoDeleteOldPrints;
state.notificationOpacity = notificationOpacity;
state.saveInstanceEmoji = saveInstanceEmoji;
state.vrcRegistryAutoBackup = vrcRegistryAutoBackup;
handleSetAppLauncherSettings();
}
initAdvancedSettings();
watch(
() => watchState.isLoggedIn,
() => {
state.currentUserInventory.clear();
state.isVRChatConfigDialogVisible = false;
},
{ flush: 'sync' }
);
const enablePrimaryPassword = computed({
get: () => state.enablePrimaryPassword,
set: (value) => (state.enablePrimaryPassword = value)
});
const relaunchVRChatAfterCrash = computed(
() => state.relaunchVRChatAfterCrash
);
const vrcQuitFix = computed(() => state.vrcQuitFix);
const autoSweepVRChatCache = computed(() => state.autoSweepVRChatCache);
const saveInstancePrints = computed(() => state.saveInstancePrints);
const cropInstancePrints = computed(() => state.cropInstancePrints);
const saveInstanceStickers = computed(() => state.saveInstanceStickers);
const avatarRemoteDatabase = computed(() => state.avatarRemoteDatabase);
const enableAppLauncher = computed(() => state.enableAppLauncher);
const enableAppLauncherAutoClose = computed(
() => state.enableAppLauncherAutoClose
);
const screenshotHelper = computed(() => state.screenshotHelper);
``;
const screenshotHelperModifyFilename = computed(
() => state.screenshotHelperModifyFilename
);
const screenshotHelperCopyToClipboard = computed(
() => state.screenshotHelperCopyToClipboard
);
const youTubeApi = computed(() => state.youTubeApi);
const youTubeApiKey = computed({
get: () => state.youTubeApiKey,
set: (value) => (state.youTubeApiKey = value)
});
const progressPie = computed(() => state.progressPie);
const progressPieFilter = computed(() => state.progressPieFilter);
const showConfirmationOnSwitchAvatar = computed(
() => state.showConfirmationOnSwitchAvatar
);
const gameLogDisabled = computed(() => state.gameLogDisabled);
const sqliteTableSizes = computed(() => state.sqliteTableSizes);
const ugcFolderPath = computed(() => state.ugcFolderPath);
const autoDeleteOldPrints = computed(() => state.autoDeleteOldPrints);
const notificationOpacity = computed(() => state.notificationOpacity);
const currentUserInventory = computed({
get: () => state.currentUserInventory,
set: (value) => {
state.currentUserInventory = value;
}
});
const isVRChatConfigDialogVisible = computed({
get: () => state.isVRChatConfigDialogVisible,
set: (value) => (state.isVRChatConfigDialogVisible = value)
});
const saveInstanceEmoji = computed({
get: () => state.saveInstanceEmoji,
set: (value) => (state.saveInstanceEmoji = value)
});
const vrcRegistryAutoBackup = computed(() => state.vrcRegistryAutoBackup);
/**
* @param {boolean} value
*/
function setEnablePrimaryPasswordConfigRepository(value) {
configRepository.setBool('enablePrimaryPassword', value);
}
function setRelaunchVRChatAfterCrash() {
state.relaunchVRChatAfterCrash = !state.relaunchVRChatAfterCrash;
configRepository.setBool(
'VRCX_relaunchVRChatAfterCrash',
state.relaunchVRChatAfterCrash
);
}
function setVrcQuitFix() {
state.vrcQuitFix = !state.vrcQuitFix;
configRepository.setBool('VRCX_vrcQuitFix', state.vrcQuitFix);
}
function setAutoSweepVRChatCache() {
state.autoSweepVRChatCache = !state.autoSweepVRChatCache;
configRepository.setBool(
'VRCX_autoSweepVRChatCache',
state.autoSweepVRChatCache
);
}
function setSaveInstancePrints() {
state.saveInstancePrints = !state.saveInstancePrints;
configRepository.setBool(
'VRCX_saveInstancePrints',
state.saveInstancePrints
);
}
function setCropInstancePrints() {
state.cropInstancePrints = !state.cropInstancePrints;
configRepository.setBool(
'VRCX_cropInstancePrints',
state.cropInstancePrints
);
}
function setSaveInstanceStickers() {
state.saveInstanceStickers = !state.saveInstanceStickers;
configRepository.setBool(
'VRCX_saveInstanceStickers',
state.saveInstanceStickers
);
}
/**
* @param {boolean} value
*/
function setAvatarRemoteDatabase(value) {
state.avatarRemoteDatabase = value;
configRepository.setBool(
'VRCX_avatarRemoteDatabase',
state.avatarRemoteDatabase
);
}
async function setEnableAppLauncher() {
state.enableAppLauncher = !state.enableAppLauncher;
await configRepository.setBool(
'VRCX_enableAppLauncher',
state.enableAppLauncher
);
handleSetAppLauncherSettings();
}
async function setEnableAppLauncherAutoClose() {
state.enableAppLauncherAutoClose = !state.enableAppLauncherAutoClose;
await configRepository.setBool(
'VRCX_enableAppLauncherAutoClose',
state.enableAppLauncherAutoClose
);
handleSetAppLauncherSettings();
}
async function setScreenshotHelper() {
state.screenshotHelper = !state.screenshotHelper;
await configRepository.setBool(
'VRCX_screenshotHelper',
state.screenshotHelper
);
}
async function setScreenshotHelperModifyFilename() {
state.screenshotHelperModifyFilename =
!state.screenshotHelperModifyFilename;
await configRepository.setBool(
'VRCX_screenshotHelperModifyFilename',
state.screenshotHelperModifyFilename
);
}
async function setScreenshotHelperCopyToClipboard() {
state.screenshotHelperCopyToClipboard =
!state.screenshotHelperCopyToClipboard;
await configRepository.setBool(
'VRCX_screenshotHelperCopyToClipboard',
state.screenshotHelperCopyToClipboard
);
}
async function setYouTubeApi() {
state.youTubeApi = !state.youTubeApi;
await configRepository.setBool('VRCX_youtubeAPI', state.youTubeApi);
}
/**
* @param {string} value
*/
async function setYouTubeApiKey(value) {
state.youTubeApiKey = value;
await configRepository.setString(
'VRCX_youtubeAPIKey',
state.youTubeApiKey
);
}
async function setProgressPie() {
state.progressPie = !state.progressPie;
await configRepository.setBool('VRCX_progressPie', state.progressPie);
}
async function setProgressPieFilter() {
state.progressPieFilter = !state.progressPieFilter;
await configRepository.setBool(
'VRCX_progressPieFilter',
state.progressPieFilter
);
}
async function setShowConfirmationOnSwitchAvatar() {
state.showConfirmationOnSwitchAvatar =
!state.showConfirmationOnSwitchAvatar;
await configRepository.setBool(
'VRCX_showConfirmationOnSwitchAvatar',
state.showConfirmationOnSwitchAvatar
);
}
async function setGameLogDisabled() {
state.gameLogDisabled = !state.gameLogDisabled;
await configRepository.setBool(
'VRCX_gameLogDisabled',
state.gameLogDisabled
);
}
async function setSaveInstanceEmoji() {
state.saveInstanceEmoji = !state.saveInstanceEmoji;
await configRepository.setBool(
'VRCX_saveInstanceEmoji',
state.saveInstanceEmoji
);
}
async function setUGCFolderPath(path) {
if (typeof path !== 'string') {
path = '';
}
state.ugcFolderPath = path;
await configRepository.setString('VRCX_userGeneratedContentPath', path);
}
async function setAutoDeleteOldPrints() {
state.autoDeleteOldPrints = !state.autoDeleteOldPrints;
await configRepository.setBool(
'VRCX_autoDeleteOldPrints',
state.autoDeleteOldPrints
);
}
async function setNotificationOpacity(value) {
state.notificationOpacity = value;
await configRepository.setInt('VRCX_notificationOpacity', value);
}
async function setVrcRegistryAutoBackup() {
state.vrcRegistryAutoBackup = !state.vrcRegistryAutoBackup;
await configRepository.setBool(
'VRCX_vrcRegistryAutoBackup',
state.vrcRegistryAutoBackup
);
}
async function getSqliteTableSizes() {
const [
gps,
status,
bio,
avatar,
onlineOffline,
friendLogHistory,
notification,
location,
joinLeave,
portalSpawn,
videoPlay,
event,
external
] = await Promise.all([
database.getGpsTableSize(),
database.getStatusTableSize(),
database.getBioTableSize(),
database.getAvatarTableSize(),
database.getOnlineOfflineTableSize(),
database.getFriendLogHistoryTableSize(),
database.getNotificationTableSize(),
database.getLocationTableSize(),
database.getJoinLeaveTableSize(),
database.getPortalSpawnTableSize(),
database.getVideoPlayTableSize(),
database.getEventTableSize(),
database.getExternalTableSize()
]);
state.sqliteTableSizes = {
gps,
status,
bio,
avatar,
onlineOffline,
friendLogHistory,
notification,
location,
joinLeave,
portalSpawn,
videoPlay,
event,
external
};
}
function handleSetAppLauncherSettings() {
AppApi.SetAppLauncherSettings(
state.enableAppLauncher,
state.enableAppLauncherAutoClose
);
}
/**
* @param {string} videoId
*/
async function lookupYouTubeVideo(videoId) {
if (!state.youTubeApi) {
console.warn('no Youtube API key configured');
return null;
}
let data = null;
let apiKey = '';
if (state.youTubeApiKey) {
apiKey = state.youTubeApiKey;
}
try {
const response = await webApiService.execute({
url: `https://www.googleapis.com/youtube/v3/videos?id=${encodeURIComponent(
videoId
)}&part=snippet,contentDetails&key=${apiKey}`,
method: 'GET',
headers: {
Referer: 'https://vrcx.app'
}
});
const json = JSON.parse(response.data);
if (AppGlobal.debugWebRequests) {
console.log(json, response);
}
if (response.status === 200) {
data = json;
} else {
throw new Error(`Error: ${response.data}`);
}
} catch {
console.error(`YouTube video lookup failed for ${videoId}`);
}
return data;
}
function cropPrintsChanged() {
if (!state.cropInstancePrints) return;
$app.$confirm(
t(
'view.settings.advanced.advanced.save_instance_prints_to_file.crop_convert_old'
),
{
confirmButtonText: t(
'view.settings.advanced.advanced.save_instance_prints_to_file.crop_convert_old_confirm'
),
cancelButtonText: t(
'view.settings.advanced.advanced.save_instance_prints_to_file.crop_convert_old_cancel'
),
type: 'info',
showInput: false,
callback: async (action) => {
if (action === 'confirm') {
const msgBox = $app.$message({
message: 'Batch print cropping in progress...',
type: 'warning',
duration: 0
});
try {
await AppApi.CropAllPrints(state.ugcFolderPath);
$app.$message({
message: 'Batch print cropping complete',
type: 'success'
});
} catch (err) {
console.error(err);
$app.$message({
message: `Batch print cropping failed: ${err}`,
type: 'error'
});
} finally {
msgBox.close();
}
}
}
}
);
}
function resetUGCFolder() {
setUGCFolderPath('');
}
async function openUGCFolder() {
if (LINUX && state.ugcFolderPath == null) {
resetUGCFolder();
}
await AppApi.OpenUGCPhotosFolder(state.ugcFolderPath);
}
async function folderSelectorDialog(oldPath) {
if (state.folderSelectorDialogVisible) return;
if (!oldPath) {
oldPath = '';
}
state.folderSelectorDialogVisible = true;
let newFolder = '';
if (LINUX) {
newFolder = await window.electron.openDirectoryDialog();
} else {
newFolder = await AppApi.OpenFolderSelectorDialog(oldPath);
}
state.folderSelectorDialogVisible = false;
return newFolder;
}
async function openUGCFolderSelector() {
const path = await folderSelectorDialog(state.ugcFolderPath);
await setUGCFolderPath(path);
}
async function showVRChatConfig() {
state.isVRChatConfigDialogVisible = true;
if (!gameStore.VRChatUsedCacheSize) {
gameStore.getVRChatCacheSize();
}
}
function promptAutoClearVRCXCacheFrequency() {
$app.$prompt(
t('prompt.auto_clear_cache.description'),
t('prompt.auto_clear_cache.header'),
{
distinguishCancelAndClose: true,
confirmButtonText: t('prompt.auto_clear_cache.ok'),
cancelButtonText: t('prompt.auto_clear_cache.cancel'),
inputValue: vrcxStore.clearVRCXCacheFrequency / 3600 / 2,
inputPattern: /\d+$/,
inputErrorMessage: t('prompt.auto_clear_cache.input_error'),
callback: async (action, instance) => {
if (
action === 'confirm' &&
instance.inputValue &&
!isNaN(instance.inputValue)
) {
vrcxStore.clearVRCXCacheFrequency = Math.trunc(
Number(instance.inputValue) * 3600 * 2
);
await configRepository.setString(
'VRCX_clearVRCXCacheFrequency',
vrcxStore.clearVRCXCacheFrequency
);
}
}
}
);
}
return {
state,
enablePrimaryPassword,
relaunchVRChatAfterCrash,
vrcQuitFix,
autoSweepVRChatCache,
saveInstancePrints,
cropInstancePrints,
saveInstanceStickers,
avatarRemoteDatabase,
enableAppLauncher,
enableAppLauncherAutoClose,
screenshotHelper,
screenshotHelperModifyFilename,
screenshotHelperCopyToClipboard,
youTubeApi,
youTubeApiKey,
progressPie,
progressPieFilter,
showConfirmationOnSwitchAvatar,
gameLogDisabled,
sqliteTableSizes,
ugcFolderPath,
currentUserInventory,
autoDeleteOldPrints,
notificationOpacity,
isVRChatConfigDialogVisible,
saveInstanceEmoji,
vrcRegistryAutoBackup,
setEnablePrimaryPasswordConfigRepository,
setRelaunchVRChatAfterCrash,
setVrcQuitFix,
setAutoSweepVRChatCache,
setSaveInstancePrints,
setCropInstancePrints,
setSaveInstanceStickers,
setAvatarRemoteDatabase,
setEnableAppLauncher,
setEnableAppLauncherAutoClose,
setScreenshotHelper,
setScreenshotHelperModifyFilename,
setScreenshotHelperCopyToClipboard,
setYouTubeApi,
setYouTubeApiKey,
setProgressPie,
setProgressPieFilter,
setShowConfirmationOnSwitchAvatar,
setGameLogDisabled,
setUGCFolderPath,
cropPrintsChanged,
setAutoDeleteOldPrints,
setNotificationOpacity,
getSqliteTableSizes,
handleSetAppLauncherSettings,
lookupYouTubeVideo,
resetUGCFolder,
openUGCFolder,
openUGCFolderSelector,
folderSelectorDialog,
showVRChatConfig,
promptAutoClearVRCXCacheFrequency,
setSaveInstanceEmoji,
setVrcRegistryAutoBackup
};
});
+780
View File
@@ -0,0 +1,780 @@
import { defineStore } from 'pinia';
import { computed, reactive, watch } from 'vue';
import { $app } from '../../app';
import { i18n, t } from '../../plugin';
import configRepository from '../../service/config';
import { database } from '../../service/database';
import { watchState } from '../../service/watchState';
import {
changeAppDarkStyle,
changeAppThemeStyle,
changeCJKFontsOrder,
getNameColour,
HueToHex,
systemIsDarkMode,
updateTrustColorClasses
} from '../../shared/utils';
import { useFeedStore } from '../feed';
import { useFriendStore } from '../friend';
import { useGameLogStore } from '../gameLog';
import { useModerationStore } from '../moderation';
import { useNotificationStore } from '../notification';
import { useUserStore } from '../user';
import { useVrStore } from '../vr';
import { useVrcxStore } from '../vrcx';
export const useAppearanceSettingsStore = defineStore(
'AppearanceSettings',
() => {
const friendStore = useFriendStore();
const vrStore = useVrStore();
const notificationStore = useNotificationStore();
const feedStore = useFeedStore();
const moderationStore = useModerationStore();
const gameLogStore = useGameLogStore();
const vrcxStore = useVrcxStore();
const userStore = useUserStore();
const state = reactive({
appLanguage: 'en',
themeMode: '',
isDarkMode: false,
displayVRCPlusIconsAsAvatar: false,
hideNicknames: false,
hideTooltips: false,
isAgeGatedInstancesVisible: false,
sortFavorites: true,
instanceUsersSortAlphabetical: false,
tablePageSize: 15,
dtHour12: false,
dtIsoFormat: false,
sidebarSortMethod1: 'Sort Private to Bottom',
sidebarSortMethod2: 'Sort by Time in Instance',
sidebarSortMethod3: 'Sort by Last Active',
sidebarSortMethods: [
'Sort Private to Bottom',
'Sort by Time in Instance',
'Sort by Last Active'
],
asideWidth: 300,
isSidebarGroupByInstance: true,
isHideFriendsInSameInstance: false,
isSidebarDivideByFriendGroup: false,
hideUserNotes: false,
hideUserMemos: false,
hideUnfriends: false,
randomUserColours: false,
trustColor: {
untrusted: '#CCCCCC',
basic: '#1778FF',
known: '#2BCF5C',
trusted: '#FF7B42',
veteran: '#B18FFF',
vip: '#FF2626',
troll: '#782F2F'
},
currentCulture: ''
});
async function initAppearanceSettings() {
const [
appLanguage,
themeMode,
displayVRCPlusIconsAsAvatar,
hideNicknames,
hideTooltips,
isAgeGatedInstancesVisible,
sortFavorites,
instanceUsersSortAlphabetical,
tablePageSize,
dtHour12,
dtIsoFormat,
sidebarSortMethods,
asideWidth,
isSidebarGroupByInstance,
isHideFriendsInSameInstance,
isSidebarDivideByFriendGroup,
hideUserNotes,
hideUserMemos,
hideUnfriends,
randomUserColours,
trustColor
] = await Promise.all([
configRepository.getString('VRCX_appLanguage'),
configRepository.getString('VRCX_ThemeMode', 'system'),
configRepository.getBool('displayVRCPlusIconsAsAvatar', true),
configRepository.getBool('VRCX_hideNicknames', false),
configRepository.getBool('VRCX_hideTooltips', false),
configRepository.getBool(
'VRCX_isAgeGatedInstancesVisible',
true
),
configRepository.getBool('VRCX_sortFavorites', true),
configRepository.getBool(
'VRCX_instanceUsersSortAlphabetical',
false
),
configRepository.getInt('VRCX_tablePageSize', 15),
configRepository.getBool('VRCX_dtHour12', false),
configRepository.getBool('VRCX_dtIsoFormat', false),
configRepository.getString(
'VRCX_sidebarSortMethods',
JSON.stringify([
'Sort Private to Bottom',
'Sort by Time in Instance',
'Sort by Last Active'
])
),
configRepository.getInt('VRCX_sidePanelWidth', 300),
configRepository.getBool('VRCX_sidebarGroupByInstance', true),
configRepository.getBool(
'VRCX_hideFriendsInSameInstance',
false
),
configRepository.getBool(
'VRCX_sidebarDivideByFriendGroup',
true
),
configRepository.getBool('VRCX_hideUserNotes', false),
configRepository.getBool('VRCX_hideUserMemos', false),
configRepository.getBool('VRCX_hideUnfriends', false),
configRepository.getBool('VRCX_randomUserColours', false),
configRepository.getString(
'VRCX_trustColor',
JSON.stringify({
untrusted: '#CCCCCC',
basic: '#1778FF',
known: '#2BCF5C',
trusted: '#FF7B42',
veteran: '#B18FFF',
vip: '#FF2626',
troll: '#782F2F'
})
)
]);
if (!appLanguage) {
const result = await AppApi.CurrentLanguage();
const lang = result.split('-')[0];
i18n.availableLocales.forEach((ref) => {
const refLang = ref.split('_')[0];
if (refLang === lang) {
changeAppLanguage(ref);
}
});
} else {
state.appLanguage = appLanguage;
}
changeCJKFontsOrder(state.appLanguage);
state.themeMode = themeMode;
applyThemeMode(themeMode);
state.displayVRCPlusIconsAsAvatar = displayVRCPlusIconsAsAvatar;
state.hideNicknames = hideNicknames;
state.hideTooltips = hideTooltips;
state.isAgeGatedInstancesVisible = isAgeGatedInstancesVisible;
state.sortFavorites = sortFavorites;
state.instanceUsersSortAlphabetical = instanceUsersSortAlphabetical;
setTablePageSize(tablePageSize);
handleSetTablePageSize(state.tablePageSize);
state.dtHour12 = dtHour12;
state.dtIsoFormat = dtIsoFormat;
state.currentCulture = await AppApi.CurrentCulture();
state.sidebarSortMethods = JSON.parse(sidebarSortMethods);
if (state.sidebarSortMethods?.length === 3) {
state.sidebarSortMethod1 = state.sidebarSortMethods[0];
state.sidebarSortMethod2 = state.sidebarSortMethods[1];
state.sidebarSortMethod3 = state.sidebarSortMethods[2];
}
state.trustColor = JSON.parse(trustColor);
state.asideWidth = asideWidth;
state.isSidebarGroupByInstance = isSidebarGroupByInstance;
state.isHideFriendsInSameInstance = isHideFriendsInSameInstance;
state.isSidebarDivideByFriendGroup = isSidebarDivideByFriendGroup;
state.hideUserNotes = hideUserNotes;
state.hideUserMemos = hideUserMemos;
state.hideUnfriends = hideUnfriends;
state.randomUserColours = randomUserColours;
// Migrate old settings
// Assume all exist if one does
await mergeOldSortMethodsSettings();
updateTrustColorClasses(state.trustColor);
vrStore.updateVRConfigVars();
}
initAppearanceSettings();
const appLanguage = computed(() => state.appLanguage);
const themeMode = computed(() => state.themeMode);
const isDarkMode = computed(() => state.isDarkMode);
const displayVRCPlusIconsAsAvatar = computed(
() => state.displayVRCPlusIconsAsAvatar
);
const hideNicknames = computed(() => state.hideNicknames);
const hideTooltips = computed(() => state.hideTooltips);
const isAgeGatedInstancesVisible = computed(
() => state.isAgeGatedInstancesVisible
);
const sortFavorites = computed(() => state.sortFavorites);
const instanceUsersSortAlphabetical = computed(
() => state.instanceUsersSortAlphabetical
);
const tablePageSize = computed(() => state.tablePageSize);
const dtHour12 = computed(() => state.dtHour12);
const dtIsoFormat = computed(() => state.dtIsoFormat);
const sidebarSortMethod1 = computed(() => state.sidebarSortMethod1);
const sidebarSortMethod2 = computed(() => state.sidebarSortMethod2);
const sidebarSortMethod3 = computed(() => state.sidebarSortMethod3);
const sidebarSortMethods = computed(() => state.sidebarSortMethods);
const asideWidth = computed(() => state.asideWidth);
const isSidebarGroupByInstance = computed(
() => state.isSidebarGroupByInstance
);
const isHideFriendsInSameInstance = computed(
() => state.isHideFriendsInSameInstance
);
const isSidebarDivideByFriendGroup = computed(
() => state.isSidebarDivideByFriendGroup
);
const hideUserNotes = computed(() => state.hideUserNotes);
const hideUserMemos = computed(() => state.hideUserMemos);
const hideUnfriends = computed(() => state.hideUnfriends);
const randomUserColours = computed(() => state.randomUserColours);
const trustColor = computed(() => state.trustColor);
const currentCulture = computed(() => state.currentCulture);
watch(
() => watchState.isFriendsLoaded,
(isFriendsLoaded) => {
if (isFriendsLoaded) {
tryInitUserColours();
}
},
{ flush: 'sync' }
);
/**
*
* @param {string} language
*/
function changeAppLanguage(language) {
setAppLanguage(language);
vrStore.updateVRConfigVars();
}
/**
* @param {string} language
*/
function setAppLanguage(language) {
console.log('Language changed:', language);
state.appLanguage = language;
configRepository.setString('VRCX_appLanguage', language);
changeCJKFontsOrder(state.appLanguage);
i18n.locale = state.appLanguage;
}
/**
* @param {string} newThemeMode
* @returns {Promise<void>}
*/
async function saveThemeMode(newThemeMode) {
setThemeMode(newThemeMode);
await changeThemeMode();
}
async function changeThemeMode() {
await changeAppThemeStyle(state.themeMode);
vrStore.updateVRConfigVars();
await updateTrustColor();
}
/**
*
* @param {string} field
* @param {string} color
* @param {boolean} setRandomColor
* @returns {Promise<void>}
*/
async function updateTrustColor(field, color, setRandomColor = false) {
if (setRandomColor) {
setRandomUserColours();
}
if (typeof userStore.currentUser?.id === 'undefined') {
return;
}
if (field && color) {
setTrustColor({
...state.trustColor,
[field]: color
});
}
if (state.randomUserColours) {
const colour = await getNameColour(userStore.currentUser.id);
userStore.currentUser.$userColour = colour;
userColourInit();
} else {
applyUserTrustLevel(userStore.currentUser);
userStore.cachedUsers.forEach((ref) => {
applyUserTrustLevel(ref);
});
}
updateTrustColorClasses(state.trustColor);
}
async function userColourInit() {
let dictObject = await AppApi.GetColourBulk(
Array.from(userStore.cachedUsers.keys())
);
if (LINUX) {
dictObject = Object.fromEntries(dictObject);
}
for (const [userId, hue] of Object.entries(dictObject)) {
const ref = userStore.cachedUsers.get(userId);
if (typeof ref !== 'undefined') {
ref.$userColour = HueToHex(hue);
}
}
}
/**
*
* @param {object} ref
*/
function applyUserTrustLevel(ref) {
ref.$isModerator =
ref.developerType && ref.developerType !== 'none';
ref.$isTroll = false;
ref.$isProbableTroll = false;
let trustColor = '';
const { tags } = ref;
if (tags.includes('admin_moderator')) {
ref.$isModerator = true;
}
if (tags.includes('system_troll')) {
ref.$isTroll = true;
}
if (tags.includes('system_probable_troll') && !ref.$isTroll) {
ref.$isProbableTroll = true;
}
if (tags.includes('system_trust_veteran')) {
ref.$trustLevel = 'Trusted User';
ref.$trustClass = 'x-tag-veteran';
trustColor = 'veteran';
ref.$trustSortNum = 5;
} else if (tags.includes('system_trust_trusted')) {
ref.$trustLevel = 'Known User';
ref.$trustClass = 'x-tag-trusted';
trustColor = 'trusted';
ref.$trustSortNum = 4;
} else if (tags.includes('system_trust_known')) {
ref.$trustLevel = 'User';
ref.$trustClass = 'x-tag-known';
trustColor = 'known';
ref.$trustSortNum = 3;
} else if (tags.includes('system_trust_basic')) {
ref.$trustLevel = 'New User';
ref.$trustClass = 'x-tag-basic';
trustColor = 'basic';
ref.$trustSortNum = 2;
} else {
ref.$trustLevel = 'Visitor';
ref.$trustClass = 'x-tag-untrusted';
trustColor = 'untrusted';
ref.$trustSortNum = 1;
}
if (ref.$isTroll || ref.$isProbableTroll) {
trustColor = 'troll';
ref.$trustSortNum += 0.1;
}
if (ref.$isModerator) {
trustColor = 'vip';
ref.$trustSortNum += 0.3;
}
if (state.randomUserColours && watchState.isFriendsLoaded) {
if (!ref.$userColour) {
getNameColour(ref.id).then((colour) => {
ref.$userColour = colour;
});
}
} else {
ref.$userColour = state.trustColor[trustColor];
}
}
window
.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', async () => {
if (state.themeMode === 'system') {
await changeThemeMode();
}
});
/**
* @param {string} mode
*/
function setThemeMode(mode) {
state.themeMode = mode;
configRepository.setString('VRCX_ThemeMode', mode);
applyThemeMode();
}
function applyThemeMode() {
if (state.themeMode === 'light') {
setIsDarkMode(false);
} else if (state.themeMode === 'system') {
setIsDarkMode(systemIsDarkMode());
} else {
setIsDarkMode(true);
}
}
/**
* @param {boolean} isDark
*/
function setIsDarkMode(isDark) {
state.isDarkMode = isDark;
changeAppDarkStyle(isDark);
}
function setDisplayVRCPlusIconsAsAvatar() {
state.displayVRCPlusIconsAsAvatar =
!state.displayVRCPlusIconsAsAvatar;
configRepository.setBool(
'displayVRCPlusIconsAsAvatar',
state.displayVRCPlusIconsAsAvatar
);
}
function setHideNicknames() {
state.hideNicknames = !state.hideNicknames;
configRepository.setBool('VRCX_hideNicknames', state.hideNicknames);
}
function setHideTooltips() {
state.hideTooltips = !state.hideTooltips;
configRepository.setBool('VRCX_hideTooltips', state.hideTooltips);
}
function setIsAgeGatedInstancesVisible() {
state.isAgeGatedInstancesVisible =
!state.isAgeGatedInstancesVisible;
configRepository.setBool(
'VRCX_isAgeGatedInstancesVisible',
state.isAgeGatedInstancesVisible
);
}
function setSortFavorites() {
state.sortFavorites = !state.sortFavorites;
configRepository.setBool('VRCX_sortFavorites', state.sortFavorites);
}
function setInstanceUsersSortAlphabetical() {
state.instanceUsersSortAlphabetical =
!state.instanceUsersSortAlphabetical;
configRepository.setBool(
'VRCX_instanceUsersSortAlphabetical',
state.instanceUsersSortAlphabetical
);
}
/**
* @param {number} size
*/
function setTablePageSize(size) {
state.tablePageSize = size;
configRepository.setInt('VRCX_tablePageSize', size);
}
function setDtHour12() {
state.dtHour12 = !state.dtHour12;
configRepository.setBool('VRCX_dtHour12', state.dtHour12);
}
function setDtIsoFormat() {
state.dtIsoFormat = !state.dtIsoFormat;
configRepository.setBool('VRCX_dtIsoFormat', state.dtIsoFormat);
}
/**
* @param {string} method
*/
function setSidebarSortMethod1(method) {
state.sidebarSortMethod1 = method;
handleSaveSidebarSortOrder();
}
/**
* @param {string} method
*/
function setSidebarSortMethod2(method) {
state.sidebarSortMethod2 = method;
handleSaveSidebarSortOrder();
}
/**
* @param {string} method
*/
function setSidebarSortMethod3(method) {
state.sidebarSortMethod3 = method;
handleSaveSidebarSortOrder();
}
/**
* @param {Array<string>} methods
*/
function setSidebarSortMethods(methods) {
state.sidebarSortMethods = methods;
configRepository.setString(
'VRCX_sidebarSortMethods',
JSON.stringify(methods)
);
}
/**
* @param {number} width
*/
function setAsideWidth(width) {
requestAnimationFrame(() => {
state.asideWidth = width;
configRepository.setInt('VRCX_sidePanelWidth', width);
});
}
function setIsSidebarGroupByInstance() {
state.isSidebarGroupByInstance = !state.isSidebarGroupByInstance;
configRepository.setBool(
'VRCX_sidebarGroupByInstance',
state.isSidebarGroupByInstance
);
}
function setIsHideFriendsInSameInstance() {
state.isHideFriendsInSameInstance =
!state.isHideFriendsInSameInstance;
configRepository.setBool(
'VRCX_hideFriendsInSameInstance',
state.isHideFriendsInSameInstance
);
}
function setIsSidebarDivideByFriendGroup() {
state.isSidebarDivideByFriendGroup =
!state.isSidebarDivideByFriendGroup;
configRepository.setBool(
'VRCX_sidebarDivideByFriendGroup',
state.isSidebarDivideByFriendGroup
);
}
function setHideUserNotes() {
state.hideUserNotes = !state.hideUserNotes;
configRepository.setBool('VRCX_hideUserNotes', state.hideUserNotes);
}
function setHideUserMemos() {
state.hideUserMemos = !state.hideUserMemos;
configRepository.setBool('VRCX_hideUserMemos', state.hideUserMemos);
}
function setHideUnfriends() {
state.hideUnfriends = !state.hideUnfriends;
configRepository.setBool('VRCX_hideUnfriends', state.hideUnfriends);
}
function setRandomUserColours() {
state.randomUserColours = !state.randomUserColours;
configRepository.setBool(
'VRCX_randomUserColours',
state.randomUserColours
);
}
/**
* @param {object} color
*/
function setTrustColor(color) {
state.trustColor = color;
configRepository.setString(
'VRCX_trustColor',
JSON.stringify(color)
);
}
function handleSaveSidebarSortOrder() {
if (state.sidebarSortMethod1 === state.sidebarSortMethod2) {
state.sidebarSortMethod2 = '';
}
if (state.sidebarSortMethod1 === state.sidebarSortMethod3) {
state.sidebarSortMethod3 = '';
}
if (state.sidebarSortMethod2 === state.sidebarSortMethod3) {
state.sidebarSortMethod3 = '';
}
if (!state.sidebarSortMethod1) {
state.sidebarSortMethod2 = '';
}
if (!state.sidebarSortMethod2) {
state.sidebarSortMethod3 = '';
}
const sidebarSortMethods = [
state.sidebarSortMethod1,
state.sidebarSortMethod2,
state.sidebarSortMethod3
];
setSidebarSortMethods(sidebarSortMethods);
}
async function mergeOldSortMethodsSettings() {
const orderFriendsGroupPrivate = await configRepository.getBool(
'orderFriendGroupPrivate'
);
if (orderFriendsGroupPrivate !== null) {
await configRepository.remove('orderFriendGroupPrivate');
const orderFriendsGroupStatus = await configRepository.getBool(
'orderFriendsGroupStatus'
);
await configRepository.remove('orderFriendsGroupStatus');
const orderFriendsGroupGPS = await configRepository.getBool(
'orderFriendGroupGPS'
);
await configRepository.remove('orderFriendGroupGPS');
const orderOnlineFor =
await configRepository.getBool('orderFriendGroup0');
await configRepository.remove('orderFriendGroup0');
await configRepository.remove('orderFriendGroup1');
await configRepository.remove('orderFriendGroup2');
await configRepository.remove('orderFriendGroup3');
const sortOrder = [];
if (orderFriendsGroupPrivate) {
sortOrder.push('Sort Private to Bottom');
}
if (orderFriendsGroupStatus) {
sortOrder.push('Sort by Status');
}
if (orderOnlineFor && orderFriendsGroupGPS) {
sortOrder.push('Sort by Time in Instance');
}
if (!orderOnlineFor) {
sortOrder.push('Sort Alphabetically');
}
if (sortOrder.length > 0) {
while (sortOrder.length < 3) {
sortOrder.push('');
}
state.sidebarSortMethods = sortOrder;
state.sidebarSortMethod1 = sortOrder[0];
state.sidebarSortMethod2 = sortOrder[1];
state.sidebarSortMethod3 = sortOrder[2];
}
setSidebarSortMethods(sortOrder);
}
}
async function handleSetTablePageSize(pageSize) {
feedStore.feedTable.pageSize = pageSize;
gameLogStore.gameLogTable.pageSize = pageSize;
friendStore.friendLogTable.pageSize = pageSize;
moderationStore.playerModerationTable.pageSize = pageSize;
notificationStore.notificationTable.pageSize = pageSize;
setTablePageSize(pageSize);
}
function promptMaxTableSizeDialog() {
$app.$prompt(
t('prompt.change_table_size.description'),
t('prompt.change_table_size.header'),
{
distinguishCancelAndClose: true,
confirmButtonText: t('prompt.change_table_size.save'),
cancelButtonText: t('prompt.change_table_size.cancel'),
inputValue: vrcxStore.maxTableSize,
inputPattern: /\d+$/,
inputErrorMessage: t(
'prompt.change_table_size.input_error'
),
callback: async (action, instance) => {
if (action === 'confirm' && instance.inputValue) {
if (instance.inputValue > 10000) {
instance.inputValue = 10000;
}
vrcxStore.maxTableSize = instance.inputValue;
await configRepository.setString(
'VRCX_maxTableSize',
vrcxStore.maxTableSize
);
database.setMaxTableSize(vrcxStore.maxTableSize);
feedStore.feedTableLookup();
gameLogStore.gameLogTableLookup();
}
}
}
);
}
async function tryInitUserColours() {
if (!state.randomUserColours) {
return;
}
const colour = await getNameColour(userStore.currentUser.id);
userStore.currentUser.$userColour = colour;
await userColourInit();
}
return {
state,
appLanguage,
themeMode,
isDarkMode,
displayVRCPlusIconsAsAvatar,
hideNicknames,
hideTooltips,
isAgeGatedInstancesVisible,
sortFavorites,
instanceUsersSortAlphabetical,
tablePageSize,
dtHour12,
dtIsoFormat,
sidebarSortMethod1,
sidebarSortMethod2,
sidebarSortMethod3,
sidebarSortMethods,
asideWidth,
isSidebarGroupByInstance,
isHideFriendsInSameInstance,
isSidebarDivideByFriendGroup,
hideUserNotes,
hideUserMemos,
hideUnfriends,
randomUserColours,
trustColor,
currentCulture,
setAppLanguage,
setDisplayVRCPlusIconsAsAvatar,
setHideNicknames,
setHideTooltips,
setIsAgeGatedInstancesVisible,
setSortFavorites,
setInstanceUsersSortAlphabetical,
setTablePageSize,
setDtHour12,
setDtIsoFormat,
setSidebarSortMethod1,
setSidebarSortMethod2,
setSidebarSortMethod3,
setSidebarSortMethods,
setAsideWidth,
setIsSidebarGroupByInstance,
setIsHideFriendsInSameInstance,
setIsSidebarDivideByFriendGroup,
setHideUserNotes,
setHideUserMemos,
setHideUnfriends,
setRandomUserColours,
setTrustColor,
saveThemeMode,
tryInitUserColours,
updateTrustColor,
changeThemeMode,
userColourInit,
applyUserTrustLevel,
changeAppLanguage,
handleSetTablePageSize,
promptMaxTableSizeDialog
};
}
);
+358
View File
@@ -0,0 +1,358 @@
import { defineStore } from 'pinia';
import { computed, reactive } from 'vue';
import { userRequest, worldRequest } from '../../api';
import configRepository from '../../service/config';
import {
getGroupName,
getLaunchURL,
isRealInstance,
isRpcWorld,
parseLocation
} from '../../shared/utils';
import { useGameStore } from '../game';
import { useGameLogStore } from '../gameLog';
import { useLocationStore } from '../location';
import { useUpdateLoopStore } from '../updateLoop';
import { useUserStore } from '../user';
import { useWorldStore } from '../world';
import { useAdvancedSettingsStore } from './advanced';
import { ActivityType } from '../../shared/constants/discord';
export const useDiscordPresenceSettingsStore = defineStore(
'DiscordPresenceSettings',
() => {
const locationStore = useLocationStore();
const gameStore = useGameStore();
const advancedSettingsStore = useAdvancedSettingsStore();
const worldStore = useWorldStore();
const gameLogStore = useGameLogStore();
const userStore = useUserStore();
const updateLoopStore = useUpdateLoopStore();
const state = reactive({
discordActive: false,
discordInstance: true,
discordHideInvite: true,
discordJoinButton: false,
discordHideImage: false,
isDiscordActive: false
});
async function initDiscordPresenceSettings() {
const [
discordActive,
discordInstance,
discordHideInvite,
discordJoinButton,
discordHideImage
] = await Promise.all([
configRepository.getBool('discordActive', false),
configRepository.getBool('discordInstance', true),
configRepository.getBool('discordHideInvite', true),
configRepository.getBool('discordJoinButton', false),
configRepository.getBool('discordHideImage', false)
]);
state.discordActive = discordActive;
state.discordInstance = discordInstance;
state.discordHideInvite = discordHideInvite;
state.discordJoinButton = discordJoinButton;
state.discordHideImage = discordHideImage;
}
const discordActive = computed(() => state.discordActive);
const discordInstance = computed(() => state.discordInstance);
const discordHideInvite = computed(() => state.discordHideInvite);
const discordJoinButton = computed(() => state.discordJoinButton);
const discordHideImage = computed(() => state.discordHideImage);
function setDiscordActive() {
state.discordActive = !state.discordActive;
configRepository.setBool('discordActive', state.discordActive);
}
function setDiscordInstance() {
state.discordInstance = !state.discordInstance;
configRepository.setBool('discordInstance', state.discordInstance);
}
function setDiscordHideInvite() {
state.discordHideInvite = !state.discordHideInvite;
configRepository.setBool(
'discordHideInvite',
state.discordHideInvite
);
}
function setDiscordJoinButton() {
state.discordJoinButton = !state.discordJoinButton;
configRepository.setBool(
'discordJoinButton',
state.discordJoinButton
);
}
function setDiscordHideImage() {
state.discordHideImage = !state.discordHideImage;
configRepository.setBool(
'discordHideImage',
state.discordHideImage
);
}
initDiscordPresenceSettings();
function updateDiscord() {
let platform;
let currentLocation = locationStore.lastLocation.location;
let timeStamp = locationStore.lastLocation.date;
if (locationStore.lastLocation.location === 'traveling') {
currentLocation = locationStore.lastLocationDestination;
timeStamp = locationStore.lastLocationDestinationTime;
}
if (
!state.discordActive ||
(!gameStore.isGameRunning &&
!advancedSettingsStore.gameLogDisabled) ||
(!currentLocation && !locationStore.lastLocation$.tag)
) {
setIsDiscordActive(false);
return;
}
setIsDiscordActive(true);
let L = locationStore.lastLocation$;
if (currentLocation !== locationStore.lastLocation$.tag) {
Discord.SetTimestamps(timeStamp, 0);
L = parseLocation(currentLocation);
L.worldName = '';
L.thumbnailImageUrl = '';
L.worldCapacity = 0;
L.joinUrl = '';
L.accessName = '';
if (L.worldId) {
const ref = worldStore.cachedWorlds.get(L.worldId);
if (ref) {
L.worldName = ref.name;
L.thumbnailImageUrl = ref.thumbnailImageUrl;
L.worldCapacity = ref.capacity;
} else {
worldRequest
.getWorld({
worldId: L.worldId
})
.then((args) => {
L.worldName = args.ref.name;
L.thumbnailImageUrl =
args.ref.thumbnailImageUrl;
L.worldCapacity = args.ref.capacity;
return args;
});
}
if (gameStore.isGameNoVR) {
platform = 'Desktop';
} else {
platform = 'VR';
}
let groupAccessType = '';
if (L.groupAccessType) {
if (L.groupAccessType === 'public') {
groupAccessType = 'Public';
} else if (L.groupAccessType === 'plus') {
groupAccessType = 'Plus';
}
}
switch (L.accessType) {
case 'public':
L.joinUrl = getLaunchURL(L);
L.accessName = `Public #${L.instanceName} (${platform})`;
break;
case 'invite+':
L.accessName = `Invite+ #${L.instanceName} (${platform})`;
break;
case 'invite':
L.accessName = `Invite #${L.instanceName} (${platform})`;
break;
case 'friends':
L.accessName = `Friends #${L.instanceName} (${platform})`;
break;
case 'friends+':
L.accessName = `Friends+ #${L.instanceName} (${platform})`;
break;
case 'group':
L.accessName = `Group #${L.instanceName} (${platform})`;
getGroupName(L.groupId).then((groupName) => {
if (groupName) {
L.accessName = `Group${groupAccessType}(${groupName}) #${L.instanceName} (${platform})`;
}
});
break;
}
}
locationStore.lastLocation$ = L;
}
let hidePrivate = false;
if (
state.discordHideInvite &&
(L.accessType === 'invite' ||
L.accessType === 'invite+' ||
L.groupAccessType === 'members')
) {
hidePrivate = true;
}
switch (userStore.currentUser.status) {
case 'active':
L.statusName = 'Online';
L.statusImage = 'active';
break;
case 'join me':
L.statusName = 'Join Me';
L.statusImage = 'joinme';
break;
case 'ask me':
L.statusName = 'Ask Me';
L.statusImage = 'askme';
if (state.discordHideInvite) {
hidePrivate = true;
}
break;
case 'busy':
L.statusName = 'Do Not Disturb';
L.statusImage = 'busy';
hidePrivate = true;
break;
}
let activityType = ActivityType.Playing;
let appId = '883308884863901717';
let bigIcon = 'vrchat';
let partyId = `${L.worldId}:${L.instanceName}`;
let partySize = locationStore.lastLocation.playerList.size;
let partyMaxSize = L.worldCapacity;
if (partySize > partyMaxSize) {
partyMaxSize = partySize;
}
let buttonText = 'Join';
let buttonUrl = L.joinUrl;
if (!state.discordJoinButton) {
buttonText = '';
buttonUrl = '';
}
if (!state.discordInstance) {
partySize = 0;
partyMaxSize = 0;
}
if (hidePrivate) {
partyId = '';
partySize = 0;
partyMaxSize = 0;
buttonText = '';
buttonUrl = '';
} else if (isRpcWorld(L.tag)) {
// custom world rpc
if (
L.worldId === 'wrld_f20326da-f1ac-45fc-a062-609723b097b1' ||
L.worldId === 'wrld_10e5e467-fc65-42ed-8957-f02cace1398c' ||
L.worldId === 'wrld_04899f23-e182-4a8d-b2c7-2c74c7c15534'
) {
activityType = ActivityType.Listening;
appId = '784094509008551956';
bigIcon = 'pypy';
} else if (
L.worldId === 'wrld_42377cf1-c54f-45ed-8996-5875b0573a83' ||
L.worldId === 'wrld_dd6d2888-dbdc-47c2-bc98-3d631b2acd7c'
) {
activityType = ActivityType.Listening;
appId = '846232616054030376';
bigIcon = 'vr_dancing';
} else if (
L.worldId === 'wrld_52bdcdab-11cd-4325-9655-0fb120846945' ||
L.worldId === 'wrld_2d40da63-8f1f-4011-8a9e-414eb8530acd'
) {
activityType = ActivityType.Listening;
appId = '939473404808007731';
bigIcon = 'zuwa_zuwa_dance';
} else if (
L.worldId === 'wrld_74970324-58e8-4239-a17b-2c59dfdf00db' ||
L.worldId === 'wrld_db9d878f-6e76-4776-8bf2-15bcdd7fc445' ||
L.worldId === 'wrld_435bbf25-f34f-4b8b-82c6-cd809057eb8e' ||
L.worldId === 'wrld_f767d1c8-b249-4ecc-a56f-614e433682c8'
) {
activityType = ActivityType.Watching;
appId = '968292722391785512';
bigIcon = 'ls_media';
} else if (
L.worldId === 'wrld_266523e8-9161-40da-acd0-6bd82e075833' ||
L.worldId === 'wrld_27c7e6b2-d938-447e-a270-3d1a873e2cf3'
) {
activityType = ActivityType.Watching;
appId = '1095440531821170820';
bigIcon = 'popcorn_palace';
}
if (gameLogStore.nowPlaying.name) {
L.worldName = gameLogStore.nowPlaying.name;
}
if (gameLogStore.nowPlaying.playing) {
Discord.SetTimestamps(
gameLogStore.nowPlaying.startTime * 1000,
(gameLogStore.nowPlaying.startTime +
gameLogStore.nowPlaying.length) *
1000
);
}
} else if (!state.discordHideImage && L.thumbnailImageUrl) {
bigIcon = L.thumbnailImageUrl;
}
Discord.SetAssets(
bigIcon, // big icon
'Powered by VRCX', // big icon hover text
L.statusImage, // small icon
L.statusName, // small icon hover text
partyId, // party id
partySize, // party size
partyMaxSize, // party max size
buttonText, // button text
buttonUrl, // button url
appId, // app id
activityType // activity type
);
// NOTE
// 글자 수가 짧으면 업데이트가 안된다..
if (L.worldName.length < 2) {
L.worldName += '\uFFA0'.repeat(2 - L.worldName.length);
}
if (hidePrivate) {
Discord.SetText('Private', '');
Discord.SetTimestamps(0, 0);
} else if (state.discordInstance) {
Discord.SetText(L.worldName, L.accessName);
} else {
Discord.SetText(L.worldName, '');
}
}
async function setIsDiscordActive(active) {
if (active !== state.isDiscordActive) {
state.isDiscordActive = await Discord.SetActive(active);
}
}
async function saveDiscordOption(configLabel = '') {
locationStore.lastLocation$.tag = '';
updateLoopStore.nextDiscordUpdate = 3;
updateDiscord();
}
return {
state,
discordActive,
discordInstance,
discordHideInvite,
discordJoinButton,
discordHideImage,
setDiscordActive,
setDiscordInstance,
setDiscordHideInvite,
setDiscordJoinButton,
setDiscordHideImage,
updateDiscord,
saveDiscordOption
};
}
);
+321
View File
@@ -0,0 +1,321 @@
import { defineStore } from 'pinia';
import { computed, reactive } from 'vue';
import * as workerTimers from 'worker-timers';
import { $app } from '../../app';
import { t } from '../../plugin';
import configRepository from '../../service/config';
import { useVrcxStore } from '../vrcx';
import { useVRCXUpdaterStore } from '../vrcxUpdater';
export const useGeneralSettingsStore = defineStore('GeneralSettings', () => {
const vrcxStore = useVrcxStore();
const VRCXUpdaterStore = useVRCXUpdaterStore();
const state = reactive({
isStartAtWindowsStartup: false,
isStartAsMinimizedState: false,
isCloseToTray: false,
disableGpuAcceleration: false,
disableVrOverlayGpuAcceleration: false,
localFavoriteFriendsGroups: [],
udonExceptionLogging: false,
logResourceLoad: false,
logEmptyAvatars: false,
autoStateChangeEnabled: false,
autoStateChangeAloneStatus: 'join me',
autoStateChangeCompanyStatus: 'busy',
autoStateChangeInstanceTypes: [],
autoStateChangeNoFriends: false,
autoAcceptInviteRequests: 'Off'
});
async function initGeneralSettings() {
const [
isStartAtWindowsStartup,
isStartAsMinimizedState,
isCloseToTray,
isCloseToTrayConfigBool,
disableGpuAccelerationStr,
disableVrOverlayGpuAccelerationStr,
localFavoriteFriendsGroupsStr,
udonExceptionLogging,
logResourceLoad,
logEmptyAvatars,
autoStateChangeEnabled,
autoStateChangeAloneStatus,
autoStateChangeCompanyStatus,
autoStateChangeInstanceTypesStr,
autoAcceptInviteRequests
] = await Promise.all([
configRepository.getBool('VRCX_StartAtWindowsStartup', false),
VRCXStorage.Get('VRCX_StartAsMinimizedState'),
VRCXStorage.Get('VRCX_CloseToTray'),
configRepository.getBool('VRCX_CloseToTray'),
VRCXStorage.Get('VRCX_DisableGpuAcceleration'),
VRCXStorage.Get('VRCX_DisableVrOverlayGpuAcceleration'),
configRepository.getString('VRCX_localFavoriteFriendsGroups', '[]'),
configRepository.getBool('VRCX_udonExceptionLogging', false),
configRepository.getBool('VRCX_logResourceLoad', false),
configRepository.getBool('VRCX_logEmptyAvatars', false),
configRepository.getBool('VRCX_autoStateChangeEnabled', false),
configRepository.getString(
'VRCX_autoStateChangeAloneStatus',
'join me'
),
configRepository.getString(
'VRCX_autoStateChangeCompanyStatus',
'busy'
),
configRepository.getString(
'VRCX_autoStateChangeInstanceTypes',
'[]'
),
configRepository.getString('VRCX_autoAcceptInviteRequests', 'Off')
]);
state.isStartAtWindowsStartup = isStartAtWindowsStartup;
state.isStartAsMinimizedState = isStartAsMinimizedState === 'true';
if (isCloseToTrayConfigBool) {
state.isCloseToTray = isCloseToTrayConfigBool;
await VRCXStorage.Set(
'VRCX_CloseToTray',
state.isCloseToTray.toString()
);
await configRepository.remove('VRCX_CloseToTray');
} else {
state.isCloseToTray = isCloseToTray === 'true';
}
state.disableGpuAcceleration = disableGpuAccelerationStr === 'true';
state.disableVrOverlayGpuAcceleration =
disableVrOverlayGpuAccelerationStr === 'true';
state.localFavoriteFriendsGroups = JSON.parse(
localFavoriteFriendsGroupsStr
);
state.udonExceptionLogging = udonExceptionLogging;
state.logResourceLoad = logResourceLoad;
state.logEmptyAvatars = logEmptyAvatars;
state.autoStateChangeEnabled = autoStateChangeEnabled;
state.autoStateChangeAloneStatus = autoStateChangeAloneStatus;
state.autoStateChangeCompanyStatus = autoStateChangeCompanyStatus;
state.autoStateChangeInstanceTypes = JSON.parse(
autoStateChangeInstanceTypesStr
);
state.autoAcceptInviteRequests = autoAcceptInviteRequests;
}
initGeneralSettings();
const isStartAtWindowsStartup = computed(
() => state.isStartAtWindowsStartup
);
const isStartAsMinimizedState = computed(
() => state.isStartAsMinimizedState
);
const disableGpuAcceleration = computed(() => state.disableGpuAcceleration);
const isCloseToTray = computed(() => state.isCloseToTray);
const disableVrOverlayGpuAcceleration = computed(
() => state.disableVrOverlayGpuAcceleration
);
const localFavoriteFriendsGroups = computed(
() => state.localFavoriteFriendsGroups
);
const udonExceptionLogging = computed(() => state.udonExceptionLogging);
const logResourceLoad = computed(() => state.logResourceLoad);
const logEmptyAvatars = computed(() => state.logEmptyAvatars);
const autoStateChangeEnabled = computed(() => state.autoStateChangeEnabled);
const autoStateChangeAloneStatus = computed(
() => state.autoStateChangeAloneStatus
);
const autoStateChangeCompanyStatus = computed(
() => state.autoStateChangeCompanyStatus
);
const autoStateChangeInstanceTypes = computed(
() => state.autoStateChangeInstanceTypes
);
const autoStateChangeNoFriends = computed(
() => state.autoStateChangeNoFriends
);
const autoAcceptInviteRequests = computed(
() => state.autoAcceptInviteRequests
);
function setIsStartAtWindowsStartup() {
state.isStartAtWindowsStartup = !state.isStartAtWindowsStartup;
configRepository.setBool(
'VRCX_StartAtWindowsStartup',
state.isStartAtWindowsStartup
);
AppApi.SetStartup(state.isStartAtWindowsStartup);
}
function setIsStartAsMinimizedState() {
state.isStartAsMinimizedState = !state.isStartAsMinimizedState;
VRCXStorage.Set(
'VRCX_StartAsMinimizedState',
state.isStartAsMinimizedState.toString()
);
}
function setIsCloseToTray() {
state.isCloseToTray = !state.isCloseToTray;
VRCXStorage.Set('VRCX_CloseToTray', state.isCloseToTray.toString());
}
function setDisableGpuAcceleration() {
state.disableGpuAcceleration = !state.disableGpuAcceleration;
VRCXStorage.Set(
'VRCX_DisableGpuAcceleration',
state.disableGpuAcceleration.toString()
);
}
function setDisableVrOverlayGpuAcceleration() {
state.disableVrOverlayGpuAcceleration =
!state.disableVrOverlayGpuAcceleration;
VRCXStorage.Set(
'VRCX_DisableVrOverlayGpuAcceleration',
state.disableVrOverlayGpuAcceleration.toString()
);
}
/**
* @param {string[]} value
*/
function setLocalFavoriteFriendsGroups(value) {
state.localFavoriteFriendsGroups = value;
configRepository.setString(
'VRCX_localFavoriteFriendsGroups',
JSON.stringify(value)
);
}
function setUdonExceptionLogging() {
state.udonExceptionLogging = !state.udonExceptionLogging;
configRepository.setBool(
'VRCX_udonExceptionLogging',
state.udonExceptionLogging
);
}
function setLogResourceLoad() {
state.logResourceLoad = !state.logResourceLoad;
configRepository.setBool('VRCX_logResourceLoad', state.logResourceLoad);
}
function setLogEmptyAvatars() {
state.logEmptyAvatars = !state.logEmptyAvatars;
configRepository.setBool('VRCX_logEmptyAvatars', state.logEmptyAvatars);
}
function setAutoStateChangeEnabled() {
state.autoStateChangeEnabled = !state.autoStateChangeEnabled;
configRepository.setBool(
'VRCX_autoStateChangeEnabled',
state.autoStateChangeEnabled
);
}
/**
* @param {string} value
*/
function setAutoStateChangeAloneStatus(value) {
state.autoStateChangeAloneStatus = value;
configRepository.setString(
'VRCX_autoStateChangeAloneStatus',
state.autoStateChangeAloneStatus
);
}
/**
* @param {string} value
*/
function setAutoStateChangeCompanyStatus(value) {
state.autoStateChangeCompanyStatus = value;
configRepository.setString(
'VRCX_autoStateChangeCompanyStatus',
state.autoStateChangeCompanyStatus
);
}
function setAutoStateChangeInstanceTypes(value) {
state.autoStateChangeInstanceTypes = value;
configRepository.setString(
'VRCX_autoStateChangeInstanceTypes',
JSON.stringify(state.autoStateChangeInstanceTypes)
);
}
function setAutoStateChangeNoFriends() {
state.autoStateChangeNoFriends = !state.autoStateChangeNoFriends;
configRepository.setBool(
'VRCX_autoStateChangeNoFriends',
state.autoStateChangeNoFriends
);
}
/**
* @param {string} value
*/
function setAutoAcceptInviteRequests(value) {
state.autoAcceptInviteRequests = value;
configRepository.setString(
'VRCX_autoAcceptInviteRequests',
state.autoAcceptInviteRequests
);
}
function promptProxySettings() {
$app.$prompt(
t('prompt.proxy_settings.description'),
t('prompt.proxy_settings.header'),
{
distinguishCancelAndClose: true,
confirmButtonText: t('prompt.proxy_settings.restart'),
cancelButtonText: t('prompt.proxy_settings.close'),
inputValue: vrcxStore.proxyServer,
inputPlaceholder: t('prompt.proxy_settings.placeholder'),
callback: async (action, instance) => {
vrcxStore.proxyServer = instance.inputValue;
await VRCXStorage.Set(
'VRCX_ProxyServer',
vrcxStore.proxyServer
);
await VRCXStorage.Flush();
await new Promise((resolve) => {
workerTimers.setTimeout(resolve, 100);
});
if (action === 'confirm') {
const { restartVRCX } = VRCXUpdaterStore;
const isUpgrade = false;
restartVRCX(isUpgrade);
}
}
}
);
}
return {
state,
isStartAtWindowsStartup,
isStartAsMinimizedState,
isCloseToTray,
disableGpuAcceleration,
disableVrOverlayGpuAcceleration,
localFavoriteFriendsGroups,
udonExceptionLogging,
logResourceLoad,
logEmptyAvatars,
autoStateChangeEnabled,
autoStateChangeAloneStatus,
autoStateChangeCompanyStatus,
autoStateChangeInstanceTypes,
autoStateChangeNoFriends,
autoAcceptInviteRequests,
setIsStartAtWindowsStartup,
setIsStartAsMinimizedState,
setIsCloseToTray,
setDisableGpuAcceleration,
setDisableVrOverlayGpuAcceleration,
setLocalFavoriteFriendsGroups,
setUdonExceptionLogging,
setLogResourceLoad,
setLogEmptyAvatars,
setAutoStateChangeEnabled,
setAutoStateChangeAloneStatus,
setAutoStateChangeCompanyStatus,
setAutoStateChangeInstanceTypes,
setAutoStateChangeNoFriends,
setAutoAcceptInviteRequests,
promptProxySettings
};
});
+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
};
}
);
+173
View File
@@ -0,0 +1,173 @@
import { defineStore } from 'pinia';
import { computed, reactive } from 'vue';
import configRepository from '../../service/config';
export const useWristOverlaySettingsStore = defineStore(
'WristOverlaySettings',
() => {
const state = reactive({
overlayWrist: true,
hidePrivateFromFeed: false,
openVRAlways: false,
overlaybutton: false,
overlayHand: 0,
vrBackgroundEnabled: false,
minimalFeed: false,
hideDevicesFromFeed: false,
vrOverlayCpuUsage: false,
hideUptimeFromFeed: false,
pcUptimeOnFeed: false
});
async function initWristOverlaySettings() {
const [
overlayWrist,
hidePrivateFromFeed,
openVRAlways,
overlaybutton,
overlayHand,
vrBackgroundEnabled,
minimalFeed,
hideDevicesFromFeed,
vrOverlayCpuUsage,
hideUptimeFromFeed,
pcUptimeOnFeed
] = await Promise.all([
configRepository.getBool('VRCX_overlayWrist', false),
configRepository.getBool('VRCX_hidePrivateFromFeed', false),
configRepository.getBool('openVRAlways', false),
configRepository.getBool('VRCX_overlaybutton', false),
configRepository.getInt('VRCX_overlayHand', 0),
configRepository.getBool('VRCX_vrBackgroundEnabled', false),
configRepository.getBool('VRCX_minimalFeed', false),
configRepository.getBool('VRCX_hideDevicesFromFeed', false),
configRepository.getBool('VRCX_vrOverlayCpuUsage', false),
configRepository.getBool('VRCX_hideUptimeFromFeed', false),
configRepository.getBool('VRCX_pcUptimeOnFeed', false)
]);
state.overlayWrist = overlayWrist;
state.hidePrivateFromFeed = hidePrivateFromFeed;
state.openVRAlways = openVRAlways;
state.overlaybutton = overlaybutton;
state.overlayHand = overlayHand;
state.vrBackgroundEnabled = vrBackgroundEnabled;
state.minimalFeed = minimalFeed;
state.hideDevicesFromFeed = hideDevicesFromFeed;
state.vrOverlayCpuUsage = vrOverlayCpuUsage;
state.hideUptimeFromFeed = hideUptimeFromFeed;
state.pcUptimeOnFeed = pcUptimeOnFeed;
}
const overlayWrist = computed(() => state.overlayWrist);
const hidePrivateFromFeed = computed(() => state.hidePrivateFromFeed);
const openVRAlways = computed(() => state.openVRAlways);
const overlaybutton = computed(() => state.overlaybutton);
const overlayHand = computed(() => state.overlayHand);
const vrBackgroundEnabled = computed(() => state.vrBackgroundEnabled);
const minimalFeed = computed(() => state.minimalFeed);
const hideDevicesFromFeed = computed(() => state.hideDevicesFromFeed);
const vrOverlayCpuUsage = computed(() => state.vrOverlayCpuUsage);
const hideUptimeFromFeed = computed(() => state.hideUptimeFromFeed);
const pcUptimeOnFeed = computed(() => state.pcUptimeOnFeed);
function setOverlayWrist() {
state.overlayWrist = !state.overlayWrist;
configRepository.setBool('VRCX_overlayWrist', state.overlayWrist);
}
function setHidePrivateFromFeed() {
state.hidePrivateFromFeed = !state.hidePrivateFromFeed;
configRepository.setBool(
'VRCX_hidePrivateFromFeed',
state.hidePrivateFromFeed
);
}
function setOpenVRAlways() {
state.openVRAlways = !state.openVRAlways;
configRepository.setBool('openVRAlways', state.openVRAlways);
}
function setOverlaybutton() {
state.overlaybutton = !state.overlaybutton;
configRepository.setBool('VRCX_overlaybutton', state.overlaybutton);
}
/**
* @param {string} value
*/
function setOverlayHand(value) {
state.overlayHand = parseInt(value, 10);
if (isNaN(state.overlayHand)) {
state.overlayHand = 0;
}
configRepository.setInt('VRCX_overlayHand', value);
}
function setVrBackgroundEnabled() {
state.vrBackgroundEnabled = !state.vrBackgroundEnabled;
configRepository.setBool(
'VRCX_vrBackgroundEnabled',
state.vrBackgroundEnabled
);
}
function setMinimalFeed() {
state.minimalFeed = !state.minimalFeed;
configRepository.setBool('VRCX_minimalFeed', state.minimalFeed);
}
function setHideDevicesFromFeed() {
state.hideDevicesFromFeed = !state.hideDevicesFromFeed;
configRepository.setBool(
'VRCX_hideDevicesFromFeed',
state.hideDevicesFromFeed
);
}
function setVrOverlayCpuUsage() {
state.vrOverlayCpuUsage = !state.vrOverlayCpuUsage;
configRepository.setBool(
'VRCX_vrOverlayCpuUsage',
state.vrOverlayCpuUsage
);
}
function setHideUptimeFromFeed() {
state.hideUptimeFromFeed = !state.hideUptimeFromFeed;
configRepository.setBool(
'VRCX_hideUptimeFromFeed',
state.hideUptimeFromFeed
);
}
function setPcUptimeOnFeed() {
state.pcUptimeOnFeed = !state.pcUptimeOnFeed;
configRepository.setBool(
'VRCX_pcUptimeOnFeed',
state.pcUptimeOnFeed
);
}
initWristOverlaySettings();
return {
state,
overlayWrist,
hidePrivateFromFeed,
openVRAlways,
overlaybutton,
overlayHand,
vrBackgroundEnabled,
minimalFeed,
hideDevicesFromFeed,
vrOverlayCpuUsage,
hideUptimeFromFeed,
pcUptimeOnFeed,
setOverlayWrist,
setHidePrivateFromFeed,
setOpenVRAlways,
setOverlaybutton,
setOverlayHand,
setVrBackgroundEnabled,
setMinimalFeed,
setHideDevicesFromFeed,
setVrOverlayCpuUsage,
setHideUptimeFromFeed,
setPcUptimeOnFeed
};
}
);