UX: More improvements to login/signup forms (#29417)
Some checks are pending
Licenses / run (push) Waiting to run
Linting / run (push) Waiting to run
Tests / ${{ matrix.target }} ${{ matrix.build_type }} (annotations, core) (push) Waiting to run
Tests / ${{ matrix.target }} ${{ matrix.build_type }} (backend, core) (push) Waiting to run
Tests / ${{ matrix.target }} ${{ matrix.build_type }} (backend, plugins) (push) Waiting to run
Tests / ${{ matrix.target }} ${{ matrix.build_type }} (frontend, plugins) (push) Waiting to run
Tests / ${{ matrix.target }} ${{ matrix.build_type }} (frontend, themes) (push) Waiting to run
Tests / ${{ matrix.target }} ${{ matrix.build_type }} (system, chat) (push) Waiting to run
Tests / ${{ matrix.target }} ${{ matrix.build_type }} (system, core) (push) Waiting to run
Tests / ${{ matrix.target }} ${{ matrix.build_type }} (system, plugins) (push) Waiting to run
Tests / ${{ matrix.target }} ${{ matrix.build_type }} (system, themes) (push) Waiting to run
Tests / core frontend (${{ matrix.browser }}) (Chrome) (push) Waiting to run
Tests / core frontend (${{ matrix.browser }}) (Firefox ESR) (push) Waiting to run
Tests / core frontend (${{ matrix.browser }}) (Firefox Evergreen) (push) Waiting to run

This commit is contained in:
Jan Cernik 2024-10-30 13:33:06 -03:00 committed by GitHub
parent 852ff18fb9
commit 81396467d0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 175 additions and 22 deletions

View File

@ -2,6 +2,7 @@
<div id="credentials" class={{this.credentialsClass}}>
<div class="input-group" {{did-insert this.passkeyConditionalLogin}}>
<Input
{{on "focusin" this.scrollInputIntoView}}
@value={{@loginName}}
@type="email"
id="login-account-name"
@ -32,6 +33,7 @@
</div>
<div class="input-group">
<PasswordField
{{on "focusin" this.scrollInputIntoView}}
{{on "keydown" this.loginOnEnter}}
@value={{@loginPassword}}
@capsLockOn={{this.capsLockOn}}
@ -90,6 +92,7 @@
<SecondFactorInput
{{on "keydown" this.loginOnEnter}}
{{on "input" (with-event-value (fn (mut @secondFactorToken)))}}
{{on "focusin" this.scrollInputIntoView}}
@secondFactorMethod={{@secondFactorMethod}}
value={{@secondFactorToken}}
id="login-second-factor"

View File

@ -39,6 +39,14 @@ export default class LocalLoginForm extends Component {
}
}
@action
scrollInputIntoView(event) {
event.target.scrollIntoView({
behavior: "smooth",
block: "center",
});
}
@action
togglePasswordMask() {
this.maskPassword = !this.maskPassword;

View File

@ -20,7 +20,7 @@ const LoginPageCta = <template>
{{/unless}}
{{#if @showSignupLink}}
<span class="signup-page-cta__no-account-yet">
<span class="login-page-cta__no-account-yet">
{{i18n "create_account.no_account_yet"}}
</span>
<DButton

View File

@ -41,6 +41,7 @@
<div class="input-group create-account-email">
<Input
{{on "focusout" this.checkEmailAvailability}}
{{on "focusin" this.scrollInputIntoView}}
@type="email"
@value={{this.model.accountEmail}}
disabled={{this.emailDisabled}}
@ -54,7 +55,12 @@
<label class="alt-placeholder" for="new-account-email">
{{i18n "user.email.title"}}
</label>
{{#if this.emailValidation.reason}}
{{#if
(or
this.emailValidation.ok
(and this.emailValidationVisible this.emailValidation.reason)
)
}}
<InputTip
@validation={{this.emailValidation}}
id="account-email-validation"
@ -68,6 +74,7 @@
<div class="input-group create-account__username">
<Input
{{on "focusin" this.scrollInputIntoView}}
@value={{this.model.accountUsername}}
disabled={{this.usernameDisabled}}
maxlength={{this.maxUsernameLength}}
@ -102,6 +109,8 @@
<div class="input-group create-account__password">
{{#if this.passwordRequired}}
<PasswordField
{{on "focusout" this.showPasswordValidation}}
{{on "focusin" this.scrollInputIntoView}}
@value={{this.accountPassword}}
@capsLockOn={{this.capsLockOn}}
type={{if this.maskPassword "password" "text"}}
@ -117,10 +126,20 @@
<div class="create-account__password-info">
<div class="create-account__password-tip-validation">
<InputTip
@validation={{this.passwordValidation}}
id="password-validation"
/>
{{#if
(or
this.passwordValidation.ok
(and
this.passwordValidationVisible
this.passwordValidation.reason
)
)
}}
<InputTip
@validation={{this.passwordValidation}}
id="password-validation"
/>
{{/if}}
<div
class={{concat-class
"caps-lock-warning"
@ -157,6 +176,7 @@
{{#if this.requireInviteCode}}
<div class="input-group create-account__invite-code">
<Input
{{on "focusin" this.scrollInputIntoView}}
@value={{this.inviteCode}}
id="inviteCode"
class={{value-entered this.inviteCode}}
@ -189,6 +209,7 @@
>
{{#if this.fullnameRequired}}
<TextField
{{on "focusin" this.scrollInputIntoView}}
@disabled={{this.nameDisabled}}
@value={{this.model.accountName}}
@id="new-account-name"
@ -212,6 +233,7 @@
{{#each this.userFields as |f|}}
<div class="input-group">
<UserField
{{on "focusin" this.scrollInputIntoView}}
@field={{f.field}}
@value={{f.value}}
@validation={{f.validation}}

View File

@ -39,6 +39,8 @@ export default class CreateAccount extends Component.extend(
userFields = null;
isDeveloper = false;
maskPassword = true;
passwordValidationVisible = false;
emailValidationVisible = false;
@notEmpty("model.authOptions") hasAuthOptions;
@setting("enable_local_logins") canCreateLocal;
@ -215,8 +217,18 @@ export default class CreateAccount extends Component.extend(
});
}
@action
showPasswordValidation() {
this.set(
"passwordValidationVisible",
Boolean(this.passwordValidation.reason)
);
}
@action
checkEmailAvailability() {
this.set("emailValidationVisible", Boolean(this.emailValidation.reason));
if (
!this.emailValidation.ok ||
this.serverAccountEmail === this.model.accountEmail
@ -438,6 +450,14 @@ export default class CreateAccount extends Component.extend(
});
}
@action
scrollInputIntoView(event) {
event.target.scrollIntoView({
behavior: "smooth",
block: "center",
});
}
@action
togglePasswordMask() {
this.toggleProperty("maskPassword");
@ -453,6 +473,8 @@ export default class CreateAccount extends Component.extend(
createAccount() {
this.set("flash", "");
this.set("forceValidationReason", true);
this.set("emailValidationVisible", true);
this.set("passwordValidationVisible", true);
const validation = [
this.emailValidation,

View File

@ -35,7 +35,7 @@ export default class LoginPageController extends Controller {
@tracked loggingIn = false;
@tracked loggedIn = false;
@tracked showLoginButtons = true;
@tracked showLogin = false;
@tracked showLogin = true;
@tracked showSecondFactor = false;
@tracked loginPassword = "";
@tracked loginName = "";

View File

@ -39,6 +39,8 @@ export default class SignupPageController extends Controller.extend(
userFields = null;
isDeveloper = false;
maskPassword = true;
passwordValidationVisible = false;
emailValidationVisible = false;
@notEmpty("authOptions") hasAuthOptions;
@setting("enable_local_logins") canCreateLocal;
@ -204,8 +206,23 @@ export default class SignupPageController extends Controller.extend(
});
}
@action
showPasswordValidation() {
if (this.passwordValidation.reason) {
this.set("passwordValidationVisible", true);
} else {
this.set("passwordValidationVisible", false);
}
}
@action
checkEmailAvailability() {
if (this.emailValidation.reason) {
this.set("emailValidationVisible", true);
} else {
this.set("emailValidationVisible", false);
}
if (
!this.emailValidation.ok ||
this.serverAccountEmail === this.accountEmail
@ -423,6 +440,14 @@ export default class SignupPageController extends Controller.extend(
});
}
@action
scrollInputIntoView(event) {
event.target.scrollIntoView({
behavior: "smooth",
block: "center",
});
}
@action
togglePasswordMask() {
this.toggleProperty("maskPassword");
@ -438,6 +463,8 @@ export default class SignupPageController extends Controller.extend(
createAccount() {
this.set("flash", "");
this.set("forceValidationReason", true);
this.set("emailValidationVisible", true);
this.set("passwordValidationVisible", true);
const validation = [
this.emailValidation,

View File

@ -300,6 +300,9 @@ export default class ApplicationRoute extends DiscourseRoute {
} else if (this.siteSettings.experimental_full_page_login) {
this.router.transitionTo("login").then((login) => {
login.controller.set("canSignUp", this.controller.canSignUp);
if (this.siteSettings.login_required) {
login.controller.set("showLogin", true);
}
});
} else {
this.modal.show(LoginModal, {

View File

@ -21,7 +21,9 @@ export default class LoginRoute extends DiscourseRoute {
}
model() {
return StaticPage.find("login");
if (this.siteSettings.login_required) {
return StaticPage.find("login");
}
}
setupController(controller) {
@ -31,5 +33,9 @@ export default class LoginRoute extends DiscourseRoute {
controller.set("canSignUp", canSignUp);
controller.set("flashType", "");
controller.set("flash", "");
if (this.siteSettings.login_required) {
controller.set("showLogin", false);
}
}
}

View File

@ -70,7 +70,7 @@ export default RouteTemplate(
} else if (response.needs_approval) {
this.needsApproval = true;
} else {
setTimeout(this.loadHomepage, 2000);
setTimeout(this.loadHomepage, 3000);
}
} catch (error) {
this.errorMessage = i18n("user.activate_account.already_done");

View File

@ -7,8 +7,9 @@
{{hide-application-header-buttons "search" "login" "signup" "menu"}}
{{hide-application-sidebar}}
<FlashMessage @flash={{this.flash}} @type={{this.flashType}} />
<div class="login-fullpage">
<FlashMessage @flash={{this.flash}} @type={{this.flashType}} />
<div class={{concat-class "login-body" this.bodyClasses}}>
<PluginOutlet @name="login-before-modal-body" @connectorTagName="div" />

View File

@ -2,9 +2,9 @@
{{hide-application-header-buttons "search" "login" "signup" "menu"}}
{{hide-application-sidebar}}
<FlashMessage @flash={{this.flash}} @type={{this.flashType}} />
<div class="signup-fullpage">
<FlashMessage @flash={{this.flash}} @type={{this.flashType}} />
<div class={{concat-class "signup-body" this.bodyClasses}}>
<PluginOutlet
@name="create-account-before-modal-body"
@ -37,6 +37,7 @@
<div class="input-group create-account-email">
<Input
{{on "focusout" this.checkEmailAvailability}}
{{on "focusin" this.scrollInputIntoView}}
@type="email"
@value={{this.accountEmail}}
disabled={{this.emailDisabled}}
@ -50,7 +51,12 @@
<label class="alt-placeholder" for="new-account-email">
{{i18n "user.email.title"}}
</label>
{{#if this.emailValidation.reason}}
{{#if
(or
this.emailValidation.ok
(and this.emailValidationVisible this.emailValidation.reason)
)
}}
<InputTip
@validation={{this.emailValidation}}
id="account-email-validation"
@ -64,6 +70,7 @@
<div class="input-group create-account__username">
<Input
{{on "focusin" this.scrollInputIntoView}}
@value={{this.accountUsername}}
disabled={{this.usernameDisabled}}
maxlength={{this.maxUsernameLength}}
@ -98,6 +105,8 @@
<div class="input-group create-account__password">
{{#if this.passwordRequired}}
<PasswordField
{{on "focusout" this.showPasswordValidation}}
{{on "focusin" this.scrollInputIntoView}}
@value={{this.accountPassword}}
@capsLockOn={{this.capsLockOn}}
type={{if this.maskPassword "password" "text"}}
@ -113,10 +122,20 @@
<div class="create-account__password-info">
<div class="create-account__password-tip-validation">
<InputTip
@validation={{this.passwordValidation}}
id="password-validation"
/>
{{#if
(or
this.passwordValidation.ok
(and
this.passwordValidationVisible
this.passwordValidation.reason
)
)
}}
<InputTip
@validation={{this.passwordValidation}}
id="password-validation"
/>
{{/if}}
<div
class={{concat-class
"caps-lock-warning"
@ -153,6 +172,7 @@
{{#if this.requireInviteCode}}
<div class="input-group create-account__invite-code">
<Input
{{on "focusin" this.scrollInputIntoView}}
@value={{this.inviteCode}}
id="inviteCode"
class={{value-entered this.inviteCode}}
@ -185,6 +205,7 @@
>
{{#if this.fullnameRequired}}
<TextField
{{on "focusin" this.scrollInputIntoView}}
@disabled={{this.nameDisabled}}
@value={{this.accountName}}
@id="new-account-name"
@ -208,6 +229,7 @@
{{#each this.userFields as |f|}}
<div class="input-group">
<UserField
{{on "focusin" this.scrollInputIntoView}}
@field={{f.field}}
@value={{f.value}}
@validation={{f.validation}}

View File

@ -9,6 +9,9 @@
padding: 0;
height: 100%;
}
.above-main-container-outlet {
display: none;
}
}
.activate-account-page .alert-error {
@ -20,7 +23,6 @@
max-width: 500px;
padding: 2rem 3rem;
background: var(--secondary);
box-shadow: var(--shadow-menu-panel);
margin: 10vh auto 1em auto;
@media screen and (max-height: 700px) {
margin: 1em auto 1em auto;

View File

@ -9,6 +9,17 @@
flex-direction: column;
}
#main-outlet:has(.login-fullpage, .signup-fullpage, .invites-show) {
& ~ .powered-by-discourse,
.above-main-container-outlet {
display: none;
}
}
body:has(.login-fullpage, .signup-fullpage) {
background-color: var(--secondary);
}
.login-fullpage,
.signup-fullpage,
.invites-show {
@ -18,6 +29,14 @@
justify-content: center;
width: 100%;
max-width: 800px;
box-sizing: border-box;
}
.alert {
width: 100%;
max-width: 800px;
box-sizing: border-box;
margin: 0;
}
.login-page-cta,

View File

@ -217,11 +217,14 @@
/* end shared styles */
.d-modal.create-account {
.d-modal.create-account,
.d-modal.login-modal {
&:not(:has(.login-right-side)) .d-modal__container {
max-width: 500px;
}
}
.d-modal.create-account {
.d-modal {
&__container {
width: 100%;

View File

@ -7,7 +7,6 @@
max-width: 500px;
padding: 2rem 3rem;
background: var(--secondary);
box-shadow: var(--shadow-menu-panel);
margin: 10vh auto 1em auto;
@media screen and (max-height: 700px) {
margin: 1em auto 1em auto;

View File

@ -3,6 +3,10 @@
.login-fullpage,
.signup-fullpage,
.invites-show {
&:not(:has(.has-alt-auth)) .alert {
max-width: 500px;
}
.login-page-cta,
.signup-page-cta {
&__buttons {
@ -71,3 +75,9 @@
}
}
}
.invites-show.container {
box-sizing: border-box;
box-shadow: none;
max-width: 550px;
}

View File

@ -158,9 +158,12 @@
}
&__signup {
background: none !important;
font-size: var(--font-down);
font-size: var(--font-up-1);
padding: 0;
}
&__no-account-yet {
font-size: var(--font-up-1);
}
}
}
@ -176,9 +179,12 @@
}
&__login {
background: none !important;
font-size: var(--font-down);
font-size: var(--font-up-1);
padding: 0;
}
&__existing-account {
font-size: var(--font-up-1);
}
}
.login-right-side::before {