web/satellite: succesful registration flow reworked

Change-Id: I29f7b0fd2286c32ada39bc1e86d58feda624c645
This commit is contained in:
VitaliiShpital 2020-03-17 13:44:47 +02:00 committed by Vitalii Shpital
parent fe39845a8c
commit d7558db5ed
14 changed files with 236 additions and 279 deletions

View File

@ -31,7 +31,6 @@ export default class App extends Vue {
'sortTeamMemberByDropdown',
'sortTeamMemberByDropdownButton',
'notificationArea',
'successfulRegistrationPopup',
'paymentSelectButton',
'paymentSelect',
];

View File

@ -0,0 +1,96 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
<template src="./registrationSuccess.html"></template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import VButton from '@/components/common/VButton.vue';
import { AuthHttpApi } from '@/api/auth';
import { LocalData } from '@/utils/localData';
@Component({
components: {
VButton,
},
})
export default class RegistrationSuccess extends Vue {
private isResendEmailButtonDisabled: boolean = true;
private timeToEnableResendEmailButton: string = '00:30';
private intervalID: any = null;
private readonly auth: AuthHttpApi = new AuthHttpApi();
/**
* Lifecycle hook after initial render.
* Starts resend email button availability countdown.
*/
public mounted(): void {
this.startResendEmailCountdown();
}
/**
* Lifecycle hook before component destroying.
* Resets interval.
*/
public beforeDestroy(): void {
if (this.intervalID) {
clearInterval(this.intervalID);
}
}
/**
* Checks if page is inside iframe.
*/
public get isInsideIframe(): boolean {
return window.self !== window.top;
}
/**
* Resend email if interval timer is expired.
*/
public async onResendEmailButtonClick(): Promise<void> {
if (this.isResendEmailButtonDisabled) {
return;
}
this.isResendEmailButtonDisabled = true;
const userId = LocalData.getUserId();
if (!userId) {
return;
}
try {
await this.auth.resendEmail(userId);
} catch (error) {
await this.$notify.error('Could not send email.');
}
this.startResendEmailCountdown();
}
/**
* Resets timer blocking email resend button spamming.
*/
private startResendEmailCountdown(): void {
let countdown = 30;
this.intervalID = setInterval(() => {
countdown--;
const secondsLeft = countdown > 9 ? countdown : `0${countdown}`;
this.timeToEnableResendEmailButton = `00:${secondsLeft}`;
if (countdown <= 0) {
clearInterval(this.intervalID);
this.isResendEmailButtonDisabled = false;
}
}, 1000);
}
}
</script>
<style scoped lang="scss" src="./registrationSuccess.scss"></style>

View File

@ -1,212 +0,0 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
<template src="./registrationSuccessPopup.html"></template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import VButton from '@/components/common/VButton.vue';
import CloseCrossIcon from '@/../static/images/common/closeCross.svg';
import RegistrationSuccessIcon from '@/../static/images/register/registerSuccess.svg';
import { AuthHttpApi } from '@/api/auth';
import { RouteConfig } from '@/router';
import { APP_STATE_ACTIONS } from '@/utils/constants/actionNames';
import { LocalData } from '@/utils/localData';
@Component({
components: {
VButton,
RegistrationSuccessIcon,
CloseCrossIcon,
},
})
export default class RegistrationSuccessPopup extends Vue {
private isResendEmailButtonDisabled: boolean = true;
private timeToEnableResendEmailButton: string = '00:30';
private intervalID: any = null;
private readonly auth: AuthHttpApi = new AuthHttpApi();
public beforeDestroy(): void {
if (this.intervalID) {
clearInterval(this.intervalID);
}
}
/**
* Resend email if interval timer is expired.
*/
public async onResendEmailButtonClick(): Promise<void> {
if (this.isResendEmailButtonDisabled) {
return;
}
this.isResendEmailButtonDisabled = true;
const userId = LocalData.getUserId();
if (!userId) {
return;
}
try {
await this.auth.resendEmail(userId);
} catch (error) {
await this.$notify.error('Could not send email.');
}
this.startResendEmailCountdown();
}
/**
* Closes popup and redirects to login page.
*/
public onCloseClick(): void {
this.$store.dispatch(APP_STATE_ACTIONS.CLOSE_POPUPS);
this.$router.push(RouteConfig.Login.path);
}
/**
* Indicates if component should be rendered.
*/
public get isPopupShown(): boolean {
return this.$store.state.appStateModule.appState.isSuccessfulRegistrationPopupShown;
}
/**
* Resets timer blocking email resend button spamming.
*/
private startResendEmailCountdown(): void {
let countdown = 30;
this.intervalID = setInterval(() => {
countdown--;
const secondsLeft = countdown > 9 ? countdown : `0${countdown}`;
this.timeToEnableResendEmailButton = `00:${secondsLeft}`;
if (countdown <= 0) {
clearInterval(this.intervalID);
this.isResendEmailButtonDisabled = false;
}
}, 1000);
}
}
</script>
<style scoped lang="scss">
.register-success-popup-container {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(134, 134, 148, 0.4);
z-index: 1000;
display: flex;
justify-content: center;
align-items: center;
}
.register-success-popup {
width: 100%;
max-width: 845px;
background-color: #fff;
border-radius: 6px;
display: flex;
flex-direction: row;
align-items: flex-start;
position: relative;
justify-content: center;
padding: 80px 100px 80px 50px;
&__info-panel-container {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
margin-right: 100px;
margin-top: 20px;
}
&__form-container {
width: 100%;
max-width: 440px;
margin-top: 10px;
&__title {
font-family: 'font_bold', sans-serif;
font-size: 32px;
line-height: 39px;
color: #384b65;
margin: 0 0 25px 0;
}
&__text {
font-family: 'font_medium', sans-serif;
font-size: 16px;
line-height: 21px;
color: #354049;
padding: 27px 0 0 0;
margin: 0;
}
&__verification-cooldown {
font-family: 'font_medium', sans-serif;
font-size: 12px;
line-height: 16px;
color: #354049;
padding: 27px 0 0 0;
margin: 0;
&__bold-text {
color: #2683ff;
}
}
&__button-container {
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
margin-top: 15px;
}
}
&__close-cross-container {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
right: 30px;
top: 40px;
height: 24px;
width: 24px;
cursor: pointer;
&:hover .close-cross-svg-path {
fill: #2683ff;
}
}
}
@media screen and (max-width: 720px) {
.register-success-popup {
&__info-panel-container {
display: none;
}
&__form-container {
&__button-container {
width: 100%;
}
}
}
}
</style>

View File

@ -0,0 +1,19 @@
<!--Copyright (C) 2019 Storj Labs, Inc.-->
<!--See LICENSE for copying information.-->
<div class="register-success-area" >
<div class="register-success-area__form-container">
<h2 class="register-success-area__form-container__title">Account Created!</h2>
<span class="register-success-area__form-container__sub-title">Check your email to complete registration.</span>
<p class="register-success-area__form-container__text">Didnt receive a verification email?<b class="register-success-area__form-container__verification-cooldown__bold-text"> {{timeToEnableResendEmailButton}}</b></p>
<div class="register-success-area__form-container__button-container">
<VButton
label="Resend Email"
width="450px"
height="50px"
:on-press="onResendEmailButtonClick"
:is-disabled="isResendEmailButtonDisabled"
/>
</div>
</div>
</div>

View File

@ -0,0 +1,70 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
.register-success-area {
width: calc(100% - 150px);
display: flex;
align-items: center;
justify-content: center;
padding: 80px 100px 80px 50px;
font-family: 'font_regular', sans-serif;
&__form-container {
max-width: 440px;
&__title {
font-family: 'font_bold', sans-serif;
font-size: 32px;
line-height: 39px;
color: #384b65;
margin: 0 0 25px 0;
}
&__sub-title {
font-size: 16px;
line-height: 21px;
color: #354049;
}
&__text {
font-family: 'font_medium', sans-serif;
font-size: 16px;
line-height: 21px;
color: #354049;
margin: 27px 0 0 0;
}
&__verification-cooldown {
font-family: 'font_medium', sans-serif;
font-size: 12px;
line-height: 16px;
color: #354049;
padding: 27px 0 0 0;
margin: 0;
&__bold-text {
color: #2683ff;
}
}
&__button-container {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
margin-top: 15px;
}
}
}
@media screen and (max-width: 650px) {
.register-success-area {
width: 100%;
padding: 100px 0 50px 0;
}
/deep/ .container {
width: 100% !important;
}
}

View File

@ -1,26 +0,0 @@
<!--Copyright (C) 2019 Storj Labs, Inc.-->
<!--See LICENSE for copying information.-->
<div v-if="isPopupShown" class="register-success-popup-container" id="successfulRegistrationPopup">
<div class="register-success-popup" >
<div class="register-success-popup__info-panel-container">
<RegistrationSuccessIcon/>
</div>
<div class="register-success-popup__form-container">
<h2 class="register-success-popup__form-container__title">Check your inbox for your verification email</h2>
<p class="register-success-popup__form-container__text">Didnt receive a verification email?<b class="register-success-popup__form-container__verification-cooldown__bold-text"> {{timeToEnableResendEmailButton}}</b></p>
<div class="register-success-popup__form-container__button-container">
<VButton
label="Resend Email"
width="450px"
height="50px"
:on-press="onResendEmailButtonClick"
:is-disabled="isResendEmailButtonDisabled"
/>
</div>
</div>
<div class="register-success-popup__close-cross-container" @click="onCloseClick">
<CloseCrossIcon/>
</div>
</div>
</div>

View File

@ -49,7 +49,7 @@ export default class ProjectDashboard extends Vue {
position: absolute;
right: 65px;
top: 44px;
z-index: 99;
z-index: 19;
}
}

View File

@ -58,6 +58,7 @@ export abstract class RouteConfig {
export const notProjectRelatedRoutes = [
RouteConfig.Login.name,
RouteConfig.Register.name,
RouteConfig.ForgotPassword.name,
RouteConfig.Billing.name,
RouteConfig.BillingHistory.name,
RouteConfig.Settings.name,

View File

@ -18,7 +18,7 @@ export const appStateModule = {
isDeleteProjectPopupShown: false,
isDeleteAccountPopupShown: false,
isSortProjectMembersByPopupShown: false,
isSuccessfulRegistrationPopupShown: false,
isSuccessfulRegistrationShown: false,
isSuccessfulProjectCreationPopupShown: false,
isEditProfilePopupShown: false,
isChangePasswordPopupShown: false,
@ -57,15 +57,15 @@ export const appStateModule = {
[APP_STATE_MUTATIONS.TOGGLE_DELETE_ACCOUNT_DROPDOWN](state: any): void {
state.appState.isDeleteAccountPopupShown = !state.appState.isDeleteAccountPopupShown;
},
// Mutation changing 'sort project members by' popup visibility
// Mutation changing 'sort project members by' popup visibility.
[APP_STATE_MUTATIONS.TOGGLE_SORT_PM_BY_DROPDOWN](state: any): void {
state.appState.isSortProjectMembersByPopupShown = !state.appState.isSortProjectMembersByPopupShown;
},
// Mutation changing 'successful registration' popup visibility
[APP_STATE_MUTATIONS.TOGGLE_SUCCESSFUL_REGISTRATION_POPUP](state: any): void {
state.appState.isSuccessfulRegistrationPopupShown = !state.appState.isSuccessfulRegistrationPopupShown;
// Mutation changing 'successful registration' area visibility.
[APP_STATE_MUTATIONS.TOGGLE_SUCCESSFUL_REGISTRATION](state: any): void {
state.appState.isSuccessfulRegistrationShown = !state.appState.isSuccessfulRegistrationShown;
},
// Mutation changing 'successful project creation' popup visibility
// Mutation changing 'successful project creation' popup visibility.
[APP_STATE_MUTATIONS.TOGGLE_SUCCESSFUL_PROJECT_CREATION_POPUP](state: any): void {
state.appState.isSuccessfulProjectCreationPopupShown = !state.appState.isSuccessfulProjectCreationPopupShown;
},
@ -98,7 +98,6 @@ export const appStateModule = {
state.appState.isAccountDropdownShown = false;
state.appState.isProjectsDropdownShown = false;
state.appState.isSortProjectMembersByPopupShown = false;
state.appState.isSuccessfulRegistrationPopupShown = false;
},
[APP_STATE_MUTATIONS.CHANGE_STATE](state: any, newFetchState: AppState): void {
state.appState.fetchState = newFetchState;
@ -163,12 +162,12 @@ export const appStateModule = {
commit(APP_STATE_MUTATIONS.TOGGLE_SORT_PM_BY_DROPDOWN);
},
[APP_STATE_ACTIONS.TOGGLE_SUCCESSFUL_REGISTRATION_POPUP]: function ({commit, state}: any): void {
if (!state.appState.isSuccessfullRegistrationPopupShown) {
[APP_STATE_ACTIONS.TOGGLE_SUCCESSFUL_REGISTRATION]: function ({commit, state}: any): void {
if (!state.appState.isSuccessfulRegistrationShown) {
commit(APP_STATE_MUTATIONS.CLOSE_ALL);
}
commit(APP_STATE_MUTATIONS.TOGGLE_SUCCESSFUL_REGISTRATION_POPUP);
commit(APP_STATE_MUTATIONS.TOGGLE_SUCCESSFUL_REGISTRATION);
},
[APP_STATE_ACTIONS.TOGGLE_SUCCESSFUL_PROJECT_CREATION_POPUP]: function ({commit, state}: any): void {
if (!state.appState.isSuccessfulProjectCreationPopupShown) {

View File

@ -17,7 +17,7 @@ export const APP_STATE_MUTATIONS = {
TOGGLE_DELETE_PROJECT_DROPDOWN: 'TOGGLE_DELETE_PROJECT_DROPDOWN',
TOGGLE_DELETE_ACCOUNT_DROPDOWN: 'TOGGLE_DELETE_ACCOUNT_DROPDOWN',
TOGGLE_SORT_PM_BY_DROPDOWN: 'TOGGLE_SORT_PM_BY_DROPDOWN',
TOGGLE_SUCCESSFUL_REGISTRATION_POPUP: 'TOGGLE_SUCCESSFUL_REGISTRATION_POPUP',
TOGGLE_SUCCESSFUL_REGISTRATION: 'TOGGLE_SUCCESSFUL_REGISTRATION',
TOGGLE_SUCCESSFUL_PROJECT_CREATION_POPUP: 'TOGGLE_SUCCESSFUL_PROJECT_CREATION_POPUP',
TOGGLE_EDIT_PROFILE_POPUP: 'TOGGLE_EDIT_PROFILE_POPUP',
TOGGLE_CHANGE_PASSWORD_POPUP: 'TOGGLE_CHANGE_PASSWORD_POPUP',
@ -32,8 +32,3 @@ export const APP_STATE_MUTATIONS = {
SHOW_CONTENT_BLUR: 'SHOW_CONTENT_BLUR',
HIDE_CONTENT_BLUR: 'HIDE_CONTENT_BLUR',
};
export const PROJECT_PAYMENT_METHODS_MUTATIONS = {
FETCH: 'FETCH',
CLEAR: 'CLEAR',
};

View File

@ -9,7 +9,7 @@ export const APP_STATE_ACTIONS = {
TOGGLE_DEL_PROJ: 'toggleDeleteProjectPopup',
TOGGLE_DEL_ACCOUNT: 'toggleDeleteAccountPopup',
TOGGLE_SORT_PM_BY_DROPDOWN: 'toggleSortProjectMembersByPopup',
TOGGLE_SUCCESSFUL_REGISTRATION_POPUP: 'toggleSuccessfulRegistrationPopup',
TOGGLE_SUCCESSFUL_REGISTRATION: 'TOGGLE_SUCCESSFUL_REGISTRATION',
TOGGLE_SUCCESSFUL_PROJECT_CREATION_POPUP: 'toggleSuccessfulProjectCreationPopup',
TOGGLE_EDIT_PROFILE_POPUP: 'toggleEditProfilePopup',
TOGGLE_CHANGE_PASSWORD_POPUP: 'toggleChangePasswordPopup',

View File

@ -8,7 +8,7 @@ import { Component, Vue } from 'vue-property-decorator';
import HeaderlessInput from '@/components/common/HeaderlessInput.vue';
import PasswordStrength from '@/components/common/PasswordStrength.vue';
import RegistrationSuccessPopup from '@/components/common/RegistrationSuccessPopup.vue';
import RegistrationSuccess from '@/components/common/RegistrationSuccess.vue';
import AuthIcon from '@/../static/images/AuthImage.svg';
import InfoIcon from '@/../static/images/info.svg';
@ -24,7 +24,7 @@ import { validateEmail, validatePassword } from '@/utils/validation';
@Component({
components: {
HeaderlessInput,
RegistrationSuccessPopup,
RegistrationSuccess,
AuthIcon,
LogoIcon,
InfoIcon,
@ -85,22 +85,35 @@ export default class RegisterArea extends Vue {
}
/**
* Checks if page is inside iframe
* Indicates if registration successful area shown.
*/
public get isRegistrationSuccessful(): boolean {
return this.$store.state.appStateModule.appState.isSuccessfulRegistrationShown;
}
/**
* Checks if page is inside iframe.
*/
public get isInsideIframe(): boolean {
return window.self !== window.top;
}
/**
* Makes password strength container visible.
*/
public showPasswordStrength(): void {
this.isPasswordStrengthShown = true;
}
/**
* Hides password strength container.
*/
public hidePasswordStrength(): void {
this.isPasswordStrengthShown = false;
}
/**
* Register user.
* Validates input fields and proceeds user creation.
*/
public async onCreateClick(): Promise<void> {
if (this.isLoading) {
@ -134,27 +147,42 @@ export default class RegisterArea extends Vue {
this.$router.push(RouteConfig.Login.path);
}
/**
* Sets user's email field from value string.
*/
public setEmail(value: string): void {
this.user.email = value.trim();
this.emailError = '';
}
/**
* Sets user's full name field from value string.
*/
public setFullName(value: string): void {
this.user.fullName = value.trim();
this.fullNameError = '';
}
/**
* Sets user's password field from value string.
*/
public setPassword(value: string): void {
this.user.password = value.trim();
this.password = value;
this.passwordError = '';
}
/**
* Sets user's repeat password field from value string.
*/
public setRepeatedPassword(value: string): void {
this.repeatedPassword = value;
this.repeatedPasswordError = '';
}
/**
* Validates input values to satisfy expected rules.
*/
private validateFields(): boolean {
let isNoErrors = true;
@ -186,6 +214,9 @@ export default class RegisterArea extends Vue {
return isNoErrors;
}
/**
* Creates user and toggles successful registration area visibility.
*/
private async createUser(): Promise<void> {
try {
this.userId = this.referralToken ?
@ -199,13 +230,7 @@ export default class RegisterArea extends Vue {
referralToken: this.referralToken,
});
// TODO: improve it
await this.$store.dispatch(APP_STATE_ACTIONS.TOGGLE_SUCCESSFUL_REGISTRATION_POPUP);
const registrationSuccessPopupRef = this.$refs['register_success_popup'];
if (registrationSuccessPopupRef) {
(registrationSuccessPopupRef as any).startResendEmailCountdown();
}
await this.$store.dispatch(APP_STATE_ACTIONS.TOGGLE_SUCCESSFUL_REGISTRATION);
} catch (error) {
await this.$notify.error(error.message);
this.isLoading = false;

View File

@ -18,7 +18,7 @@
</div>
</div>
<div class="register-area-wrapper">
<div class="register-area">
<div class="register-area" v-if="!isRegistrationSuccessful">
<div class="register-area__title-container">
<h1 class="register-area__title-container__title">Sign Up to Storj</h1>
</div>
@ -83,7 +83,7 @@
</div>
</div>
</div>
<RegistrationSuccess v-if="isRegistrationSuccessful"/>
</div>
</div>
<RegistrationSuccessPopup ref="register_success_popup"/>
</div>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 7.2 KiB