[Soft Delete] - cipher search rem deleted flag, filter array conditional
This commit is contained in:
parent
549fcc18ff
commit
3a10c1ff30
|
@ -4,7 +4,8 @@ export abstract class SearchService {
|
||||||
clearIndex: () => void;
|
clearIndex: () => void;
|
||||||
isSearchable: (query: string) => boolean;
|
isSearchable: (query: string) => boolean;
|
||||||
indexCiphers: () => Promise<void>;
|
indexCiphers: () => Promise<void>;
|
||||||
searchCiphers: (query: string, filter?: (cipher: CipherView) => boolean,
|
searchCiphers: (query: string,
|
||||||
ciphers?: CipherView[], deleted?: boolean) => Promise<CipherView[]>;
|
filter?: ((cipher: CipherView) => boolean) | (Array<(cipher: CipherView) => boolean>),
|
||||||
|
ciphers?: CipherView[]) => Promise<CipherView[]>;
|
||||||
searchCiphersBasic: (ciphers: CipherView[], query: string, deleted?: boolean) => CipherView[];
|
searchCiphersBasic: (ciphers: CipherView[], query: string, deleted?: boolean) => CipherView[];
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ export class AddEditComponent implements OnInit {
|
||||||
@Input() organizationId: string = null;
|
@Input() organizationId: string = null;
|
||||||
@Output() onSavedCipher = new EventEmitter<CipherView>();
|
@Output() onSavedCipher = new EventEmitter<CipherView>();
|
||||||
@Output() onDeletedCipher = new EventEmitter<CipherView>();
|
@Output() onDeletedCipher = new EventEmitter<CipherView>();
|
||||||
|
@Output() onRestoredCipher = new EventEmitter<CipherView>();
|
||||||
@Output() onCancelled = new EventEmitter<CipherView>();
|
@Output() onCancelled = new EventEmitter<CipherView>();
|
||||||
@Output() onEditAttachments = new EventEmitter<CipherView>();
|
@Output() onEditAttachments = new EventEmitter<CipherView>();
|
||||||
@Output() onShareCipher = new EventEmitter<CipherView>();
|
@Output() onShareCipher = new EventEmitter<CipherView>();
|
||||||
|
@ -63,6 +64,7 @@ export class AddEditComponent implements OnInit {
|
||||||
title: string;
|
title: string;
|
||||||
formPromise: Promise<any>;
|
formPromise: Promise<any>;
|
||||||
deletePromise: Promise<any>;
|
deletePromise: Promise<any>;
|
||||||
|
restorePromise: Promise<any>;
|
||||||
checkPasswordPromise: Promise<number>;
|
checkPasswordPromise: Promise<number>;
|
||||||
showPassword: boolean = false;
|
showPassword: boolean = false;
|
||||||
showCardCode: boolean = false;
|
showCardCode: boolean = false;
|
||||||
|
@ -221,6 +223,10 @@ export class AddEditComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
async submit(): Promise<boolean> {
|
async submit(): Promise<boolean> {
|
||||||
|
if (this.cipher.isDeleted) {
|
||||||
|
return this.restore();
|
||||||
|
}
|
||||||
|
|
||||||
if (this.cipher.name == null || this.cipher.name === '') {
|
if (this.cipher.name == null || this.cipher.name === '') {
|
||||||
this.platformUtilsService.showToast('error', this.i18nService.t('errorOccurred'),
|
this.platformUtilsService.showToast('error', this.i18nService.t('errorOccurred'),
|
||||||
this.i18nService.t('nameRequired'));
|
this.i18nService.t('nameRequired'));
|
||||||
|
@ -331,10 +337,35 @@ export class AddEditComponent implements OnInit {
|
||||||
try {
|
try {
|
||||||
this.deletePromise = this.deleteCipher();
|
this.deletePromise = this.deleteCipher();
|
||||||
await this.deletePromise;
|
await this.deletePromise;
|
||||||
this.platformUtilsService.eventTrack('Deleted Cipher');
|
this.platformUtilsService.eventTrack((this.cipher.isDeleted ? 'Permanently ' : '') + 'Deleted Cipher');
|
||||||
this.platformUtilsService.showToast('success', null, this.i18nService.t('deletedItem'));
|
this.platformUtilsService.showToast('success', null,
|
||||||
|
this.i18nService.t(this.cipher.isDeleted ? 'permanentlyDeletedItem' : 'deletedItem'));
|
||||||
this.onDeletedCipher.emit(this.cipher);
|
this.onDeletedCipher.emit(this.cipher);
|
||||||
this.messagingService.send('deletedCipher');
|
this.messagingService.send(this.cipher.isDeleted ? 'permanentlyDeletedCipher' : 'deletedCipher');
|
||||||
|
} catch { }
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async restore(): Promise<boolean> {
|
||||||
|
if (!this.cipher.isDeleted) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmed = await this.platformUtilsService.showDialog(
|
||||||
|
this.i18nService.t('restoreItemConfirmation'), this.i18nService.t('restoreItem'),
|
||||||
|
this.i18nService.t('yes'), this.i18nService.t('no'), 'warning');
|
||||||
|
if (!confirmed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.restorePromise = this.restoreCipher();
|
||||||
|
await this.restorePromise;
|
||||||
|
this.platformUtilsService.eventTrack('Restored Cipher');
|
||||||
|
this.platformUtilsService.showToast('success', null, this.i18nService.t('restoredItem'));
|
||||||
|
this.onRestoredCipher.emit(this.cipher);
|
||||||
|
this.messagingService.send('restoredCipher');
|
||||||
} catch { }
|
} catch { }
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -449,6 +480,11 @@ export class AddEditComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected deleteCipher() {
|
protected deleteCipher() {
|
||||||
return this.cipherService.deleteWithServer(this.cipher.id);
|
return this.cipher.isDeleted ? this.cipherService.deleteWithServer(this.cipher.id)
|
||||||
|
: this.cipherService.softDeleteWithServer(this.cipher.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected restoreCipher() {
|
||||||
|
return this.cipherService.restoreWithServer(this.cipher.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ export class CiphersComponent {
|
||||||
async refresh() {
|
async refresh() {
|
||||||
try {
|
try {
|
||||||
this.refreshing = true;
|
this.refreshing = true;
|
||||||
await this.reload(this.filter);
|
await this.reload(this.filter, this.deleted);
|
||||||
} finally {
|
} finally {
|
||||||
this.refreshing = false;
|
this.refreshing = false;
|
||||||
}
|
}
|
||||||
|
@ -80,14 +80,15 @@ export class CiphersComponent {
|
||||||
if (this.searchTimeout != null) {
|
if (this.searchTimeout != null) {
|
||||||
clearTimeout(this.searchTimeout);
|
clearTimeout(this.searchTimeout);
|
||||||
}
|
}
|
||||||
|
const deletedFilter: (cipher: CipherView) => boolean = (c) => c.isDeleted === this.deleted;
|
||||||
if (timeout == null) {
|
if (timeout == null) {
|
||||||
this.ciphers = await this.searchService.searchCiphers(this.searchText, this.filter, null, this.deleted);
|
this.ciphers = await this.searchService.searchCiphers(this.searchText, [this.filter, deletedFilter], null);
|
||||||
await this.resetPaging();
|
await this.resetPaging();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.searchPending = true;
|
this.searchPending = true;
|
||||||
this.searchTimeout = setTimeout(async () => {
|
this.searchTimeout = setTimeout(async () => {
|
||||||
this.ciphers = await this.searchService.searchCiphers(this.searchText, this.filter, null, this.deleted);
|
this.ciphers = await this.searchService.searchCiphers(this.searchText, [this.filter, deletedFilter], null);
|
||||||
await this.resetPaging();
|
await this.resetPaging();
|
||||||
this.searchPending = false;
|
this.searchPending = false;
|
||||||
}, timeout);
|
}, timeout);
|
||||||
|
|
|
@ -34,6 +34,7 @@ export class ViewComponent implements OnDestroy, OnInit {
|
||||||
@Input() cipherId: string;
|
@Input() cipherId: string;
|
||||||
@Output() onEditCipher = new EventEmitter<CipherView>();
|
@Output() onEditCipher = new EventEmitter<CipherView>();
|
||||||
@Output() onCloneCipher = new EventEmitter<CipherView>();
|
@Output() onCloneCipher = new EventEmitter<CipherView>();
|
||||||
|
@Output() onRestoreCipher = new EventEmitter<CipherView>();
|
||||||
|
|
||||||
cipher: CipherView;
|
cipher: CipherView;
|
||||||
showPassword: boolean;
|
showPassword: boolean;
|
||||||
|
@ -110,6 +111,13 @@ export class ViewComponent implements OnDestroy, OnInit {
|
||||||
this.onCloneCipher.emit(this.cipher);
|
this.onCloneCipher.emit(this.cipher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
restore() {
|
||||||
|
if (!this.cipher.isDeleted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.onRestoreCipher.emit(this.cipher);
|
||||||
|
}
|
||||||
|
|
||||||
togglePassword() {
|
togglePassword() {
|
||||||
this.platformUtilsService.eventTrack('Toggled Password');
|
this.platformUtilsService.eventTrack('Toggled Password');
|
||||||
this.showPassword = !this.showPassword;
|
this.showPassword = !this.showPassword;
|
||||||
|
|
|
@ -71,8 +71,9 @@ export class SearchService implements SearchServiceAbstraction {
|
||||||
console.timeEnd('search indexing');
|
console.timeEnd('search indexing');
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchCiphers(query: string, filter: (cipher: CipherView) => boolean = null, ciphers: CipherView[] = null,
|
async searchCiphers(query: string,
|
||||||
deleted: boolean = false):
|
filter: (((cipher: CipherView) => boolean) | (Array<(cipher: CipherView) => boolean>)) = null,
|
||||||
|
ciphers: CipherView[] = null):
|
||||||
Promise<CipherView[]> {
|
Promise<CipherView[]> {
|
||||||
const results: CipherView[] = [];
|
const results: CipherView[] = [];
|
||||||
if (query != null) {
|
if (query != null) {
|
||||||
|
@ -86,15 +87,11 @@ export class SearchService implements SearchServiceAbstraction {
|
||||||
ciphers = await this.cipherService.getAllDecrypted();
|
ciphers = await this.cipherService.getAllDecrypted();
|
||||||
}
|
}
|
||||||
|
|
||||||
ciphers = ciphers.filter((c) => {
|
if (filter != null && Array.isArray(filter) && filter.length > 0) {
|
||||||
if (deleted !== c.isDeleted) {
|
ciphers = ciphers.filter((c) => filter.every((f) => f == null || f(c)));
|
||||||
return false;
|
} else if (filter != null) {
|
||||||
|
ciphers = ciphers.filter(filter as (cipher: CipherView) => boolean);
|
||||||
}
|
}
|
||||||
if (filter != null) {
|
|
||||||
return filter(c);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!this.isSearchable(query)) {
|
if (!this.isSearchable(query)) {
|
||||||
return ciphers;
|
return ciphers;
|
||||||
|
|
Loading…
Reference in New Issue