1
0
mirror of https://github.com/bitwarden/desktop.git synced 2024-11-28 12:35:40 +01:00

Merge pull request #446 from bitwarden/soft-delete

Soft delete feature
This commit is contained in:
Chad Scharf 2020-05-08 11:17:13 -04:00 committed by GitHub
commit 2a38350b99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 89 additions and 20 deletions

View File

@ -39,8 +39,8 @@
</ng-container>
</div>
<div class="footer">
<button appBlurClick (click)="addCipher()" (contextmenu)="addCipherOptions()" class="block primary"
appA11yTitle="{{'addItem' | i18n}}">
<button appBlurClick (click)="addCipher()" (contextmenu)="addCipherOptions()"
class="block primary" appA11yTitle="{{'addItem' | i18n}}" [disabled]="deleted">
<i class="fa fa-plus fa-lg" aria-hidden="true"></i>
</button>
</div>

View File

@ -13,6 +13,11 @@
<i class="fa fa-fw fa-star" aria-hidden="true"></i>&nbsp;{{'favorites' | i18n}}
</a>
</li>
<li [ngClass]="{active: selectedTrash}">
<a href="#" appStopClick appBlurClick (click)="selectTrash()">
<i class="fa fa-fw fa-trash-o" aria-hidden="true"></i>&nbsp;{{'trash' | i18n}}
</a>
</li>
</ul>
<h2>{{'types' | i18n}}</h2>
<ul>

View File

@ -2,7 +2,7 @@
<app-vault-groupings id="groupings" (onAllClicked)="clearGroupingFilters()" (onFavoritesClicked)="filterFavorites()"
(onCipherTypeClicked)="filterCipherType($event)" (onFolderClicked)="filterFolder($event.id)"
(onAddFolder)="addFolder()" (onEditFolder)="editFolder($event.id)"
(onCollectionClicked)="filterCollection($event.id)">
(onCollectionClicked)="filterCollection($event.id)" (onTrashClicked)="filterDeleted()">
</app-vault-groupings>
<app-vault-ciphers id="items" [activeCipherId]="cipherId" (onCipherClicked)="viewCipher($event)"
(onCipherRightClicked)="viewCipherMenu($event)" (onAddCipher)="addCipher($event)"
@ -10,7 +10,8 @@
</app-vault-ciphers>
<app-vault-view id="details" *ngIf="cipherId && action === 'view'" [cipherId]="cipherId"
(onCloneCipher)="cloneCipher($event)" (onEditCipher)="editCipher($event)"
(onViewCipherPasswordHistory)="viewCipherPasswordHistory($event)">
(onViewCipherPasswordHistory)="viewCipherPasswordHistory($event)" (onRestoredCipher)="restoredCipher($event)"
(onDeletedCipher)="deletedCipher($event)">
</app-vault-view>
<app-vault-add-edit id="details" *ngIf="action === 'add' || action === 'edit' || action === 'clone'"
[cloneMode]="action === 'clone'"

View File

@ -76,6 +76,7 @@ export class VaultComponent implements OnInit, OnDestroy {
addOrganizationId: string = null;
addCollectionIds: string[] = null;
showingModal = false;
deleted = false;
private modal: ModalComponent = null;
@ -218,7 +219,10 @@ export class VaultComponent implements OnInit, OnDestroy {
await this.addCipher();
}
if (params.favorites) {
if (params.deleted) {
this.groupingsComponent.selectedTrash = true;
await this.filterDeleted();
} else if (params.favorites) {
this.groupingsComponent.selectedFavorites = true;
await this.filterFavorites();
} else if (params.type) {
@ -263,18 +267,20 @@ export class VaultComponent implements OnInit, OnDestroy {
this.viewCipher(cipher);
}),
}));
menu.append(new remote.MenuItem({
label: this.i18nService.t('edit'),
click: () => this.functionWithChangeDetection(() => {
this.editCipher(cipher);
}),
}));
menu.append(new remote.MenuItem({
label: this.i18nService.t('clone'),
click: () => this.functionWithChangeDetection(() => {
this.cloneCipher(cipher);
}),
}));
if (!cipher.isDeleted) {
menu.append(new remote.MenuItem({
label: this.i18nService.t('edit'),
click: () => this.functionWithChangeDetection(() => {
this.editCipher(cipher);
}),
}));
menu.append(new remote.MenuItem({
label: this.i18nService.t('clone'),
click: () => this.functionWithChangeDetection(() => {
this.cloneCipher(cipher);
}),
}));
}
switch (cipher.type) {
case CipherType.Login:
@ -402,6 +408,13 @@ export class VaultComponent implements OnInit, OnDestroy {
await this.ciphersComponent.refresh();
}
async restoredCipher(cipher: CipherView) {
this.cipherId = null;
this.action = null;
this.go();
await this.ciphersComponent.refresh();
}
editCipherAttachments(cipher: CipherView) {
if (this.modal != null) {
this.modal.close();
@ -498,6 +511,15 @@ export class VaultComponent implements OnInit, OnDestroy {
this.go();
}
async filterDeleted() {
this.ciphersComponent.searchPlaceholder = this.i18nService.t('searchTrash');
this.ciphersComponent.deleted = true;
await this.ciphersComponent.reload(null, true);
this.clearFilters();
this.deleted = true;
this.go();
}
async filterCipherType(type: CipherType) {
this.ciphersComponent.searchPlaceholder = this.i18nService.t('searchType');
await this.ciphersComponent.reload((c) => c.type === type);
@ -630,6 +652,7 @@ export class VaultComponent implements OnInit, OnDestroy {
this.addCollectionIds = null;
this.addType = null;
this.addOrganizationId = null;
this.deleted = false;
}
private go(queryParams: any = null) {
@ -641,6 +664,7 @@ export class VaultComponent implements OnInit, OnDestroy {
type: this.type,
folderId: this.folderId,
collectionId: this.collectionId,
deleted: this.deleted ? true : null,
};
}

View File

@ -273,10 +273,21 @@
</div>
</div>
<div class="footer">
<button appBlurClick class="primary" (click)="edit()" appA11yTitle="{{'edit' | i18n}}">
<button appBlurClick class="primary" (click)="edit()" appA11yTitle="{{'edit' | i18n}}" *ngIf="!cipher.isDeleted">
<i class="fa fa-pencil fa-fw fa-lg" aria-hidden="true"></i>
</button>
<button appBlurClick class="primary" *ngIf="!cipher?.organizationId" (click)="clone()" appA11yTitle="{{'clone' | i18n}}">
<button appBlurClick class="primary" (click)="restore()" appA11yTitle="{{'restore' | i18n}}"
*ngIf="cipher.isDeleted">
<i class="fa fa-undo fa-fw fa-lg" aria-hidden="true"></i>
</button>
<button appBlurClick class="primary" *ngIf="!cipher?.organizationId && !cipher.isDeleted" (click)="clone()"
appA11yTitle="{{'clone' | i18n}}">
<i class="fa fa-clone fa-fw fa-lg" aria-hidden="true"></i>
</button>
<div class="right">
<button appBlurClick type="button" (click)="delete()" class="danger"
appA11yTitle="{{(cipher.isDeleted ? 'permanentlyDelete' : 'delete') | i18n}}">
<i class="fa fa-trash-o fa-lg fa-fw" aria-hidden="true"></i>
</button>
</div>
</div>

View File

@ -343,7 +343,7 @@
"message": "Are you sure you want to delete this item?"
},
"deletedItem": {
"message": "Deleted item"
"message": "Sent item to trash"
},
"overwritePasswordConfirmation": {
"message": "Are you sure you want to overwrite the current password?"
@ -1299,6 +1299,34 @@
"message": "Lock",
"description": "Verb form: to make secure or inaccesible by"
},
"trash": {
"message": "Trash",
"description": "Noun: a special folder to hold deleted items"
},
"searchTrash": {
"message": "Search trash"
},
"permanentlyDeleteItem": {
"message": "Permanently Delete Item"
},
"permanentlyDeleteItemConfirmation": {
"message": "Are you sure you want to permanently delete this item?"
},
"permanentlyDeletedItem": {
"message": "Permanently Deleted item"
},
"restoreItem": {
"message": "Restore Item"
},
"restoreItemConfirmation": {
"message": "Are you sure you want to restore this item?"
},
"restoredItem": {
"message": "Restored Item"
},
"permanentlyDelete": {
"message": "Permanently Delete"
},
"vaultTimeoutLogOutConfirmation": {
"message": "Logging out will remove all access to your vault and requires online authentication after the timeout period. Are you sure you want to use this setting?"
},