diff --git a/src/localization/en.json b/src/localization/en.json index cf574f22..c508633f 100644 --- a/src/localization/en.json +++ b/src/localization/en.json @@ -547,7 +547,9 @@ "logging": { "header": "Logging", "resource_load": "Log Udon string/image load", - "empty_avatar": "Log avatars in feed without names" + "empty_avatar": "Log avatars in feed without names", + "auto_login_delay": "Auto-login delay", + "auto_login_delay_button": "Set delay seconds" }, "automation": { "header": "Automation", @@ -2051,6 +2053,7 @@ "traveling": "Traveling" }, "message": { + "auto_login_delay_countdown": "Auto-login in {seconds}s...", "vrcx_updater": { "failed": "Failed to check for update, {message}", "failed_install": "Failed to install update", @@ -2370,6 +2373,11 @@ "save": "Save", "input_error": "Valid number is required" }, + "auto_login_delay": { + "header": "Auto-login delay", + "description": "Enter delay in seconds (0 to disable, max 10).", + "input_error": "Please enter a whole number from 0 to 10." + }, "proxy_settings": { "header": "Proxy Settings", "description": "Enter proxy server address and port", diff --git a/src/stores/auth.js b/src/stores/auth.js index 0b003d5c..b0b95435 100644 --- a/src/stores/auth.js +++ b/src/stores/auth.js @@ -12,6 +12,7 @@ import { database } from '../service/database'; import { escapeTag } from '../shared/utils'; import { request } from '../service/request'; import { useAdvancedSettingsStore } from './settings/advanced'; +import { useGeneralSettingsStore } from './settings/general'; import { useModalStore } from './modal'; import { useNotificationStore } from './notification'; import { useUpdateLoopStore } from './updateLoop'; @@ -22,8 +23,11 @@ import configRepository from '../service/config'; import security from '../service/security'; import webApiService from '../service/webapi'; +import * as workerTimers from 'worker-timers'; + export const useAuthStore = defineStore('Auth', () => { const advancedSettingsStore = useAdvancedSettingsStore(); + const generalSettingsStore = useGeneralSettingsStore(); const notificationStore = useNotificationStore(); const userStore = useUserStore(); const updateLoopStore = useUpdateLoopStore(); @@ -187,6 +191,7 @@ export const useAuthStore = defineStore('Auth', () => { AppDebug.endpointDomain = user.loginParams.endpoint; AppDebug.websocketDomain = user.loginParams.websocket; } + await applyAutoLoginDelay(); // login at startup loginForm.value.loading = true; authRequest @@ -847,6 +852,34 @@ export const useAuthStore = defineStore('Auth', () => { }); } + async function applyAutoLoginDelay() { + if (!generalSettingsStore.autoLoginDelayEnabled) { + return; + } + const seconds = generalSettingsStore.autoLoginDelaySeconds; + if (!seconds || seconds <= 0) { + return; + } + let toastId = null; + for (let remaining = seconds; remaining > 0; remaining--) { + if (toastId) { + toast.dismiss(toastId); + } + toastId = toast.info( + t('message.auto_login_delay_countdown', { + seconds: remaining + }), + { duration: Infinity } + ); + await new Promise((resolve) => { + workerTimers.setTimeout(resolve, 1000); + }); + } + if (toastId) { + toast.dismiss(toastId); + } + } + async function loginComplete() { await database.initUserTables(userStore.currentUser.id); watchState.isLoggedIn = true; diff --git a/src/stores/settings/general.js b/src/stores/settings/general.js index ae934b67..4a15f427 100644 --- a/src/stores/settings/general.js +++ b/src/stores/settings/general.js @@ -28,6 +28,8 @@ export const useGeneralSettingsStore = defineStore('GeneralSettings', () => { const udonExceptionLogging = ref(false); const logResourceLoad = ref(false); const logEmptyAvatars = ref(false); + const autoLoginDelayEnabled = ref(false); + const autoLoginDelaySeconds = ref(0); const autoStateChangeEnabled = ref(false); const autoStateChangeAloneStatus = ref('join me'); const autoStateChangeCompanyStatus = ref('busy'); @@ -47,6 +49,8 @@ export const useGeneralSettingsStore = defineStore('GeneralSettings', () => { udonExceptionLoggingConfig, logResourceLoadConfig, logEmptyAvatarsConfig, + autoLoginDelayEnabledConfig, + autoLoginDelaySecondsConfig, autoStateChangeEnabledConfig, autoStateChangeAloneStatusConfig, autoStateChangeCompanyStatusConfig, @@ -64,6 +68,8 @@ export const useGeneralSettingsStore = defineStore('GeneralSettings', () => { configRepository.getBool('VRCX_udonExceptionLogging', false), configRepository.getBool('VRCX_logResourceLoad', false), configRepository.getBool('VRCX_logEmptyAvatars', false), + configRepository.getBool('VRCX_autoLoginDelayEnabled', false), + configRepository.getInt('VRCX_autoLoginDelaySeconds', 0), configRepository.getBool('VRCX_autoStateChangeEnabled', false), configRepository.getString( 'VRCX_autoStateChangeAloneStatus', @@ -107,6 +113,8 @@ export const useGeneralSettingsStore = defineStore('GeneralSettings', () => { udonExceptionLogging.value = udonExceptionLoggingConfig; logResourceLoad.value = logResourceLoadConfig; logEmptyAvatars.value = logEmptyAvatarsConfig; + autoLoginDelayEnabled.value = autoLoginDelayEnabledConfig; + autoLoginDelaySeconds.value = autoLoginDelaySecondsConfig; autoStateChangeEnabled.value = autoStateChangeEnabledConfig; autoStateChangeAloneStatus.value = autoStateChangeAloneStatusConfig; autoStateChangeCompanyStatus.value = autoStateChangeCompanyStatusConfig; @@ -179,6 +187,40 @@ export const useGeneralSettingsStore = defineStore('GeneralSettings', () => { logEmptyAvatars.value = !logEmptyAvatars.value; configRepository.setBool('VRCX_logEmptyAvatars', logEmptyAvatars.value); } + function setAutoLoginDelayEnabled() { + autoLoginDelayEnabled.value = !autoLoginDelayEnabled.value; + configRepository.setBool( + 'VRCX_autoLoginDelayEnabled', + autoLoginDelayEnabled.value + ); + } + function setAutoLoginDelaySeconds(value) { + const parsed = parseInt(value, 10); + autoLoginDelaySeconds.value = Number.isNaN(parsed) + ? 0 + : Math.min(10, Math.max(0, parsed)); + configRepository.setInt( + 'VRCX_autoLoginDelaySeconds', + autoLoginDelaySeconds.value + ); + } + function promptAutoLoginDelaySeconds() { + modalStore + .prompt({ + title: t('prompt.auto_login_delay.header'), + description: t('prompt.auto_login_delay.description'), + inputValue: String(autoLoginDelaySeconds.value), + pattern: /^(10|[0-9])$/, + errorMessage: t('prompt.auto_login_delay.input_error') + }) + .then(({ ok, value }) => { + if (!ok) return; + setAutoLoginDelaySeconds(value); + }) + .catch((err) => { + console.error(err); + }); + } function setAutoStateChangeEnabled() { autoStateChangeEnabled.value = !autoStateChangeEnabled.value; configRepository.setBool( @@ -285,6 +327,8 @@ export const useGeneralSettingsStore = defineStore('GeneralSettings', () => { udonExceptionLogging, logResourceLoad, logEmptyAvatars, + autoLoginDelayEnabled, + autoLoginDelaySeconds, autoStateChangeEnabled, autoStateChangeAloneStatus, autoStateChangeCompanyStatus, @@ -301,6 +345,8 @@ export const useGeneralSettingsStore = defineStore('GeneralSettings', () => { setUdonExceptionLogging, setLogResourceLoad, setLogEmptyAvatars, + setAutoLoginDelayEnabled, + promptAutoLoginDelaySeconds, setAutoStateChangeEnabled, setAutoStateChangeAloneStatus, setAutoStateChangeCompanyStatus, diff --git a/src/views/Settings/components/Tabs/GeneralTab.vue b/src/views/Settings/components/Tabs/GeneralTab.vue index 65c40cc4..90da802a 100644 --- a/src/views/Settings/components/Tabs/GeneralTab.vue +++ b/src/views/Settings/components/Tabs/GeneralTab.vue @@ -139,6 +139,15 @@ :label="t('view.settings.general.logging.empty_avatar')" :value="logEmptyAvatars" @change="setLogEmptyAvatars" /> + +
+ +
{{ t('view.settings.general.automation.header') }} @@ -333,6 +342,7 @@ udonExceptionLogging, logResourceLoad, logEmptyAvatars, + autoLoginDelayEnabled, autoStateChangeEnabled, autoStateChangeAloneStatus, autoStateChangeCompanyStatus, @@ -350,6 +360,8 @@ setUdonExceptionLogging, setLogResourceLoad, setLogEmptyAvatars, + setAutoLoginDelayEnabled, + promptAutoLoginDelaySeconds, setAutoStateChangeEnabled, setAutoStateChangeAloneStatus, setAutoStateChangeCompanyStatus,