diff --git a/src/app/accounts/login.component.html b/src/app/accounts/login.component.html index 9d4f05f79f..52fe05eb3f 100644 --- a/src/app/accounts/login.component.html +++ b/src/app/accounts/login.component.html @@ -1,37 +1,35 @@ -
-
-
- bitwarden -

{{'loginOrCreateNewAccount' | i18n}}

-
-
-
- - -
-
- - -
+ +
+ bitwarden +

{{'loginOrCreateNewAccount' | i18n}}

+
+
+
+ + +
+
+ +
-
- - -
- - -  {{'settings' | i18n}} +
+ - -
+ + +  {{'settings' | i18n}} + +
+ diff --git a/src/app/accounts/login.component.ts b/src/app/accounts/login.component.ts index 4d697c06cc..1d57749a1d 100644 --- a/src/app/accounts/login.component.ts +++ b/src/app/accounts/login.component.ts @@ -24,7 +24,7 @@ export class LoginComponent { constructor(private authService: AuthService, private router: Router, private analytics: Angulartics2, private toasterService: ToasterService, private i18nService: I18nService) { } - async onSubmit() { + async submit() { if (this.email == null || this.email === '') { this.toasterService.popAsync('error', this.i18nService.t('errorOccurred'), this.i18nService.t('emailRequired')); diff --git a/src/app/accounts/register.component.html b/src/app/accounts/register.component.html new file mode 100644 index 0000000000..7b84f34250 --- /dev/null +++ b/src/app/accounts/register.component.html @@ -0,0 +1,45 @@ +
+
+

{{'createAccount' | i18n}}

+
+
+
+ + +
+
+ + +
+
+ +
+
+
+
+ + +
+
+ + +
+
+ +
+
+ + {{'cancel' | i18n}} +
+
+
diff --git a/src/app/accounts/register.component.ts b/src/app/accounts/register.component.ts new file mode 100644 index 0000000000..1d79641065 --- /dev/null +++ b/src/app/accounts/register.component.ts @@ -0,0 +1,78 @@ +import * as template from './register.component.html'; + +import { + Component, +} from '@angular/core'; + +import { Router } from '@angular/router'; + +import { Angulartics2 } from 'angulartics2'; +import { ToasterService } from 'angular2-toaster'; + +import { RegisterRequest } from 'jslib/models/request/registerRequest'; + +import { ApiService } from 'jslib/abstractions/api.service'; +import { AuthService } from 'jslib/abstractions/auth.service'; +import { CryptoService } from 'jslib/abstractions/crypto.service'; +import { I18nService } from 'jslib/abstractions/i18n.service'; + +@Component({ + selector: 'app-register', + template: template, +}) +export class RegisterComponent { + email: string = ''; + masterPassword: string = ''; + confirmMasterPassword: string = ''; + hint: string = ''; + formPromise: Promise; + + constructor(private authService: AuthService, private router: Router, private analytics: Angulartics2, + private toasterService: ToasterService, private i18nService: I18nService, + private cryptoService: CryptoService, private apiService: ApiService) { } + + async submit() { + if (this.email == null || this.email === '') { + this.toasterService.popAsync('error', this.i18nService.t('errorOccurred'), + this.i18nService.t('emailRequired')); + return; + } + if (this.email.indexOf('@') === -1) { + this.toasterService.popAsync('error', this.i18nService.t('errorOccurred'), + this.i18nService.t('invalidEmail')); + return; + } + if (this.masterPassword == null || this.masterPassword === '') { + this.toasterService.popAsync('error', this.i18nService.t('errorOccurred'), + this.i18nService.t('masterPassRequired')); + return; + } + if (this.masterPassword.length < 8) { + this.toasterService.popAsync('error', this.i18nService.t('errorOccurred'), + this.i18nService.t('masterPassLength')); + return; + } + if (this.masterPassword !== this.confirmMasterPassword) { + this.toasterService.popAsync('error', this.i18nService.t('errorOccurred'), + this.i18nService.t('masterPassDoesntMatch')); + return; + } + + try { + this.formPromise = this.register(); + await this.formPromise; + this.analytics.eventTrack.next({ action: 'Registered' }); + this.toasterService.popAsync('success', null, this.i18nService.t('newAccountCreated')); + this.router.navigate(['login']); + } catch { } + } + + private async register() { + this.email = this.email.toLowerCase(); + const key = this.cryptoService.makeKey(this.masterPassword, this.email); + const encKey = await this.cryptoService.makeEncKey(key); + const hashedPassword = await this.cryptoService.hashPassword(this.masterPassword, key); + const request = new RegisterRequest(this.email, hashedPassword, this.hint, encKey.encryptedString); + await this.apiService.postRegister(request); + } +} diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index fc858be02e..25bb879908 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -5,11 +5,13 @@ import { } from '@angular/router'; import { LoginComponent } from './accounts/login.component'; +import { RegisterComponent } from './accounts/register.component'; import { VaultComponent } from './vault/vault.component'; const routes: Routes = [ { path: '', redirectTo: '/login', pathMatch: 'full' }, { path: 'login', component: LoginComponent }, + { path: 'register', component: RegisterComponent }, { path: 'vault', component: VaultComponent }, ]; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 8c42e6187e..11f8bcc1fe 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -26,6 +26,7 @@ import { IconComponent } from './vault/icon.component'; import { LoginComponent } from './accounts/login.component'; import { ModalComponent } from './modal.component'; import { PasswordGeneratorComponent } from './vault/password-generator.component'; +import { RegisterComponent } from './accounts/register.component'; import { SearchCiphersPipe } from './pipes/search-ciphers.pipe'; import { StopClickDirective } from './directives/stop-click.directive'; import { StopPropDirective } from './directives/stop-prop.directive'; @@ -62,6 +63,7 @@ import { ViewComponent } from './vault/view.component'; LoginComponent, ModalComponent, PasswordGeneratorComponent, + RegisterComponent, SearchCiphersPipe, StopClickDirective, StopPropDirective, diff --git a/src/app/services/services.module.ts b/src/app/services/services.module.ts index a35233134d..b591d5e113 100644 --- a/src/app/services/services.module.ts +++ b/src/app/services/services.module.ts @@ -130,7 +130,7 @@ function initFactory(i18n: I18nService, platformUtilsService: DesktopPlatformUti { provide: CryptoServiceAbstraction, useValue: cryptoService }, { provide: PlatformUtilsServiceAbstraction, useValue: platformUtilsService }, { provide: PasswordGenerationServiceAbstraction, useValue: passwordGenerationService }, - { provide: PasswordGenerationServiceAbstraction, useValue: passwordGenerationService }, + { provide: ApiServiceAbstraction, useValue: apiService }, { provide: APP_INITIALIZER, useFactory: initFactory, diff --git a/src/scss/buttons.scss b/src/scss/buttons.scss index 51353290ff..8ef719f6ef 100644 --- a/src/scss/buttons.scss +++ b/src/scss/buttons.scss @@ -7,6 +7,8 @@ border: 1px solid $button-border-color; font-size: $font-size-base; color: $button-color; + white-space: nowrap; + text-align: center; &:hover:not([disabled]), &:focus:not([disabled]) { cursor: pointer; diff --git a/src/scss/pages.scss b/src/scss/pages.scss index ee8a89c5f6..8b44428dac 100644 --- a/src/scss/pages.scss +++ b/src/scss/pages.scss @@ -31,9 +31,17 @@ margin-bottom: 20px; } + .box { + margin-bottom: 20px; + + &.last { + margin-bottom: 15px; + } + } + .buttons { display: flex; - margin: 15px 0 20px; + margin-bottom: 20px; button { margin-right: 10px;