show waiting state while redirecting to the OAuth 2.0 authorization page

This commit is contained in:
MaysWind
2025-10-23 00:42:29 +08:00
parent 234e7a55ff
commit b21fff5b15
4 changed files with 43 additions and 35 deletions
+4 -2
View File
@@ -29,7 +29,8 @@ export function useLoginPageBase(platform: 'mobile' | 'desktop') {
const twoFAVerifyType = ref<string>('passcode'); const twoFAVerifyType = ref<string>('passcode');
const oauth2ClientSessionId = ref<string>(''); const oauth2ClientSessionId = ref<string>('');
const logining = ref<boolean>(false); const loggingInByPassword = ref<boolean>(false);
const loggingInByOAuth2 = ref<boolean>(false);
const verifying = ref<boolean>(false); const verifying = ref<boolean>(false);
const inputIsEmpty = computed<boolean>(() => !username.value || !password.value); const inputIsEmpty = computed<boolean>(() => !username.value || !password.value);
@@ -73,7 +74,8 @@ export function useLoginPageBase(platform: 'mobile' | 'desktop') {
tempToken, tempToken,
twoFAVerifyType, twoFAVerifyType,
oauth2ClientSessionId, oauth2ClientSessionId,
logining, loggingInByPassword,
loggingInByOAuth2,
verifying, verifying,
// computed states // computed states
inputIsEmpty, inputIsEmpty,
+17 -14
View File
@@ -36,7 +36,7 @@
type="text" type="text"
autocomplete="username" autocomplete="username"
:autofocus="true" :autofocus="true"
:disabled="show2faInput || logining || verifying" :disabled="show2faInput || loggingInByPassword || loggingInByOAuth2 || verifying"
:label="tt('Username')" :label="tt('Username')"
:placeholder="tt('Your username or email')" :placeholder="tt('Your username or email')"
v-model="username" v-model="username"
@@ -50,7 +50,7 @@
autocomplete="current-password" autocomplete="current-password"
ref="passwordInput" ref="passwordInput"
type="password" type="password"
:disabled="show2faInput || logining || verifying" :disabled="show2faInput || loggingInByPassword || loggingInByOAuth2 || verifying"
:label="tt('Password')" :label="tt('Password')"
:placeholder="tt('Your password')" :placeholder="tt('Your password')"
v-model="password" v-model="password"
@@ -64,7 +64,7 @@
type="number" type="number"
autocomplete="one-time-code" autocomplete="one-time-code"
ref="passcodeInput" ref="passcodeInput"
:disabled="logining || verifying" :disabled="loggingInByPassword || loggingInByOAuth2 || verifying"
:label="tt('Passcode')" :label="tt('Passcode')"
:placeholder="tt('Passcode')" :placeholder="tt('Passcode')"
:append-inner-icon="mdiHelpCircleOutline" :append-inner-icon="mdiHelpCircleOutline"
@@ -75,7 +75,7 @@
/> />
<v-text-field <v-text-field
type="text" type="text"
:disabled="logining || verifying" :disabled="loggingInByPassword || loggingInByOAuth2 || verifying"
:label="tt('Backup Code')" :label="tt('Backup Code')"
:placeholder="tt('Backup Code')" :placeholder="tt('Backup Code')"
:append-inner-icon="mdiOnepassword" :append-inner-icon="mdiOnepassword"
@@ -100,10 +100,10 @@
</v-col> </v-col>
<v-col cols="12"> <v-col cols="12">
<v-btn block :disabled="inputIsEmpty || logining || verifying" <v-btn block :disabled="inputIsEmpty || loggingInByPassword || loggingInByOAuth2 || verifying"
@click="login" v-if="isInternalAuthEnabled() && !show2faInput"> @click="login" v-if="isInternalAuthEnabled() && !show2faInput">
{{ tt('Log In') }} {{ tt('Log In') }}
<v-progress-circular indeterminate size="22" class="ms-2" v-if="logining"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="loggingInByPassword"></v-progress-circular>
</v-btn> </v-btn>
<v-col cols="12" class="d-flex align-center px-0" v-if="isInternalAuthEnabled() && isOAuth2Enabled()"> <v-col cols="12" class="d-flex align-center px-0" v-if="isInternalAuthEnabled() && isOAuth2Enabled()">
@@ -112,10 +112,12 @@
<v-divider class="ms-3" /> <v-divider class="ms-3" />
</v-col> </v-col>
<v-btn block :disabled="logining || verifying" :href="oauth2LoginUrl" v-if="isOAuth2Enabled()"> <v-btn block :disabled="loggingInByPassword || loggingInByOAuth2 || verifying" :href="oauth2LoginUrl"
@click="loggingInByOAuth2 = true" v-if="isOAuth2Enabled()">
{{ oauth2LoginDisplayName }} {{ oauth2LoginDisplayName }}
<v-progress-circular indeterminate size="22" class="ms-2" v-if="loggingInByOAuth2"></v-progress-circular>
</v-btn> </v-btn>
<v-btn block :disabled="twoFAInputIsEmpty || logining || verifying" <v-btn block :disabled="twoFAInputIsEmpty || loggingInByPassword || loggingInByOAuth2 || verifying"
@click="verify" v-else-if="show2faInput"> @click="verify" v-else-if="show2faInput">
{{ tt('Continue') }} {{ tt('Continue') }}
<v-progress-circular indeterminate size="22" class="ms-2" v-if="verifying"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="verifying"></v-progress-circular>
@@ -140,7 +142,7 @@
<v-card-text class="pt-0"> <v-card-text class="pt-0">
<v-row> <v-row>
<v-col cols="12" class="text-center"> <v-col cols="12" class="text-center">
<language-select-button :disabled="logining || verifying" /> <language-select-button :disabled="loggingInByPassword || loggingInByOAuth2 || verifying" />
</v-col> </v-col>
<v-col cols="12" class="d-flex align-center pt-0"> <v-col cols="12" class="d-flex align-center pt-0">
@@ -212,7 +214,8 @@ const {
tempToken, tempToken,
twoFAVerifyType, twoFAVerifyType,
oauth2ClientSessionId, oauth2ClientSessionId,
logining, loggingInByPassword,
loggingInByOAuth2,
verifying, verifying,
inputIsEmpty, inputIsEmpty,
twoFAInputIsEmpty, twoFAInputIsEmpty,
@@ -247,17 +250,17 @@ function login(): void {
return; return;
} }
if (logining.value) { if (loggingInByPassword.value) {
return; return;
} }
logining.value = true; loggingInByPassword.value = true;
rootStore.authorize({ rootStore.authorize({
loginName: username.value, loginName: username.value,
password: password.value password: password.value
}).then(authResponse => { }).then(authResponse => {
logining.value = false; loggingInByPassword.value = false;
if (authResponse.need2FA) { if (authResponse.need2FA) {
tempToken.value = authResponse.token; tempToken.value = authResponse.token;
@@ -276,7 +279,7 @@ function login(): void {
doAfterLogin(authResponse); doAfterLogin(authResponse);
router.replace('/'); router.replace('/');
}).catch(error => { }).catch(error => {
logining.value = false; loggingInByPassword.value = false;
if (isUserVerifyEmailEnabled() && error.error && error.error.errorCode === KnownErrorCode.UserEmailNotVerified && error.error.context && error.error.context.email) { if (isUserVerifyEmailEnabled() && error.error && error.error.errorCode === KnownErrorCode.UserEmailNotVerified && error.error.context && error.error.context.email) {
router.push(`/verify_email?email=${encodeURIComponent(error.error.context.email)}&emailSent=${error.error.context.hasValidEmailVerifyToken || false}`); router.push(`/verify_email?email=${encodeURIComponent(error.error.context.email)}&emailSent=${error.error.context.hasValidEmailVerifyToken || false}`);
+12 -12
View File
@@ -38,7 +38,7 @@
type="password" type="password"
autocomplete="password" autocomplete="password"
:autofocus="true" :autofocus="true"
:disabled="logining" :disabled="loggingInByOAuth2"
:label="tt('Password')" :label="tt('Password')"
:placeholder="tt('Your password')" :placeholder="tt('Your password')"
v-model="password" v-model="password"
@@ -47,15 +47,15 @@
</v-col> </v-col>
<v-col cols="12"> <v-col cols="12">
<v-btn block type="submit" :disabled="!password || logining" @click="verifyAndLogin"> <v-btn block type="submit" :disabled="!password || loggingInByOAuth2" @click="verifyAndLogin">
{{ tt('Continue') }} {{ tt('Continue') }}
<v-progress-circular indeterminate size="22" class="ms-2" v-if="logining"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="loggingInByOAuth2"></v-progress-circular>
</v-btn> </v-btn>
</v-col> </v-col>
<v-col cols="12"> <v-col cols="12">
<router-link class="d-flex align-center justify-center" to="/login" <router-link class="d-flex align-center justify-center" to="/login"
:class="{ 'disabled': logining }"> :class="{ 'disabled': loggingInByOAuth2 }">
<v-icon class="icon-with-direction" :icon="mdiChevronLeft"/> <v-icon class="icon-with-direction" :icon="mdiChevronLeft"/>
<span>{{ tt('Back to login page') }}</span> <span>{{ tt('Back to login page') }}</span>
</router-link> </router-link>
@@ -71,7 +71,7 @@
<v-card-text class="pt-0"> <v-card-text class="pt-0">
<v-row> <v-row>
<v-col cols="12" class="text-center"> <v-col cols="12" class="text-center">
<language-select-button :disabled="logining" /> <language-select-button :disabled="loggingInByOAuth2" />
</v-col> </v-col>
<v-col cols="12" class="d-flex align-center pt-0"> <v-col cols="12" class="d-flex align-center pt-0">
@@ -146,7 +146,7 @@ const rootStore = useRootStore();
const { const {
version, version,
password, password,
logining, loggingInByOAuth2,
doAfterLogin doAfterLogin
} = useLoginPageBase('desktop'); } = useLoginPageBase('desktop');
@@ -197,17 +197,17 @@ function verifyAndLogin(): void {
return; return;
} }
logining.value = true; loggingInByOAuth2.value = true;
rootStore.authorizeOAuth2({ rootStore.authorizeOAuth2({
password: password.value, password: password.value,
token: props.token || '' token: props.token || ''
}).then(authResponse => { }).then(authResponse => {
logining.value = false; loggingInByOAuth2.value = false;
doAfterLogin(authResponse); doAfterLogin(authResponse);
navigateToHome(); navigateToHome();
}).catch(error => { }).catch(error => {
logining.value = false; loggingInByOAuth2.value = false;
if (isUserVerifyEmailEnabled() && error.error && error.error.errorCode === KnownErrorCode.UserEmailNotVerified && error.error.context && error.error.context.email) { if (isUserVerifyEmailEnabled() && error.error && error.error.errorCode === KnownErrorCode.UserEmailNotVerified && error.error.context && error.error.context.email) {
router.push(`/verify_email?email=${encodeURIComponent(error.error.context.email)}&emailSent=${error.error.context.hasValidEmailVerifyToken || false}`); router.push(`/verify_email?email=${encodeURIComponent(error.error.context.email)}&emailSent=${error.error.context.hasValidEmailVerifyToken || false}`);
@@ -221,16 +221,16 @@ function verifyAndLogin(): void {
} }
if (!error.value && props.platform && props.token && !props.userName) { if (!error.value && props.platform && props.token && !props.userName) {
logining.value = true; loggingInByOAuth2.value = true;
rootStore.authorizeOAuth2({ rootStore.authorizeOAuth2({
token: props.token token: props.token
}).then(authResponse => { }).then(authResponse => {
logining.value = false; loggingInByOAuth2.value = false;
doAfterLogin(authResponse); doAfterLogin(authResponse);
navigateToHome(); navigateToHome();
}).catch(error => { }).catch(error => {
logining.value = false; loggingInByOAuth2.value = false;
if (isUserVerifyEmailEnabled() && error.error && error.error.errorCode === KnownErrorCode.UserEmailNotVerified && error.error.context && error.error.context.email) { if (isUserVerifyEmailEnabled() && error.error && error.error.errorCode === KnownErrorCode.UserEmailNotVerified && error.error.context && error.error.context.email) {
router.push(`/verify_email?email=${encodeURIComponent(error.error.context.email)}&emailSent=${error.error.context.hasValidEmailVerifyToken || false}`); router.push(`/verify_email?email=${encodeURIComponent(error.error.context.email)}&emailSent=${error.error.context.hasValidEmailVerifyToken || false}`);
+10 -7
View File
@@ -47,13 +47,15 @@
</f7-list> </f7-list>
<f7-list class="margin-vertical-half"> <f7-list class="margin-vertical-half">
<f7-list-button :class="{ 'disabled': inputIsEmpty || logining }" :text="tt('Log In')" @click="login" v-if="isInternalAuthEnabled()"></f7-list-button> <f7-list-button :class="{ 'disabled': inputIsEmpty || loggingInByPassword || loggingInByOAuth2 }" :text="tt('Log In')"
@click="login" v-if="isInternalAuthEnabled()"></f7-list-button>
<f7-list-item class="login-divider display-flex align-items-center" v-if="isInternalAuthEnabled() && isOAuth2Enabled()"> <f7-list-item class="login-divider display-flex align-items-center" v-if="isInternalAuthEnabled() && isOAuth2Enabled()">
<hr class="margin-inline-end-half" /> <hr class="margin-inline-end-half" />
<small>{{ tt('or') }}</small> <small>{{ tt('or') }}</small>
<hr class="margin-inline-start-half" /> <hr class="margin-inline-start-half" />
</f7-list-item> </f7-list-item>
<f7-list-button external :class="{ 'disabled': logining }" :href="oauth2LoginUrl" :text="oauth2LoginDisplayName" v-if="isOAuth2Enabled()"></f7-list-button> <f7-list-button external :class="{ 'disabled': loggingInByPassword || loggingInByOAuth2 }" :href="oauth2LoginUrl" :text="oauth2LoginDisplayName"
@click="loggingInByOAuth2 = true" v-if="isOAuth2Enabled()"></f7-list-button>
<f7-block-footer v-if="isInternalAuthEnabled()"> <f7-block-footer v-if="isInternalAuthEnabled()">
<span>{{ tt('Don\'t have an account?') }}</span>&nbsp; <span>{{ tt('Don\'t have an account?') }}</span>&nbsp;
<f7-link :class="{'disabled': !isUserRegistrationEnabled()}" href="/signup" :text="tt('Create an account')"></f7-link> <f7-link :class="{'disabled': !isUserRegistrationEnabled()}" href="/signup" :text="tt('Create an account')"></f7-link>
@@ -212,7 +214,8 @@ const {
tempToken, tempToken,
twoFAVerifyType, twoFAVerifyType,
oauth2ClientSessionId, oauth2ClientSessionId,
logining, loggingInByPassword,
loggingInByOAuth2,
verifying, verifying,
inputIsEmpty, inputIsEmpty,
twoFAInputIsEmpty, twoFAInputIsEmpty,
@@ -258,17 +261,17 @@ function login(): void {
return; return;
} }
logining.value = true; loggingInByPassword.value = true;
resendVerifyEmail.value = ''; resendVerifyEmail.value = '';
hasValidEmailVerifyToken.value = false; hasValidEmailVerifyToken.value = false;
currentPasswordForResendVerifyEmail.value = ''; currentPasswordForResendVerifyEmail.value = '';
showLoading(() => logining.value); showLoading(() => loggingInByPassword.value);
rootStore.authorize({ rootStore.authorize({
loginName: username.value, loginName: username.value,
password: password.value password: password.value
}).then(authResponse => { }).then(authResponse => {
logining.value = false; loggingInByPassword.value = false;
hideLoading(); hideLoading();
if (authResponse.need2FA) { if (authResponse.need2FA) {
@@ -280,7 +283,7 @@ function login(): void {
doAfterLogin(authResponse); doAfterLogin(authResponse);
router.refreshPage(); router.refreshPage();
}).catch(error => { }).catch(error => {
logining.value = false; loggingInByPassword.value = false;
hideLoading(); hideLoading();
if (isUserVerifyEmailEnabled() && error.error && error.error.errorCode === KnownErrorCode.UserEmailNotVerified && error.error.context && error.error.context.email) { if (isUserVerifyEmailEnabled() && error.error && error.error.errorCode === KnownErrorCode.UserEmailNotVerified && error.error.context && error.error.context.email) {