add/edit folders

This commit is contained in:
Kyle Spearrin 2018-01-30 15:40:06 -05:00
parent 7c181ee5f3
commit 442042f59d
9 changed files with 194 additions and 5 deletions

View File

@ -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,
],

View File

@ -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();
}
}
}

View File

@ -0,0 +1,32 @@
<div class="modal fade">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-body">
<div class="box">
<div class="box-header">
{{'addFolder' | i18n}}
</div>
<div class="box-content">
<div class="box-content-row" appBoxRow>
<label for="name">{{'name' | i18n}}</label>
<input id="name" type="text" name="Name" [(ngModel)]="folder.name"
[appAutofocus]="!editMode">
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="primary" appBlurClick (click)="save()">
<i class="fa fa-lg fa-save"></i> {{'save' | i18n}}
</button>
<button type="button" data-dismiss="modal">{{'close' | i18n}}</button>
<div class="right">
<button appBlurClick (click)="delete()" class="danger" title="{{'delete' | i18n}}"
*ngIf="editMode">
<i class="fa fa-trash-o fa-lg"></i>
</button>
</div>
</div>
</div>
</div>
</div>

View File

@ -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<FolderView>();
@Output() onDeletedFolder = new EventEmitter<FolderView>();
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);
}
}

View File

@ -25,6 +25,8 @@ export class GroupingsComponent implements OnInit {
@Output() onFavoritesClicked = new EventEmitter();
@Output() onCipherTypeClicked = new EventEmitter<CipherType>();
@Output() onFolderClicked = new EventEmitter<FolderView>();
@Output() onAddFolder = new EventEmitter();
@Output() onEditFolder = new EventEmitter<FolderView>();
@Output() onCollectionClicked = new EventEmitter<CollectionView>();
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;

View File

@ -17,7 +17,7 @@
</div>
<div class="box">
<div class="box-header">
Options
{{'options' | i18n}}
</div>
<div class="box-content condensed">
<div class="box-content-row box-content-row-slider" appBoxRow>

View File

@ -4,6 +4,8 @@
(onFavoritesClicked)="filterFavorites()"
(onCipherTypeClicked)="filterCipherType($event)"
(onFolderClicked)="filterFolder($event.id)"
(onAddFolder)="addFolder()"
(onEditFolder)="editFolder($event.id)"
(onCollectionClicked)="filterCollection($event.id)">
</app-vault-groupings>
<app-vault-ciphers id="items"
@ -28,4 +30,5 @@
</app-vault-add-edit>
<ng-template #passwordGenerator></ng-template>
<ng-template #attachments></ng-template>
<ng-template #folderAddEdit></ng-template>
</div>

View File

@ -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>(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>(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;

View File

@ -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"
}
}