mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-29 11:43:48 +02:00
refactor: reorganize settings tabs
This commit is contained in:
@@ -4,27 +4,30 @@
|
||||
<span class="text-lg font-semibold text-foreground">{{ t('view.settings.header') }}</span>
|
||||
</div>
|
||||
<TabsUnderline
|
||||
default-value="general"
|
||||
default-value="system"
|
||||
:items="settingsTabs"
|
||||
:unmount-on-hide="false"
|
||||
fill>
|
||||
<template #general>
|
||||
<GeneralTab />
|
||||
<template #system>
|
||||
<SystemTab />
|
||||
</template>
|
||||
<template #appearance>
|
||||
<AppearanceTab />
|
||||
<template #interface>
|
||||
<InterfaceTab />
|
||||
</template>
|
||||
<template #social>
|
||||
<SocialTab />
|
||||
</template>
|
||||
<template #notifications>
|
||||
<NotificationsTab />
|
||||
</template>
|
||||
<template #wrist-overlay>
|
||||
<WristOverlayTab />
|
||||
<template #vr>
|
||||
<VrTab />
|
||||
</template>
|
||||
<template #discord>
|
||||
<DiscordPresenceTab />
|
||||
<template #media>
|
||||
<MediaTab />
|
||||
</template>
|
||||
<template #pictures>
|
||||
<PicturesTab />
|
||||
<template #integrations>
|
||||
<IntegrationsTab />
|
||||
</template>
|
||||
<template #advanced>
|
||||
<AdvancedTab />
|
||||
@@ -39,21 +42,23 @@
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import AdvancedTab from './components/Tabs/AdvancedTab.vue';
|
||||
import AppearanceTab from './components/Tabs/AppearanceTab.vue';
|
||||
import DiscordPresenceTab from './components/Tabs/DiscordPresenceTab.vue';
|
||||
import GeneralTab from './components/Tabs/GeneralTab.vue';
|
||||
import InterfaceTab from './components/Tabs/InterfaceTab.vue';
|
||||
import IntegrationsTab from './components/Tabs/IntegrationsTab.vue';
|
||||
import MediaTab from './components/Tabs/MediaTab.vue';
|
||||
import NotificationsTab from './components/Tabs/NotificationsTab.vue';
|
||||
import PicturesTab from './components/Tabs/PicturesTab.vue';
|
||||
import WristOverlayTab from './components/Tabs/WristOverlayTab.vue';
|
||||
import SocialTab from './components/Tabs/SocialTab.vue';
|
||||
import SystemTab from './components/Tabs/SystemTab.vue';
|
||||
import VrTab from './components/Tabs/VrTab.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const settingsTabs = computed(() => [
|
||||
{ value: 'general', label: t('view.settings.category.general') },
|
||||
{ value: 'appearance', label: t('view.settings.category.appearance') },
|
||||
{ value: 'system', label: t('view.settings.category.system') },
|
||||
{ value: 'interface', label: t('view.settings.category.interface') },
|
||||
{ value: 'social', label: t('view.settings.category.social') },
|
||||
{ value: 'notifications', label: t('view.settings.category.notifications') },
|
||||
{ value: 'wrist-overlay', label: t('view.settings.category.wrist_overlay') },
|
||||
{ value: 'discord', label: t('view.settings.category.discord_presence') },
|
||||
{ value: 'pictures', label: t('view.settings.category.pictures') },
|
||||
{ value: 'vr', label: t('view.settings.category.vr') },
|
||||
{ value: 'media', label: t('view.settings.category.media') },
|
||||
{ value: 'integrations', label: t('view.settings.category.integrations') },
|
||||
{ value: 'advanced', label: t('view.settings.category.advanced') }
|
||||
]);
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
</SettingsItem>
|
||||
</SettingsGroup>
|
||||
|
||||
<SettingsGroup :title="t('view.settings.advanced.advanced.vrcx_settings.header')">
|
||||
<SettingsGroup :title="t('view.settings.advanced_groups.security.header')">
|
||||
<SettingsItem :label="t('view.settings.advanced.advanced.primary_password.header')"
|
||||
:description="t('view.settings.advanced.advanced.primary_password.description')">
|
||||
<Switch
|
||||
@@ -30,13 +30,6 @@
|
||||
:disabled="!enablePrimaryPassword"
|
||||
@update:modelValue="enablePrimaryPasswordChange" />
|
||||
</SettingsItem>
|
||||
|
||||
<template v-if="branch === 'Nightly'">
|
||||
<SettingsItem :label="t('view.settings.advanced.advanced.anonymous_error_reporting.header')"
|
||||
:description="t('view.settings.advanced.advanced.anonymous_error_reporting.description')">
|
||||
<Switch :model-value="sentryErrorReporting" @update:modelValue="setSentryErrorReporting()" />
|
||||
</SettingsItem>
|
||||
</template>
|
||||
</SettingsGroup>
|
||||
|
||||
<SettingsGroup :title="t('view.settings.general.logging.header')">
|
||||
@@ -64,36 +57,6 @@
|
||||
</SettingsItem>
|
||||
</SettingsGroup>
|
||||
|
||||
<SettingsGroup :title="t('view.profile.game_info.header')">
|
||||
<div class="px-1 py-1">
|
||||
<div class="flex-1 cursor-pointer" @click="getVisits">
|
||||
<span class="block truncate font-medium text-sm leading-[18px]">{{
|
||||
t('view.profile.game_info.online_users')
|
||||
}}</span>
|
||||
<span v-if="visits" class="block truncate text-xs text-muted-foreground">{{
|
||||
t('view.profile.game_info.user_online', { count: visits })
|
||||
}}</span>
|
||||
<span v-else class="block truncate text-xs text-muted-foreground">{{
|
||||
t('view.profile.game_info.refresh')
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</SettingsGroup>
|
||||
|
||||
<SettingsGroup :title="t('view.settings.advanced.advanced.remote_database.header')">
|
||||
<SettingsItem :label="t('view.settings.advanced.advanced.remote_database.enable')">
|
||||
<Switch
|
||||
:model-value="avatarRemoteDatabase"
|
||||
@update:modelValue="setAvatarRemoteDatabase(!avatarRemoteDatabase)" />
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem :label="t('view.settings.advanced.advanced.remote_database.avatar_database_provider')">
|
||||
<Button size="sm" variant="outline" @click="showAvatarProviderDialog">{{
|
||||
t('view.settings.advanced.advanced.remote_database.avatar_database_provider')
|
||||
}}</Button>
|
||||
</SettingsItem>
|
||||
</SettingsGroup>
|
||||
|
||||
<template v-if="!isLinux">
|
||||
<SettingsGroup :title="t('view.settings.advanced.advanced.app_launcher.header')">
|
||||
<SettingsItem :label="t('view.settings.advanced.advanced.app_launcher.folder')">
|
||||
@@ -121,50 +84,6 @@
|
||||
</SettingsGroup>
|
||||
</template>
|
||||
|
||||
<SettingsGroup :title="t('view.settings.advanced.advanced.youtube_api.header')">
|
||||
<SettingsItem :label="t('view.settings.advanced.advanced.youtube_api.enable')"
|
||||
:description="t('view.settings.advanced.advanced.youtube_api.enable_tooltip')">
|
||||
<Switch :model-value="youTubeApi" @update:modelValue="changeYouTubeApi('VRCX_youtubeAPI')" />
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem :label="t('view.settings.advanced.advanced.youtube_api.youtube_api_key')">
|
||||
<Button size="sm" variant="outline" @click="showYouTubeApiDialog">{{
|
||||
t('view.settings.advanced.advanced.youtube_api.youtube_api_key')
|
||||
}}</Button>
|
||||
</SettingsItem>
|
||||
</SettingsGroup>
|
||||
|
||||
<SettingsGroup :title="t('view.settings.advanced.advanced.translation_api.header')">
|
||||
<SettingsItem :label="t('view.settings.advanced.advanced.translation_api.enable')"
|
||||
:description="t('view.settings.advanced.advanced.translation_api.enable_tooltip')">
|
||||
<Switch :model-value="translationApi" @update:modelValue="changeTranslationAPI('VRCX_translationAPI')" />
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem :label="t('view.settings.advanced.advanced.translation_api.translation_api_key')">
|
||||
<Button size="sm" variant="outline" @click="showTranslationApiDialog">
|
||||
<Languages class="h-4 w-4 mr-1.5" />
|
||||
{{ t('view.settings.advanced.advanced.translation_api.translation_api_key') }}
|
||||
</Button>
|
||||
</SettingsItem>
|
||||
</SettingsGroup>
|
||||
|
||||
<SettingsGroup :title="t('view.settings.advanced.advanced.video_progress_pie.header')">
|
||||
<SettingsItem :label="t('view.settings.advanced.advanced.video_progress_pie.enable')"
|
||||
:description="t('view.settings.advanced.advanced.video_progress_pie.enable_tooltip')">
|
||||
<Switch
|
||||
:model-value="progressPie"
|
||||
:disabled="!openVR"
|
||||
@update:modelValue="changeYouTubeApi('VRCX_progressPie')" />
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem :label="t('view.settings.advanced.advanced.video_progress_pie.dance_world_only')">
|
||||
<Switch
|
||||
:model-value="progressPieFilter"
|
||||
:disabled="!openVR"
|
||||
@update:modelValue="changeYouTubeApi('VRCX_progressPieFilter')" />
|
||||
</SettingsItem>
|
||||
</SettingsGroup>
|
||||
|
||||
<SettingsGroup :title="t('view.settings.advanced.advanced.launch_commands.header')">
|
||||
<SettingsItem
|
||||
:label="t('view.settings.advanced.advanced.launch_commands.show_confirmation_on_switch_avatar_enable')"
|
||||
@@ -224,7 +143,7 @@
|
||||
</SettingsItem>
|
||||
</SettingsGroup>
|
||||
|
||||
<SettingsGroup :title="t('view.settings.advanced.advanced.sqlite_table_size.header')">
|
||||
<SettingsGroup :title="t('view.settings.advanced_groups.database.header')">
|
||||
<SettingsItem :label="t('view.settings.advanced.advanced.sqlite_table_size.refresh')">
|
||||
<Button size="sm" variant="outline" @click="getSqliteTableSizes">{{
|
||||
t('view.settings.advanced.advanced.sqlite_table_size.refresh')
|
||||
@@ -245,9 +164,7 @@
|
||||
<span>{{ t('view.settings.advanced.advanced.sqlite_table_size.video_play') }} <span v-text="sqliteTableSizes.videoPlay"></span></span>
|
||||
<span>{{ t('view.settings.advanced.advanced.sqlite_table_size.event') }} <span v-text="sqliteTableSizes.event"></span></span>
|
||||
</div>
|
||||
</SettingsGroup>
|
||||
|
||||
<SettingsGroup :title="t('view.settings.advanced.advanced.database_cleanup.header')">
|
||||
<SettingsItem
|
||||
:label="t('view.settings.advanced.advanced.database_cleanup.auto_cleanup')"
|
||||
:description="t('view.settings.advanced.advanced.database_cleanup.auto_cleanup_description')">
|
||||
@@ -320,41 +237,64 @@
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<SettingsGroup :title="t('view.profile.config_json')">
|
||||
<SettingsGroup :title="t('view.settings.advanced_groups.diagnostics.header')">
|
||||
<SettingsGroup :title="t('view.profile.game_info.header')">
|
||||
<div class="px-1 py-1">
|
||||
<div class="flex-1 cursor-pointer" @click="getVisits">
|
||||
<span class="block truncate font-medium text-sm leading-[18px]">{{
|
||||
t('view.profile.game_info.online_users')
|
||||
}}</span>
|
||||
<span v-if="visits" class="block truncate text-xs text-muted-foreground">{{
|
||||
t('view.profile.game_info.user_online', { count: visits })
|
||||
}}</span>
|
||||
<span v-else class="block truncate text-xs text-muted-foreground">{{
|
||||
t('view.profile.game_info.refresh')
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</SettingsGroup>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<TooltipWrapper side="top" :content="t('view.profile.refresh_tooltip')">
|
||||
<Button class="rounded-full" size="icon-sm" variant="outline" @click="refreshConfigTreeData()">
|
||||
<RefreshCcw />
|
||||
</Button>
|
||||
</TooltipWrapper>
|
||||
<TooltipWrapper side="top" :content="t('view.profile.clear_results_tooltip')">
|
||||
<Button class="rounded-full" size="icon-sm" variant="outline" @click="configTreeData = {}">
|
||||
<Trash2 />
|
||||
</Button>
|
||||
</TooltipWrapper>
|
||||
</div>
|
||||
<vue-json-pretty
|
||||
v-if="Object.keys(configTreeData).length > 0"
|
||||
:data="configTreeData"
|
||||
:deep="2"
|
||||
:theme="isDarkMode ? 'dark' : 'light'"
|
||||
:height="800"
|
||||
:dynamic-height="false"
|
||||
virtual
|
||||
show-icon />
|
||||
<SettingsGroup :title="t('view.profile.config_json')">
|
||||
<div class="flex items-center gap-2">
|
||||
<TooltipWrapper side="top" :content="t('view.profile.refresh_tooltip')">
|
||||
<Button class="rounded-full" size="icon-sm" variant="outline" @click="refreshConfigTreeData()">
|
||||
<RefreshCcw />
|
||||
</Button>
|
||||
</TooltipWrapper>
|
||||
<TooltipWrapper side="top" :content="t('view.profile.clear_results_tooltip')">
|
||||
<Button class="rounded-full" size="icon-sm" variant="outline" @click="configTreeData = {}">
|
||||
<Trash2 />
|
||||
</Button>
|
||||
</TooltipWrapper>
|
||||
</div>
|
||||
<vue-json-pretty
|
||||
v-if="Object.keys(configTreeData).length > 0"
|
||||
:data="configTreeData"
|
||||
:deep="2"
|
||||
:theme="isDarkMode ? 'dark' : 'light'"
|
||||
:height="800"
|
||||
:dynamic-height="false"
|
||||
virtual
|
||||
show-icon />
|
||||
</SettingsGroup>
|
||||
</SettingsGroup>
|
||||
|
||||
<template v-if="branch === 'Nightly'">
|
||||
<SettingsGroup :title="t('view.settings.advanced_groups.nightly.header')">
|
||||
<SettingsItem :label="t('view.settings.advanced.advanced.anonymous_error_reporting.header')"
|
||||
:description="t('view.settings.advanced.advanced.anonymous_error_reporting.description')">
|
||||
<Switch :model-value="sentryErrorReporting" @update:modelValue="setSentryErrorReporting()" />
|
||||
</SettingsItem>
|
||||
</SettingsGroup>
|
||||
</template>
|
||||
|
||||
<RegistryBackupDialog />
|
||||
<YouTubeApiDialog v-model:isYouTubeApiDialogVisible="isYouTubeApiDialogVisible" />
|
||||
<TranslationApiDialog v-model:isTranslationApiDialogVisible="isTranslationApiDialogVisible" />
|
||||
<AvatarProviderDialog v-model:isAvatarProviderDialogVisible="isAvatarProviderDialogVisible" />
|
||||
<PhotonSettings v-if="photonLoggingEnabled" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Languages, RefreshCcw, Trash2, TriangleAlert } from 'lucide-vue-next';
|
||||
import { RefreshCcw, Trash2, TriangleAlert } from 'lucide-vue-next';
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
@@ -370,17 +310,14 @@
|
||||
useAdvancedSettingsStore,
|
||||
useAppearanceSettingsStore,
|
||||
useAuthStore,
|
||||
useAvatarProviderStore,
|
||||
useAvatarStore,
|
||||
useGeneralSettingsStore,
|
||||
useGroupStore,
|
||||
useInstanceStore,
|
||||
useNotificationsSettingsStore,
|
||||
usePhotonStore,
|
||||
useUiStore,
|
||||
useUserStore,
|
||||
useVRCXUpdaterStore,
|
||||
useVrStore,
|
||||
useWorldStore
|
||||
} from '@/stores';
|
||||
import { authRequest, queryRequest } from '@/api';
|
||||
@@ -388,19 +325,15 @@
|
||||
import { clearVRCXCache } from '@/coordinators/vrcxCoordinator';
|
||||
import { openExternalLink } from '@/shared/utils';
|
||||
|
||||
import AvatarProviderDialog from '../../dialogs/AvatarProviderDialog.vue';
|
||||
import PhotonSettings from '../PhotonSettings.vue';
|
||||
import RegistryBackupDialog from '../../../Tools/dialogs/RegistryBackupDialog.vue';
|
||||
import TranslationApiDialog from '../../dialogs/TranslationApiDialog.vue';
|
||||
import YouTubeApiDialog from '../../dialogs/YouTubeApiDialog.vue';
|
||||
import SettingsGroup from '../SettingsGroup.vue';
|
||||
import SettingsItem from '../SettingsItem.vue';
|
||||
import { TooltipWrapper } from '@/components/ui/tooltip';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const advancedSettingsStore = useAdvancedSettingsStore();
|
||||
const notificationsSettingsStore = useNotificationsSettingsStore();
|
||||
const { updateVRLastLocation, updateOpenVR } = useVrStore();
|
||||
const { enablePrimaryPasswordChange } = useAuthStore();
|
||||
const { cachedConfig } = storeToRefs(useAuthStore());
|
||||
const { showConsole } = useUiStore();
|
||||
@@ -424,7 +357,6 @@
|
||||
|
||||
const { photonLoggingEnabled } = storeToRefs(usePhotonStore());
|
||||
const { branch } = storeToRefs(useVRCXUpdaterStore());
|
||||
const { openVR } = storeToRefs(notificationsSettingsStore);
|
||||
|
||||
const { isDarkMode } = storeToRefs(useAppearanceSettingsStore());
|
||||
|
||||
@@ -434,14 +366,9 @@
|
||||
vrcQuitFix,
|
||||
autoSweepVRChatCache,
|
||||
selfInviteOverride,
|
||||
avatarRemoteDatabase,
|
||||
enableAppLauncher,
|
||||
enableAppLauncherAutoClose,
|
||||
enableAppLauncherRunProcessOnce,
|
||||
youTubeApi,
|
||||
translationApi,
|
||||
progressPie,
|
||||
progressPieFilter,
|
||||
showConfirmationOnSwitchAvatar,
|
||||
gameLogDisabled,
|
||||
sqliteTableSizes,
|
||||
@@ -455,7 +382,6 @@
|
||||
setVrcQuitFix,
|
||||
setAutoSweepVRChatCache,
|
||||
setSelfInviteOverride,
|
||||
setAvatarRemoteDatabase,
|
||||
setEnableAppLauncher,
|
||||
setEnableAppLauncherAutoClose,
|
||||
setEnableAppLauncherRunProcessOnce,
|
||||
@@ -463,16 +389,10 @@
|
||||
getSqliteTableSizes,
|
||||
setAvatarAutoCleanup,
|
||||
purgeAvatarFeedData,
|
||||
showVRChatConfig,
|
||||
promptAutoClearVRCXCacheFrequency,
|
||||
setSentryErrorReporting
|
||||
} = advancedSettingsStore;
|
||||
|
||||
const { isAvatarProviderDialogVisible } = storeToRefs(useAvatarProviderStore());
|
||||
const { showAvatarProviderDialog } = useAvatarProviderStore();
|
||||
|
||||
const isYouTubeApiDialogVisible = ref(false);
|
||||
const isTranslationApiDialogVisible = ref(false);
|
||||
const configTreeData = ref({});
|
||||
const visits = ref(0);
|
||||
const selectedPurgePeriod = ref('180');
|
||||
@@ -505,20 +425,6 @@
|
||||
AppApi.OpenShortcutFolder();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function showYouTubeApiDialog() {
|
||||
isYouTubeApiDialogVisible.value = true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function showTranslationApiDialog() {
|
||||
isTranslationApiDialogVisible.value = true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -531,32 +437,6 @@
|
||||
cacheSize.cachedInstances = cachedInstances.size;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param configKey
|
||||
*/
|
||||
async function changeYouTubeApi(configKey = '') {
|
||||
if (configKey === 'VRCX_youtubeAPI') {
|
||||
advancedSettingsStore.setYouTubeApi();
|
||||
} else if (configKey === 'VRCX_progressPie') {
|
||||
advancedSettingsStore.setProgressPie();
|
||||
} else if (configKey === 'VRCX_progressPieFilter') {
|
||||
advancedSettingsStore.setProgressPieFilter();
|
||||
}
|
||||
updateVRLastLocation();
|
||||
updateOpenVR();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param configKey
|
||||
*/
|
||||
async function changeTranslationAPI(configKey = '') {
|
||||
if (configKey === 'VRCX_translationAPI') {
|
||||
advancedSettingsStore.setTranslationApi();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div class="flex flex-col gap-10 py-2">
|
||||
<!-- Discord Rich Presence -->
|
||||
<SettingsGroup :title="t('view.settings.discord_presence.discord_presence.header')">
|
||||
<template #description>
|
||||
<p class="m-0">{{ t('view.settings.discord_presence.discord_presence.description') }}</p>
|
||||
@@ -94,21 +95,83 @@
|
||||
" />
|
||||
</SettingsItem>
|
||||
</SettingsGroup>
|
||||
|
||||
<!-- Translation API -->
|
||||
<SettingsGroup :title="t('view.settings.advanced.advanced.translation_api.header')">
|
||||
<SettingsItem :label="t('view.settings.advanced.advanced.translation_api.enable')"
|
||||
:description="t('view.settings.advanced.advanced.translation_api.enable_tooltip')">
|
||||
<Switch :model-value="translationApi" @update:modelValue="changeTranslationAPI('VRCX_translationAPI')" />
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem :label="t('view.settings.advanced.advanced.translation_api.translation_api_key')">
|
||||
<Button size="sm" variant="outline" @click="showTranslationApiDialog">
|
||||
<Languages class="h-4 w-4 mr-1.5" />
|
||||
{{ t('view.settings.advanced.advanced.translation_api.translation_api_key') }}
|
||||
</Button>
|
||||
</SettingsItem>
|
||||
</SettingsGroup>
|
||||
|
||||
<!-- YouTube API -->
|
||||
<SettingsGroup :title="t('view.settings.advanced.advanced.youtube_api.header')">
|
||||
<SettingsItem :label="t('view.settings.advanced.advanced.youtube_api.enable')"
|
||||
:description="t('view.settings.advanced.advanced.youtube_api.enable_tooltip')">
|
||||
<Switch :model-value="youTubeApi" @update:modelValue="changeYouTubeApi('VRCX_youtubeAPI')" />
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem :label="t('view.settings.advanced.advanced.youtube_api.youtube_api_key')">
|
||||
<Button size="sm" variant="outline" @click="showYouTubeApiDialog">{{
|
||||
t('view.settings.advanced.advanced.youtube_api.youtube_api_key')
|
||||
}}</Button>
|
||||
</SettingsItem>
|
||||
</SettingsGroup>
|
||||
|
||||
<!-- Remote Database -->
|
||||
<SettingsGroup :title="t('view.settings.advanced.advanced.remote_database.header')">
|
||||
<SettingsItem :label="t('view.settings.advanced.advanced.remote_database.enable')">
|
||||
<Switch
|
||||
:model-value="avatarRemoteDatabase"
|
||||
@update:modelValue="setAvatarRemoteDatabase(!avatarRemoteDatabase)" />
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem :label="t('view.settings.advanced.advanced.remote_database.avatar_database_provider')">
|
||||
<Button size="sm" variant="outline" @click="showAvatarProviderDialog">{{
|
||||
t('view.settings.advanced.advanced.remote_database.avatar_database_provider')
|
||||
}}</Button>
|
||||
</SettingsItem>
|
||||
</SettingsGroup>
|
||||
|
||||
<TranslationApiDialog v-model:isTranslationApiDialogVisible="isTranslationApiDialogVisible" />
|
||||
<YouTubeApiDialog v-model:isYouTubeApiDialogVisible="isYouTubeApiDialogVisible" />
|
||||
<AvatarProviderDialog v-model:isAvatarProviderDialogVisible="isAvatarProviderDialogVisible" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { Languages } from 'lucide-vue-next';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { useAdvancedSettingsStore, useDiscordPresenceSettingsStore } from '@/stores';
|
||||
import {
|
||||
useAdvancedSettingsStore,
|
||||
useAvatarProviderStore,
|
||||
useDiscordPresenceSettingsStore,
|
||||
useVrStore
|
||||
} from '@/stores';
|
||||
|
||||
import AvatarProviderDialog from '../../dialogs/AvatarProviderDialog.vue';
|
||||
import TranslationApiDialog from '../../dialogs/TranslationApiDialog.vue';
|
||||
import YouTubeApiDialog from '../../dialogs/YouTubeApiDialog.vue';
|
||||
import SettingsGroup from '../SettingsGroup.vue';
|
||||
import SettingsItem from '../SettingsItem.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const advancedSettingsStore = useAdvancedSettingsStore();
|
||||
const { updateVRLastLocation, updateOpenVR } = useVrStore();
|
||||
|
||||
const {
|
||||
setDiscordActive,
|
||||
setDiscordInstance,
|
||||
@@ -132,5 +195,57 @@
|
||||
discordWorldNameAsDiscordStatus
|
||||
} = storeToRefs(useDiscordPresenceSettingsStore());
|
||||
|
||||
const { showVRChatConfig } = useAdvancedSettingsStore();
|
||||
const { showVRChatConfig } = advancedSettingsStore;
|
||||
|
||||
const {
|
||||
avatarRemoteDatabase,
|
||||
youTubeApi,
|
||||
translationApi
|
||||
} = storeToRefs(advancedSettingsStore);
|
||||
|
||||
const {
|
||||
setAvatarRemoteDatabase
|
||||
} = advancedSettingsStore;
|
||||
|
||||
const { isAvatarProviderDialogVisible } = storeToRefs(useAvatarProviderStore());
|
||||
const { showAvatarProviderDialog } = useAvatarProviderStore();
|
||||
|
||||
const isYouTubeApiDialogVisible = ref(false);
|
||||
const isTranslationApiDialogVisible = ref(false);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function showYouTubeApiDialog() {
|
||||
isYouTubeApiDialogVisible.value = true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function showTranslationApiDialog() {
|
||||
isTranslationApiDialogVisible.value = true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param configKey
|
||||
*/
|
||||
async function changeYouTubeApi(configKey = '') {
|
||||
if (configKey === 'VRCX_youtubeAPI') {
|
||||
advancedSettingsStore.setYouTubeApi();
|
||||
}
|
||||
updateVRLastLocation();
|
||||
updateOpenVR();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param configKey
|
||||
*/
|
||||
async function changeTranslationAPI(configKey = '') {
|
||||
if (configKey === 'VRCX_translationAPI') {
|
||||
advancedSettingsStore.setTranslationApi();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -134,7 +134,7 @@
|
||||
</SettingsItem>
|
||||
</SettingsGroup>
|
||||
|
||||
<SettingsGroup :title="t('view.settings.appearance.sorting_tables.header')">
|
||||
<SettingsGroup :title="t('view.settings.interface.lists_tables.header')">
|
||||
<SettingsItem :label="t('view.settings.appearance.appearance.sort_favorite_by')">
|
||||
<RadioGroup
|
||||
:model-value="sortFavorites ? 'true' : 'false'"
|
||||
@@ -264,31 +264,6 @@
|
||||
<SettingsItem :label="t('view.settings.appearance.user_dialog.vrcx_memos')">
|
||||
<Switch :model-value="!hideUserMemos" @update:modelValue="setHideUserMemos" />
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
:label="t('view.settings.appearance.user_dialog.recent_action_cooldown')"
|
||||
:description="t('view.settings.appearance.user_dialog.recent_action_cooldown_description')">
|
||||
<Switch :model-value="recentActionCooldownEnabled" @update:modelValue="setRecentActionCooldownEnabled" />
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
v-if="recentActionCooldownEnabled"
|
||||
:label="t('view.settings.appearance.user_dialog.recent_action_cooldown_minutes')">
|
||||
<NumberField
|
||||
:model-value="recentActionCooldownMinutes"
|
||||
:min="1"
|
||||
:max="1440"
|
||||
:step="1"
|
||||
:format-options="{ maximumFractionDigits: 0 }"
|
||||
class="w-32"
|
||||
@update:modelValue="setRecentActionCooldownMinutes">
|
||||
<NumberFieldContent>
|
||||
<NumberFieldDecrement />
|
||||
<NumberFieldInput />
|
||||
<NumberFieldIncrement />
|
||||
</NumberFieldContent>
|
||||
</NumberField>
|
||||
</SettingsItem>
|
||||
</SettingsGroup>
|
||||
|
||||
<SettingsGroup :title="t('view.settings.appearance.friend_log.header')">
|
||||
@@ -360,7 +335,7 @@
|
||||
import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||
import { computed, onBeforeUnmount, ref, watch } from 'vue';
|
||||
import { CheckIcon, ChevronDown } from 'lucide-vue-next';
|
||||
import { useAppearanceSettingsStore, useFavoriteStore, useGeneralSettingsStore, useVrStore } from '@/stores';
|
||||
import { useAppearanceSettingsStore, useFavoriteStore, useVrStore } from '@/stores';
|
||||
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { getLanguageName, languageCodes } from '@/localization';
|
||||
@@ -380,7 +355,6 @@
|
||||
const { t } = useI18n();
|
||||
|
||||
const appearanceSettingsStore = useAppearanceSettingsStore();
|
||||
const generalSettingsStore = useGeneralSettingsStore();
|
||||
const { saveOpenVROption, updateVRConfigVars } = useVrStore();
|
||||
|
||||
const {
|
||||
@@ -432,15 +406,7 @@
|
||||
setAppCjkFontPack
|
||||
} = appearanceSettingsStore;
|
||||
|
||||
const {
|
||||
recentActionCooldownEnabled,
|
||||
recentActionCooldownMinutes
|
||||
} = storeToRefs(generalSettingsStore);
|
||||
|
||||
const {
|
||||
setRecentActionCooldownEnabled,
|
||||
setRecentActionCooldownMinutes
|
||||
} = generalSettingsStore;
|
||||
|
||||
const trustColorEntries = [
|
||||
{
|
||||
@@ -14,172 +14,6 @@
|
||||
</SettingsItem>
|
||||
</SettingsGroup>
|
||||
|
||||
<SettingsGroup :title="t('view.settings.notifications.notifications.steamvr_notifications.header')">
|
||||
<SettingsItem
|
||||
:label="t('view.settings.notifications.notifications.desktop_notifications.when_to_display')">
|
||||
<ToggleGroup
|
||||
type="single"
|
||||
required
|
||||
variant="outline"
|
||||
size="sm"
|
||||
:model-value="overlayToast"
|
||||
:disabled="
|
||||
(!overlayNotifications || !openVR) &&
|
||||
!xsNotifications &&
|
||||
!ovrtHudNotifications &&
|
||||
!ovrtWristNotifications
|
||||
"
|
||||
@update:model-value="
|
||||
setOverlayToast($event);
|
||||
saveOpenVROption();
|
||||
">
|
||||
<ToggleGroupItem value="Never">{{
|
||||
t('view.settings.notifications.notifications.conditions.never')
|
||||
}}</ToggleGroupItem>
|
||||
<ToggleGroupItem value="Game Running">{{
|
||||
t('view.settings.notifications.notifications.conditions.inside_vrchat')
|
||||
}}</ToggleGroupItem>
|
||||
<ToggleGroupItem value="Game Closed">{{
|
||||
t('view.settings.notifications.notifications.conditions.outside_vrchat')
|
||||
}}</ToggleGroupItem>
|
||||
<ToggleGroupItem value="Always">{{
|
||||
t('view.settings.notifications.notifications.conditions.always')
|
||||
}}</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
:label="t('view.settings.notifications.notifications.steamvr_notifications.steamvr_overlay')">
|
||||
<Switch
|
||||
:model-value="openVR"
|
||||
@update:modelValue="
|
||||
setOpenVR();
|
||||
saveOpenVROption();
|
||||
" />
|
||||
</SettingsItem>
|
||||
|
||||
<template v-if="openVR">
|
||||
<SettingsItem
|
||||
:label="
|
||||
t('view.settings.notifications.notifications.steamvr_notifications.overlay_notifications')
|
||||
">
|
||||
<Switch
|
||||
:model-value="overlayNotifications"
|
||||
:disabled="!openVR"
|
||||
@update:modelValue="
|
||||
setOverlayNotifications();
|
||||
saveOpenVROption();
|
||||
" />
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
:label="
|
||||
t('view.settings.notifications.notifications.steamvr_notifications.notification_position')
|
||||
">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
:disabled="!overlayNotifications || !openVR"
|
||||
@click="showNotificationPositionDialog"
|
||||
>{{
|
||||
t('view.settings.notifications.notifications.steamvr_notifications.notification_position')
|
||||
}}</Button
|
||||
>
|
||||
</SettingsItem>
|
||||
</template>
|
||||
|
||||
<SettingsItem
|
||||
:label="
|
||||
t('view.settings.notifications.notifications.steamvr_notifications.notification_opacity')
|
||||
">
|
||||
<div class="w-75 max-w-full pt-1">
|
||||
<Slider v-model="notificationOpacityValue" :min="0" :max="100" />
|
||||
</div>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
:label="
|
||||
t('view.settings.notifications.notifications.steamvr_notifications.notification_timeout')
|
||||
">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
:disabled="(!overlayNotifications || !openVR) && !xsNotifications"
|
||||
@click="promptNotificationTimeout"
|
||||
>{{
|
||||
t('view.settings.notifications.notifications.steamvr_notifications.notification_timeout')
|
||||
}}</Button
|
||||
>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
:label="t('view.settings.notifications.notifications.steamvr_notifications.user_images')">
|
||||
<Switch
|
||||
:model-value="imageNotifications"
|
||||
@update:modelValue="
|
||||
setImageNotifications();
|
||||
saveOpenVROption();
|
||||
" />
|
||||
</SettingsItem>
|
||||
|
||||
<template v-if="!isLinux">
|
||||
<SettingsItem
|
||||
:label="
|
||||
t('view.settings.notifications.notifications.steamvr_notifications.xsoverlay_notifications')
|
||||
">
|
||||
<Switch
|
||||
:model-value="xsNotifications"
|
||||
@update:modelValue="
|
||||
setXsNotifications();
|
||||
saveOpenVROption();
|
||||
" />
|
||||
</SettingsItem>
|
||||
</template>
|
||||
<template v-else>
|
||||
<SettingsItem
|
||||
:label="
|
||||
t('view.settings.notifications.notifications.steamvr_notifications.wayvr_notifications')
|
||||
">
|
||||
<Switch
|
||||
:model-value="xsNotifications"
|
||||
@update:modelValue="
|
||||
setXsNotifications();
|
||||
saveOpenVROption();
|
||||
" />
|
||||
</SettingsItem>
|
||||
</template>
|
||||
|
||||
<template v-if="!isLinux">
|
||||
<SettingsItem
|
||||
:label="
|
||||
t(
|
||||
'view.settings.notifications.notifications.steamvr_notifications.ovrtoolkit_hud_notifications'
|
||||
)
|
||||
">
|
||||
<Switch
|
||||
:model-value="ovrtHudNotifications"
|
||||
@update:modelValue="
|
||||
setOvrtHudNotifications();
|
||||
saveOpenVROption();
|
||||
" />
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
:label="
|
||||
t(
|
||||
'view.settings.notifications.notifications.steamvr_notifications.ovrtoolkit_wrist_notifications'
|
||||
)
|
||||
">
|
||||
<Switch
|
||||
:model-value="ovrtWristNotifications"
|
||||
@update:modelValue="
|
||||
setOvrtWristNotifications();
|
||||
saveOpenVROption();
|
||||
" />
|
||||
</SettingsItem>
|
||||
</template>
|
||||
</SettingsGroup>
|
||||
|
||||
<SettingsGroup :title="t('view.settings.notifications.notifications.desktop_notifications.header')">
|
||||
<SettingsItem
|
||||
:label="t('view.settings.notifications.notifications.desktop_notifications.when_to_display')">
|
||||
@@ -294,7 +128,6 @@
|
||||
</div>
|
||||
</SettingsGroup>
|
||||
|
||||
<NotificationPositionDialog v-model:isNotificationPositionDialogVisible="isNotificationPositionDialogVisible" />
|
||||
<FeedFiltersDialog v-model:feedFiltersDialogMode="feedFiltersDialogMode" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -303,7 +136,6 @@
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { Slider } from '@/components/ui/slider';
|
||||
import { computed, ref } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
@@ -312,31 +144,19 @@
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import {
|
||||
useAdvancedSettingsStore,
|
||||
useNotificationStore,
|
||||
useNotificationsSettingsStore,
|
||||
useVrStore
|
||||
useNotificationsSettingsStore
|
||||
} from '@/stores';
|
||||
|
||||
import FeedFiltersDialog from '../../dialogs/FeedFiltersDialog.vue';
|
||||
import NotificationPositionDialog from '../../dialogs/NotificationPositionDialog.vue';
|
||||
import SettingsGroup from '../SettingsGroup.vue';
|
||||
import SettingsItem from '../SettingsItem.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const notificationsSettingsStore = useNotificationsSettingsStore();
|
||||
const advancedSettingsStore = useAdvancedSettingsStore();
|
||||
const { saveOpenVROption } = useVrStore();
|
||||
|
||||
const {
|
||||
overlayToast,
|
||||
openVR,
|
||||
overlayNotifications,
|
||||
xsNotifications,
|
||||
ovrtHudNotifications,
|
||||
ovrtWristNotifications,
|
||||
imageNotifications,
|
||||
desktopToast,
|
||||
afkDesktopToast,
|
||||
notificationTTS,
|
||||
@@ -346,42 +166,19 @@
|
||||
TTSvoices
|
||||
} = storeToRefs(notificationsSettingsStore);
|
||||
|
||||
const { notificationOpacity } = storeToRefs(advancedSettingsStore);
|
||||
|
||||
const {
|
||||
setOverlayToast,
|
||||
setOpenVR,
|
||||
setOverlayNotifications,
|
||||
setXsNotifications,
|
||||
setOvrtHudNotifications,
|
||||
setOvrtWristNotifications,
|
||||
setImageNotifications,
|
||||
setDesktopToast,
|
||||
setAfkDesktopToast,
|
||||
setNotificationTTSNickName,
|
||||
getTTSVoiceName,
|
||||
changeTTSVoice,
|
||||
saveNotificationTTS,
|
||||
testNotificationTTS,
|
||||
promptNotificationTimeout
|
||||
testNotificationTTS
|
||||
} = notificationsSettingsStore;
|
||||
|
||||
const { testNotification } = useNotificationStore();
|
||||
|
||||
const { setNotificationOpacity } = advancedSettingsStore;
|
||||
|
||||
const feedFiltersDialogMode = ref('');
|
||||
const isNotificationPositionDialogVisible = ref(false);
|
||||
const isLinux = computed(() => LINUX);
|
||||
const notificationOpacityValue = computed({
|
||||
get: () => [notificationOpacity.value],
|
||||
set: (value) => {
|
||||
const next = value?.[0];
|
||||
if (typeof next === 'number') {
|
||||
setNotificationOpacity(next);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const ttsVoiceIndex = computed({
|
||||
get: () => {
|
||||
@@ -402,11 +199,4 @@
|
||||
function showNotyFeedFiltersDialog() {
|
||||
feedFiltersDialogMode.value = 'noty';
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function showNotificationPositionDialog() {
|
||||
isNotificationPositionDialogVisible.value = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
116
src/views/Settings/components/Tabs/SocialTab.vue
Normal file
116
src/views/Settings/components/Tabs/SocialTab.vue
Normal file
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<div class="flex flex-col gap-10 py-2">
|
||||
<SettingsGroup :title="t('view.settings.social.interaction.header')">
|
||||
<SettingsItem
|
||||
:label="t('view.settings.appearance.user_dialog.recent_action_cooldown')"
|
||||
:description="t('view.settings.appearance.user_dialog.recent_action_cooldown_description')">
|
||||
<Switch :model-value="recentActionCooldownEnabled" @update:modelValue="setRecentActionCooldownEnabled" />
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
v-if="recentActionCooldownEnabled"
|
||||
:label="t('view.settings.appearance.user_dialog.recent_action_cooldown_minutes')">
|
||||
<NumberField
|
||||
:model-value="recentActionCooldownMinutes"
|
||||
:min="1"
|
||||
:max="1440"
|
||||
:step="1"
|
||||
:format-options="{ maximumFractionDigits: 0 }"
|
||||
class="w-32"
|
||||
@update:modelValue="setRecentActionCooldownMinutes">
|
||||
<NumberFieldContent>
|
||||
<NumberFieldDecrement />
|
||||
<NumberFieldInput />
|
||||
<NumberFieldIncrement />
|
||||
</NumberFieldContent>
|
||||
</NumberField>
|
||||
</SettingsItem>
|
||||
</SettingsGroup>
|
||||
|
||||
<SettingsGroup>
|
||||
<template #description>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<span class="text-base font-semibold text-foreground">{{ t('view.settings.general.favorites.header') }}</span>
|
||||
<TooltipWrapper side="top" :content="t('view.settings.general.favorites.header_tooltip')">
|
||||
<Info class="size-3 text-muted-foreground cursor-help" />
|
||||
</TooltipWrapper>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<Select
|
||||
:model-value="localFavoriteFriendsGroups"
|
||||
multiple
|
||||
@update:modelValue="setLocalFavoriteFriendsGroups">
|
||||
<SelectTrigger>
|
||||
<SelectValue :placeholder="t('view.settings.general.favorites.group_placeholder')" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem v-for="group in favoriteFriendGroups" :key="group.key" :value="group.key">
|
||||
{{ group.displayName }}
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
<template v-if="localFriendFavoriteGroups.length">
|
||||
<SelectSeparator />
|
||||
<SelectGroup>
|
||||
<SelectItem
|
||||
v-for="group in localFriendFavoriteGroups"
|
||||
:key="'local:' + group"
|
||||
:value="'local:' + group">
|
||||
{{ group }}
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
</template>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</SettingsGroup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { Info } from 'lucide-vue-next';
|
||||
import {
|
||||
NumberField,
|
||||
NumberFieldContent,
|
||||
NumberFieldDecrement,
|
||||
NumberFieldIncrement,
|
||||
NumberFieldInput
|
||||
} from '@/components/ui/number-field';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectSeparator,
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
} from '@/components/ui/select';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { useFavoriteStore, useGeneralSettingsStore } from '@/stores';
|
||||
|
||||
import SettingsGroup from '../SettingsGroup.vue';
|
||||
import SettingsItem from '../SettingsItem.vue';
|
||||
import { TooltipWrapper } from '@/components/ui/tooltip';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const generalSettingsStore = useGeneralSettingsStore();
|
||||
const favoriteStore = useFavoriteStore();
|
||||
|
||||
const {
|
||||
recentActionCooldownEnabled,
|
||||
recentActionCooldownMinutes,
|
||||
localFavoriteFriendsGroups
|
||||
} = storeToRefs(generalSettingsStore);
|
||||
|
||||
const {
|
||||
setRecentActionCooldownEnabled,
|
||||
setRecentActionCooldownMinutes,
|
||||
setLocalFavoriteFriendsGroups
|
||||
} = generalSettingsStore;
|
||||
|
||||
const { favoriteFriendGroups, localFriendFavoriteGroups } = storeToRefs(favoriteStore);
|
||||
</script>
|
||||
@@ -121,44 +121,6 @@
|
||||
</SettingsItem>
|
||||
</SettingsGroup>
|
||||
|
||||
<SettingsGroup>
|
||||
<template #description>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<span class="text-base font-semibold text-foreground">{{ t('view.settings.general.favorites.header') }}</span>
|
||||
<TooltipWrapper side="top" :content="t('view.settings.general.favorites.header_tooltip')">
|
||||
<Info class="size-3 text-muted-foreground cursor-help" />
|
||||
</TooltipWrapper>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<Select
|
||||
:model-value="localFavoriteFriendsGroups"
|
||||
multiple
|
||||
@update:modelValue="setLocalFavoriteFriendsGroups">
|
||||
<SelectTrigger>
|
||||
<SelectValue :placeholder="t('view.settings.general.favorites.group_placeholder')" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem v-for="group in favoriteFriendGroups" :key="group.key" :value="group.key">
|
||||
{{ group.displayName }}
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
<template v-if="localFriendFavoriteGroups.length">
|
||||
<SelectSeparator />
|
||||
<SelectGroup>
|
||||
<SelectItem
|
||||
v-for="group in localFriendFavoriteGroups"
|
||||
:key="'local:' + group"
|
||||
:value="'local:' + group">
|
||||
{{ group }}
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
</template>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</SettingsGroup>
|
||||
|
||||
<SettingsGroup :title="t('view.settings.general.contributors.header')">
|
||||
<div>
|
||||
<img
|
||||
@@ -198,21 +160,11 @@
|
||||
import { computed, defineAsyncComponent, ref } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { Info } from 'lucide-vue-next';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectSeparator,
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
} from '@/components/ui/select';
|
||||
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
|
||||
import { useFavoriteStore, useGeneralSettingsStore, useVRCXUpdaterStore } from '@/stores';
|
||||
import { useGeneralSettingsStore, useVRCXUpdaterStore } from '@/stores';
|
||||
import { links } from '@/shared/constants';
|
||||
import { openExternalLink } from '@/shared/utils';
|
||||
|
||||
@@ -223,15 +175,13 @@
|
||||
|
||||
const generalSettingsStore = useGeneralSettingsStore();
|
||||
const vrcxUpdaterStore = useVRCXUpdaterStore();
|
||||
const favoriteStore = useFavoriteStore();
|
||||
|
||||
const {
|
||||
isStartAtWindowsStartup,
|
||||
isStartAsMinimizedState,
|
||||
isCloseToTray,
|
||||
disableGpuAcceleration,
|
||||
disableVrOverlayGpuAcceleration,
|
||||
localFavoriteFriendsGroups
|
||||
disableVrOverlayGpuAcceleration
|
||||
} = storeToRefs(generalSettingsStore);
|
||||
|
||||
const {
|
||||
@@ -240,12 +190,9 @@
|
||||
setIsCloseToTray,
|
||||
setDisableGpuAcceleration,
|
||||
setDisableVrOverlayGpuAcceleration,
|
||||
setLocalFavoriteFriendsGroups,
|
||||
promptProxySettings
|
||||
} = generalSettingsStore;
|
||||
|
||||
const { favoriteFriendGroups, localFriendFavoriteGroups } = storeToRefs(favoriteStore);
|
||||
|
||||
const { appVersion, autoUpdateVRCX, latestAppVersion, noUpdater } = storeToRefs(vrcxUpdaterStore);
|
||||
const { setAutoUpdateVRCX, checkForVRCXUpdate, showVRCXUpdateDialog, showChangeLogDialog } = vrcxUpdaterStore;
|
||||
|
||||
332
src/views/Settings/components/Tabs/VrTab.vue
Normal file
332
src/views/Settings/components/Tabs/VrTab.vue
Normal file
@@ -0,0 +1,332 @@
|
||||
<template>
|
||||
<div class="flex flex-col gap-10 py-2">
|
||||
<!-- VR Core -->
|
||||
<SettingsGroup :title="t('view.settings.vr.vr_core.header')">
|
||||
<SettingsItem
|
||||
:label="t('view.settings.notifications.notifications.steamvr_notifications.steamvr_overlay')">
|
||||
<Switch
|
||||
:model-value="openVR"
|
||||
@update:modelValue="
|
||||
setOpenVR();
|
||||
saveOpenVROption();
|
||||
" />
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
:label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.start_overlay_with')">
|
||||
<RadioGroup
|
||||
:model-value="openVRAlways ? 'true' : 'false'"
|
||||
:disabled="!openVR"
|
||||
class="gap-2 flex"
|
||||
@update:modelValue="handleOpenVRAlwaysRadio">
|
||||
<div class="flex items-center space-x-2">
|
||||
<RadioGroupItem id="openVRAlways-false" value="false" />
|
||||
<label for="openVRAlways-false">{{ 'VRChat' }}</label>
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<RadioGroupItem id="openVRAlways-true" value="true" />
|
||||
<label for="openVRAlways-true">{{ 'SteamVR' }}</label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</SettingsItem>
|
||||
|
||||
<template v-if="!isLinux">
|
||||
<SettingsItem
|
||||
:label="
|
||||
t('view.settings.notifications.notifications.steamvr_notifications.xsoverlay_notifications')
|
||||
">
|
||||
<Switch
|
||||
:model-value="xsNotifications"
|
||||
@update:modelValue="
|
||||
setXsNotifications();
|
||||
saveOpenVROption();
|
||||
" />
|
||||
</SettingsItem>
|
||||
</template>
|
||||
<template v-else>
|
||||
<SettingsItem
|
||||
:label="
|
||||
t('view.settings.notifications.notifications.steamvr_notifications.wayvr_notifications')
|
||||
">
|
||||
<Switch
|
||||
:model-value="xsNotifications"
|
||||
@update:modelValue="
|
||||
setXsNotifications();
|
||||
saveOpenVROption();
|
||||
" />
|
||||
</SettingsItem>
|
||||
</template>
|
||||
|
||||
<template v-if="!isLinux">
|
||||
<SettingsItem
|
||||
:label="
|
||||
t(
|
||||
'view.settings.notifications.notifications.steamvr_notifications.ovrtoolkit_hud_notifications'
|
||||
)
|
||||
">
|
||||
<Switch
|
||||
:model-value="ovrtHudNotifications"
|
||||
@update:modelValue="
|
||||
setOvrtHudNotifications();
|
||||
saveOpenVROption();
|
||||
" />
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
:label="
|
||||
t(
|
||||
'view.settings.notifications.notifications.steamvr_notifications.ovrtoolkit_wrist_notifications'
|
||||
)
|
||||
">
|
||||
<Switch
|
||||
:model-value="ovrtWristNotifications"
|
||||
@update:modelValue="
|
||||
setOvrtWristNotifications();
|
||||
saveOpenVROption();
|
||||
" />
|
||||
</SettingsItem>
|
||||
</template>
|
||||
</SettingsGroup>
|
||||
|
||||
<!-- VR Notifications -->
|
||||
<SettingsGroup :title="t('view.settings.vr.vr_notifications.header')">
|
||||
<SettingsItem
|
||||
:label="t('view.settings.notifications.notifications.desktop_notifications.when_to_display')">
|
||||
<ToggleGroup
|
||||
type="single"
|
||||
required
|
||||
variant="outline"
|
||||
size="sm"
|
||||
:model-value="overlayToast"
|
||||
:disabled="
|
||||
(!overlayNotifications || !openVR) &&
|
||||
!xsNotifications &&
|
||||
!ovrtHudNotifications &&
|
||||
!ovrtWristNotifications
|
||||
"
|
||||
@update:model-value="
|
||||
setOverlayToast($event);
|
||||
saveOpenVROption();
|
||||
">
|
||||
<ToggleGroupItem value="Never">{{
|
||||
t('view.settings.notifications.notifications.conditions.never')
|
||||
}}</ToggleGroupItem>
|
||||
<ToggleGroupItem value="Game Running">{{
|
||||
t('view.settings.notifications.notifications.conditions.inside_vrchat')
|
||||
}}</ToggleGroupItem>
|
||||
<ToggleGroupItem value="Game Closed">{{
|
||||
t('view.settings.notifications.notifications.conditions.outside_vrchat')
|
||||
}}</ToggleGroupItem>
|
||||
<ToggleGroupItem value="Always">{{
|
||||
t('view.settings.notifications.notifications.conditions.always')
|
||||
}}</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
:label="
|
||||
t('view.settings.notifications.notifications.steamvr_notifications.overlay_notifications')
|
||||
">
|
||||
<Switch
|
||||
:model-value="overlayNotifications"
|
||||
:disabled="!openVR"
|
||||
@update:modelValue="
|
||||
setOverlayNotifications();
|
||||
saveOpenVROption();
|
||||
" />
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
:label="
|
||||
t('view.settings.notifications.notifications.steamvr_notifications.notification_position')
|
||||
">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
:disabled="!overlayNotifications || !openVR"
|
||||
@click="showNotificationPositionDialog"
|
||||
>{{
|
||||
t('view.settings.notifications.notifications.steamvr_notifications.notification_position')
|
||||
}}</Button
|
||||
>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
:label="
|
||||
t('view.settings.notifications.notifications.steamvr_notifications.notification_opacity')
|
||||
">
|
||||
<div class="w-75 max-w-full pt-1">
|
||||
<Slider v-model="notificationOpacityValue" :min="0" :max="100" />
|
||||
</div>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
:label="
|
||||
t('view.settings.notifications.notifications.steamvr_notifications.notification_timeout')
|
||||
">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
:disabled="(!overlayNotifications || !openVR) && !xsNotifications"
|
||||
@click="promptNotificationTimeout"
|
||||
>{{
|
||||
t('view.settings.notifications.notifications.steamvr_notifications.notification_timeout')
|
||||
}}</Button
|
||||
>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
:label="t('view.settings.notifications.notifications.steamvr_notifications.user_images')">
|
||||
<Switch
|
||||
:model-value="imageNotifications"
|
||||
@update:modelValue="
|
||||
setImageNotifications();
|
||||
saveOpenVROption();
|
||||
" />
|
||||
</SettingsItem>
|
||||
</SettingsGroup>
|
||||
|
||||
<!-- Wrist Overlay -->
|
||||
<WristOverlaySettings @open-feed-filters="showWristFeedFiltersDialog" />
|
||||
|
||||
<!-- VR Extras -->
|
||||
<SettingsGroup :title="t('view.settings.vr.vr_extras.header')">
|
||||
<SettingsItem :label="t('view.settings.advanced.advanced.video_progress_pie.header')"
|
||||
:description="t('view.settings.advanced.advanced.video_progress_pie.enable_tooltip')">
|
||||
<Switch
|
||||
:model-value="progressPie"
|
||||
:disabled="!openVR"
|
||||
@update:modelValue="changeYouTubeApi('VRCX_progressPie')" />
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem :label="t('view.settings.advanced.advanced.video_progress_pie.dance_world_only')">
|
||||
<Switch
|
||||
:model-value="progressPieFilter"
|
||||
:disabled="!openVR"
|
||||
@update:modelValue="changeYouTubeApi('VRCX_progressPieFilter')" />
|
||||
</SettingsItem>
|
||||
</SettingsGroup>
|
||||
|
||||
<NotificationPositionDialog v-model:isNotificationPositionDialogVisible="isNotificationPositionDialogVisible" />
|
||||
<FeedFiltersDialog v-model:feedFiltersDialogMode="feedFiltersDialogMode" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { Slider } from '@/components/ui/slider';
|
||||
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
||||
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import {
|
||||
useAdvancedSettingsStore,
|
||||
useNotificationsSettingsStore,
|
||||
useVrStore,
|
||||
useWristOverlaySettingsStore
|
||||
} from '@/stores';
|
||||
|
||||
import FeedFiltersDialog from '../../dialogs/FeedFiltersDialog.vue';
|
||||
import NotificationPositionDialog from '../../dialogs/NotificationPositionDialog.vue';
|
||||
import WristOverlaySettings from '../WristOverlaySettings.vue';
|
||||
import SettingsGroup from '../SettingsGroup.vue';
|
||||
import SettingsItem from '../SettingsItem.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const notificationsSettingsStore = useNotificationsSettingsStore();
|
||||
const advancedSettingsStore = useAdvancedSettingsStore();
|
||||
const wristOverlaySettingsStore = useWristOverlaySettingsStore();
|
||||
const { saveOpenVROption } = useVrStore();
|
||||
const { updateVRLastLocation, updateOpenVR } = useVrStore();
|
||||
|
||||
const {
|
||||
overlayToast,
|
||||
openVR,
|
||||
overlayNotifications,
|
||||
xsNotifications,
|
||||
ovrtHudNotifications,
|
||||
ovrtWristNotifications,
|
||||
imageNotifications
|
||||
} = storeToRefs(notificationsSettingsStore);
|
||||
|
||||
const { notificationOpacity } = storeToRefs(advancedSettingsStore);
|
||||
|
||||
const { openVRAlways } = storeToRefs(wristOverlaySettingsStore);
|
||||
const { setOpenVRAlways } = wristOverlaySettingsStore;
|
||||
|
||||
const {
|
||||
progressPie,
|
||||
progressPieFilter
|
||||
} = storeToRefs(advancedSettingsStore);
|
||||
|
||||
const {
|
||||
setOverlayToast,
|
||||
setOpenVR,
|
||||
setOverlayNotifications,
|
||||
setXsNotifications,
|
||||
setOvrtHudNotifications,
|
||||
setOvrtWristNotifications,
|
||||
setImageNotifications,
|
||||
promptNotificationTimeout
|
||||
} = notificationsSettingsStore;
|
||||
|
||||
const { setNotificationOpacity } = advancedSettingsStore;
|
||||
|
||||
const isNotificationPositionDialogVisible = ref(false);
|
||||
const feedFiltersDialogMode = ref('');
|
||||
const isLinux = computed(() => LINUX);
|
||||
|
||||
const notificationOpacityValue = computed({
|
||||
get: () => [notificationOpacity.value],
|
||||
set: (value) => {
|
||||
const next = value?.[0];
|
||||
if (typeof next === 'number') {
|
||||
setNotificationOpacity(next);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function showNotificationPositionDialog() {
|
||||
isNotificationPositionDialogVisible.value = true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function showWristFeedFiltersDialog() {
|
||||
feedFiltersDialogMode.value = 'wrist';
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
function handleOpenVRAlwaysRadio(value) {
|
||||
const nextValue = value === 'true';
|
||||
if (nextValue !== openVRAlways.value) {
|
||||
setOpenVRAlways();
|
||||
saveOpenVROption();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param configKey
|
||||
*/
|
||||
async function changeYouTubeApi(configKey = '') {
|
||||
if (configKey === 'VRCX_progressPie') {
|
||||
advancedSettingsStore.setProgressPie();
|
||||
} else if (configKey === 'VRCX_progressPieFilter') {
|
||||
advancedSettingsStore.setProgressPieFilter();
|
||||
}
|
||||
updateVRLastLocation();
|
||||
updateOpenVR();
|
||||
}
|
||||
</script>
|
||||
@@ -1,19 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<WristOverlaySettings @open-feed-filters="showWristFeedFiltersDialog" />
|
||||
<FeedFiltersDialog v-model:feedFiltersDialogMode="feedFiltersDialogMode" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import FeedFiltersDialog from '../../dialogs/FeedFiltersDialog.vue';
|
||||
import WristOverlaySettings from '../WristOverlaySettings.vue';
|
||||
|
||||
const feedFiltersDialogMode = ref('');
|
||||
|
||||
function showWristFeedFiltersDialog() {
|
||||
feedFiltersDialogMode.value = 'wrist';
|
||||
}
|
||||
</script>
|
||||
@@ -1,99 +0,0 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { mount } from '@vue/test-utils';
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
discordStore: {
|
||||
setDiscordActive: vi.fn(),
|
||||
setDiscordInstance: vi.fn(),
|
||||
setDiscordHideInvite: vi.fn(),
|
||||
setDiscordJoinButton: vi.fn(),
|
||||
setDiscordHideImage: vi.fn(),
|
||||
setDiscordShowPlatform: vi.fn(),
|
||||
setDiscordWorldIntegration: vi.fn(),
|
||||
setDiscordWorldNameAsDiscordStatus: vi.fn(),
|
||||
saveDiscordOption: vi.fn(),
|
||||
discordActive: { __v_isRef: true, value: true },
|
||||
discordInstance: { __v_isRef: true, value: true },
|
||||
discordHideInvite: { __v_isRef: true, value: false },
|
||||
discordJoinButton: { __v_isRef: true, value: true },
|
||||
discordHideImage: { __v_isRef: true, value: false },
|
||||
discordShowPlatform: { __v_isRef: true, value: true },
|
||||
discordWorldIntegration: { __v_isRef: true, value: true },
|
||||
discordWorldNameAsDiscordStatus: { __v_isRef: true, value: false }
|
||||
},
|
||||
showVRChatConfig: vi.fn()
|
||||
}));
|
||||
|
||||
vi.mock('pinia', async (i) => ({ ...(await i()), storeToRefs: (s) => s }));
|
||||
vi.mock('vue-i18n', () => ({ useI18n: () => ({ t: (k) => k }) }));
|
||||
vi.mock('../../../../../stores', () => ({
|
||||
useDiscordPresenceSettingsStore: () => mocks.discordStore,
|
||||
useAdvancedSettingsStore: () => ({
|
||||
showVRChatConfig: (...a) => mocks.showVRChatConfig(...a)
|
||||
})
|
||||
}));
|
||||
|
||||
vi.mock('@/components/ui/switch', () => ({
|
||||
Switch: {
|
||||
props: ['modelValue', 'disabled'],
|
||||
emits: ['update:modelValue'],
|
||||
template:
|
||||
'<button data-testid="switch" :data-disabled="disabled || undefined" @click="$emit(\'update:modelValue\', !modelValue)"><slot /></button>'
|
||||
}
|
||||
}));
|
||||
|
||||
vi.mock('../../SettingsGroup.vue', () => ({
|
||||
default: { template: '<div><slot /><slot name="description" /></div>' }
|
||||
}));
|
||||
|
||||
vi.mock('../../SettingsItem.vue', () => ({
|
||||
default: {
|
||||
props: ['label', 'description'],
|
||||
template: '<div :data-label="label"><slot /></div>'
|
||||
}
|
||||
}));
|
||||
|
||||
import DiscordPresenceTab from '../DiscordPresenceTab.vue';
|
||||
|
||||
describe('DiscordPresenceTab.vue', () => {
|
||||
beforeEach(() => {
|
||||
mocks.discordStore.discordActive.value = true;
|
||||
mocks.discordStore.discordInstance.value = true;
|
||||
mocks.discordStore.setDiscordActive.mockClear();
|
||||
mocks.discordStore.saveDiscordOption.mockClear();
|
||||
mocks.showVRChatConfig.mockClear();
|
||||
});
|
||||
|
||||
it('opens VRChat config and handles switch changes', async () => {
|
||||
const wrapper = mount(DiscordPresenceTab);
|
||||
|
||||
const tooltipRow = wrapper
|
||||
.findAll('p')
|
||||
.find((node) =>
|
||||
node
|
||||
.text()
|
||||
.includes(
|
||||
'view.settings.discord_presence.discord_presence.enable_tooltip'
|
||||
)
|
||||
);
|
||||
expect(tooltipRow).toBeTruthy();
|
||||
await tooltipRow.trigger('click');
|
||||
|
||||
expect(mocks.showVRChatConfig).toHaveBeenCalledTimes(1);
|
||||
|
||||
const switches = wrapper.findAll('[data-testid="switch"]');
|
||||
await switches[0].trigger('click');
|
||||
expect(mocks.discordStore.setDiscordActive).toHaveBeenCalledTimes(1);
|
||||
expect(mocks.discordStore.saveDiscordOption).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('passes disabled state to dependent switches when discord is disabled', () => {
|
||||
mocks.discordStore.discordActive.value = false;
|
||||
const wrapper = mount(DiscordPresenceTab);
|
||||
|
||||
const switches = wrapper.findAll('[data-testid="switch"]');
|
||||
const worldIntegrationSwitch = switches[1];
|
||||
|
||||
expect(worldIntegrationSwitch?.attributes('data-disabled')).toBe('true');
|
||||
});
|
||||
});
|
||||
@@ -1,36 +0,0 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { mount } from '@vue/test-utils';
|
||||
|
||||
vi.mock('../../WristOverlaySettings.vue', () => ({
|
||||
default: {
|
||||
emits: ['open-feed-filters'],
|
||||
template:
|
||||
'<button data-testid="open-filters" @click="$emit(\'open-feed-filters\')">open</button>'
|
||||
}
|
||||
}));
|
||||
|
||||
vi.mock('../../../dialogs/FeedFiltersDialog.vue', () => ({
|
||||
default: {
|
||||
props: ['feedFiltersDialogMode'],
|
||||
template:
|
||||
'<div data-testid="feed-dialog" :data-mode="feedFiltersDialogMode" />'
|
||||
}
|
||||
}));
|
||||
|
||||
import WristOverlayTab from '../WristOverlayTab.vue';
|
||||
|
||||
describe('WristOverlayTab.vue', () => {
|
||||
it('sets feed dialog mode to wrist when child emits open-feed-filters', async () => {
|
||||
const wrapper = mount(WristOverlayTab);
|
||||
|
||||
expect(
|
||||
wrapper.get('[data-testid="feed-dialog"]').attributes('data-mode')
|
||||
).toBe('');
|
||||
|
||||
await wrapper.get('[data-testid="open-filters"]').trigger('click');
|
||||
|
||||
expect(
|
||||
wrapper.get('[data-testid="feed-dialog"]').attributes('data-mode')
|
||||
).toBe('wrist');
|
||||
});
|
||||
});
|
||||
@@ -17,15 +17,6 @@
|
||||
>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem :label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.steamvr_overlay')">
|
||||
<Switch
|
||||
:model-value="openVR"
|
||||
@update:modelValue="
|
||||
setOpenVR();
|
||||
saveOpenVROption();
|
||||
" />
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem :label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.wrist_feed_overlay')">
|
||||
<Switch
|
||||
:model-value="overlayWrist"
|
||||
@@ -46,24 +37,6 @@
|
||||
" />
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
:label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.start_overlay_with')">
|
||||
<RadioGroup
|
||||
:model-value="openVRAlways ? 'true' : 'false'"
|
||||
:disabled="!openVR"
|
||||
class="gap-2 flex"
|
||||
@update:modelValue="handleOpenVRAlwaysRadio">
|
||||
<div class="flex items-center space-x-2">
|
||||
<RadioGroupItem id="openVRAlways-false" value="false" />
|
||||
<label for="openVRAlways-false">{{ 'VRChat' }}</label>
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<RadioGroupItem id="openVRAlways-true" value="true" />
|
||||
<label for="openVRAlways-true">{{ 'SteamVR' }}</label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem :label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.overlay_button')">
|
||||
<RadioGroup
|
||||
:model-value="overlaybutton ? 'true' : 'false'"
|
||||
@@ -196,10 +169,10 @@
|
||||
|
||||
const { openVR } = storeToRefs(notificationsSettingsStore);
|
||||
|
||||
|
||||
const {
|
||||
overlayWrist,
|
||||
hidePrivateFromFeed,
|
||||
openVRAlways,
|
||||
overlaybutton,
|
||||
overlayHand,
|
||||
vrBackgroundEnabled,
|
||||
@@ -210,12 +183,9 @@
|
||||
pcUptimeOnFeed
|
||||
} = storeToRefs(wristOverlaySettingsStore);
|
||||
|
||||
const { setOpenVR } = notificationsSettingsStore;
|
||||
|
||||
const {
|
||||
setOverlayWrist,
|
||||
setHidePrivateFromFeed,
|
||||
setOpenVRAlways,
|
||||
setOverlaybutton,
|
||||
setOverlayHand,
|
||||
setVrBackgroundEnabled,
|
||||
@@ -228,18 +198,6 @@
|
||||
|
||||
const { saveOpenVROption } = useVrStore();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
function handleOpenVRAlwaysRadio(value) {
|
||||
const nextValue = value === 'true';
|
||||
if (nextValue !== openVRAlways.value) {
|
||||
setOpenVRAlways();
|
||||
saveOpenVROption();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value
|
||||
|
||||
@@ -9,7 +9,6 @@ const mocks = vi.hoisted(() => ({
|
||||
wristStore: {
|
||||
overlayWrist: { value: true },
|
||||
hidePrivateFromFeed: { value: false },
|
||||
openVRAlways: { value: false },
|
||||
overlaybutton: { value: false },
|
||||
overlayHand: { value: '1' },
|
||||
vrBackgroundEnabled: { value: false },
|
||||
@@ -20,7 +19,6 @@ const mocks = vi.hoisted(() => ({
|
||||
pcUptimeOnFeed: { value: false },
|
||||
setOverlayWrist: vi.fn(),
|
||||
setHidePrivateFromFeed: vi.fn(),
|
||||
setOpenVRAlways: vi.fn(),
|
||||
setOverlaybutton: vi.fn(),
|
||||
setOverlayHand: vi.fn(),
|
||||
setVrBackgroundEnabled: vi.fn(),
|
||||
@@ -95,10 +93,9 @@ describe('WristOverlaySettings.vue', () => {
|
||||
beforeEach(() => {
|
||||
mocks.notificationsStore.openVR.value = true;
|
||||
mocks.wristStore.overlayWrist.value = true;
|
||||
mocks.wristStore.openVRAlways.value = false;
|
||||
mocks.wristStore.overlaybutton.value = false;
|
||||
mocks.notificationsStore.setOpenVR.mockClear();
|
||||
mocks.wristStore.setOpenVRAlways.mockClear();
|
||||
mocks.wristStore.setOverlayWrist.mockClear();
|
||||
mocks.wristStore.setHidePrivateFromFeed.mockClear();
|
||||
mocks.wristStore.setOverlaybutton.mockClear();
|
||||
mocks.wristStore.setOverlayHand.mockClear();
|
||||
mocks.saveOpenVROption.mockClear();
|
||||
@@ -107,32 +104,33 @@ describe('WristOverlaySettings.vue', () => {
|
||||
it('emits open-feed-filters and handles switch/radio/toggle updates', async () => {
|
||||
const wrapper = mount(WristOverlaySettings);
|
||||
|
||||
// Feed filters button emits event
|
||||
await wrapper.get('[data-testid="filters-btn"]').trigger('click');
|
||||
expect(wrapper.emitted('open-feed-filters')).toBeTruthy();
|
||||
|
||||
// First switch is now overlayWrist (SteamVR Overlay moved to VrTab)
|
||||
const switches = wrapper.findAll('[data-testid="switch"]');
|
||||
await switches[0].trigger('click');
|
||||
expect(mocks.notificationsStore.setOpenVR).toHaveBeenCalledTimes(1);
|
||||
expect(mocks.wristStore.setOverlayWrist).toHaveBeenCalledTimes(1);
|
||||
expect(mocks.saveOpenVROption).toHaveBeenCalled();
|
||||
|
||||
// First (and only) radio group is now overlay button (Start Overlay With moved to VrTab)
|
||||
const radioGroups = wrapper.findAll('[data-testid="radio-group"]');
|
||||
await radioGroups[0].get('[data-testid="radio-true"]').trigger('click');
|
||||
expect(mocks.wristStore.setOpenVRAlways).toHaveBeenCalledTimes(1);
|
||||
|
||||
await radioGroups[1].get('[data-testid="radio-true"]').trigger('click');
|
||||
expect(mocks.wristStore.setOverlaybutton).toHaveBeenCalledTimes(1);
|
||||
|
||||
// Toggle group for overlay hand
|
||||
await wrapper.get('[data-testid="toggle-right"]').trigger('click');
|
||||
expect(mocks.wristStore.setOverlayHand).toHaveBeenCalledWith('2');
|
||||
});
|
||||
|
||||
it('does not toggle openVRAlways when the value is unchanged', async () => {
|
||||
mocks.wristStore.openVRAlways.value = true;
|
||||
it('does not toggle overlaybutton when the value is unchanged', async () => {
|
||||
mocks.wristStore.overlaybutton.value = true;
|
||||
const wrapper = mount(WristOverlaySettings);
|
||||
|
||||
const firstRadio = wrapper.findAll('[data-testid="radio-group"]')[0];
|
||||
await firstRadio.get('[data-testid="radio-true"]').trigger('click');
|
||||
|
||||
expect(mocks.wristStore.setOpenVRAlways).not.toHaveBeenCalled();
|
||||
expect(mocks.wristStore.setOverlaybutton).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user