From 442042f59d25049b3b28e695a97c7c4ac316b65c Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Tue, 30 Jan 2018 15:40:06 -0500 Subject: [PATCH] add/edit folders --- src/app/app.module.ts | 5 ++ src/app/directives/autofocus.directive.ts | 24 +++++++ src/app/vault/folder-add-edit.component.html | 32 +++++++++ src/app/vault/folder-add-edit.component.ts | 69 +++++++++++++++++++ src/app/vault/groupings.component.ts | 16 ++++- .../vault/password-generator.component.html | 2 +- src/app/vault/vault.component.html | 3 + src/app/vault/vault.component.ts | 30 ++++++++ src/locales/en/messages.json | 18 ++++- 9 files changed, 194 insertions(+), 5 deletions(-) create mode 100644 src/app/directives/autofocus.directive.ts create mode 100644 src/app/vault/folder-add-edit.component.html create mode 100644 src/app/vault/folder-add-edit.component.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 061908bfa3..61439fefd8 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -13,10 +13,12 @@ import { ToasterModule } from 'angular2-toaster'; import { AddEditComponent } from './vault/add-edit.component'; import { AppComponent } from './app.component'; import { AttachmentsComponent } from './vault/attachments.component'; +import { AutofocusDirective } from './directives/autofocus.directive'; import { BlurClickDirective } from './directives/blur-click.directive'; import { BoxRowDirective } from './directives/box-row.directive'; import { CiphersComponent } from './vault/ciphers.component'; import { FallbackSrcDirective } from './directives/fallback-src.directive'; +import { FolderAddEditComponent } from './vault/folder-add-edit.component'; import { GroupingsComponent } from './vault/groupings.component'; import { I18nPipe } from './pipes/i18n.pipe'; import { IconComponent } from './vault/icon.component'; @@ -46,10 +48,12 @@ import { ViewComponent } from './vault/view.component'; AddEditComponent, AppComponent, AttachmentsComponent, + AutofocusDirective, BlurClickDirective, BoxRowDirective, CiphersComponent, FallbackSrcDirective, + FolderAddEditComponent, GroupingsComponent, I18nPipe, IconComponent, @@ -64,6 +68,7 @@ import { ViewComponent } from './vault/view.component'; ], entryComponents: [ AttachmentsComponent, + FolderAddEditComponent, ModalComponent, PasswordGeneratorComponent, ], diff --git a/src/app/directives/autofocus.directive.ts b/src/app/directives/autofocus.directive.ts new file mode 100644 index 0000000000..07442d1060 --- /dev/null +++ b/src/app/directives/autofocus.directive.ts @@ -0,0 +1,24 @@ +import { + Directive, + ElementRef, + Input, +} from '@angular/core'; + +@Directive({ + selector: '[appAutofocus]', +}) +export class AutofocusDirective { + @Input() set appAutofocus(condition: boolean | string) { + this._autofocus = condition === '' || condition === true; + } + + private _autofocus: boolean; + + constructor(private el: ElementRef) { } + + ngOnInit() { + if (this._autofocus) { + this.el.nativeElement.focus(); + } + } +} diff --git a/src/app/vault/folder-add-edit.component.html b/src/app/vault/folder-add-edit.component.html new file mode 100644 index 0000000000..7462039da6 --- /dev/null +++ b/src/app/vault/folder-add-edit.component.html @@ -0,0 +1,32 @@ + diff --git a/src/app/vault/folder-add-edit.component.ts b/src/app/vault/folder-add-edit.component.ts new file mode 100644 index 0000000000..826258ce4b --- /dev/null +++ b/src/app/vault/folder-add-edit.component.ts @@ -0,0 +1,69 @@ +import * as template from './folder-add-edit.component.html'; + +import { + Component, + EventEmitter, + Input, + OnInit, + Output, +} from '@angular/core'; + +import { Angulartics2 } from 'angulartics2'; +import { ToasterService } from 'angular2-toaster'; + +import { FolderService } from 'jslib/abstractions/folder.service'; +import { I18nService } from 'jslib/abstractions/i18n.service'; + +import { FolderView } from 'jslib/models/view/folderView'; + +@Component({ + selector: 'app-folder-add-edit', + template: template, +}) +export class FolderAddEditComponent implements OnInit { + @Input() folderId: string; + @Output() onSavedFolder = new EventEmitter(); + @Output() onDeletedFolder = new EventEmitter(); + + editMode: boolean = false; + folder: FolderView = new FolderView(); + + constructor(private folderService: FolderService, private i18nService: I18nService, + private analytics: Angulartics2, private toasterService: ToasterService) { } + + async ngOnInit() { + this.editMode = this.folderId != null; + + if (this.editMode) { + this.editMode = true; + const folder = await this.folderService.get(this.folderId); + this.folder = await folder.decrypt(); + } + } + + async save() { + if (this.folder.name == null || this.folder.name === '') { + this.toasterService.popAsync('error', this.i18nService.t('errorOccurred'), + this.i18nService.t('nameRequired')); + return; + } + + const folder = await this.folderService.encrypt(this.folder); + await this.folderService.saveWithServer(folder); + this.analytics.eventTrack.next({ action: this.editMode ? 'Edited Folder' : 'Added Folder' }); + this.toasterService.popAsync('success', null, + this.i18nService.t(this.editMode ? 'editedFolder' : 'addedFolder')); + this.onSavedFolder.emit(this.folder); + } + + async delete() { + if (!confirm(this.i18nService.t('deleteFolderConfirmation'))) { + return; + } + + await this.folderService.deleteWithServer(this.folder.id); + this.analytics.eventTrack.next({ action: 'Deleted Folder' }); + this.toasterService.popAsync('success', null, this.i18nService.t('deletedFolder')); + this.onDeletedFolder.emit(this.folder); + } +} diff --git a/src/app/vault/groupings.component.ts b/src/app/vault/groupings.component.ts index 03c7f62652..172346ec55 100644 --- a/src/app/vault/groupings.component.ts +++ b/src/app/vault/groupings.component.ts @@ -25,6 +25,8 @@ export class GroupingsComponent implements OnInit { @Output() onFavoritesClicked = new EventEmitter(); @Output() onCipherTypeClicked = new EventEmitter(); @Output() onFolderClicked = new EventEmitter(); + @Output() onAddFolder = new EventEmitter(); + @Output() onEditFolder = new EventEmitter(); @Output() onCollectionClicked = new EventEmitter(); folders: any[]; @@ -42,10 +44,14 @@ export class GroupingsComponent implements OnInit { } async ngOnInit() { - this.folders = await this.folderService.getAllDecrypted(); + await this.loadFolders(); this.collections = await this.collectionService.getAllDecrypted(); } + async loadFolders() { + this.folders = await this.folderService.getAllDecrypted(); + } + all() { this.clearSelections(); this.selectedAll = true; @@ -71,6 +77,14 @@ export class GroupingsComponent implements OnInit { this.onFolderClicked.emit(folder); } + addFolder() { + this.onAddFolder.emit(); + } + + editFolder(folder: FolderView) { + this.onEditFolder.emit(folder); + } + collection(collection: CollectionView) { this.clearSelections(); this.selectedCollectionId = collection.id; diff --git a/src/app/vault/password-generator.component.html b/src/app/vault/password-generator.component.html index 27764763b1..ae61bc393b 100644 --- a/src/app/vault/password-generator.component.html +++ b/src/app/vault/password-generator.component.html @@ -17,7 +17,7 @@
- Options + {{'options' | i18n}}
diff --git a/src/app/vault/vault.component.html b/src/app/vault/vault.component.html index 6d6bd9bd2d..9dc3a7839c 100644 --- a/src/app/vault/vault.component.html +++ b/src/app/vault/vault.component.html @@ -4,6 +4,8 @@ (onFavoritesClicked)="filterFavorites()" (onCipherTypeClicked)="filterCipherType($event)" (onFolderClicked)="filterFolder($event.id)" + (onAddFolder)="addFolder()" + (onEditFolder)="editFolder($event.id)" (onCollectionClicked)="filterCollection($event.id)"> +
diff --git a/src/app/vault/vault.component.ts b/src/app/vault/vault.component.ts index 91ea3e46ff..2c8a3839a0 100644 --- a/src/app/vault/vault.component.ts +++ b/src/app/vault/vault.component.ts @@ -18,6 +18,7 @@ import { Location } from '@angular/common'; import { AttachmentsComponent } from './attachments.component'; import { AddEditComponent } from './add-edit.component'; import { CiphersComponent } from './ciphers.component'; +import { FolderAddEditComponent } from './folder-add-edit.component'; import { GroupingsComponent } from './groupings.component'; import { PasswordGeneratorComponent } from './password-generator.component'; import { ModalComponent } from '../modal.component'; @@ -40,6 +41,7 @@ export class VaultComponent implements OnInit { @ViewChild(GroupingsComponent) groupingsComponent: GroupingsComponent; @ViewChild('passwordGenerator', { read: ViewContainerRef }) passwordGeneratorModal: ViewContainerRef; @ViewChild('attachments', { read: ViewContainerRef }) attachmentsModal: ViewContainerRef; + @ViewChild('folderAddEdit', { read: ViewContainerRef }) folderAddEditModal: ViewContainerRef; action: string; cipherId: string = null; @@ -201,6 +203,34 @@ export class VaultComponent implements OnInit { }); } + async addFolder() { + const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent); + const modal = this.folderAddEditModal.createComponent(factory).instance; + const childComponent = modal.show(FolderAddEditComponent, this.folderAddEditModal); + + childComponent.folderId = null; + childComponent.onSavedFolder.subscribe(async (folder: FolderView) => { + modal.close(); + await this.groupingsComponent.loadFolders(); + }); + } + + async editFolder(folderId: string) { + const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent); + const modal = this.folderAddEditModal.createComponent(factory).instance; + const childComponent = modal.show(FolderAddEditComponent, this.folderAddEditModal); + + childComponent.folderId = folderId; + childComponent.onSavedFolder.subscribe(async (folder: FolderView) => { + modal.close(); + await this.groupingsComponent.loadFolders(); + }); + childComponent.onDeletedFolder.subscribe(async (folder: FolderView) => { + modal.close(); + await this.groupingsComponent.loadFolders(); + }); + } + private clearFilters() { this.folderId = null; this.collectionId = null; diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json index 186e315bc7..9f9e70a656 100644 --- a/src/locales/en/messages.json +++ b/src/locales/en/messages.json @@ -309,9 +309,6 @@ "editFolder": { "message": "Edit Folder" }, - "deleteFolder": { - "message": "Delete Folder" - }, "regeneratePassword": { "message": "Regenerate Password" }, @@ -370,5 +367,20 @@ }, "updateKey": { "message": "You cannot use this feature until you update your encryption key." + }, + "options": { + "message": "Options" + }, + "editedFolder": { + "message": "Edited folder" + }, + "addedFolder": { + "message": "Added folder" + }, + "deleteFolderConfirmation": { + "message": "Are you sure you want to delete this folder?" + }, + "deletedFolder": { + "message": "Deleted folder" } }