menu with messages back to app

This commit is contained in:
Kyle Spearrin 2018-02-08 15:58:47 -05:00
parent 5e8ccd19c5
commit c76b4821c6
10 changed files with 233 additions and 61 deletions

View File

@ -6,6 +6,7 @@ import { Angulartics2GoogleAnalytics } from 'angulartics2/ga';
import { import {
Component, Component,
NgZone,
OnInit, OnInit,
} from '@angular/core'; } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
@ -21,6 +22,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 { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service'; import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { SettingsService } from 'jslib/abstractions/settings.service'; import { SettingsService } from 'jslib/abstractions/settings.service';
import { SyncService } from 'jslib/abstractions/sync.service'; import { SyncService } from 'jslib/abstractions/sync.service';
import { TokenService } from 'jslib/abstractions/token.service'; import { TokenService } from 'jslib/abstractions/token.service';
@ -47,45 +49,60 @@ export class AppComponent implements OnInit {
private settingsService: SettingsService, private syncService: SyncService, private settingsService: SettingsService, private syncService: SyncService,
private passwordGenerationService: PasswordGenerationService, private cipherService: CipherService, private passwordGenerationService: PasswordGenerationService, private cipherService: CipherService,
private authService: AuthService, private router: Router, private analytics: Angulartics2, private authService: AuthService, private router: Router, private analytics: Angulartics2,
private toasterService: ToasterService, private i18nService: I18nService) { } private toasterService: ToasterService, private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService, private ngZone: NgZone) { }
ngOnInit() { ngOnInit() {
this.broadcasterService.subscribe(async (message: any) => { this.broadcasterService.subscribe((message: any) => {
switch (message.command) { this.ngZone.run(async () => {
case 'loggedIn': switch (message.command) {
break; case 'loggedIn':
case 'logout': break;
const userId = await this.userService.getUserId(); case 'logout':
this.logOut(message.expired);
await Promise.all([ break;
this.syncService.setLastSync(new Date(0)), case 'doneLoggingOut':
this.tokenService.clearToken(), this.doneLoggingOut(message.expired);
this.cryptoService.clearKeys(), break;
this.userService.clear(), case 'locked':
this.settingsService.clear(userId), break;
this.cipherService.clear(userId), case 'unlocked':
this.folderService.clear(userId), break;
this.passwordGenerationService.clear(), case 'syncStarted':
]); break;
case 'syncCompleted':
this.doneLoggingOut(message.expired); break;
break; case 'confirmLogout':
case 'doneLoggingOut': const logoutConfirmed = await this.platformUtilsService.showDialog(
this.doneLoggingOut(message.expired); this.i18nService.t('logOutConfirmation'), this.i18nService.t('logOut'),
break; this.i18nService.t('logOut'), this.i18nService.t('cancel'));
case 'locked': if (logoutConfirmed) {
break; this.logOut(false);
case 'unlocked': }
break; break;
case 'syncStarted': default:
break; }
case 'syncCompleted': });
break;
default:
}
}); });
} }
private async logOut(expired: boolean) {
const userId = await this.userService.getUserId();
await Promise.all([
this.syncService.setLastSync(new Date(0)),
this.tokenService.clearToken(),
this.cryptoService.clearKeys(),
this.userService.clear(),
this.settingsService.clear(userId),
this.cipherService.clear(userId),
this.folderService.clear(userId),
this.passwordGenerationService.clear(),
]);
this.doneLoggingOut(expired);
}
private doneLoggingOut(expired: boolean) { private doneLoggingOut(expired: boolean) {
this.authService.logOut(() => { this.authService.logOut(() => {
this.analytics.eventTrack.next({ action: 'Logged Out' }); this.analytics.eventTrack.next({ action: 'Logged Out' });

View File

@ -35,6 +35,7 @@ import { SecureNoteView } from 'jslib/models/view/secureNoteView';
export class AddEditComponent implements OnChanges { export class AddEditComponent implements OnChanges {
@Input() folderId: string; @Input() folderId: string;
@Input() cipherId: string; @Input() cipherId: string;
@Input() type: CipherType;
@Output() onSavedCipher = new EventEmitter<CipherView>(); @Output() onSavedCipher = new EventEmitter<CipherView>();
@Output() onDeletedCipher = new EventEmitter<CipherView>(); @Output() onDeletedCipher = new EventEmitter<CipherView>();
@Output() onCancelled = new EventEmitter<CipherView>(); @Output() onCancelled = new EventEmitter<CipherView>();
@ -119,7 +120,7 @@ export class AddEditComponent implements OnChanges {
this.title = this.i18nService.t('addItem'); this.title = this.i18nService.t('addItem');
this.cipher = new CipherView(); this.cipher = new CipherView();
this.cipher.folderId = this.folderId; this.cipher.folderId = this.folderId;
this.cipher.type = CipherType.Login; this.cipher.type = this.type == null ? CipherType.Login : this.type;
this.cipher.login = new LoginView(); this.cipher.login = new LoginView();
this.cipher.card = new CardView(); this.cipher.card = new CardView();
this.cipher.identity = new IdentityView(); this.cipher.identity = new IdentityView();

View File

@ -21,6 +21,7 @@
<app-vault-add-edit id="details" <app-vault-add-edit id="details"
*ngIf="action === 'add' || action === 'edit'" *ngIf="action === 'add' || action === 'edit'"
[folderId]="action === 'add' && folderId !== 'none' ? folderId : null" [folderId]="action === 'add' && folderId !== 'none' ? folderId : null"
[type]="action === 'add' ? addType : null"
[cipherId]="action === 'edit' ? cipherId : null" [cipherId]="action === 'edit' ? cipherId : null"
(onSavedCipher)="savedCipher($event)" (onSavedCipher)="savedCipher($event)"
(onDeletedCipher)="deletedCipher($event)" (onDeletedCipher)="deletedCipher($event)"

View File

@ -2,8 +2,10 @@ import * as template from './vault.component.html';
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { import {
ChangeDetectorRef,
Component, Component,
ComponentFactoryResolver, ComponentFactoryResolver,
NgZone,
OnInit, OnInit,
ViewChild, ViewChild,
ViewContainerRef, ViewContainerRef,
@ -15,6 +17,8 @@ import {
import { ModalComponent } from '../modal.component'; import { ModalComponent } from '../modal.component';
import { BroadcasterService } from '../services/broadcaster.service';
import { AddEditComponent } from './add-edit.component'; import { AddEditComponent } from './add-edit.component';
import { AttachmentsComponent } from './attachments.component'; import { AttachmentsComponent } from './attachments.component';
import { CiphersComponent } from './ciphers.component'; import { CiphersComponent } from './ciphers.component';
@ -48,12 +52,46 @@ export class VaultComponent implements OnInit {
type: CipherType = null; type: CipherType = null;
folderId: string = null; folderId: string = null;
collectionId: string = null; collectionId: string = null;
addType: CipherType = null;
constructor(private route: ActivatedRoute, private router: Router, private location: Location, constructor(private route: ActivatedRoute, private router: Router, private location: Location,
private componentFactoryResolver: ComponentFactoryResolver, private i18nService: I18nService) { private componentFactoryResolver: ComponentFactoryResolver, private i18nService: I18nService,
private broadcasterService: BroadcasterService, private changeDetectorRef: ChangeDetectorRef,
private ngZone: NgZone) {
} }
async ngOnInit() { async ngOnInit() {
this.broadcasterService.subscribe((message: any) => {
this.ngZone.run(async () => {
let detectChanges = true;
switch (message.command) {
case 'newLogin':
this.addCipher(CipherType.Login);
break;
case 'newCard':
this.addCipher(CipherType.Card);
break;
case 'newIdentity':
this.addCipher(CipherType.Identity);
break;
case 'newSecureNote':
this.addCipher(CipherType.SecureNote);
break;
case 'newFolder':
await this.addFolder();
break;
default:
detectChanges = false;
break;
}
if (detectChanges) {
this.changeDetectorRef.detectChanges();
}
});
});
this.route.queryParams.subscribe(async (params) => { this.route.queryParams.subscribe(async (params) => {
if (params.cipherId) { if (params.cipherId) {
const cipherView = new CipherView(); const cipherView = new CipherView();
@ -108,11 +146,12 @@ export class VaultComponent implements OnInit {
this.go(); this.go();
} }
addCipher() { addCipher(type: CipherType = null) {
if (this.action === 'add') { if (this.action === 'add') {
return; return;
} }
this.addType = type;
this.action = 'add'; this.action = 'add';
this.cipherId = null; this.cipherId = null;
this.go(); this.go();

View File

@ -600,5 +600,11 @@
}, },
"loginExpired": { "loginExpired": {
"message": "Your login session has expired." "message": "Your login session has expired."
},
"logOutConfirmation": {
"message": "Are you sure you want to log out?"
},
"logOut": {
"message": "Log Out"
} }
} }

View File

@ -17,10 +17,9 @@ if (watch) {
const i18nService = new I18nService('en', './locales/'); const i18nService = new I18nService('en', './locales/');
i18nService.init().then(() => { }); i18nService.init().then(() => { });
let win: BrowserWindow; const windowMain = new WindowMain(dev);
const messagingMain = new MessagingMain(); const messagingMain = new MessagingMain();
const menuMain = new MenuMain(); const menuMain = new MenuMain(windowMain);
const windowMain = new WindowMain(win, dev);
messagingMain.init(); messagingMain.init();
menuMain.init(); menuMain.init();

View File

@ -1,10 +1,69 @@
import { app, Menu, MenuItemConstructorOptions } from 'electron'; import {
app,
BrowserWindow,
Menu,
MenuItemConstructorOptions,
ipcMain,
} from 'electron';
import { WindowMain } from './window.main';
export class MenuMain { export class MenuMain {
constructor() { } constructor(private windowMain: WindowMain) { }
init() { init() {
const self = this;
const template: MenuItemConstructorOptions[] = [ const template: MenuItemConstructorOptions[] = [
{
label: 'bitwarden'
},
{
label: 'File',
submenu: [
{
label: 'New Item',
submenu: [
{
label: 'New Login',
click() {
self.send('newLogin');
}
},
{
label: 'New Card',
click() {
self.send('newCard');
}
},
{
label: 'New Identity',
click() {
self.send('newIdentity');
}
},
{
label: 'New Secure Note',
click() {
self.send('newSecureNote');
}
}
]
},
{
label: 'New Login',
click() {
self.send('newLogin');
}
},
{
label: 'New Folder',
click() {
self.send('newFolder');
}
}
]
},
{ {
label: 'Edit', label: 'Edit',
submenu: [ submenu: [
@ -33,6 +92,17 @@ export class MenuMain {
{ role: 'togglefullscreen' } { role: 'togglefullscreen' }
] ]
}, },
{
label: 'Account',
submenu: [
{
label: 'Log Out',
click() {
self.send('confirmLogout');
}
},
]
},
{ {
role: 'window', role: 'window',
submenu: [ submenu: [
@ -52,32 +122,64 @@ export class MenuMain {
]; ];
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
template.unshift({ template[0].submenu = [
label: app.getName(), {
submenu: [ label: 'Settings',
{ role: 'about' }, click() {
{ type: 'separator' }, self.send('openSettings');
{ role: 'services', submenu: [] }, }
{ type: 'separator' }, },
{ role: 'hide' }, {
{ role: 'hideothers' }, label: 'Lock',
{ role: 'unhide' }, click() {
{ type: 'separator' }, self.send('lockApp');
{ role: 'quit' } }
] },
}) { type: 'separator' },
{ role: 'about' },
{ type: 'separator' },
{ role: 'services', submenu: [] },
{ type: 'separator' },
{ role: 'hide' },
{ role: 'hideothers' },
{ role: 'unhide' },
{ type: 'separator' },
{ role: 'quit' }
];
// Window menu // Window menu
template[3].submenu = [ template[4].submenu = [
{ role: 'close' }, { role: 'close' },
{ role: 'minimize' }, { role: 'minimize' },
{ role: 'zoom' }, { role: 'zoom' },
{ type: 'separator' }, { type: 'separator' },
{ role: 'front' } { role: 'front' }
] ]
}; } else {
template[0].submenu = [
{
label: 'Settings',
click() {
self.send('openSettings');
}
},
{
label: 'Lock',
click() {
self.send('lockApp');
}
},
];
}
const menu = Menu.buildFromTemplate(template); const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu); Menu.setApplicationMenu(menu);
} }
send(command: string, message?: any) {
this.windowMain.win.webContents.send('messagingService', {
command: command,
message: message,
});
}
} }

View File

@ -12,7 +12,6 @@ export class MessagingMain {
case 'logout': case 'logout':
break; break;
case 'syncCompleted': case 'syncCompleted':
console.log('sync completed!!');
break; break;
default: default:
break; break;

View File

@ -3,7 +3,9 @@ import * as path from 'path';
import * as url from 'url'; import * as url from 'url';
export class WindowMain { export class WindowMain {
constructor(private win: BrowserWindow, private dev: boolean) { } win: BrowserWindow;
constructor(private dev: boolean) { }
init() { init() {
try { try {

View File

@ -5,7 +5,13 @@ import { MessagingService } from 'jslib/abstractions';
import { BroadcasterService } from '../app/services/broadcaster.service'; import { BroadcasterService } from '../app/services/broadcaster.service';
export class DesktopMessagingService implements MessagingService { export class DesktopMessagingService implements MessagingService {
constructor(private broadcasterService: BroadcasterService) { } constructor(private broadcasterService: BroadcasterService) {
ipcRenderer.on('messagingService', async (event: any, message: any) => {
if (message.command) {
this.send(message.command, message);
}
});
}
send(subscriber: string, arg: any = {}) { send(subscriber: string, arg: any = {}) {
const message = Object.assign({}, { command: subscriber }, arg); const message = Object.assign({}, { command: subscriber }, arg);