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" />
+