diff --git a/jslib b/jslib index 5db94cc9d0..a6b95b15e3 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit 5db94cc9d06ba478a29e9b625993108dfa0d7ec8 +Subproject commit a6b95b15e36737ccf2e7664ed3c881bc19366b84 diff --git a/package-lock.json b/package-lock.json index bd883693f7..11c612e6b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,11 +11,11 @@ "dependencies": { "@bitwarden/jslib-angular": "file:jslib/angular", "@bitwarden/jslib-common": "file:jslib/common", - "angular2-toaster": "^11.0.1", "core-js": "^3.11.0", "date-input-polyfill": "^2.14.0", "font-awesome": "4.7.0", "mousetrap": "^1.6.5", + "ngx-toastr": "^13.2.1", "nord": "^0.2.1", "sweetalert2": "^10.16.6", "web-animations-js": "^2.3.2" @@ -1293,20 +1293,6 @@ "ajv": "^6.9.1" } }, - "node_modules/angular2-toaster": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/angular2-toaster/-/angular2-toaster-11.0.1.tgz", - "integrity": "sha512-IRXE5zujPMNOhckcp+Hk2n+UrKSrlAviz55wGvSd9ECrqsSRjgh148UEtgsqkcYQ8leKcybZ4d0lrueDuQofNA==", - "dependencies": { - "tslib": "^2.0.0" - }, - "peerDependencies": { - "@angular/common": "^11.0.0", - "@angular/compiler": "^11.0.0", - "@angular/core": "^11.0.0", - "rxjs": "^6.5.3" - } - }, "node_modules/ansi-colors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", @@ -7777,6 +7763,19 @@ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, + "node_modules/ngx-toastr": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-13.2.1.tgz", + "integrity": "sha512-UAzp7/xWK9IXA2LsOmhpaaIGCqscvJokoQpBNpAMrjEkDeSlFf8PWQAuQY795KW0mJb3qF9UG/s23nsXfMYKmg==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/common": ">=10.0.0-0", + "@angular/core": ">=10.0.0-0", + "@angular/platform-browser": ">=10.0.0-0" + } + }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -13764,14 +13763,6 @@ "dev": true, "requires": {} }, - "angular2-toaster": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/angular2-toaster/-/angular2-toaster-11.0.1.tgz", - "integrity": "sha512-IRXE5zujPMNOhckcp+Hk2n+UrKSrlAviz55wGvSd9ECrqsSRjgh148UEtgsqkcYQ8leKcybZ4d0lrueDuQofNA==", - "requires": { - "tslib": "^2.0.0" - } - }, "ansi-colors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", @@ -18925,6 +18916,14 @@ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, + "ngx-toastr": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-13.2.1.tgz", + "integrity": "sha512-UAzp7/xWK9IXA2LsOmhpaaIGCqscvJokoQpBNpAMrjEkDeSlFf8PWQAuQY795KW0mJb3qF9UG/s23nsXfMYKmg==", + "requires": { + "tslib": "^2.0.0" + } + }, "no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", diff --git a/package.json b/package.json index 74c122479b..f5adb4d27c 100644 --- a/package.json +++ b/package.json @@ -71,11 +71,11 @@ "dependencies": { "@bitwarden/jslib-angular": "file:jslib/angular", "@bitwarden/jslib-common": "file:jslib/common", - "angular2-toaster": "^11.0.1", "core-js": "^3.11.0", "date-input-polyfill": "^2.14.0", "font-awesome": "4.7.0", "mousetrap": "^1.6.5", + "ngx-toastr": "^13.2.1", "nord": "^0.2.1", "sweetalert2": "^10.16.6", "web-animations-js": "^2.3.2" diff --git a/src/popup/app.component.ts b/src/popup/app.component.ts index b556ecfb3d..bd3bb1fda2 100644 --- a/src/popup/app.component.ts +++ b/src/popup/app.component.ts @@ -1,13 +1,3 @@ -import { BrowserApi } from '../browser/browserApi'; - -import { - BodyOutputType, - Toast, - ToasterConfig, - ToasterService, -} from 'angular2-toaster'; -import Swal, { SweetAlertIcon } from 'sweetalert2/src/sweetalert2.js'; - import { ChangeDetectorRef, Component, @@ -21,6 +11,12 @@ import { Router, RouterOutlet, } from '@angular/router'; +import { + IndividualConfig, + ToastrService, +} from 'ngx-toastr'; +import Swal, { SweetAlertIcon } from 'sweetalert2/src/sweetalert2.js'; +import { BrowserApi } from '../browser/browserApi'; import { AuthService } from 'jslib-common/abstractions/auth.service'; import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service'; @@ -40,24 +36,15 @@ import { routerTransition } from './app-routing.animations'; 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, + constructor(private toastrService: ToastrService, private storageService: StorageService, private broadcasterService: BroadcasterService, private authService: AuthService, private i18nService: I18nService, private router: Router, private stateService: StateService, private messagingService: MessagingService, @@ -183,30 +170,29 @@ export class AppComponent implements OnInit { } private showToast(msg: any) { - const toast: Toast = { - type: msg.type, - title: msg.title, - }; + let message = ''; + + const options: Partial = {}; + if (typeof (msg.text) === 'string') { - toast.body = msg.text; + message = msg.text; } else if (msg.text.length === 1) { - toast.body = msg.text[0]; + message = 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; + options.enableHtml = true; } if (msg.options != null) { if (msg.options.trustedHtml === true) { - toast.bodyOutputType = BodyOutputType.TrustedHtml; + options.enableHtml = true; } if (msg.options.timeout != null && msg.options.timeout > 0) { - toast.timeout = msg.options.timeout; + options.timeOut = msg.options.timeout; } } - this.toasterService.popAsync(toast); + + this.toastrService.show(message, msg.title, options, 'toast-' + msg.type); } private async showDialog(msg: any) { diff --git a/src/popup/app.module.ts b/src/popup/app.module.ts index 61ba5b1cd8..8b7f1258d7 100644 --- a/src/popup/app.module.ts +++ b/src/popup/app.module.ts @@ -1,7 +1,6 @@ import { A11yModule } from '@angular/cdk/a11y'; import { DragDropModule } from '@angular/cdk/drag-drop'; import { ScrollingModule } from '@angular/cdk/scrolling'; -import { ToasterModule } from 'angular2-toaster'; import { AppRoutingModule } from './app-routing.module'; import { ServicesModule } from './services/services.module'; @@ -85,6 +84,7 @@ import { VerifyMasterPasswordComponent } from './components/verify-master-passwo import { CalloutComponent } from 'jslib-angular/components/callout.component'; import { IconComponent } from 'jslib-angular/components/icon.component'; +import { BitwardenToastModule } from 'jslib-angular/components/toastr.component'; import { CurrencyPipe, @@ -188,7 +188,12 @@ registerLocaleData(localeZhTw, 'zh-TW'); ReactiveFormsModule, ScrollingModule, ServicesModule, - ToasterModule.forRoot(), + BitwardenToastModule.forRoot({ + maxOpened: 2, + autoDismiss: true, + closeButton: true, + positionClass: 'toast-bottom-full-width', + }), ], declarations: [ A11yTitleDirective, diff --git a/src/popup/components/action-buttons.component.ts b/src/popup/components/action-buttons.component.ts index 9c0f273fb0..b7cc088ddf 100644 --- a/src/popup/components/action-buttons.component.ts +++ b/src/popup/components/action-buttons.component.ts @@ -5,8 +5,6 @@ import { Output, } from '@angular/core'; -import { ToasterService } from 'angular2-toaster'; - import { CipherRepromptType } from 'jslib-common/enums/cipherRepromptType'; import { CipherType } from 'jslib-common/enums/cipherType'; import { EventType } from 'jslib-common/enums/eventType'; @@ -33,7 +31,7 @@ export class ActionButtonsComponent { cipherType = CipherType; userHasPremiumAccess = false; - constructor(private toasterService: ToasterService, private i18nService: I18nService, + constructor(private i18nService: I18nService, private platformUtilsService: PlatformUtilsService, private eventService: EventService, private totpService: TotpService, private userService: UserService, private passwordRepromptService: PasswordRepromptService) { } @@ -63,7 +61,7 @@ export class ActionButtonsComponent { } this.platformUtilsService.copyToClipboard(value, { window: window }); - this.toasterService.popAsync('info', null, + this.platformUtilsService.showToast('info', null, this.i18nService.t('valueCopied', this.i18nService.t(typeI18nKey))); if (typeI18nKey === 'password' || typeI18nKey === 'verificationCodeTotp') { diff --git a/src/popup/scss/plugins.scss b/src/popup/scss/plugins.scss index 9ccb3ee5f0..9062e4cbdb 100644 --- a/src/popup/scss/plugins.scss +++ b/src/popup/scss/plugins.scss @@ -1,6 +1,6 @@ $fa-font-path: "~font-awesome/fonts"; @import "~font-awesome/scss/font-awesome.scss"; -@import "~angular2-toaster/toaster"; +@import '~ngx-toastr/toastr'; @import "~sweetalert2/src/sweetalert2.scss"; @import "variables.scss"; @@ -9,32 +9,36 @@ $fa-font-path: "~font-awesome/fonts"; // Toaster .toast-container { - &.toast-bottom-full-width div.toast { - margin: 0 10px 10px; - width: calc(100% - 20px); - box-shadow: 0 0 8px rgba(0, 0, 0, 0.35); - - &:hover { - box-shadow: 0 0 10px rgba(0, 0, 0, 0.6); - } + .toast-close-button { + font-size: 18px; + margin-right: 4px; } - .toast { - &:before { + .ngx-toastr { + align-items: center; + background-image: none !important; + border-radius: $border-radius; + box-shadow: 0 0 8px rgba(0, 0, 0, 0.35); + display: flex; + padding: 15px; + + .toast-close-button { + position: absolute; + right: 5px; + top: 0; + } + + &:hover { + box-shadow: 0 0 10px rgba(0, 0, 0, 0.6); + } + + .icon i::before { + float: left; + font-style: normal; font-family: FontAwesome; font-size: 25px; line-height: 20px; - float: left; - color: #ffffff; - margin: auto 0 auto 15px; - } - - .toast-content { - padding: 15px; - } - - .toaster-icon { - display: none; + padding-right: 15px; } .toast-message { @@ -48,49 +52,41 @@ $fa-font-path: "~font-awesome/fonts"; } &.toast-danger, &.toast-error { - background-image: none !important; - @include themify($themes) { background-color: themed('dangerColor'); } - &:before { + .icon i::before { content: "\f0e7"; } } &.toast-warning { - background-image: none !important; - @include themify($themes) { background-color: themed('warningColor'); } - &:before { + .icon i::before { content: "\f071"; } } &.toast-info { - background-image: none !important; - @include themify($themes) { background-color: themed('infoColor'); } - &:before { + .icon i:before { content: "\f05a"; } } &.toast-success { - background-image: none !important; - @include themify($themes) { background-color: themed('successColor'); } - &:before { + .icon i:before { content: "\f00C"; } } diff --git a/src/popup/settings/settings.component.ts b/src/popup/settings/settings.component.ts index 94fb371272..aa0c6dcc83 100644 --- a/src/popup/settings/settings.component.ts +++ b/src/popup/settings/settings.component.ts @@ -6,7 +6,6 @@ import { } from '@angular/core'; import { FormControl } from '@angular/forms'; import { Router } from '@angular/router'; -import { ToasterService } from 'angular2-toaster'; import Swal from 'sweetalert2/src/sweetalert2.js'; import { BrowserApi } from '../../browser/browserApi'; @@ -68,7 +67,7 @@ export class SettingsComponent implements OnInit { public messagingService: MessagingService, private router: Router, private environmentService: EnvironmentService, private cryptoService: CryptoService, private userService: UserService, private popupUtilsService: PopupUtilsService, - private modalService: ModalService, private toasterService: ToasterService, + private modalService: ModalService, private keyConnectorService: KeyConnectorService) { } @@ -136,7 +135,7 @@ export class SettingsComponent implements OnInit { } if (!this.vaultTimeout.valid) { - this.toasterService.popAsync('error', null, this.i18nService.t('vaultTimeoutToLarge')); + this.platformUtilsService.showToast('error', null, this.i18nService.t('vaultTimeoutToLarge')); return; } @@ -165,7 +164,7 @@ export class SettingsComponent implements OnInit { } if (!this.vaultTimeout.valid) { - this.toasterService.popAsync('error', null, this.i18nService.t('vaultTimeoutToLarge')); + this.platformUtilsService.showToast('error', null, this.i18nService.t('vaultTimeoutToLarge')); return; } diff --git a/src/popup/settings/sync.component.ts b/src/popup/settings/sync.component.ts index fd720a520c..9ec6b5fffd 100644 --- a/src/popup/settings/sync.component.ts +++ b/src/popup/settings/sync.component.ts @@ -1,11 +1,10 @@ -import { ToasterService } from 'angular2-toaster'; - import { Component, OnInit, } from '@angular/core'; import { I18nService } from 'jslib-common/abstractions/i18n.service'; +import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service'; import { SyncService } from 'jslib-common/abstractions/sync.service'; @Component({ @@ -16,7 +15,7 @@ export class SyncComponent implements OnInit { lastSync = '--'; syncPromise: Promise; - constructor(private syncService: SyncService, private toasterService: ToasterService, + constructor(private syncService: SyncService, private platformUtilsService: PlatformUtilsService, private i18nService: I18nService) { } @@ -29,9 +28,9 @@ export class SyncComponent implements OnInit { const success = await this.syncPromise; if (success) { await this.setLastSync(); - this.toasterService.popAsync('success', null, this.i18nService.t('syncingComplete')); + this.platformUtilsService.showToast('success', null, this.i18nService.t('syncingComplete')); } else { - this.toasterService.popAsync('error', null, this.i18nService.t('syncingFailed')); + this.platformUtilsService.showToast('error', null, this.i18nService.t('syncingFailed')); } } diff --git a/src/popup/vault/current-tab.component.ts b/src/popup/vault/current-tab.component.ts index ffa3f1586d..b02bbe184d 100644 --- a/src/popup/vault/current-tab.component.ts +++ b/src/popup/vault/current-tab.component.ts @@ -5,11 +5,8 @@ import { OnDestroy, OnInit, } from '@angular/core'; - import { Router } from '@angular/router'; -import { ToasterService } from 'angular2-toaster'; - import { BrowserApi } from '../../browser/browserApi'; import { CipherRepromptType } from 'jslib-common/enums/cipherRepromptType'; @@ -59,7 +56,7 @@ export class CurrentTabComponent implements OnInit, OnDestroy { constructor(private platformUtilsService: PlatformUtilsService, private cipherService: CipherService, private popupUtilsService: PopupUtilsService, private autofillService: AutofillService, - private toasterService: ToasterService, private i18nService: I18nService, private router: Router, + private i18nService: I18nService, private router: Router, private ngZone: NgZone, private broadcasterService: BroadcasterService, private changeDetectorRef: ChangeDetectorRef, private syncService: SyncService, private searchService: SearchService, private storageService: StorageService, @@ -140,7 +137,7 @@ export class CurrentTabComponent implements OnInit, OnDestroy { } if (this.pageDetails == null || this.pageDetails.length === 0) { - this.toasterService.popAsync('error', null, this.i18nService.t('autofillError')); + this.platformUtilsService.showToast('error', null, this.i18nService.t('autofillError')); return; } @@ -164,7 +161,7 @@ export class CurrentTabComponent implements OnInit, OnDestroy { } } catch { this.ngZone.run(() => { - this.toasterService.popAsync('error', null, this.i18nService.t('autofillError')); + this.platformUtilsService.showToast('error', null, this.i18nService.t('autofillError')); this.changeDetectorRef.detectChanges(); }); }