import { BrowserApi } from '../browser/browserApi'; import { BodyOutputType, Toast, ToasterConfig, ToasterService, } from 'angular2-toaster'; import Swal, { SweetAlertIcon } from 'sweetalert2/src/sweetalert2.js'; import { ChangeDetectorRef, Component, NgZone, OnInit, SecurityContext, } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; import { NavigationEnd, Router, RouterOutlet, } from '@angular/router'; import { BroadcasterService } from 'jslib-angular/services/broadcaster.service'; import { AuthService } from 'jslib-common/abstractions/auth.service'; import { I18nService } from 'jslib-common/abstractions/i18n.service'; import { MessagingService } from 'jslib-common/abstractions/messaging.service'; import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service'; import { StateService } from 'jslib-common/abstractions/state.service'; import { StorageService } from 'jslib-common/abstractions/storage.service'; import { ConstantsService } from 'jslib-common/services/constants.service'; import BrowserPlatformUtilsService from 'src/services/browserPlatformUtils.service'; import { routerTransition } from './app-routing.animations'; @Component({ selector: 'app-root', styles: [], animations: [routerTransition], template: `
`, }) export class AppComponent implements OnInit { toasterConfig: ToasterConfig = new ToasterConfig({ showCloseButton: false, mouseoverTimerStop: true, animation: 'slideUp', limit: 2, positionClass: 'toast-bottom-full-width', newestOnTop: false, }); private lastActivity: number = null; constructor(private toasterService: ToasterService, private storageService: StorageService, private broadcasterService: BroadcasterService, private authService: AuthService, private i18nService: I18nService, private router: Router, private stateService: StateService, private messagingService: MessagingService, private changeDetectorRef: ChangeDetectorRef, private ngZone: NgZone, private sanitizer: DomSanitizer, private platformUtilsService: PlatformUtilsService) { } ngOnInit() { if (BrowserApi.getBackgroundPage() == null) { return; } this.ngZone.runOutsideAngular(() => { 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 = async (msg: any, sender: any, sendResponse: any) => { if (msg.command === 'doneLoggingOut') { this.ngZone.run(async () => { this.authService.logOut(() => { if (msg.expired) { this.showToast({ type: 'warning', title: this.i18nService.t('loggedOut'), text: this.i18nService.t('loginExpired'), }); } this.router.navigate(['home']); this.stateService.purge(); }); this.changeDetectorRef.detectChanges(); }); } else if (msg.command === 'authBlocked') { this.ngZone.run(() => { this.router.navigate(['home']); }); } else if (msg.command === 'locked') { this.stateService.purge(); this.ngZone.run(() => { this.router.navigate(['lock']); }); } else if (msg.command === 'showDialog') { await this.showDialog(msg); } else if (msg.command === 'showPasswordDialog') { await this.showPasswordDialog(msg); } else if (msg.command === 'showToast') { this.ngZone.run(() => { this.showToast(msg); }); } else if (msg.command === 'reloadProcess') { const windowReload = this.platformUtilsService.isSafari() || this.platformUtilsService.isFirefox() || this.platformUtilsService.isOpera(); if (windowReload) { // Wait to make sure background has reloaded first. window.setTimeout(() => BrowserApi.reloadExtension(window), 2000); } } else if (msg.command === 'reloadPopup') { this.ngZone.run(() => { this.router.navigate(['/']); }); } else { msg.webExtSender = sender; this.broadcasterService.send(msg); } }; BrowserApi.messageListener('app.component', (window as any).bitwardenPopupMainMessageListener); this.router.events.subscribe(event => { if (event instanceof NavigationEnd) { const url = event.urlAfterRedirects || event.url || ''; if (url.startsWith('/tabs/') && (window as any).previousPopupUrl != null && (window as any).previousPopupUrl.startsWith('/tabs/')) { this.stateService.remove('GroupingsComponent'); this.stateService.remove('GroupingsComponentScope'); this.stateService.remove('CiphersComponent'); this.stateService.remove('SendGroupingsComponent'); this.stateService.remove('SendGroupingsComponentScope'); this.stateService.remove('SendTypeComponent'); } if (url.startsWith('/tabs/')) { this.stateService.remove('addEditCipherInfo'); } (window as any).previousPopupUrl = url; // Clear route direction after animation (400ms) if ((window as any).routeDirection != null) { window.setTimeout(() => { (window as any).routeDirection = null; }, 400); } } }); } getState(outlet: RouterOutlet) { if (outlet.activatedRouteData.state === 'ciphers') { const routeDirection = (window as any).routeDirection != null ? (window as any).routeDirection : ''; return 'ciphers_direction=' + routeDirection + '_' + (outlet.activatedRoute.queryParams as any).value.folderId + '_' + (outlet.activatedRoute.queryParams as any).value.collectionId; } else { return outlet.activatedRouteData.state; } } 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); } private showToast(msg: any) { const toast: Toast = { type: msg.type, title: msg.title, }; if (typeof (msg.text) === 'string') { toast.body = msg.text; } else if (msg.text.length === 1) { toast.body = msg.text[0]; } else { let message = ''; msg.text.forEach((t: string) => message += ('

' + this.sanitizer.sanitize(SecurityContext.HTML, t) + '

')); toast.body = message; toast.bodyOutputType = BodyOutputType.TrustedHtml; } if (msg.options != null) { if (msg.options.trustedHtml === true) { toast.bodyOutputType = BodyOutputType.TrustedHtml; } if (msg.options.timeout != null && msg.options.timeout > 0) { toast.timeout = msg.options.timeout; } } this.toasterService.popAsync(toast); } private async showDialog(msg: any) { let iconClasses: string = null; const type = msg.type; if (type != null) { // If you add custom types to this part, the type to SweetAlertIcon cast below needs to be changed. switch (type) { case 'success': iconClasses = 'fa-check text-success'; break; case 'warning': iconClasses = 'fa-warning text-warning'; break; case 'error': iconClasses = 'fa-bolt text-danger'; break; case 'info': iconClasses = 'fa-info-circle text-info'; break; default: break; } } const cancelText = msg.cancelText; const confirmText = msg.confirmText; const confirmed = await Swal.fire({ heightAuto: false, buttonsStyling: false, icon: type as SweetAlertIcon, // required to be any of the SweetAlertIcons to output the iconHtml. iconHtml: iconClasses != null ? `` : undefined, text: msg.text, html: msg.html, titleText: msg.title, showCancelButton: (cancelText != null), cancelButtonText: cancelText, showConfirmButton: true, confirmButtonText: confirmText == null ? this.i18nService.t('ok') : confirmText, timer: 300000, }); this.messagingService.send('showDialogResolve', { dialogId: msg.dialogId, confirmed: confirmed.value, }); } private async showPasswordDialog(msg: any) { const platformUtils = this.platformUtilsService as BrowserPlatformUtilsService; const result = await Swal.fire({ heightAuto: false, titleText: msg.title, input: 'password', text: msg.body, confirmButtonText: this.i18nService.t('ok'), showCancelButton: true, cancelButtonText: this.i18nService.t('cancel'), inputAttributes: { autocapitalize: 'off', autocorrect: 'off', }, inputValidator: async (value: string): Promise => { if (await platformUtils.resolvePasswordDialogPromise(msg.dialogId, false, value)) { return false; } return this.i18nService.t('invalidMasterPassword'); }, }); platformUtils.resolvePasswordDialogPromise(msg.dialogId, true, null); } }