From a3fc1c8d308c35aaf0e6e378243b08811e7e0f40 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Mon, 9 Apr 2018 14:17:58 -0400 Subject: [PATCH] manage state for vault pages --- src/popup2/app.component.ts | 17 ++++++++- src/popup2/services/popup-utils.service.ts | 12 +++++++ src/popup2/tabs.component.ts | 12 +------ src/popup2/vault/ciphers.component.ts | 28 ++++++++++++--- src/popup2/vault/groupings.component.ts | 40 ++++++++++++++++------ 5 files changed, 83 insertions(+), 26 deletions(-) diff --git a/src/popup2/app.component.ts b/src/popup2/app.component.ts index ca00d18589..3fc79b290e 100644 --- a/src/popup2/app.component.ts +++ b/src/popup2/app.component.ts @@ -9,6 +9,7 @@ import { OnInit, } from '@angular/core'; import { + NavigationEnd, Router, RouterOutlet, } from '@angular/router'; @@ -22,6 +23,7 @@ import { BroadcasterService } from 'jslib/angular/services/broadcaster.service'; import { AuthService } from 'jslib/abstractions/auth.service'; import { I18nService } from 'jslib/abstractions/i18n.service'; +import { StateService } from 'jslib/abstractions/state.service'; import { StorageService } from 'jslib/abstractions/storage.service'; import { ConstantsService } from 'jslib/services/constants.service'; @@ -49,11 +51,13 @@ export class AppComponent implements OnInit { }); private lastActivity: number = null; + private previousUrl: string = ''; constructor(private angulartics2GoogleAnalytics: Angulartics2GoogleAnalytics, private analytics: Angulartics2, private toasterService: ToasterService, private storageService: StorageService, private broadcasterService: BroadcasterService, private authService: AuthService, - private i18nService: I18nService, private router: Router) { } + private i18nService: I18nService, private router: Router, + private stateService: StateService) { } ngOnInit() { window.onmousemove = () => this.recordActivity(); @@ -80,6 +84,17 @@ export class AppComponent implements OnInit { }; BrowserApi.messageListener((window as any).bitwardenPopupMainMessageListener); + + this.router.events.subscribe(event => { + if (event instanceof NavigationEnd) { + const url = event.urlAfterRedirects || event.url || ''; + if (url.startsWith('/tabs/') && this.previousUrl.startsWith('/tabs/')) { + this.stateService.remove('GroupingsComponent'); + this.stateService.remove('CiphersComponent'); + } + this.previousUrl = url; + } + }); } getState(outlet: RouterOutlet) { diff --git a/src/popup2/services/popup-utils.service.ts b/src/popup2/services/popup-utils.service.ts index a794f14146..cb15af9bfe 100644 --- a/src/popup2/services/popup-utils.service.ts +++ b/src/popup2/services/popup-utils.service.ts @@ -18,4 +18,16 @@ export class PopupUtilsService { return win.location.search === '' || win.location.search.indexOf('uilocation=') === -1 || win.location.search.indexOf('uilocation=popup') > -1; } + + getContentScrollY(win: Window): number { + const content = win.document.getElementsByTagName('content')[0]; + return content.scrollTop; + } + + setContentScrollY(win: Window, scrollY: number): void { + if (scrollY != null) { + const content = win.document.getElementsByTagName('content')[0]; + content.scrollTop = scrollY; + } + } } diff --git a/src/popup2/tabs.component.ts b/src/popup2/tabs.component.ts index a0104db859..8c5df54565 100644 --- a/src/popup2/tabs.component.ts +++ b/src/popup2/tabs.component.ts @@ -1,14 +1,4 @@ -import { - Component, - ComponentFactoryResolver, - NgZone, - OnDestroy, - OnInit, - Type, - ViewChild, - ViewContainerRef, -} from '@angular/core'; -import { Router } from '@angular/router'; +import { Component } from '@angular/core'; @Component({ selector: 'app-tabs', diff --git a/src/popup2/vault/ciphers.component.ts b/src/popup2/vault/ciphers.component.ts index 56fec2e8ba..2848271799 100644 --- a/src/popup2/vault/ciphers.component.ts +++ b/src/popup2/vault/ciphers.component.ts @@ -12,6 +12,7 @@ import { } from '@angular/router'; import { CipherService } from 'jslib/abstractions/cipher.service'; +import { StateService } from 'jslib/abstractions/state.service'; import { CipherView } from 'jslib/models/view/cipherView'; @@ -19,7 +20,9 @@ import { BroadcasterService } from 'jslib/angular/services/broadcaster.service'; import { CiphersComponent as BaseCiphersComponent } from 'jslib/angular/components/ciphers.component'; -const BroadcasterSubscriptionId = 'CiphersComponent'; +import { PopupUtilsService } from '../services/popup-utils.service'; + +const ComponentId = 'CiphersComponent'; @Component({ selector: 'app-vault-ciphers', @@ -27,11 +30,13 @@ const BroadcasterSubscriptionId = 'CiphersComponent'; }) export class CiphersComponent extends BaseCiphersComponent implements OnInit, OnDestroy { searchText: string; + state: any; constructor(cipherService: CipherService, private route: ActivatedRoute, private router: Router, private location: Location, private ngZone: NgZone, private broadcasterService: BroadcasterService, - private changeDetectorRef: ChangeDetectorRef) { + private changeDetectorRef: ChangeDetectorRef, private stateService: StateService, + private popupUtils: PopupUtilsService) { super(cipherService); } @@ -47,9 +52,15 @@ export class CiphersComponent extends BaseCiphersComponent implements OnInit, On } else { await super.load(); } + + this.state = (await this.stateService.get(ComponentId)) || {}; + if (this.state.searchText) { + this.searchText = this.state.searchText; + } + window.setTimeout(() => this.popupUtils.setContentScrollY(window, this.state.scrollY), 0); }); - this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => { + this.broadcasterService.subscribe(ComponentId, (message: any) => { this.ngZone.run(async () => { switch (message.command) { case 'syncCompleted': @@ -67,7 +78,8 @@ export class CiphersComponent extends BaseCiphersComponent implements OnInit, On } ngOnDestroy() { - this.broadcasterService.unsubscribe(BroadcasterSubscriptionId); + this.saveState(); + this.broadcasterService.unsubscribe(ComponentId); } selectCipher(cipher: CipherView) { @@ -83,4 +95,12 @@ export class CiphersComponent extends BaseCiphersComponent implements OnInit, On back() { this.location.back(); } + + private async saveState() { + this.state = { + scrollY: this.popupUtils.getContentScrollY(window), + searchText: this.searchText, + }; + await this.stateService.save(ComponentId, this.state); + } } diff --git a/src/popup2/vault/groupings.component.ts b/src/popup2/vault/groupings.component.ts index f1b54c7ac1..44992c0732 100644 --- a/src/popup2/vault/groupings.component.ts +++ b/src/popup2/vault/groupings.component.ts @@ -19,12 +19,15 @@ import { FolderView } from 'jslib/models/view/folderView'; import { CollectionService } from 'jslib/abstractions/collection.service'; import { CipherService } from 'jslib/abstractions/cipher.service'; import { FolderService } from 'jslib/abstractions/folder.service'; +import { StateService } from 'jslib/abstractions/state.service'; import { BroadcasterService } from 'jslib/angular/services/broadcaster.service'; import { GroupingsComponent as BaseGroupingsComponent } from 'jslib/angular/components/groupings.component'; -const BroadcasterSubscriptionId = 'GroupingsComponent'; +import { PopupUtilsService } from '../services/popup-utils.service'; + +const ComponentId = 'GroupingsComponent'; @Component({ selector: 'app-vault-groupings', @@ -40,16 +43,20 @@ export class GroupingsComponent extends BaseGroupingsComponent implements OnInit typeCounts = new Map(); showNoFolderCiphers = false; searchText: string; + state: any; constructor(collectionService: CollectionService, folderService: FolderService, private cipherService: CipherService, private router: Router, private ngZone: NgZone, private broadcasterService: BroadcasterService, - private changeDetectorRef: ChangeDetectorRef, private route: ActivatedRoute) { + private changeDetectorRef: ChangeDetectorRef, private route: ActivatedRoute, + private stateService: StateService, private popupUtils: PopupUtilsService) { super(collectionService, folderService); } ngOnInit() { - this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => { + this.stateService.remove('CiphersComponent'); + + this.broadcasterService.subscribe(ComponentId, (message: any) => { this.ngZone.run(async () => { switch (message.command) { case 'syncCompleted': @@ -66,16 +73,21 @@ export class GroupingsComponent extends BaseGroupingsComponent implements OnInit }); this.route.queryParams.subscribe(async (params) => { - if (params.searchText) { + this.state = (await this.stateService.get(ComponentId)) || {}; + if (this.state.searchText) { + this.searchText = this.state.searchText; + } else if (params.searchText) { this.searchText = params.searchText; } this.load(); + window.setTimeout(() => this.popupUtils.setContentScrollY(window, this.state.scrollY), 0); }); } ngOnDestroy() { - this.broadcasterService.unsubscribe(BroadcasterSubscriptionId); + this.saveState(); + this.broadcasterService.unsubscribe(ComponentId); } async load() { @@ -137,26 +149,34 @@ export class GroupingsComponent extends BaseGroupingsComponent implements OnInit }); } - selectType(type: CipherType) { + async selectType(type: CipherType) { super.selectType(type); this.router.navigate(['/ciphers'], { queryParams: { type: type } }); } - selectFolder(folder: FolderView) { + async selectFolder(folder: FolderView) { super.selectFolder(folder); this.router.navigate(['/ciphers'], { queryParams: { folderId: folder.id } }); } - selectCollection(collection: CollectionView) { + async selectCollection(collection: CollectionView) { super.selectCollection(collection); this.router.navigate(['/ciphers'], { queryParams: { collectionId: collection.id } }); } - selectCipher(cipher: CipherView) { + async selectCipher(cipher: CipherView) { this.router.navigate(['/view-cipher'], { queryParams: { cipherId: cipher.id } }); } - addCipher() { + async addCipher() { this.router.navigate(['/add-cipher']); } + + private async saveState() { + this.state = { + scrollY: this.popupUtils.getContentScrollY(window), + searchText: this.searchText, + }; + await this.stateService.save(ComponentId, this.state); + } }