diff --git a/src/popup/app-routing.module.ts b/src/popup/app-routing.module.ts index 7a83903220..7fe2ddfeaf 100644 --- a/src/popup/app-routing.module.ts +++ b/src/popup/app-routing.module.ts @@ -8,6 +8,7 @@ import { import { AuthGuardService } from 'jslib-angular/services/auth-guard.service'; +import { DebounceNavigationService } from './services/debounceNavigationService'; import { LaunchGuardService } from './services/launch-guard.service'; import { LockGuardService } from './services/lock-guard.service'; @@ -142,14 +143,16 @@ const routes: Routes = [ { path: 'add-cipher', component: AddEditComponent, - canActivate: [AuthGuardService], + canActivate: [AuthGuardService, DebounceNavigationService], data: { state: 'add-cipher' }, + runGuardsAndResolvers: 'always', }, { path: 'edit-cipher', component: AddEditComponent, - canActivate: [AuthGuardService], + canActivate: [AuthGuardService, DebounceNavigationService], data: { state: 'edit-cipher' }, + runGuardsAndResolvers: 'always', }, { path: 'share-cipher', diff --git a/src/popup/services/debounceNavigationService.ts b/src/popup/services/debounceNavigationService.ts new file mode 100644 index 0000000000..b5f40ffb7d --- /dev/null +++ b/src/popup/services/debounceNavigationService.ts @@ -0,0 +1,53 @@ +import { + Injectable, + OnDestroy +} from '@angular/core'; +import { + CanActivate, + NavigationEnd, + NavigationStart, + Router, +} from '@angular/router'; + +import { Subscription } from 'rxjs'; +import { + filter, + pairwise, +} from 'rxjs/operators'; + +@Injectable() +export class DebounceNavigationService implements CanActivate, OnDestroy { + navigationStartSub: Subscription; + navigationSuccessSub: Subscription; + + private lastNavigation: NavigationStart; + private thisNavigation: NavigationStart; + private lastNavigationSuccessId: number; + + constructor(private router: Router) { + this.navigationStartSub = this.router.events + .pipe(filter(event => event instanceof NavigationStart), pairwise()) + .subscribe((events: [NavigationStart, NavigationStart]) => [this.lastNavigation, this.thisNavigation] = events); + + this.navigationSuccessSub = this.router.events + .pipe(filter(event => event instanceof NavigationEnd)) + .subscribe((event: NavigationEnd) => this.lastNavigationSuccessId = event.id); + } + + async canActivate() { + return !(this.thisNavigation?.navigationTrigger === 'hashchange' && + this.lastNavigation.navigationTrigger === 'popstate' && + this.lastNavigationSuccessId === this.lastNavigation.id && + this.lastNavigation.url === this.thisNavigation?.url); + } + + ngOnDestroy() { + if (this.navigationStartSub != null) { + this.navigationStartSub.unsubscribe(); + } + + if (this.navigationSuccessSub != null) { + this.navigationSuccessSub.unsubscribe(); + } + } +} diff --git a/src/popup/services/services.module.ts b/src/popup/services/services.module.ts index a41236151c..26ffbf2709 100644 --- a/src/popup/services/services.module.ts +++ b/src/popup/services/services.module.ts @@ -6,8 +6,9 @@ import { import { ToasterModule } from 'angular2-toaster'; -import { LockGuardService } from './lock-guard.service'; +import { DebounceNavigationService } from './debounceNavigationService'; import { LaunchGuardService } from './launch-guard.service'; +import { LockGuardService } from './lock-guard.service'; import { UnauthGuardService } from './unauth-guard.service'; import { AuthGuardService } from 'jslib-angular/services/auth-guard.service'; @@ -121,6 +122,7 @@ export function initFactory(platformUtilsService: PlatformUtilsService, i18nServ LockGuardService, LaunchGuardService, UnauthGuardService, + DebounceNavigationService, PopupUtilsService, BroadcasterService, { provide: MessagingService, useValue: messagingService },