From 26fab7b26c68130d4cc0215bfba981fcb4435cc3 Mon Sep 17 00:00:00 2001 From: Vladimir Y Date: Sat, 30 Jun 2018 09:33:11 +0300 Subject: [PATCH] simplify "keepass-request" component --- src/electron-main/index.ts | 6 - src/electron-main/ipc-main-api.ts | 10 +- src/electron-main/window.ts | 4 + .../src/app/+accounts/account.component.html | 4 +- .../src/app/+accounts/account.component.ts | 13 +- src/web/src/app/+accounts/accounts.module.ts | 2 + .../keepass-request.component.html | 8 +- .../keepass-request.component.scss | 2 +- .../+accounts/keepass-request.component.ts | 97 +++++++++++++ .../app/+shared/keepass-request.component.ts | 132 ------------------ src/web/src/app/+shared/let.directive.ts | 23 --- src/web/src/app/+shared/shared.module.ts | 8 -- 12 files changed, 120 insertions(+), 189 deletions(-) rename src/web/src/app/{+shared => +accounts}/keepass-request.component.html (51%) rename src/web/src/app/{+shared => +accounts}/keepass-request.component.scss (98%) create mode 100644 src/web/src/app/+accounts/keepass-request.component.ts delete mode 100644 src/web/src/app/+shared/keepass-request.component.ts delete mode 100644 src/web/src/app/+shared/let.directive.ts diff --git a/src/electron-main/index.ts b/src/electron-main/index.ts index c7c99f23f..870d1f725 100644 --- a/src/electron-main/index.ts +++ b/src/electron-main/index.ts @@ -27,12 +27,6 @@ export async function initApp(ctx: Context) { app.exit(); } - if ((process.env.NODE_ENV as BuildEnvironment) === "development") { - app.on("web-contents-created", (webContentsCreatedEvent, contents) => { - contents.openDevTools(); - }); - } - app.on("ready", async () => { const endpoints = await initEndpoints(ctx); const {checkForUpdatesAndNotify} = await endpoints.readConfig().toPromise(); diff --git a/src/electron-main/ipc-main-api.ts b/src/electron-main/ipc-main-api.ts index e09d9dbd2..1544928c1 100644 --- a/src/electron-main/ipc-main-api.ts +++ b/src/electron-main/ipc-main-api.ts @@ -71,20 +71,20 @@ export const initEndpoints = async (ctx: Context): Promise => { hasSavedPassword: !!password, }; })()), - keePassRecordRequest: (payload) => from((async () => { - const client = new KeePassHttpClient(payload.keePassClientConf); + keePassRecordRequest: ({keePassClientConf, keePassRef, suppressErrors}) => from((async () => { + const client = new KeePassHttpClient(keePassClientConf); let response; try { await client.testAssociate(); - response = await client.getLogins({url: payload.keePassRef.url}); + response = await client.getLogins({url: keePassRef.url}); } catch (error) { - return handleKeePassRequestError(error, payload.suppressErrors); + return handleKeePassRequestError(error, suppressErrors); } if (response.Entries) { for (const entry of response.Entries) { - if (entry && entry.Uuid === payload.keePassRef.uuid) { + if (entry && entry.Uuid === keePassRef.uuid) { return {password: entry.Password}; } } diff --git a/src/electron-main/window.ts b/src/electron-main/window.ts index f7ffdfb19..03a89a2a2 100644 --- a/src/electron-main/window.ts +++ b/src/electron-main/window.ts @@ -69,6 +69,10 @@ export async function initBrowserWindow(ctx: Context): Promise { // execute after handlers subscriptions await keepState(ctx, browserWindow); + if ((process.env.NODE_ENV as BuildEnvironment) === "development") { + browserWindow.webContents.openDevTools(); + } + return browserWindow; } diff --git a/src/web/src/app/+accounts/account.component.html b/src/web/src/app/+accounts/account.component.html index 1f26eb587..deeb988f5 100644 --- a/src/web/src/app/+accounts/account.component.html +++ b/src/web/src/app/+accounts/account.component.html @@ -1,5 +1,5 @@ this.webView.openDevTools()); + // } + this.subscribePageLoadingEvents(); this.webView.addEventListener("new-window", ({url}: any) => { diff --git a/src/web/src/app/+accounts/accounts.module.ts b/src/web/src/app/+accounts/accounts.module.ts index b4ce30e71..2c5fddbdc 100644 --- a/src/web/src/app/+accounts/accounts.module.ts +++ b/src/web/src/app/+accounts/accounts.module.ts @@ -9,6 +9,7 @@ import {AccountComponent} from "./account.component"; import {AccountTitleComponent} from "./account-title.component"; import {AccountsEffects} from "./accounts.effects"; import {AccountsGuard} from "./accounts.guard"; +import {KeePassRequestComponent} from "./keepass-request.component"; @NgModule({ imports: [ @@ -21,6 +22,7 @@ import {AccountsGuard} from "./accounts.guard"; AccountsComponent, AccountComponent, AccountTitleComponent, + KeePassRequestComponent, ], providers: [ AccountsGuard, diff --git a/src/web/src/app/+shared/keepass-request.component.html b/src/web/src/app/+accounts/keepass-request.component.html similarity index 51% rename from src/web/src/app/+shared/keepass-request.component.html rename to src/web/src/app/+accounts/keepass-request.component.html index cf781d841..61374b8a6 100644 --- a/src/web/src/app/+shared/keepass-request.component.html +++ b/src/web/src/app/+accounts/keepass-request.component.html @@ -1,12 +1,12 @@
KeePass: {{ message || "Requesting..." }}
-
+
-
+
diff --git a/src/web/src/app/+shared/keepass-request.component.scss b/src/web/src/app/+accounts/keepass-request.component.scss similarity index 98% rename from src/web/src/app/+shared/keepass-request.component.scss rename to src/web/src/app/+accounts/keepass-request.component.scss index 670af3bfe..7508792a5 100644 --- a/src/web/src/app/+shared/keepass-request.component.scss +++ b/src/web/src/app/+accounts/keepass-request.component.scss @@ -6,7 +6,7 @@ $bg-color: #ffffff; $border-width: 1px; :host { - display: none; + display: block; position: relative; border: $border-width solid $fill-color; border-radius: $border-radius; diff --git a/src/web/src/app/+accounts/keepass-request.component.ts b/src/web/src/app/+accounts/keepass-request.component.ts new file mode 100644 index 000000000..9ed721b0f --- /dev/null +++ b/src/web/src/app/+accounts/keepass-request.component.ts @@ -0,0 +1,97 @@ +import {BehaviorSubject, EMPTY, interval, Observable, Subject} from "rxjs"; +import {Component, EventEmitter, Input, OnDestroy, OnInit, Output} from "@angular/core"; +import {distinctUntilChanged, scan, switchMap, takeUntil, tap, withLatestFrom} from "rxjs/operators"; +import {Store} from "@ngrx/store"; + +import {CORE_ACTIONS} from "_@web/src/app/store/actions"; +import {ElectronService} from "../+core/electron.service"; +import {KeePassClientConf, KeePassRef} from "_@shared/model/keepasshttp"; +import {State} from "_@web/src/app/store/reducers/root"; + +@Component({ + selector: `protonmail-desktop-app-keepass-request`, + templateUrl: "./keepass-request.component.html", + styleUrls: ["./keepass-request.component.scss"], +}) +export class KeePassRequestComponent implements OnInit, OnDestroy { + paused = false; + locked = false; + message?: string; + wait = 10; + progressTick = 0; + passwodRequestTick = 0; + + @Input() + keePassRef$: Observable; + @Input() + keePassClientConf$: Observable; + @Input() + locked$: Observable = new BehaviorSubject(false); + @Output() + passwordHandler = new EventEmitter(); + + unSubscribe$ = new Subject(); + + constructor(private store: Store, + private electronService: ElectronService) {} + + ngOnInit() { + this.keePassRef$ + .pipe( + distinctUntilChanged(), + withLatestFrom(this.keePassClientConf$), + switchMap(([keePassRef, keePassClientConf]) => { + if (!keePassRef) { + return EMPTY; + } + + return interval(1000).pipe( + scan((remaining) => remaining ? remaining - (this.paused ? 0 : 1) : this.wait, this.wait), + distinctUntilChanged(), + switchMap((remaining) => { + this.progressTick = (this.wait - remaining) * (100 / this.wait); + if (remaining) { + return EMPTY; + } + this.wait = this.wait < 30 ? 30 : 60; + this.passwodRequestTick++; + if (this.passwodRequestTick % 5 === 0) { + this.paused = true; + } + return this.electronService.keePassPassword(keePassClientConf, keePassRef, true); + }), + ); + }), + takeUntil(this.unSubscribe$), + ) + .subscribe( + ({password, message}) => { + if (password) { + this.passwordHandler.emit(password); + this.message = "Password received"; + } else { + this.message = message; + } + }, + (err) => { + this.store.dispatch(CORE_ACTIONS.Fail(err)); + }, + ); + + this.locked$ + .pipe(takeUntil(this.unSubscribe$)) + .subscribe((locked) => { + this.locked = locked; + this.togglePause(locked); + }); + } + + ngOnDestroy() { + this.unSubscribe$.next(); + this.unSubscribe$.complete(); + } + + togglePause(forcedValue?: boolean) { + this.paused = typeof forcedValue !== "undefined" ? forcedValue : !this.paused; + } +} diff --git a/src/web/src/app/+shared/keepass-request.component.ts b/src/web/src/app/+shared/keepass-request.component.ts deleted file mode 100644 index ef45a8702..000000000 --- a/src/web/src/app/+shared/keepass-request.component.ts +++ /dev/null @@ -1,132 +0,0 @@ -import {catchError, distinctUntilChanged, filter, mergeMap, scan, switchMap, takeUntil, withLatestFrom, mapTo} from "rxjs/operators"; -import {Observable, BehaviorSubject, Subject, interval, of} from "rxjs"; -import {Component, EventEmitter, HostBinding, Input, OnDestroy, OnInit, Output} from "@angular/core"; -import {Store} from "@ngrx/store"; - -import {CORE_ACTIONS} from "_@web/src/app/store/actions"; -import {ElectronService} from "../+core/electron.service"; -import {KeePassClientConf, KeePassRef} from "_@shared/model/keepasshttp"; - -type NumericObservable = Observable; - -@Component({ - selector: `protonmail-desktop-app-keepass-request`, - templateUrl: "./keepass-request.component.html", - styleUrls: ["./keepass-request.component.scss"], -}) -export class KeePassRequestComponent implements OnInit, OnDestroy { - tick = 0; - message?: string; - locked?: boolean; - - @HostBinding("class.d-block") - enabled: boolean; - - @Input() - keePassRef$: Observable; - @Input() - keePassClientConf$: Observable; - @Input() - lock$: Observable = new BehaviorSubject(false); - @Output() - passwordHandler = new EventEmitter(); - - // TODO might be useful to make "countdownSeconds" configurable via component's attribute - countdownSeconds = 5; - unSubscribe$ = new Subject(); - interval$: NumericObservable = interval(1000).pipe(mapTo(-1)); - counter$: BehaviorSubject = new BehaviorSubject(of(0)); - progress$: BehaviorSubject = new BehaviorSubject(false); - - // TODO pass store generic type from the outside - constructor(private store: Store, - private electronService: ElectronService) {} - - ngOnInit() { - this.keePassRef$ - .pipe( - distinctUntilChanged(), - mergeMap((keePassRef?: KeePassRef) => { - this.enabled = !!keePassRef; - this.counter$.next(this.enabled ? this.interval$ : of(0)); - - if (!keePassRef) { - return []; - } - - // TODO consider moving this to the "effects" service - // ie keep a component dump by interacting with the "store" only - // but in this case password would have to be passed through the in the store - not a perfect idea - return [ - this.progress(false).pipe( - switchMap(() => this.counter$.pipe( - switchMap((val) => val), - takeUntil(this.progress(true)), - scan((remaining, val) => val ? val + remaining : remaining, this.countdownSeconds), - )), - withLatestFrom(this.keePassClientConf$), - mergeMap(([remaining, keePassClientConf]) => { - const result = []; - - this.tick = (this.countdownSeconds - remaining) * (100 / this.countdownSeconds); - - if (remaining === 0) { - this.progress$.next(true); - result.push( - this.electronService.keePassPassword(keePassClientConf, keePassRef, true) - .pipe(catchError((error) => of(error))), - ); - } - - return result; - }), - ), - ]; - }), - switchMap((val) => val), - switchMap((val) => val), - takeUntil(this.unSubscribe$), - ) - .subscribe((arg) => { - this.progress$.next(false); - - if (arg instanceof Error) { - this.store.dispatch(CORE_ACTIONS.Fail(arg)); - } - - const {password, message} = arg; - - if (password) { - this.passwordHandler.emit(password); - this.message = "Password received"; - } else { - this.message = message; - } - }); - - this.lock$ - .pipe(takeUntil(this.unSubscribe$)) - .subscribe((locked) => { - this.locked = locked; - this.toggle(!locked); - }); - } - - ngOnDestroy() { - this.unSubscribe$.next(); - this.unSubscribe$.complete(); - } - - toggle(enable?: boolean) { - const enabled = typeof enable !== "undefined" - ? !enable - : this.counter$.getValue() === this.interval$; - - this.counter$.next(enabled ? of(0) : this.interval$); - } - - progress(value: boolean) { - return this.progress$ - .pipe(filter((progressValue) => progressValue === value)); - } -} diff --git a/src/web/src/app/+shared/let.directive.ts b/src/web/src/app/+shared/let.directive.ts deleted file mode 100644 index fd3454f9e..000000000 --- a/src/web/src/app/+shared/let.directive.ts +++ /dev/null @@ -1,23 +0,0 @@ -import {Directive, Input, TemplateRef, ViewContainerRef} from "@angular/core"; - -@Directive({ - // tslint:disable:directive-selector - selector: "[ngLet]", - // tslint:enable:directive-selector -}) -export class LetDirective { - context: any = {}; - - constructor(private vcRef: ViewContainerRef, private templateRef: TemplateRef) {} - - @Input() - set ngVar(context: any) { - this.context.$implicit = this.context.ngVar = context; - this.updateView(); - } - - updateView() { - this.vcRef.clear(); - this.vcRef.createEmbeddedView(this.templateRef, this.context); - } -} diff --git a/src/web/src/app/+shared/shared.module.ts b/src/web/src/app/+shared/shared.module.ts index 8a8f4f644..cd72da22a 100644 --- a/src/web/src/app/+shared/shared.module.ts +++ b/src/web/src/app/+shared/shared.module.ts @@ -2,22 +2,14 @@ import {NgModule} from "@angular/core"; import {CommonModule} from "@angular/common"; import {ReactiveFormsModule} from "@angular/forms"; -import {KeePassRequestComponent} from "./keepass-request.component"; -import {LetDirective} from "./let.directive"; - @NgModule({ imports: [ CommonModule, ReactiveFormsModule, ], - declarations: [ - KeePassRequestComponent, - LetDirective, - ], exports: [ CommonModule, ReactiveFormsModule, - KeePassRequestComponent, ], }) export class SharedModule {}