mirror of
https://github.com/bitwarden/desktop.git
synced 2024-11-30 12:54:31 +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 { AddEditComponent } from './vault/add-edit.component';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { AttachmentsComponent } from './vault/attachments.component';
|
import { AttachmentsComponent } from './vault/attachments.component';
|
||||||
|
import { AutofocusDirective } from './directives/autofocus.directive';
|
||||||
import { BlurClickDirective } from './directives/blur-click.directive';
|
import { BlurClickDirective } from './directives/blur-click.directive';
|
||||||
import { BoxRowDirective } from './directives/box-row.directive';
|
import { BoxRowDirective } from './directives/box-row.directive';
|
||||||
import { CiphersComponent } from './vault/ciphers.component';
|
import { CiphersComponent } from './vault/ciphers.component';
|
||||||
import { FallbackSrcDirective } from './directives/fallback-src.directive';
|
import { FallbackSrcDirective } from './directives/fallback-src.directive';
|
||||||
|
import { FolderAddEditComponent } from './vault/folder-add-edit.component';
|
||||||
import { GroupingsComponent } from './vault/groupings.component';
|
import { GroupingsComponent } from './vault/groupings.component';
|
||||||
import { I18nPipe } from './pipes/i18n.pipe';
|
import { I18nPipe } from './pipes/i18n.pipe';
|
||||||
import { IconComponent } from './vault/icon.component';
|
import { IconComponent } from './vault/icon.component';
|
||||||
@ -46,10 +48,12 @@ import { ViewComponent } from './vault/view.component';
|
|||||||
AddEditComponent,
|
AddEditComponent,
|
||||||
AppComponent,
|
AppComponent,
|
||||||
AttachmentsComponent,
|
AttachmentsComponent,
|
||||||
|
AutofocusDirective,
|
||||||
BlurClickDirective,
|
BlurClickDirective,
|
||||||
BoxRowDirective,
|
BoxRowDirective,
|
||||||
CiphersComponent,
|
CiphersComponent,
|
||||||
FallbackSrcDirective,
|
FallbackSrcDirective,
|
||||||
|
FolderAddEditComponent,
|
||||||
GroupingsComponent,
|
GroupingsComponent,
|
||||||
I18nPipe,
|
I18nPipe,
|
||||||
IconComponent,
|
IconComponent,
|
||||||
@ -64,6 +68,7 @@ import { ViewComponent } from './vault/view.component';
|
|||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
AttachmentsComponent,
|
AttachmentsComponent,
|
||||||
|
FolderAddEditComponent,
|
||||||
ModalComponent,
|
ModalComponent,
|
||||||
PasswordGeneratorComponent,
|
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() onFavoritesClicked = new EventEmitter();
|
||||||
@Output() onCipherTypeClicked = new EventEmitter<CipherType>();
|
@Output() onCipherTypeClicked = new EventEmitter<CipherType>();
|
||||||
@Output() onFolderClicked = new EventEmitter<FolderView>();
|
@Output() onFolderClicked = new EventEmitter<FolderView>();
|
||||||
|
@Output() onAddFolder = new EventEmitter();
|
||||||
|
@Output() onEditFolder = new EventEmitter<FolderView>();
|
||||||
@Output() onCollectionClicked = new EventEmitter<CollectionView>();
|
@Output() onCollectionClicked = new EventEmitter<CollectionView>();
|
||||||
|
|
||||||
folders: any[];
|
folders: any[];
|
||||||
@ -42,10 +44,14 @@ export class GroupingsComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.folders = await this.folderService.getAllDecrypted();
|
await this.loadFolders();
|
||||||
this.collections = await this.collectionService.getAllDecrypted();
|
this.collections = await this.collectionService.getAllDecrypted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async loadFolders() {
|
||||||
|
this.folders = await this.folderService.getAllDecrypted();
|
||||||
|
}
|
||||||
|
|
||||||
all() {
|
all() {
|
||||||
this.clearSelections();
|
this.clearSelections();
|
||||||
this.selectedAll = true;
|
this.selectedAll = true;
|
||||||
@ -71,6 +77,14 @@ export class GroupingsComponent implements OnInit {
|
|||||||
this.onFolderClicked.emit(folder);
|
this.onFolderClicked.emit(folder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addFolder() {
|
||||||
|
this.onAddFolder.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
editFolder(folder: FolderView) {
|
||||||
|
this.onEditFolder.emit(folder);
|
||||||
|
}
|
||||||
|
|
||||||
collection(collection: CollectionView) {
|
collection(collection: CollectionView) {
|
||||||
this.clearSelections();
|
this.clearSelections();
|
||||||
this.selectedCollectionId = collection.id;
|
this.selectedCollectionId = collection.id;
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
Options
|
{{'options' | i18n}}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content condensed">
|
<div class="box-content condensed">
|
||||||
<div class="box-content-row box-content-row-slider" appBoxRow>
|
<div class="box-content-row box-content-row-slider" appBoxRow>
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
(onFavoritesClicked)="filterFavorites()"
|
(onFavoritesClicked)="filterFavorites()"
|
||||||
(onCipherTypeClicked)="filterCipherType($event)"
|
(onCipherTypeClicked)="filterCipherType($event)"
|
||||||
(onFolderClicked)="filterFolder($event.id)"
|
(onFolderClicked)="filterFolder($event.id)"
|
||||||
|
(onAddFolder)="addFolder()"
|
||||||
|
(onEditFolder)="editFolder($event.id)"
|
||||||
(onCollectionClicked)="filterCollection($event.id)">
|
(onCollectionClicked)="filterCollection($event.id)">
|
||||||
</app-vault-groupings>
|
</app-vault-groupings>
|
||||||
<app-vault-ciphers id="items"
|
<app-vault-ciphers id="items"
|
||||||
@ -28,4 +30,5 @@
|
|||||||
</app-vault-add-edit>
|
</app-vault-add-edit>
|
||||||
<ng-template #passwordGenerator></ng-template>
|
<ng-template #passwordGenerator></ng-template>
|
||||||
<ng-template #attachments></ng-template>
|
<ng-template #attachments></ng-template>
|
||||||
|
<ng-template #folderAddEdit></ng-template>
|
||||||
</div>
|
</div>
|
||||||
|
@ -18,6 +18,7 @@ import { Location } from '@angular/common';
|
|||||||
import { AttachmentsComponent } from './attachments.component';
|
import { AttachmentsComponent } from './attachments.component';
|
||||||
import { AddEditComponent } from './add-edit.component';
|
import { AddEditComponent } from './add-edit.component';
|
||||||
import { CiphersComponent } from './ciphers.component';
|
import { CiphersComponent } from './ciphers.component';
|
||||||
|
import { FolderAddEditComponent } from './folder-add-edit.component';
|
||||||
import { GroupingsComponent } from './groupings.component';
|
import { GroupingsComponent } from './groupings.component';
|
||||||
import { PasswordGeneratorComponent } from './password-generator.component';
|
import { PasswordGeneratorComponent } from './password-generator.component';
|
||||||
import { ModalComponent } from '../modal.component';
|
import { ModalComponent } from '../modal.component';
|
||||||
@ -40,6 +41,7 @@ export class VaultComponent implements OnInit {
|
|||||||
@ViewChild(GroupingsComponent) groupingsComponent: GroupingsComponent;
|
@ViewChild(GroupingsComponent) groupingsComponent: GroupingsComponent;
|
||||||
@ViewChild('passwordGenerator', { read: ViewContainerRef }) passwordGeneratorModal: ViewContainerRef;
|
@ViewChild('passwordGenerator', { read: ViewContainerRef }) passwordGeneratorModal: ViewContainerRef;
|
||||||
@ViewChild('attachments', { read: ViewContainerRef }) attachmentsModal: ViewContainerRef;
|
@ViewChild('attachments', { read: ViewContainerRef }) attachmentsModal: ViewContainerRef;
|
||||||
|
@ViewChild('folderAddEdit', { read: ViewContainerRef }) folderAddEditModal: ViewContainerRef;
|
||||||
|
|
||||||
action: string;
|
action: string;
|
||||||
cipherId: string = null;
|
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() {
|
private clearFilters() {
|
||||||
this.folderId = null;
|
this.folderId = null;
|
||||||
this.collectionId = null;
|
this.collectionId = null;
|
||||||
|
@ -309,9 +309,6 @@
|
|||||||
"editFolder": {
|
"editFolder": {
|
||||||
"message": "Edit Folder"
|
"message": "Edit Folder"
|
||||||
},
|
},
|
||||||
"deleteFolder": {
|
|
||||||
"message": "Delete Folder"
|
|
||||||
},
|
|
||||||
"regeneratePassword": {
|
"regeneratePassword": {
|
||||||
"message": "Regenerate Password"
|
"message": "Regenerate Password"
|
||||||
},
|
},
|
||||||
@ -370,5 +367,20 @@
|
|||||||
},
|
},
|
||||||
"updateKey": {
|
"updateKey": {
|
||||||
"message": "You cannot use this feature until you update your encryption key."
|
"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