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:
parent
3666ee5a87
commit
314ab61349
@ -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,
|
||||
|
25
src/app/vault/bulk-delete.component.html
Normal file
25
src/app/vault/bulk-delete.component.html
Normal 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">×</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>
|
34
src/app/vault/bulk-delete.component.ts
Normal file
34
src/app/vault/bulk-delete.component.ts
Normal 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'));
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user