diff --git a/jslib b/jslib index 5db94cc9..a6b95b15 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit 5db94cc9d06ba478a29e9b625993108dfa0d7ec8 +Subproject commit a6b95b15e36737ccf2e7664ed3c881bc19366b84 diff --git a/package-lock.json b/package-lock.json index 96a23d39..b7861dd5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,6 @@ "@bitwarden/jslib-electron": "file:jslib/electron", "@bitwarden/jslib-node": "file:jslib/node", "@microsoft/microsoft-graph-client": "^2.2.1", - "angular2-toaster": "^11.0.1", "bootstrap": "^4.6.0", "chalk": "^4.1.1", "commander": "^7.2.0", @@ -26,6 +25,7 @@ "inquirer": "8.0.0", "ldapjs": "git+https://git@github.com/kspearrin/node-ldapjs.git", "lunr": "^2.3.9", + "ngx-toastr": "^13.2.1", "open": "^8.0.6", "proper-lockfile": "^4.1.2" }, @@ -100,6 +100,7 @@ } }, "jslib/common": { + "name": "@bitwarden/jslib-common", "version": "0.0.0", "license": "GPL-3.0", "dependencies": { @@ -126,6 +127,7 @@ } }, "jslib/electron": { + "name": "@bitwarden/jslib-electron", "version": "0.0.0", "license": "GPL-3.0", "dependencies": { @@ -145,6 +147,7 @@ } }, "jslib/node": { + "name": "@bitwarden/jslib-node", "version": "0.0.0", "license": "GPL-3.0", "dependencies": { @@ -1950,20 +1953,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-align": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", @@ -8904,6 +8893,19 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "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", @@ -16262,14 +16264,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-align": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", @@ -21689,6 +21683,14 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "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 9b207909..b2aec1df 100644 --- a/package.json +++ b/package.json @@ -176,7 +176,6 @@ "@bitwarden/jslib-electron": "file:jslib/electron", "@bitwarden/jslib-node": "file:jslib/node", "@microsoft/microsoft-graph-client": "^2.2.1", - "angular2-toaster": "^11.0.1", "bootstrap": "^4.6.0", "chalk": "^4.1.1", "commander": "^7.2.0", @@ -187,6 +186,7 @@ "inquirer": "8.0.0", "ldapjs": "git+https://git@github.com/kspearrin/node-ldapjs.git", "lunr": "^2.3.9", + "ngx-toastr": "^13.2.1", "open": "^8.0.6", "proper-lockfile": "^4.1.2" }, diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 17791d79..baac92a9 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,34 +1,27 @@ -import { - BodyOutputType, - Toast, - ToasterConfig, - ToasterContainerComponent, - ToasterService, -} from 'angular2-toaster'; - import { Component, - ComponentFactoryResolver, NgZone, OnInit, SecurityContext, - Type, ViewChild, ViewContainerRef, } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; import { Router } from '@angular/router'; +import { + IndividualConfig, + ToastrService, +} from 'ngx-toastr'; -import { ApiService } from 'jslib-common/abstractions/api.service'; import { AuthService } from 'jslib-common/abstractions/auth.service'; import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service'; import { I18nService } from 'jslib-common/abstractions/i18n.service'; import { LogService } from 'jslib-common/abstractions/log.service'; import { MessagingService } from 'jslib-common/abstractions/messaging.service'; import { StateService } from 'jslib-common/abstractions/state.service'; -import { StorageService } from 'jslib-common/abstractions/storage.service'; import { TokenService } from 'jslib-common/abstractions/token.service'; import { UserService } from 'jslib-common/abstractions/user.service'; +import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service'; import { ConfigurationService } from '../services/configuration.service'; import { SyncService } from '../services/sync.service'; @@ -39,31 +32,20 @@ const BroadcasterSubscriptionId = 'AppComponent'; selector: 'app-root', styles: [], template: ` - `, }) export class AppComponent implements OnInit { @ViewChild('settings', { read: ViewContainerRef, static: true }) settingsRef: ViewContainerRef; - toasterConfig: ToasterConfig = new ToasterConfig({ - showCloseButton: true, - mouseoverTimerStop: true, - animation: 'flyRight', - limit: 5, - }); - - private lastActivity: number = null; - constructor(private broadcasterService: BroadcasterService, private userService: UserService, - private tokenService: TokenService, private storageService: StorageService, + private tokenService: TokenService, private authService: AuthService, private router: Router, - private toasterService: ToasterService, private i18nService: I18nService, + private toastrService: ToastrService, private i18nService: I18nService, private sanitizer: DomSanitizer, private ngZone: NgZone, - private componentFactoryResolver: ComponentFactoryResolver, private messagingService: MessagingService, + private platformUtilsService: PlatformUtilsService, private messagingService: MessagingService, private configurationService: ConfigurationService, private syncService: SyncService, - private stateService: StateService, private apiService: ApiService, private logService: LogService) { - (window as any).BitwardenToasterService = toasterService; + private stateService: StateService, private logService: LogService) { } ngOnInit() { @@ -138,7 +120,7 @@ export class AppComponent implements OnInit { this.authService.logOut(async () => { if (expired) { - this.toasterService.popAsync('warning', this.i18nService.t('loggedOut'), + this.platformUtilsService.showToast('warning', this.i18nService.t('loggedOut'), this.i18nService.t('loginExpired')); } this.router.navigate(['login']); @@ -146,29 +128,28 @@ 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); } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 05ec3e99..84d55dfe 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,8 +1,6 @@ import 'core-js/stable'; import 'zone.js/dist/zone'; -import { ToasterModule } from 'angular2-toaster'; - import { AppRoutingModule } from './app-routing.module'; import { ServicesModule } from './services/services.module'; @@ -15,6 +13,7 @@ import { AppComponent } from './app.component'; 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 { ApiKeyComponent } from './accounts/apiKey.component'; import { EnvironmentComponent } from './accounts/environment.component'; @@ -42,7 +41,11 @@ import { SearchCiphersPipe } from 'jslib-angular/pipes/search-ciphers.pipe'; FormsModule, AppRoutingModule, ServicesModule, - ToasterModule.forRoot(), + BitwardenToastModule.forRoot({ + maxOpened: 5, + autoDismiss: true, + closeButton: true, + }), ], declarations: [ A11yTitleDirective, diff --git a/src/app/tabs/dashboard.component.ts b/src/app/tabs/dashboard.component.ts index de30e4db..8076cf18 100644 --- a/src/app/tabs/dashboard.component.ts +++ b/src/app/tabs/dashboard.component.ts @@ -6,12 +6,11 @@ import { OnInit, } from '@angular/core'; -import { ToasterService } from 'angular2-toaster'; - import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service'; import { I18nService } from 'jslib-common/abstractions/i18n.service'; import { MessagingService } from 'jslib-common/abstractions/messaging.service'; import { StateService } from 'jslib-common/abstractions/state.service'; +import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service'; import { SyncService } from '../../services/sync.service'; @@ -45,7 +44,7 @@ export class DashboardComponent implements OnInit, OnDestroy { constructor(private i18nService: I18nService, private syncService: SyncService, private configurationService: ConfigurationService, private broadcasterService: BroadcasterService, private ngZone: NgZone, private messagingService: MessagingService, - private toasterService: ToasterService, private changeDetectorRef: ChangeDetectorRef, + private platformUtilsService: PlatformUtilsService, private changeDetectorRef: ChangeDetectorRef, private stateService: StateService) { } async ngOnInit() { @@ -76,13 +75,13 @@ export class DashboardComponent implements OnInit, OnDestroy { await this.startPromise; this.messagingService.send('scheduleNextDirSync'); this.syncRunning = true; - this.toasterService.popAsync('success', null, this.i18nService.t('syncingStarted')); + this.platformUtilsService.showToast('success', null, this.i18nService.t('syncingStarted')); } async stop() { this.messagingService.send('cancelDirSync'); this.syncRunning = false; - this.toasterService.popAsync('success', null, this.i18nService.t('syncingStopped')); + this.platformUtilsService.showToast('success', null, this.i18nService.t('syncingStopped')); } async sync() { @@ -90,7 +89,7 @@ export class DashboardComponent implements OnInit, OnDestroy { const result = await this.syncPromise; const groupCount = result[0] != null ? result[0].length : 0; const userCount = result[1] != null ? result[1].length : 0; - this.toasterService.popAsync('success', null, + this.platformUtilsService.showToast('success', null, this.i18nService.t('syncCounts', groupCount.toString(), userCount.toString())); } diff --git a/src/app/tabs/more.component.ts b/src/app/tabs/more.component.ts index 8206aa25..ec949c64 100644 --- a/src/app/tabs/more.component.ts +++ b/src/app/tabs/more.component.ts @@ -6,8 +6,6 @@ import { OnInit, } from '@angular/core'; -import { ToasterService } from 'angular2-toaster'; - import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service'; import { I18nService } from 'jslib-common/abstractions/i18n.service'; import { MessagingService } from 'jslib-common/abstractions/messaging.service'; @@ -28,7 +26,7 @@ export class MoreComponent implements OnInit { constructor(private platformUtilsService: PlatformUtilsService, private i18nService: I18nService, private messagingService: MessagingService, private configurationService: ConfigurationService, - private toasterService: ToasterService, private broadcasterService: BroadcasterService, + private broadcasterService: BroadcasterService, private ngZone: NgZone, private changeDetectorRef: ChangeDetectorRef) { } async ngOnInit() { @@ -72,6 +70,6 @@ export class MoreComponent implements OnInit { async clearCache() { await this.configurationService.clearStatefulSettings(true); - this.toasterService.popAsync('success', null, this.i18nService.t('syncCacheCleared')); + this.platformUtilsService.showToast('success', null, this.i18nService.t('syncCacheCleared')); } } diff --git a/src/scss/plugins.scss b/src/scss/plugins.scss index f6b823e4..274f09ed 100644 --- a/src/scss/plugins.scss +++ b/src/scss/plugins.scss @@ -1,38 +1,40 @@ $fa-font-path: "~font-awesome/fonts"; @import "~font-awesome/scss/font-awesome.scss"; -@import "~angular2-toaster/toaster"; +@import '~ngx-toastr/toastr'; @import "~bootstrap/scss/_variables.scss"; -#toast-container { +.toast-container { .toast-close-button { - right: -0.15em; + font-size: 18px; + margin-right: 4px; } - .toast { - opacity: 1 !important; + .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; - align-items: center; + padding: 15px; + + .toast-close-button { + position: absolute; + right: 5px; + top: 0; + } &:hover { box-shadow: 0 0 10px rgba(0, 0, 0, 0.6); } - &:before { + .icon i::before { + float: left; + font-style: normal; font-family: FontAwesome; font-size: 25px; line-height: 20px; - float: left; - color: #ffffff; - padding-right: 10px; - margin: auto 0 auto -36px; - } - - .toaster-icon { - display: none; + padding-right: 15px; } .toast-message { @@ -46,38 +48,33 @@ $fa-font-path: "~font-awesome/fonts"; } &.toast-danger, &.toast-error { - background-image: none !important; background-color: $danger; - &:before { + .icon i::before { content: "\f0e7"; - margin-left: -30px; } } &.toast-warning { - background-image: none !important; background-color: $warning; - &:before { + .icon i::before { content: "\f071"; } } &.toast-info { - background-image: none !important; background-color: $info; - &:before { + .icon i:before { content: "\f05a"; } } &.toast-success { - background-image: none !important; background-color: $success; - &:before { + .icon i:before { content: "\f00C"; } }