mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-25 17:53:48 +02:00
login settings dialog
This commit is contained in:
@@ -114,6 +114,7 @@
|
|||||||
"forgotPassword": "Forgot Password?",
|
"forgotPassword": "Forgot Password?",
|
||||||
"updater": "Updater",
|
"updater": "Updater",
|
||||||
"proxy_settings": "Proxy settings",
|
"proxy_settings": "Proxy settings",
|
||||||
|
"settings": "Settings",
|
||||||
"language": "Language",
|
"language": "Language",
|
||||||
"field": {
|
"field": {
|
||||||
"username": "Username or Email",
|
"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>
|
<template>
|
||||||
<div class="x-login-container">
|
<div class="x-login-container">
|
||||||
<div style="position: absolute; top: 0; left: 0; margin: 5px">
|
<div style="position: absolute; top: 0; left: 0; margin: 5px">
|
||||||
|
<LoginSettingsDialog />
|
||||||
<TooltipWrapper v-if="!noUpdater" side="top" :content="t('view.login.updater')">
|
<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"
|
<Button class="rounded-full mr-2 text-xs" size="icon-sm" variant="ghost" @click="showVRCXUpdateDialog"
|
||||||
><ArrowBigDownDash
|
><ArrowBigDownDash
|
||||||
/></Button>
|
/></Button>
|
||||||
</TooltipWrapper>
|
</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>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger as-child>
|
<DropdownMenuTrigger as-child>
|
||||||
<TooltipWrapper side="top" :content="t('view.login.language')">
|
<TooltipWrapper side="top" :content="t('view.login.language')">
|
||||||
@@ -82,52 +78,7 @@
|
|||||||
<Checkbox v-model="loginForm.saveCredentials" />
|
<Checkbox v-model="loginForm.saveCredentials" />
|
||||||
<span>{{ t('view.login.field.saveCredentials') }}</span>
|
<span>{{ t('view.login.field.saveCredentials') }}</span>
|
||||||
</label>
|
</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">
|
<Field class="mt-2">
|
||||||
<Button type="submit" size="lg" style="width: 100%">{{ t('view.login.login') }}</Button>
|
<Button type="submit" size="lg" style="width: 100%">{{ t('view.login.login') }}</Button>
|
||||||
</Field>
|
</Field>
|
||||||
@@ -203,7 +154,6 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Field, FieldContent, FieldError, FieldGroup, FieldLabel } from '@/components/ui/field';
|
import { Field, FieldContent, FieldError, FieldGroup, FieldLabel } from '@/components/ui/field';
|
||||||
import { ArrowBigDownDash, Earth, Languages, Minus } from 'lucide-vue-next';
|
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuCheckboxItem,
|
DropdownMenuCheckboxItem,
|
||||||
@@ -211,6 +161,7 @@
|
|||||||
DropdownMenuTrigger
|
DropdownMenuTrigger
|
||||||
} from '@/components/ui/dropdown-menu';
|
} from '@/components/ui/dropdown-menu';
|
||||||
import { onBeforeMount, onBeforeUnmount, ref, watch } from 'vue';
|
import { onBeforeMount, onBeforeUnmount, ref, watch } from 'vue';
|
||||||
|
import { ArrowBigDownDash, Languages, Minus } from 'lucide-vue-next';
|
||||||
import { Field as VeeField, useForm } from 'vee-validate';
|
import { Field as VeeField, useForm } from 'vee-validate';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
@@ -221,23 +172,18 @@
|
|||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import {
|
import { useAppearanceSettingsStore, useAuthStore, useVRCXUpdaterStore } from '../../stores';
|
||||||
useAppearanceSettingsStore,
|
|
||||||
useAuthStore,
|
|
||||||
useGeneralSettingsStore,
|
|
||||||
useVRCXUpdaterStore
|
|
||||||
} from '../../stores';
|
|
||||||
import { getLanguageName, languageCodes } from '../../localization';
|
import { getLanguageName, languageCodes } from '../../localization';
|
||||||
import { openExternalLink, userImage } from '../../shared/utils';
|
import { openExternalLink, userImage } from '../../shared/utils';
|
||||||
import { AppDebug } from '../../service/appConfig';
|
|
||||||
import { watchState } from '../../service/watchState';
|
import { watchState } from '../../service/watchState';
|
||||||
|
|
||||||
|
import LoginSettingsDialog from './Dialog/LoginSettingsDialog.vue';
|
||||||
|
|
||||||
const { showVRCXUpdateDialog } = useVRCXUpdaterStore();
|
const { showVRCXUpdateDialog } = useVRCXUpdaterStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { loginForm, enableCustomEndpoint } = storeToRefs(useAuthStore());
|
const { loginForm } = storeToRefs(useAuthStore());
|
||||||
const { toggleCustomEndpoint, relogin, deleteSavedLogin, login, getAllSavedCredentials } = useAuthStore();
|
const { relogin, deleteSavedLogin, login, getAllSavedCredentials } = useAuthStore();
|
||||||
const { promptProxySettings } = useGeneralSettingsStore();
|
|
||||||
const { noUpdater } = storeToRefs(useVRCXUpdaterStore());
|
const { noUpdater } = storeToRefs(useVRCXUpdaterStore());
|
||||||
|
|
||||||
const appearanceSettingsStore = useAppearanceSettingsStore();
|
const appearanceSettingsStore = useAppearanceSettingsStore();
|
||||||
@@ -252,19 +198,15 @@
|
|||||||
const formSchema = toTypedSchema(
|
const formSchema = toTypedSchema(
|
||||||
z.object({
|
z.object({
|
||||||
username: z.string().min(1, requiredMessage),
|
username: z.string().min(1, requiredMessage),
|
||||||
password: z.string().min(1, requiredMessage),
|
password: z.string().min(1, requiredMessage)
|
||||||
endpoint: z.string().optional(),
|
|
||||||
websocket: z.string().optional()
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const { handleSubmit, resetForm, setValues, values } = useForm({
|
const { handleSubmit, resetForm, values } = useForm({
|
||||||
validationSchema: formSchema,
|
validationSchema: formSchema,
|
||||||
initialValues: {
|
initialValues: {
|
||||||
username: loginForm.value.username,
|
username: loginForm.value.username,
|
||||||
password: loginForm.value.password,
|
password: loginForm.value.password
|
||||||
endpoint: loginForm.value.endpoint,
|
|
||||||
websocket: loginForm.value.websocket
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -281,21 +223,10 @@
|
|||||||
const onSubmit = handleSubmit(async (formValues) => {
|
const onSubmit = handleSubmit(async (formValues) => {
|
||||||
loginForm.value.username = formValues.username ?? '';
|
loginForm.value.username = formValues.username ?? '';
|
||||||
loginForm.value.password = formValues.password ?? '';
|
loginForm.value.password = formValues.password ?? '';
|
||||||
loginForm.value.endpoint = formValues.endpoint ?? '';
|
|
||||||
loginForm.value.websocket = formValues.websocket ?? '';
|
|
||||||
await login();
|
await login();
|
||||||
await updateSavedCredentials();
|
await updateSavedCredentials();
|
||||||
});
|
});
|
||||||
|
|
||||||
async function handleCustomEndpointToggle() {
|
|
||||||
await toggleCustomEndpoint();
|
|
||||||
setValues({
|
|
||||||
...values,
|
|
||||||
endpoint: loginForm.value.endpoint,
|
|
||||||
websocket: loginForm.value.websocket
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateSavedCredentials() {
|
async function updateSavedCredentials() {
|
||||||
if (watchState.isLoggedIn) {
|
if (watchState.isLoggedIn) {
|
||||||
return;
|
return;
|
||||||
@@ -337,9 +268,7 @@
|
|||||||
resetForm({
|
resetForm({
|
||||||
values: {
|
values: {
|
||||||
username: '',
|
username: '',
|
||||||
password: '',
|
password: ''
|
||||||
endpoint: '',
|
|
||||||
websocket: ''
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
loginForm.value.username = '';
|
loginForm.value.username = '';
|
||||||
@@ -354,8 +283,6 @@
|
|||||||
(formValues) => {
|
(formValues) => {
|
||||||
loginForm.value.username = formValues.username ?? '';
|
loginForm.value.username = formValues.username ?? '';
|
||||||
loginForm.value.password = formValues.password ?? '';
|
loginForm.value.password = formValues.password ?? '';
|
||||||
loginForm.value.endpoint = formValues.endpoint ?? '';
|
|
||||||
loginForm.value.websocket = formValues.websocket ?? '';
|
|
||||||
},
|
},
|
||||||
{ deep: true }
|
{ deep: true }
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user