1
0
mirror of https://github.com/bitwarden/browser.git synced 2025-01-16 20:21:31 +01:00

notification service implementation

This commit is contained in:
Kyle Spearrin 2018-08-20 22:21:13 -04:00
parent eaf08c45d9
commit 85ca10dbb3
8 changed files with 85 additions and 9 deletions

2
jslib

@ -1 +1 @@
Subproject commit b64757132faf1ebb5438bec00720c58604fd29f6 Subproject commit 50666a761dba3d2d7d880f1faf488fd9d719ea50

5
package-lock.json generated
View File

@ -96,6 +96,11 @@
"tslib": "^1.7.1" "tslib": "^1.7.1"
} }
}, },
"@aspnet/signalr": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@aspnet/signalr/-/signalr-1.0.2.tgz",
"integrity": "sha512-sXleqUCCbodCOqUA8MjLSvtAgDTvDhEq6j3JyAq/w4RMJhpZ+dXK9+6xEMbzag2hisq5e/8vDC82JYutkcOISQ=="
},
"@ngtools/webpack": { "@ngtools/webpack": {
"version": "1.10.2", "version": "1.10.2",
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-1.10.2.tgz", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-1.10.2.tgz",

View File

@ -66,6 +66,7 @@
"@angular/platform-browser-dynamic": "5.2.0", "@angular/platform-browser-dynamic": "5.2.0",
"@angular/router": "5.2.0", "@angular/router": "5.2.0",
"@angular/upgrade": "5.2.0", "@angular/upgrade": "5.2.0",
"@aspnet/signalr": "1.0.2",
"angular2-toaster": "4.0.2", "angular2-toaster": "4.0.2",
"angulartics2": "5.0.1", "angulartics2": "5.0.1",
"bootstrap": "4.1.1", "bootstrap": "4.1.1",

View File

@ -31,6 +31,7 @@ import { CryptoService } from 'jslib/abstractions/crypto.service';
import { FolderService } from 'jslib/abstractions/folder.service'; import { FolderService } from 'jslib/abstractions/folder.service';
import { I18nService } from 'jslib/abstractions/i18n.service'; import { I18nService } from 'jslib/abstractions/i18n.service';
import { LockService } from 'jslib/abstractions/lock.service'; import { LockService } from 'jslib/abstractions/lock.service';
import { NotificationsService } from 'jslib/abstractions/notifications.service';
import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service'; import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { SearchService } from 'jslib/abstractions/search.service'; import { SearchService } from 'jslib/abstractions/search.service';
@ -71,7 +72,8 @@ export class AppComponent implements OnDestroy, OnInit {
private platformUtilsService: PlatformUtilsService, private ngZone: NgZone, private platformUtilsService: PlatformUtilsService, private ngZone: NgZone,
private lockService: LockService, private storageService: StorageService, private lockService: LockService, private storageService: StorageService,
private cryptoService: CryptoService, private collectionService: CollectionService, private cryptoService: CryptoService, private collectionService: CollectionService,
private routerService: RouterService, private searchService: SearchService) { } private routerService: RouterService, private searchService: SearchService,
private notificationsService: NotificationsService) { }
ngOnInit() { ngOnInit() {
this.ngZone.runOutsideAngular(() => { this.ngZone.runOutsideAngular(() => {
@ -87,8 +89,10 @@ export class AppComponent implements OnDestroy, OnInit {
this.ngZone.run(async () => { this.ngZone.run(async () => {
switch (message.command) { switch (message.command) {
case 'loggedIn': case 'loggedIn':
case 'unlocked':
case 'loggedOut': case 'loggedOut':
this.notificationsService.updateConnection();
break;
case 'unlocked':
break; break;
case 'logout': case 'logout':
this.logOut(!!message.expired); this.logOut(!!message.expired);

View File

@ -209,7 +209,8 @@ export class OrganizationBillingComponent implements OnInit {
} }
get isExpired() { get isExpired() {
return this.billing != null && this.billing.expiration != null && this.billing.expiration < new Date(); return this.billing != null && this.billing.expiration != null &&
new Date(this.billing.expiration) < new Date();
} }
get subscriptionMarkedForCancel() { get subscriptionMarkedForCancel() {

View File

@ -1,7 +1,10 @@
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { import {
ChangeDetectorRef,
Component, Component,
ComponentFactoryResolver, ComponentFactoryResolver,
NgZone,
OnDestroy,
OnInit, OnInit,
ViewChild, ViewChild,
ViewContainerRef, ViewContainerRef,
@ -16,6 +19,8 @@ import { MessagingService } from 'jslib/abstractions/messaging.service';
import { SyncService } from 'jslib/abstractions/sync.service'; import { SyncService } from 'jslib/abstractions/sync.service';
import { UserService } from 'jslib/abstractions/user.service'; import { UserService } from 'jslib/abstractions/user.service';
import { BroadcasterService } from 'jslib/angular/services/broadcaster.service';
import { Organization } from 'jslib/models/domain/organization'; import { Organization } from 'jslib/models/domain/organization';
import { CipherView } from 'jslib/models/view/cipherView'; import { CipherView } from 'jslib/models/view/cipherView';
@ -30,11 +35,13 @@ import { CiphersComponent } from './ciphers.component';
import { CollectionsComponent } from './collections.component'; import { CollectionsComponent } from './collections.component';
import { GroupingsComponent } from './groupings.component'; import { GroupingsComponent } from './groupings.component';
const BroadcasterSubscriptionId = 'OrgVaultComponent';
@Component({ @Component({
selector: 'app-org-vault', selector: 'app-org-vault',
templateUrl: 'vault.component.html', templateUrl: 'vault.component.html',
}) })
export class VaultComponent implements OnInit { export class VaultComponent implements OnInit, OnDestroy {
@ViewChild(GroupingsComponent) groupingsComponent: GroupingsComponent; @ViewChild(GroupingsComponent) groupingsComponent: GroupingsComponent;
@ViewChild(CiphersComponent) ciphersComponent: CiphersComponent; @ViewChild(CiphersComponent) ciphersComponent: CiphersComponent;
@ViewChild('attachments', { read: ViewContainerRef }) attachmentsModalRef: ViewContainerRef; @ViewChild('attachments', { read: ViewContainerRef }) attachmentsModalRef: ViewContainerRef;
@ -52,7 +59,9 @@ export class VaultComponent implements OnInit {
constructor(private route: ActivatedRoute, private userService: UserService, constructor(private route: ActivatedRoute, private userService: UserService,
private location: Location, private router: Router, private location: Location, private router: Router,
private syncService: SyncService, private i18nService: I18nService, private syncService: SyncService, private i18nService: I18nService,
private componentFactoryResolver: ComponentFactoryResolver, private messagingService: MessagingService) { } private componentFactoryResolver: ComponentFactoryResolver, private messagingService: MessagingService,
private broadcasterService: BroadcasterService, private ngZone: NgZone,
private changeDetectorRef: ChangeDetectorRef) { }
ngOnInit() { ngOnInit() {
this.route.parent.params.subscribe(async (params) => { this.route.parent.params.subscribe(async (params) => {
@ -65,6 +74,21 @@ export class VaultComponent implements OnInit {
this.ciphersComponent.searchText = this.groupingsComponent.searchText = qParams.search; this.ciphersComponent.searchText = this.groupingsComponent.searchText = qParams.search;
if (!this.organization.isAdmin) { if (!this.organization.isAdmin) {
await this.syncService.fullSync(false); await this.syncService.fullSync(false);
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
this.ngZone.run(async () => {
switch (message.command) {
case 'syncCompleted':
if (message.successfully) {
await Promise.all([
this.groupingsComponent.load(),
this.ciphersComponent.refresh(),
]);
this.changeDetectorRef.detectChanges();
}
break;
}
});
});
} }
await this.groupingsComponent.load(); await this.groupingsComponent.load();
@ -95,6 +119,10 @@ export class VaultComponent implements OnInit {
}); });
} }
ngOnDestroy() {
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
}
async clearGroupingFilters() { async clearGroupingFilters() {
this.ciphersComponent.showAddNew = true; this.ciphersComponent.showAddNew = true;
this.groupingsComponent.searchPlaceholder = this.i18nService.t('searchVault'); this.groupingsComponent.searchPlaceholder = this.i18nService.t('searchVault');

View File

@ -38,6 +38,7 @@ import { ExportService } from 'jslib/services/export.service';
import { FolderService } from 'jslib/services/folder.service'; import { FolderService } from 'jslib/services/folder.service';
import { ImportService } from 'jslib/services/import.service'; import { ImportService } from 'jslib/services/import.service';
import { LockService } from 'jslib/services/lock.service'; import { LockService } from 'jslib/services/lock.service';
import { NotificationsService } from 'jslib/services/notifications.service';
import { PasswordGenerationService } from 'jslib/services/passwordGeneration.service'; import { PasswordGenerationService } from 'jslib/services/passwordGeneration.service';
import { SearchService } from 'jslib/services/search.service'; import { SearchService } from 'jslib/services/search.service';
import { SettingsService } from 'jslib/services/settings.service'; import { SettingsService } from 'jslib/services/settings.service';
@ -64,6 +65,7 @@ import { ImportService as ImportServiceAbstraction } from 'jslib/abstractions/im
import { LockService as LockServiceAbstraction } from 'jslib/abstractions/lock.service'; import { LockService as LockServiceAbstraction } from 'jslib/abstractions/lock.service';
import { LogService as LogServiceAbstraction } from 'jslib/abstractions/log.service'; import { LogService as LogServiceAbstraction } from 'jslib/abstractions/log.service';
import { MessagingService as MessagingServiceAbstraction } from 'jslib/abstractions/messaging.service'; import { MessagingService as MessagingServiceAbstraction } from 'jslib/abstractions/messaging.service';
import { NotificationsService as NotificationsServiceAbstraction } from 'jslib/abstractions/notifications.service';
import { import {
PasswordGenerationService as PasswordGenerationServiceAbstraction, PasswordGenerationService as PasswordGenerationServiceAbstraction,
} from 'jslib/abstractions/passwordGeneration.service'; } from 'jslib/abstractions/passwordGeneration.service';
@ -92,7 +94,6 @@ const tokenService = new TokenService(storageService);
const appIdService = new AppIdService(storageService); const appIdService = new AppIdService(storageService);
const apiService = new ApiService(tokenService, platformUtilsService, const apiService = new ApiService(tokenService, platformUtilsService,
async (expired: boolean) => messagingService.send('logout', { expired: expired })); async (expired: boolean) => messagingService.send('logout', { expired: expired }));
const environmentService = new EnvironmentService(apiService, storageService);
const userService = new UserService(tokenService, storageService); const userService = new UserService(tokenService, storageService);
const settingsService = new SettingsService(userService, storageService); const settingsService = new SettingsService(userService, storageService);
export let searchService: SearchService = null; export let searchService: SearchService = null;
@ -114,6 +115,9 @@ const authService = new AuthService(cryptoService, apiService,
userService, tokenService, appIdService, i18nService, platformUtilsService, messagingService); userService, tokenService, appIdService, i18nService, platformUtilsService, messagingService);
const exportService = new ExportService(folderService, cipherService, apiService); const exportService = new ExportService(folderService, cipherService, apiService);
const importService = new ImportService(cipherService, folderService, apiService, i18nService, collectionService); const importService = new ImportService(cipherService, folderService, apiService, i18nService, collectionService);
const notificationsService = new NotificationsService(userService, tokenService, syncService, appIdService,
apiService);
const environmentService = new EnvironmentService(apiService, storageService, notificationsService);
const auditService = new AuditService(cryptoFunctionService, apiService); const auditService = new AuditService(cryptoFunctionService, apiService);
const analytics = new Analytics(window, () => platformUtilsService.isDev() || platformUtilsService.isSelfHost(), const analytics = new Analytics(window, () => platformUtilsService.isDev() || platformUtilsService.isSelfHost(),
@ -127,6 +131,7 @@ export function initFactory(): Function {
if (!isDev && platformUtilsService.isSelfHost()) { if (!isDev && platformUtilsService.isSelfHost()) {
environmentService.baseUrl = window.location.origin; environmentService.baseUrl = window.location.origin;
} }
environmentService.notificationsUrl = isDev ? 'http://localhost:61840' : null;
await apiService.setUrls({ await apiService.setUrls({
base: isDev ? null : window.location.origin, base: isDev ? null : window.location.origin,
api: isDev ? 'http://localhost:4000' : null, api: isDev ? 'http://localhost:4000' : null,
@ -139,6 +144,7 @@ export function initFactory(): Function {
// api: 'https://api.bitwarden.com', // api: 'https://api.bitwarden.com',
// identity: 'https://identity.bitwarden.com', // identity: 'https://identity.bitwarden.com',
}); });
setTimeout(() => notificationsService.init(environmentService), 3000);
lockService.init(true); lockService.init(true);
const locale = await storageService.get<string>(ConstantsService.localeKey); const locale = await storageService.get<string>(ConstantsService.localeKey);
@ -194,6 +200,7 @@ export function initFactory(): Function {
{ provide: ExportServiceAbstraction, useValue: exportService }, { provide: ExportServiceAbstraction, useValue: exportService },
{ provide: SearchServiceAbstraction, useValue: searchService }, { provide: SearchServiceAbstraction, useValue: searchService },
{ provide: ImportServiceAbstraction, useValue: importService }, { provide: ImportServiceAbstraction, useValue: importService },
{ provide: NotificationsServiceAbstraction, useValue: notificationsService },
{ provide: CryptoFunctionServiceAbstraction, useValue: cryptoFunctionService }, { provide: CryptoFunctionServiceAbstraction, useValue: cryptoFunctionService },
{ {
provide: APP_INITIALIZER, provide: APP_INITIALIZER,

View File

@ -1,7 +1,10 @@
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { import {
ChangeDetectorRef,
Component, Component,
ComponentFactoryResolver, ComponentFactoryResolver,
NgZone,
OnDestroy,
OnInit, OnInit,
ViewChild, ViewChild,
ViewContainerRef, ViewContainerRef,
@ -40,11 +43,15 @@ import { SyncService } from 'jslib/abstractions/sync.service';
import { TokenService } from 'jslib/abstractions/token.service'; import { TokenService } from 'jslib/abstractions/token.service';
import { UserService } from 'jslib/abstractions/user.service'; import { UserService } from 'jslib/abstractions/user.service';
import { BroadcasterService } from 'jslib/angular/services/broadcaster.service';
const BroadcasterSubscriptionId = 'VaultComponent';
@Component({ @Component({
selector: 'app-vault', selector: 'app-vault',
templateUrl: 'vault.component.html', templateUrl: 'vault.component.html',
}) })
export class VaultComponent implements OnInit { export class VaultComponent implements OnInit, OnDestroy {
@ViewChild(GroupingsComponent) groupingsComponent: GroupingsComponent; @ViewChild(GroupingsComponent) groupingsComponent: GroupingsComponent;
@ViewChild(CiphersComponent) ciphersComponent: CiphersComponent; @ViewChild(CiphersComponent) ciphersComponent: CiphersComponent;
@ViewChild(OrganizationsComponent) organizationsComponent: OrganizationsComponent; @ViewChild(OrganizationsComponent) organizationsComponent: OrganizationsComponent;
@ -74,7 +81,9 @@ export class VaultComponent implements OnInit {
private i18nService: I18nService, private componentFactoryResolver: ComponentFactoryResolver, private i18nService: I18nService, private componentFactoryResolver: ComponentFactoryResolver,
private tokenService: TokenService, private cryptoService: CryptoService, private tokenService: TokenService, private cryptoService: CryptoService,
private messagingService: MessagingService, private userService: UserService, private messagingService: MessagingService, private userService: UserService,
private platformUtilsService: PlatformUtilsService, private toasterService: ToasterService) { } private platformUtilsService: PlatformUtilsService, private toasterService: ToasterService,
private broadcasterService: BroadcasterService, private ngZone: NgZone,
private changeDetectorRef: ChangeDetectorRef) { }
async ngOnInit() { async ngOnInit() {
this.showVerifyEmail = !(await this.tokenService.getEmailVerified()); this.showVerifyEmail = !(await this.tokenService.getEmailVerified());
@ -85,6 +94,23 @@ export class VaultComponent implements OnInit {
this.route.queryParams.subscribe(async (params) => { this.route.queryParams.subscribe(async (params) => {
await this.syncService.fullSync(false); await this.syncService.fullSync(false);
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
this.ngZone.run(async () => {
switch (message.command) {
case 'syncCompleted':
if (message.successfully) {
await Promise.all([
this.groupingsComponent.load(),
this.organizationsComponent.load(),
this.ciphersComponent.refresh(),
]);
this.changeDetectorRef.detectChanges();
}
break;
}
});
});
await Promise.all([ await Promise.all([
this.groupingsComponent.load(), this.groupingsComponent.load(),
this.organizationsComponent.load(), this.organizationsComponent.load(),
@ -120,6 +146,10 @@ export class VaultComponent implements OnInit {
}); });
} }
ngOnDestroy() {
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
}
async clearGroupingFilters() { async clearGroupingFilters() {
this.ciphersComponent.showAddNew = true; this.ciphersComponent.showAddNew = true;
this.groupingsComponent.searchPlaceholder = this.i18nService.t('searchVault'); this.groupingsComponent.searchPlaceholder = this.i18nService.t('searchVault');