mirror of
https://github.com/vrcx-team/VRCX.git
synced 2026-04-06 00:32:02 +02:00
login settings dialog
This commit is contained in:
@@ -114,6 +114,7 @@
|
||||
"forgotPassword": "Forgot Password?",
|
||||
"updater": "Updater",
|
||||
"proxy_settings": "Proxy settings",
|
||||
"settings": "Settings",
|
||||
"language": "Language",
|
||||
"field": {
|
||||
"username": "Username or Email",
|
||||
|
||||
121
src/views/Login/Dialog/LoginSettingsDialog.vue
Normal file
121
src/views/Login/Dialog/LoginSettingsDialog.vue
Normal file
@@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<Dialog v-model:open="open">
|
||||
<DialogTrigger as-child>
|
||||
<TooltipWrapper side="top" :content="t('view.login.settings')">
|
||||
<Button class="rounded-full mr-2 text-xs" size="icon-sm" variant="ghost"><Settings /></Button>
|
||||
</TooltipWrapper>
|
||||
</DialogTrigger>
|
||||
<DialogContent class="max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{{ t('view.login.settings') }}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<FieldGroup class="gap-3">
|
||||
<Field>
|
||||
<FieldLabel for="login-settings-proxy">{{ t('view.login.proxy_settings') }}</FieldLabel>
|
||||
<FieldContent>
|
||||
<InputGroupField
|
||||
id="login-settings-proxy"
|
||||
v-model="proxyServerLocal"
|
||||
autocomplete="off"
|
||||
name="proxy"
|
||||
:placeholder="t('prompt.proxy_settings.description')"
|
||||
clearable />
|
||||
</FieldContent>
|
||||
</Field>
|
||||
<label class="inline-flex items-center gap-2 text-sm">
|
||||
<Checkbox v-model="enableCustomEndpoint" @update:modelValue="handleCustomEndpointToggle" />
|
||||
<span>{{ t('view.login.field.devEndpoint') }}</span>
|
||||
</label>
|
||||
<template v-if="enableCustomEndpoint">
|
||||
<Field>
|
||||
<FieldLabel for="login-settings-endpoint">{{ t('view.login.field.endpoint') }}</FieldLabel>
|
||||
<FieldContent>
|
||||
<InputGroupField
|
||||
id="login-settings-endpoint"
|
||||
v-model="loginForm.endpoint"
|
||||
autocomplete="off"
|
||||
name="endpoint"
|
||||
:placeholder="AppDebug.endpointDomainVrchat"
|
||||
clearable />
|
||||
</FieldContent>
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel for="login-settings-websocket">{{ t('view.login.field.websocket') }}</FieldLabel>
|
||||
<FieldContent>
|
||||
<InputGroupField
|
||||
id="login-settings-websocket"
|
||||
v-model="loginForm.websocket"
|
||||
autocomplete="off"
|
||||
name="websocket"
|
||||
:placeholder="AppDebug.websocketDomainVrchat"
|
||||
clearable />
|
||||
</FieldContent>
|
||||
</Field>
|
||||
</template>
|
||||
</FieldGroup>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" @click="saveAndRestart">{{ t('prompt.proxy_settings.restart') }}</Button>
|
||||
<Button @click="saveAndClose">{{ t('prompt.proxy_settings.close') }}</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger
|
||||
} from '@/components/ui/dialog';
|
||||
import { Field, FieldContent, FieldGroup, FieldLabel } from '@/components/ui/field';
|
||||
import { ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { InputGroupField } from '@/components/ui/input-group';
|
||||
import { Settings } from 'lucide-vue-next';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { useAuthStore, useVRCXUpdaterStore, useVrcxStore } from '../../../stores';
|
||||
import { AppDebug } from '../../../service/appConfig';
|
||||
|
||||
const { loginForm, enableCustomEndpoint } = storeToRefs(useAuthStore());
|
||||
const { toggleCustomEndpoint } = useAuthStore();
|
||||
const vrcxStore = useVrcxStore();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const open = ref(false);
|
||||
const proxyServerLocal = ref('');
|
||||
|
||||
watch(open, (isOpen) => {
|
||||
if (isOpen) {
|
||||
proxyServerLocal.value = vrcxStore.proxyServer || '';
|
||||
}
|
||||
});
|
||||
|
||||
async function handleCustomEndpointToggle() {
|
||||
await toggleCustomEndpoint();
|
||||
}
|
||||
|
||||
async function saveProxy() {
|
||||
vrcxStore.proxyServer = proxyServerLocal.value;
|
||||
await VRCXStorage.Set('VRCX_ProxyServer', vrcxStore.proxyServer);
|
||||
await VRCXStorage.Save();
|
||||
}
|
||||
|
||||
async function saveAndRestart() {
|
||||
await saveProxy();
|
||||
open.value = false;
|
||||
const { restartVRCX } = useVRCXUpdaterStore();
|
||||
restartVRCX(false);
|
||||
}
|
||||
|
||||
async function saveAndClose() {
|
||||
await saveProxy();
|
||||
open.value = false;
|
||||
}
|
||||
</script>
|
||||
@@ -1,16 +1,12 @@
|
||||
<template>
|
||||
<div class="x-login-container">
|
||||
<div style="position: absolute; top: 0; left: 0; margin: 5px">
|
||||
<LoginSettingsDialog />
|
||||
<TooltipWrapper v-if="!noUpdater" side="top" :content="t('view.login.updater')">
|
||||
<Button class="rounded-full mr-2 text-xs" size="icon-sm" variant="ghost" @click="showVRCXUpdateDialog"
|
||||
><ArrowBigDownDash
|
||||
/></Button>
|
||||
</TooltipWrapper>
|
||||
<TooltipWrapper side="top" :content="t('view.login.proxy_settings')">
|
||||
<Button class="rounded-full mr-2 text-xs" size="icon-sm" variant="ghost" @click="promptProxySettings"
|
||||
><Earth
|
||||
/></Button>
|
||||
</TooltipWrapper>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger as-child>
|
||||
<TooltipWrapper side="top" :content="t('view.login.language')">
|
||||
@@ -82,52 +78,7 @@
|
||||
<Checkbox v-model="loginForm.saveCredentials" />
|
||||
<span>{{ t('view.login.field.saveCredentials') }}</span>
|
||||
</label>
|
||||
<label class="inline-flex items-center gap-2 text-sm" style="margin-top: 10px">
|
||||
<Checkbox v-model="enableCustomEndpoint" @update:modelValue="handleCustomEndpointToggle" />
|
||||
<span>{{ t('view.login.field.devEndpoint') }}</span>
|
||||
</label>
|
||||
<FieldGroup v-if="enableCustomEndpoint" class="mt-3 gap-3">
|
||||
<VeeField v-slot="{ field, errors }" name="endpoint">
|
||||
<Field :data-invalid="!!errors.length">
|
||||
<FieldLabel for="login-form-endpoint">
|
||||
{{ t('view.login.field.endpoint') }}
|
||||
</FieldLabel>
|
||||
<FieldContent>
|
||||
<InputGroupField
|
||||
id="login-form-endpoint"
|
||||
:model-value="field.value"
|
||||
autocomplete="off"
|
||||
name="endpoint"
|
||||
:placeholder="AppDebug.endpointDomainVrchat"
|
||||
:aria-invalid="!!errors.length"
|
||||
clearable
|
||||
@update:modelValue="field.onChange"
|
||||
@blur="field.onBlur" />
|
||||
<FieldError v-if="errors.length" :errors="errors" />
|
||||
</FieldContent>
|
||||
</Field>
|
||||
</VeeField>
|
||||
<VeeField v-slot="{ field, errors }" name="websocket">
|
||||
<Field :data-invalid="!!errors.length">
|
||||
<FieldLabel for="login-form-websocket">
|
||||
{{ t('view.login.field.websocket') }}
|
||||
</FieldLabel>
|
||||
<FieldContent>
|
||||
<InputGroupField
|
||||
id="login-form-websocket"
|
||||
:model-value="field.value"
|
||||
autocomplete="off"
|
||||
name="websocket"
|
||||
:placeholder="AppDebug.websocketDomainVrchat"
|
||||
:aria-invalid="!!errors.length"
|
||||
clearable
|
||||
@update:modelValue="field.onChange"
|
||||
@blur="field.onBlur" />
|
||||
<FieldError v-if="errors.length" :errors="errors" />
|
||||
</FieldContent>
|
||||
</Field>
|
||||
</VeeField>
|
||||
</FieldGroup>
|
||||
|
||||
<Field class="mt-2">
|
||||
<Button type="submit" size="lg" style="width: 100%">{{ t('view.login.login') }}</Button>
|
||||
</Field>
|
||||
@@ -203,7 +154,6 @@
|
||||
|
||||
<script setup>
|
||||
import { Field, FieldContent, FieldError, FieldGroup, FieldLabel } from '@/components/ui/field';
|
||||
import { ArrowBigDownDash, Earth, Languages, Minus } from 'lucide-vue-next';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
@@ -211,6 +161,7 @@
|
||||
DropdownMenuTrigger
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { onBeforeMount, onBeforeUnmount, ref, watch } from 'vue';
|
||||
import { ArrowBigDownDash, Languages, Minus } from 'lucide-vue-next';
|
||||
import { Field as VeeField, useForm } from 'vee-validate';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -221,23 +172,18 @@
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { z } from 'zod';
|
||||
|
||||
import {
|
||||
useAppearanceSettingsStore,
|
||||
useAuthStore,
|
||||
useGeneralSettingsStore,
|
||||
useVRCXUpdaterStore
|
||||
} from '../../stores';
|
||||
import { useAppearanceSettingsStore, useAuthStore, useVRCXUpdaterStore } from '../../stores';
|
||||
import { getLanguageName, languageCodes } from '../../localization';
|
||||
import { openExternalLink, userImage } from '../../shared/utils';
|
||||
import { AppDebug } from '../../service/appConfig';
|
||||
import { watchState } from '../../service/watchState';
|
||||
|
||||
import LoginSettingsDialog from './Dialog/LoginSettingsDialog.vue';
|
||||
|
||||
const { showVRCXUpdateDialog } = useVRCXUpdaterStore();
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const { loginForm, enableCustomEndpoint } = storeToRefs(useAuthStore());
|
||||
const { toggleCustomEndpoint, relogin, deleteSavedLogin, login, getAllSavedCredentials } = useAuthStore();
|
||||
const { promptProxySettings } = useGeneralSettingsStore();
|
||||
const { loginForm } = storeToRefs(useAuthStore());
|
||||
const { relogin, deleteSavedLogin, login, getAllSavedCredentials } = useAuthStore();
|
||||
const { noUpdater } = storeToRefs(useVRCXUpdaterStore());
|
||||
|
||||
const appearanceSettingsStore = useAppearanceSettingsStore();
|
||||
@@ -252,19 +198,15 @@
|
||||
const formSchema = toTypedSchema(
|
||||
z.object({
|
||||
username: z.string().min(1, requiredMessage),
|
||||
password: z.string().min(1, requiredMessage),
|
||||
endpoint: z.string().optional(),
|
||||
websocket: z.string().optional()
|
||||
password: z.string().min(1, requiredMessage)
|
||||
})
|
||||
);
|
||||
|
||||
const { handleSubmit, resetForm, setValues, values } = useForm({
|
||||
const { handleSubmit, resetForm, values } = useForm({
|
||||
validationSchema: formSchema,
|
||||
initialValues: {
|
||||
username: loginForm.value.username,
|
||||
password: loginForm.value.password,
|
||||
endpoint: loginForm.value.endpoint,
|
||||
websocket: loginForm.value.websocket
|
||||
password: loginForm.value.password
|
||||
}
|
||||
});
|
||||
|
||||
@@ -281,21 +223,10 @@
|
||||
const onSubmit = handleSubmit(async (formValues) => {
|
||||
loginForm.value.username = formValues.username ?? '';
|
||||
loginForm.value.password = formValues.password ?? '';
|
||||
loginForm.value.endpoint = formValues.endpoint ?? '';
|
||||
loginForm.value.websocket = formValues.websocket ?? '';
|
||||
await login();
|
||||
await updateSavedCredentials();
|
||||
});
|
||||
|
||||
async function handleCustomEndpointToggle() {
|
||||
await toggleCustomEndpoint();
|
||||
setValues({
|
||||
...values,
|
||||
endpoint: loginForm.value.endpoint,
|
||||
websocket: loginForm.value.websocket
|
||||
});
|
||||
}
|
||||
|
||||
async function updateSavedCredentials() {
|
||||
if (watchState.isLoggedIn) {
|
||||
return;
|
||||
@@ -337,9 +268,7 @@
|
||||
resetForm({
|
||||
values: {
|
||||
username: '',
|
||||
password: '',
|
||||
endpoint: '',
|
||||
websocket: ''
|
||||
password: ''
|
||||
}
|
||||
});
|
||||
loginForm.value.username = '';
|
||||
@@ -354,8 +283,6 @@
|
||||
(formValues) => {
|
||||
loginForm.value.username = formValues.username ?? '';
|
||||
loginForm.value.password = formValues.password ?? '';
|
||||
loginForm.value.endpoint = formValues.endpoint ?? '';
|
||||
loginForm.value.websocket = formValues.websocket ?? '';
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user