mirror of
https://github.com/bitwarden/browser.git
synced 2024-12-26 17:08:33 +01:00
add/edit folders
This commit is contained in:
parent
7c181ee5f3
commit
442042f59d
@ -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,
|
||||
],
|
||||
|
24
src/app/directives/autofocus.directive.ts
Normal file
24
src/app/directives/autofocus.directive.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
32
src/app/vault/folder-add-edit.component.html
Normal file
32
src/app/vault/folder-add-edit.component.html
Normal 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>
|
69
src/app/vault/folder-add-edit.component.ts
Normal file
69
src/app/vault/folder-add-edit.component.ts
Normal 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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user