1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-23 11:56:00 +01:00

bulk actions stubbed out. bulk delete implemented

This commit is contained in:
Kyle Spearrin 2018-06-12 17:11:24 -04:00
parent 3666ee5a87
commit 314ab61349
7 changed files with 146 additions and 8 deletions

View File

@ -35,6 +35,7 @@ import { ToolsComponent } from './tools/tools.component';
import { AddEditComponent } from './vault/add-edit.component';
import { AttachmentsComponent } from './vault/attachments.component';
import { BulkDeleteComponent } from './vault/bulk-delete.component';
import { CiphersComponent } from './vault/ciphers.component';
import { CollectionsComponent } from './vault/collections.component';
import { FolderAddEditComponent } from './vault/folder-add-edit.component';
@ -81,6 +82,7 @@ import { Folder } from 'jslib/models/domain';
AutofocusDirective,
BlurClickDirective,
BoxRowDirective,
BulkDeleteComponent,
CiphersComponent,
CollectionsComponent,
ExportComponent,
@ -115,6 +117,7 @@ import { Folder } from 'jslib/models/domain';
entryComponents: [
AddEditComponent,
AttachmentsComponent,
BulkDeleteComponent,
CollectionsComponent,
FolderAddEditComponent,
ModalComponent,

View File

@ -0,0 +1,25 @@
<div class="modal fade">
<div class="modal-dialog">
<form class="modal-content" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
<div class="modal-header">
<h2 class="modal-title">
{{'deleteSelected' | i18n}}
</h2>
<button type="button" class="close" data-dismiss="modal" attr.aria-label="{{'close' | i18n}}">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
{{'deleteSelectedItemsDesc' | i18n: cipherIds.length}}
</div>
<div class="modal-footer">
<button appBlurClick type="submit" class="btn btn-danger" title="{{'delete' | i18n}}" [disabled]="form.loading">
<i class="fa fa-trash-o fa-lg fa-fw" [hidden]="form.loading"></i>
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!form.loading"></i>
{{'delete' | i18n}}
</button>
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal" title="{{'cancel' | i18n}}">{{'cancel' | i18n}}</button>
</div>
</form>
</div>
</div>

View File

@ -0,0 +1,34 @@
import {
Component,
EventEmitter,
Input,
Output,
} from '@angular/core';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { CipherService } from 'jslib/abstractions/cipher.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
@Component({
selector: 'app-vault-bulk-delete',
templateUrl: 'bulk-delete.component.html',
})
export class BulkDeleteComponent {
@Input() cipherIds: string[] = [];
@Output() onDeleted = new EventEmitter();
formPromise: Promise<any>;
constructor(private analytics: Angulartics2, private cipherService: CipherService,
private toasterService: ToasterService, private i18nService: I18nService) { }
async submit() {
this.formPromise = this.cipherService.deleteManyWithServer(this.cipherIds);
await this.formPromise;
this.onDeleted.emit();
this.analytics.eventTrack.next({ action: 'Bulk Deleted Items' });
this.toasterService.popAsync('success', null, this.i18nService.t('deletedItems'));
}
}

View File

@ -17,6 +17,8 @@ import { CipherType } from 'jslib/enums/cipherType';
import { CipherView } from 'jslib/models/view/cipherView';
const MaxCheckedCount = 500;
@Component({
selector: 'app-vault-ciphers',
templateUrl: 'ciphers.component.html',
@ -25,6 +27,7 @@ export class CiphersComponent extends BaseCiphersComponent {
@Output() onAttachmentsClicked = new EventEmitter<CipherView>();
@Output() onShareClicked = new EventEmitter<CipherView>();
@Output() onCollectionsClicked = new EventEmitter<CipherView>();
cipherType = CipherType;
constructor(cipherService: CipherService, private analytics: Angulartics2,
@ -37,6 +40,23 @@ export class CiphersComponent extends BaseCiphersComponent {
(c as any).checked = !(c as any).checked;
}
selectAll(select: boolean) {
if (select) {
this.selectAll(false);
}
const selectCount = select && this.ciphers.length > MaxCheckedCount ? MaxCheckedCount : this.ciphers.length;
for (let i = 0; i < selectCount; i++) {
(this.ciphers[i] as any).checked = select;
}
}
getSelected(): string[] {
if (this.ciphers == null) {
return [];
}
return this.ciphers.filter((c) => !!(c as any).checked).map((c) => c.id);
}
attachments(c: CipherView) {
this.onAttachmentsClicked.emit(c);
}

View File

@ -11,35 +11,35 @@
<h1>{{'myVault' | i18n}}</h1>
<div class="ml-auto d-flex">
<div class="dropdown mr-2" appListDropdown>
<button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown"
<button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button" id="bulkActionsButton" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<i class="fa fa-cog"></i>
</button>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
<a class="dropdown-item" href="#">
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="bulkActionsButton">
<a class="dropdown-item" href="#" appStopClick (click)="bulkMove()">
<i class="fa fa-fw fa-share"></i>
{{'moveSelected' | i18n}}
</a>
<a class="dropdown-item" href="#">
<a class="dropdown-item" href="#" appStopClick (click)="bulkShare()">
<i class="fa fa-fw fa-share-alt"></i>
{{'shareSelected' | i18n}}
</a>
<a class="dropdown-item text-danger" href="#">
<a class="dropdown-item text-danger" href="#" (click)="bulkDelete()">
<i class="fa fa-fw fa-trash-o"></i>
{{'deleteSelected' | i18n}}
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">
<a class="dropdown-item" href="#" appStopClick (click)="selectAll(true)">
<i class="fa fa-fw fa-check-square-o"></i>
{{'selectAll' | i18n}}
</a>
<a class="dropdown-item" href="#">
<a class="dropdown-item" href="#" appStopClick (click)="selectAll(false)">
<i class="fa fa-fw fa-minus-square-o"></i>
{{'unselectAll' | i18n}}
</a>
</div>
</div>
<button type="button" class="btn btn-primary btn-sm" (click)="addCipher()">
<button type="button" class="btn btn-primary btn-sm" (click)="addCipher()" appBlurClick>
<i class="fa fa-plus fa-fw"></i>{{'addItem' | i18n}}
</button>
</div>
@ -71,3 +71,6 @@
<ng-template #cipherAddEdit></ng-template>
<ng-template #share></ng-template>
<ng-template #collections></ng-template>
<ng-template #bulkDeleteTemplate></ng-template>
<ng-template #bulkMoveTemplate></ng-template>
<ng-template #bulkShareTemplate></ng-template>

View File

@ -20,6 +20,7 @@ import { ModalComponent } from '../modal.component';
import { AddEditComponent } from './add-edit.component';
import { AttachmentsComponent } from './attachments.component';
import { BulkDeleteComponent } from './bulk-delete.component';
import { CiphersComponent } from './ciphers.component';
import { CollectionsComponent } from './collections.component';
import { FolderAddEditComponent } from './folder-add-edit.component';
@ -43,6 +44,7 @@ export class VaultComponent implements OnInit {
@ViewChild('cipherAddEdit', { read: ViewContainerRef }) cipherAddEditModalRef: ViewContainerRef;
@ViewChild('share', { read: ViewContainerRef }) shareModalRef: ViewContainerRef;
@ViewChild('collections', { read: ViewContainerRef }) collectionsModalRef: ViewContainerRef;
@ViewChild('bulkDeleteTemplate', { read: ViewContainerRef }) bulkDeleteModalRef: ViewContainerRef;
cipherId: string = null;
favorites: boolean = false;
@ -277,6 +279,39 @@ export class VaultComponent implements OnInit {
return childComponent;
}
bulkDelete() {
if (this.modal != null) {
this.modal.close();
}
const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent);
this.modal = this.bulkDeleteModalRef.createComponent(factory).instance;
const childComponent = this.modal.show<BulkDeleteComponent>(
BulkDeleteComponent, this.bulkDeleteModalRef);
childComponent.cipherIds = this.ciphersComponent.getSelected();
childComponent.onDeleted.subscribe(async () => {
this.modal.close();
await this.ciphersComponent.refresh();
});
this.modal.onClosed.subscribe(() => {
this.modal = null;
});
}
bulkShare() {
//
}
bulkMove() {
//
}
selectAll(select: boolean) {
this.ciphersComponent.selectAll(select);
}
private clearFilters() {
this.folderId = null;
this.collectionId = null;

View File

@ -419,6 +419,9 @@
"sharedItem": {
"message": "Shared item"
},
"sharedItems": {
"message": "Shared items"
},
"deleteItem": {
"message": "Delete Item"
},
@ -434,6 +437,12 @@
"deletedItem": {
"message": "Deleted item"
},
"deletedItems": {
"message": "Deleted items"
},
"movedItems": {
"message": "Moved items"
},
"overwritePasswordConfirmation": {
"message": "Are you sure you want to overwrite the current password?"
},
@ -664,5 +673,14 @@
},
"collectionsDesc": {
"message": "Edit the collections that this item is being shared with. Only organization users with access to these collections will be able to see this item."
},
"deleteSelectedItemsDesc": {
"message": "You have selected $COUNT$ item(s) to delete. Are you sure you want to delete all of these items?",
"placeholders": {
"count": {
"content": "$1",
"example": "150"
}
}
}
}