diff --git a/src/popup2/accounts/two-factor.component.ts b/src/popup2/accounts/two-factor.component.ts
index 73869987487..c0da93e0e39 100644
--- a/src/popup2/accounts/two-factor.component.ts
+++ b/src/popup2/accounts/two-factor.component.ts
@@ -1,4 +1,10 @@
-import { Component } from '@angular/core';
+import {
+ ChangeDetectorRef,
+ Component,
+ NgZone,
+ OnDestroy,
+ OnInit,
+} from '@angular/core';
import { Router } from '@angular/router';
@@ -16,8 +22,12 @@ import { I18nService } from 'jslib/abstractions/i18n.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { SyncService } from 'jslib/abstractions/sync.service';
+import { BroadcasterService } from 'jslib/angular/services/broadcaster.service';
+
import { TwoFactorComponent as BaseTwoFactorComponent } from 'jslib/angular/components/two-factor.component';
+const BroadcasterSubscriptionId = 'TwoFactorComponent';
+
@Component({
selector: 'app-two-factor',
templateUrl: 'two-factor.component.html',
@@ -29,13 +39,30 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
analytics: Angulartics2, toasterService: ToasterService,
i18nService: I18nService, apiService: ApiService,
platformUtilsService: PlatformUtilsService, syncService: SyncService,
- environmentService: EnvironmentService) {
+ environmentService: EnvironmentService, private ngZone: NgZone,
+ private broadcasterService: BroadcasterService, private changeDetectorRef: ChangeDetectorRef) {
super(authService, router, analytics, toasterService, i18nService, apiService,
platformUtilsService, syncService, window, environmentService);
this.successRoute = '/tabs/vault';
}
async ngOnInit() {
+ this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
+ this.ngZone.run(async () => {
+ switch (message.command) {
+ case '2faPageResponse':
+ if (message.type === 'duo') {
+ this.token = message.data.sigValue;
+ this.submitWithTab(message.webExtSender.tab);
+ }
+ default:
+ break;
+ }
+
+ this.changeDetectorRef.detectChanges();
+ })
+ });
+
this.showNewWindowMessage = this.platformUtilsService.isSafari();
await super.ngOnInit();
@@ -62,11 +89,26 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
}
});
}, 500);
+ }
- // TODO: listen for duo data message response
+ ngOnDestroy() {
+ this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
+ super.ngOnDestroy();
}
anotherMethod() {
this.router.navigate(['2fa-options']);
}
+
+ async submitWithTab(sendSuccessToTab: any) {
+ await super.submit();
+ if (sendSuccessToTab != null) {
+ window.setTimeout(() => {
+ BrowserApi.tabSendMessage(sendSuccessToTab, {
+ command: '2faPageData',
+ data: { type: 'success' }
+ });
+ }, 1000);
+ }
+ }
}
diff --git a/src/popup2/app.component.ts b/src/popup2/app.component.ts
index 651588ea6a8..46e15160ebd 100644
--- a/src/popup2/app.component.ts
+++ b/src/popup2/app.component.ts
@@ -6,19 +6,23 @@ import { Angulartics2GoogleAnalytics } from 'angulartics2/ga';
import {
Component,
- ComponentFactoryResolver,
- NgZone,
- OnDestroy,
OnInit,
- Type,
- ViewChild,
- ViewContainerRef,
} from '@angular/core';
import { Router } from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
+import { BrowserApi } from '../browser/browserApi';
+
+import { BroadcasterService } from 'jslib/angular/services/broadcaster.service';
+
+import { AuthService } from 'jslib/abstractions/auth.service';
+import { I18nService } from 'jslib/abstractions/i18n.service';
+import { StorageService } from 'jslib/abstractions/storage.service';
+
+import { ConstantsService } from 'jslib/services/constants.service';
+
@Component({
selector: 'app-root',
styles: [],
@@ -26,7 +30,7 @@ import { Angulartics2 } from 'angulartics2';
`,
})
-export class AppComponent {
+export class AppComponent implements OnInit {
toasterConfig: ToasterConfig = new ToasterConfig({
showCloseButton: true,
mouseoverTimerStop: true,
@@ -36,6 +40,47 @@ export class AppComponent {
newestOnTop: false
});
+ private lastActivity: number = null;
+
constructor(private angulartics2GoogleAnalytics: Angulartics2GoogleAnalytics, private analytics: Angulartics2,
- private toasterService: ToasterService) { }
+ private toasterService: ToasterService, private storageService: StorageService,
+ private broadcasterService: BroadcasterService, private authService: AuthService,
+ private i18nService: I18nService, private router: Router) { }
+
+ ngOnInit() {
+ window.onmousemove = () => this.recordActivity();
+ window.onmousedown = () => this.recordActivity();
+ window.ontouchstart = () => this.recordActivity();
+ window.onclick = () => this.recordActivity();
+ window.onscroll = () => this.recordActivity();
+ window.onkeypress = () => this.recordActivity();
+
+ (window as any).bitwardenPopupMainMessageListener = (msg: any, sender: any, sendResponse: any) => {
+ if (msg.command === 'doneLoggingOut') {
+ this.authService.logOut(() => {
+ this.analytics.eventTrack.next({ action: 'Logged Out' });
+ if (msg.expired) {
+ this.toasterService.popAsync('warning', this.i18nService.t('loggedOut'),
+ this.i18nService.t('loginExpired'));
+ }
+ this.router.navigate(['home']);
+ });
+ } else {
+ msg.webExtSender = sender;
+ this.broadcasterService.send(msg);
+ }
+ };
+
+ BrowserApi.messageListener((window as any).bitwardenPopupMainMessageListener);
+ }
+
+ private async recordActivity() {
+ const now = (new Date()).getTime();
+ if (this.lastActivity != null && now - this.lastActivity < 250) {
+ return;
+ }
+
+ this.lastActivity = now;
+ this.storageService.save(ConstantsService.lastActiveKey, now);
+ }
}
diff --git a/src/popup2/services/services.module.ts b/src/popup2/services/services.module.ts
index 7ac1bc6c4bb..66dff1297a6 100644
--- a/src/popup2/services/services.module.ts
+++ b/src/popup2/services/services.module.ts
@@ -6,6 +6,7 @@ import {
import { ToasterModule } from 'angular2-toaster';
import { AuthGuardService } from 'jslib/angular/services/auth-guard.service';
+import { BroadcasterService } from 'jslib/angular/services/broadcaster.service';
import { ValidationService } from 'jslib/angular/services/validation.service';
import { BrowserApi } from '../../browser/browserApi';
@@ -79,6 +80,7 @@ export function initFactory(i18nService: I18nService, storageService: StorageSer
ValidationService,
AuthGuardService,
PopupUtilsService,
+ BroadcasterService,
{ provide: MessagingService, useValue: messagingService },
{ provide: AuthServiceAbstraction, useValue: authService },
{ provide: StateServiceAbstraction, useValue: stateService },
diff --git a/src/popup2/vault/ciphers.component.ts b/src/popup2/vault/ciphers.component.ts
index a72ac0a9203..cfb8eadc4f9 100644
--- a/src/popup2/vault/ciphers.component.ts
+++ b/src/popup2/vault/ciphers.component.ts
@@ -1,6 +1,9 @@
import { Location } from '@angular/common';
import {
+ ChangeDetectorRef,
Component,
+ NgZone,
+ OnDestroy,
OnInit,
} from '@angular/core';
import {
@@ -12,15 +15,21 @@ import { CipherService } from 'jslib/abstractions/cipher.service';
import { CipherView } from 'jslib/models/view/cipherView';
+import { BroadcasterService } from 'jslib/angular/services/broadcaster.service';
+
import { CiphersComponent as BaseCiphersComponent } from 'jslib/angular/components/ciphers.component';
+const BroadcasterSubscriptionId = 'CiphersComponent';
+
@Component({
selector: 'app-vault-ciphers',
templateUrl: 'ciphers.component.html',
})
-export class CiphersComponent extends BaseCiphersComponent implements OnInit {
+export class CiphersComponent extends BaseCiphersComponent implements OnInit, OnDestroy {
constructor(cipherService: CipherService, private route: ActivatedRoute,
- private router: Router, private location: Location) {
+ private router: Router, private location: Location,
+ private ngZone: NgZone, private broadcasterService: BroadcasterService,
+ private changeDetectorRef: ChangeDetectorRef) {
super(cipherService);
}
@@ -37,6 +46,26 @@ export class CiphersComponent extends BaseCiphersComponent implements OnInit {
await super.load();
}
});
+
+ this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
+ this.ngZone.run(async () => {
+ switch (message.command) {
+ case 'syncCompleted':
+ window.setTimeout(() => {
+ this.load();
+ }, 500);
+ break;
+ default:
+ break;
+ }
+
+ this.changeDetectorRef.detectChanges();
+ })
+ });
+ }
+
+ ngOnDestroy() {
+ this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
}
selectCipher(cipher: CipherView) {
diff --git a/src/popup2/vault/current-tab.component.ts b/src/popup2/vault/current-tab.component.ts
index a60a53767f8..e5676541ec7 100644
--- a/src/popup2/vault/current-tab.component.ts
+++ b/src/popup2/vault/current-tab.component.ts
@@ -1,12 +1,9 @@
import {
+ ChangeDetectorRef,
Component,
- ComponentFactoryResolver,
NgZone,
OnDestroy,
OnInit,
- Type,
- ViewChild,
- ViewContainerRef,
} from '@angular/core';
import { Router } from '@angular/router';
@@ -15,6 +12,8 @@ import { Angulartics2 } from 'angulartics2';
import { BrowserApi } from '../../browser/browserApi';
+import { BroadcasterService } from 'jslib/angular/services/broadcaster.service';
+
import { CipherType } from 'jslib/enums/cipherType';
import { CipherView } from 'jslib/models/view/cipherView';
@@ -26,12 +25,15 @@ import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { AutofillService } from '../../services/abstractions/autofill.service';
import { PopupUtilsService } from '../services/popup-utils.service';
+import { setTimeout } from 'timers';
+
+const BroadcasterSubscriptionId = 'CurrentTabComponent';
@Component({
selector: 'app-current-tab',
templateUrl: 'current-tab.component.html',
})
-export class CurrentTabComponent implements OnInit {
+export class CurrentTabComponent implements OnInit, OnDestroy {
pageDetails: any[] = [];
cardCiphers: CipherView[];
identityCiphers: CipherView[];
@@ -48,16 +50,49 @@ export class CurrentTabComponent implements OnInit {
constructor(private platformUtilsService: PlatformUtilsService, private cipherService: CipherService,
private popupUtilsService: PopupUtilsService, private autofillService: AutofillService,
private analytics: Angulartics2, private toasterService: ToasterService,
- private i18nService: I18nService, private router: Router) {
+ private i18nService: I18nService, private router: Router,
+ private ngZone: NgZone, private broadcasterService: BroadcasterService,
+ private changeDetectorRef: ChangeDetectorRef) {
this.inSidebar = popupUtilsService.inSidebar(window);
this.showPopout = !this.inSidebar && !platformUtilsService.isSafari();
this.disableSearch = platformUtilsService.isEdge();
}
ngOnInit() {
+ this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
+ this.ngZone.run(async () => {
+ switch (message.command) {
+ case 'syncCompleted':
+ if (this.loaded) {
+ setTimeout(() => {
+ this.load();
+ }, 500);
+ }
+ break;
+ case 'collectPageDetailsResponse':
+ if (message.sender === BroadcasterSubscriptionId) {
+ this.pageDetails.push({
+ frameId: message.webExtSender.frameId,
+ tab: message.tab,
+ details: message.details,
+ });
+ }
+ break;
+ default:
+ break;
+ }
+
+ this.changeDetectorRef.detectChanges();
+ })
+ });
+
this.load();
}
+ ngOnDestroy() {
+ this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
+ }
+
async refresh() {
await this.load();
}
@@ -117,7 +152,7 @@ export class CurrentTabComponent implements OnInit {
BrowserApi.tabSendMessage(tab, {
command: 'collectPageDetails',
tab: tab,
- sender: 'currentController',
+ sender: BroadcasterSubscriptionId,
}).then(() => {
this.canAutofill = true;
});
diff --git a/src/popup2/vault/groupings.component.ts b/src/popup2/vault/groupings.component.ts
index c974434cab4..3d6341df445 100644
--- a/src/popup2/vault/groupings.component.ts
+++ b/src/popup2/vault/groupings.component.ts
@@ -1,5 +1,8 @@
import {
+ ChangeDetectorRef,
Component,
+ NgZone,
+ OnDestroy,
OnInit,
} from '@angular/core';
import { Router } from '@angular/router';
@@ -14,13 +17,17 @@ import { CollectionService } from 'jslib/abstractions/collection.service';
import { CipherService } from 'jslib/abstractions/cipher.service';
import { FolderService } from 'jslib/abstractions/folder.service';
+import { BroadcasterService } from 'jslib/angular/services/broadcaster.service';
+
import { GroupingsComponent as BaseGroupingsComponent } from 'jslib/angular/components/groupings.component';
+const BroadcasterSubscriptionId = 'GroupingsComponent';
+
@Component({
selector: 'app-vault-groupings',
templateUrl: 'groupings.component.html',
})
-export class GroupingsComponent extends BaseGroupingsComponent implements OnInit {
+export class GroupingsComponent extends BaseGroupingsComponent implements OnInit, OnDestroy {
ciphers: CipherView[];
favoriteCiphers: CipherView[];
noFolderCiphers: CipherView[];
@@ -31,11 +38,37 @@ export class GroupingsComponent extends BaseGroupingsComponent implements OnInit
showNoFolderCiphers = false;
constructor(collectionService: CollectionService, folderService: FolderService,
- private cipherService: CipherService, private router: Router) {
+ private cipherService: CipherService, private router: Router,
+ private ngZone: NgZone, private broadcasterService: BroadcasterService,
+ private changeDetectorRef: ChangeDetectorRef) {
super(collectionService, folderService);
}
- async ngOnInit() {
+ ngOnInit() {
+ this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
+ this.ngZone.run(async () => {
+ switch (message.command) {
+ case 'syncCompleted':
+ window.setTimeout(() => {
+ this.load();
+ }, 500);
+ break;
+ default:
+ break;
+ }
+
+ this.changeDetectorRef.detectChanges();
+ })
+ });
+
+ this.load();
+ }
+
+ ngOnDestroy() {
+ this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
+ }
+
+ async load() {
await super.load();
super.loaded = false;