mirror of
https://github.com/bitwarden/browser
synced 2025-01-28 20:19:49 +01:00
[Soft Delete] jslib updates for new API updates
New API methods and cipher Deleted Date property, plus search expansion to toggle on deleted flag.
This commit is contained in:
parent
28e3fff739
commit
19668ab5f2
@ -5,6 +5,7 @@ import { EnvironmentUrls } from '../models/domain/environmentUrls';
|
|||||||
import { BitPayInvoiceRequest } from '../models/request/bitPayInvoiceRequest';
|
import { BitPayInvoiceRequest } from '../models/request/bitPayInvoiceRequest';
|
||||||
import { CipherBulkDeleteRequest } from '../models/request/cipherBulkDeleteRequest';
|
import { CipherBulkDeleteRequest } from '../models/request/cipherBulkDeleteRequest';
|
||||||
import { CipherBulkMoveRequest } from '../models/request/cipherBulkMoveRequest';
|
import { CipherBulkMoveRequest } from '../models/request/cipherBulkMoveRequest';
|
||||||
|
import { CipherBulkRestoreRequest } from '../models/request/cipherBulkRestoreRequest';
|
||||||
import { CipherBulkShareRequest } from '../models/request/cipherBulkShareRequest';
|
import { CipherBulkShareRequest } from '../models/request/cipherBulkShareRequest';
|
||||||
import { CipherCollectionsRequest } from '../models/request/cipherCollectionsRequest';
|
import { CipherCollectionsRequest } from '../models/request/cipherCollectionsRequest';
|
||||||
import { CipherCreateRequest } from '../models/request/cipherCreateRequest';
|
import { CipherCreateRequest } from '../models/request/cipherCreateRequest';
|
||||||
@ -163,6 +164,12 @@ export abstract class ApiService {
|
|||||||
postPurgeCiphers: (request: PasswordVerificationRequest, organizationId?: string) => Promise<any>;
|
postPurgeCiphers: (request: PasswordVerificationRequest, organizationId?: string) => Promise<any>;
|
||||||
postImportCiphers: (request: ImportCiphersRequest) => Promise<any>;
|
postImportCiphers: (request: ImportCiphersRequest) => Promise<any>;
|
||||||
postImportOrganizationCiphers: (organizationId: string, request: ImportOrganizationCiphersRequest) => Promise<any>;
|
postImportOrganizationCiphers: (organizationId: string, request: ImportOrganizationCiphersRequest) => Promise<any>;
|
||||||
|
putDeleteCipher: (id: string) => Promise<any>;
|
||||||
|
putDeleteCipherAdmin: (id: string) => Promise<any>;
|
||||||
|
putDeleteManyCiphers: (request: CipherBulkDeleteRequest) => Promise<any>;
|
||||||
|
putRestoreCipher: (id: string) => Promise<any>;
|
||||||
|
putRestoreCipherAdmin: (id: string) => Promise<any>;
|
||||||
|
putRestoreManyCiphers: (request: CipherBulkRestoreRequest) => Promise<any>;
|
||||||
|
|
||||||
postCipherAttachment: (id: string, data: FormData) => Promise<CipherResponse>;
|
postCipherAttachment: (id: string, data: FormData) => Promise<CipherResponse>;
|
||||||
postCipherAttachmentAdmin: (id: string, data: FormData) => Promise<CipherResponse>;
|
postCipherAttachmentAdmin: (id: string, data: FormData) => Promise<CipherResponse>;
|
||||||
|
@ -45,4 +45,10 @@ export abstract class CipherService {
|
|||||||
sortCiphersByLastUsed: (a: any, b: any) => number;
|
sortCiphersByLastUsed: (a: any, b: any) => number;
|
||||||
sortCiphersByLastUsedThenName: (a: any, b: any) => number;
|
sortCiphersByLastUsedThenName: (a: any, b: any) => number;
|
||||||
getLocaleSortingFunction: () => (a: CipherView, b: CipherView) => number;
|
getLocaleSortingFunction: () => (a: CipherView, b: CipherView) => number;
|
||||||
|
softDelete: (id: string | string[]) => Promise<any>;
|
||||||
|
softDeleteWithServer: (id: string) => Promise<any>;
|
||||||
|
softDeleteManyWithServer: (ids: string[]) => Promise<any>;
|
||||||
|
restore: (id: string | string[]) => Promise<any>;
|
||||||
|
restoreWithServer: (id: string) => Promise<any>;
|
||||||
|
restoreManyWithServer: (ids: string[]) => Promise<any>;
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,6 @@ export abstract class SearchService {
|
|||||||
isSearchable: (query: string) => boolean;
|
isSearchable: (query: string) => boolean;
|
||||||
indexCiphers: () => Promise<void>;
|
indexCiphers: () => Promise<void>;
|
||||||
searchCiphers: (query: string, filter?: (cipher: CipherView) => boolean,
|
searchCiphers: (query: string, filter?: (cipher: CipherView) => boolean,
|
||||||
ciphers?: CipherView[]) => Promise<CipherView[]>;
|
ciphers?: CipherView[], deleted?: boolean) => Promise<CipherView[]>;
|
||||||
searchCiphersBasic: (ciphers: CipherView[], query: string) => CipherView[];
|
searchCiphersBasic: (ciphers: CipherView[], query: string, deleted?: boolean) => CipherView[];
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ export class CiphersComponent {
|
|||||||
searchText: string;
|
searchText: string;
|
||||||
searchPlaceholder: string = null;
|
searchPlaceholder: string = null;
|
||||||
filter: (cipher: CipherView) => boolean = null;
|
filter: (cipher: CipherView) => boolean = null;
|
||||||
|
deleted: boolean = false;
|
||||||
|
|
||||||
protected searchPending = false;
|
protected searchPending = false;
|
||||||
protected didScroll = false;
|
protected didScroll = false;
|
||||||
@ -32,7 +33,8 @@ export class CiphersComponent {
|
|||||||
|
|
||||||
constructor(protected searchService: SearchService) { }
|
constructor(protected searchService: SearchService) { }
|
||||||
|
|
||||||
async load(filter: (cipher: CipherView) => boolean = null) {
|
async load(filter: (cipher: CipherView) => boolean = null, deleted: boolean = false) {
|
||||||
|
this.deleted = deleted || false;
|
||||||
await this.applyFilter(filter);
|
await this.applyFilter(filter);
|
||||||
this.loaded = true;
|
this.loaded = true;
|
||||||
}
|
}
|
||||||
@ -79,13 +81,13 @@ export class CiphersComponent {
|
|||||||
clearTimeout(this.searchTimeout);
|
clearTimeout(this.searchTimeout);
|
||||||
}
|
}
|
||||||
if (timeout == null) {
|
if (timeout == null) {
|
||||||
this.ciphers = await this.searchService.searchCiphers(this.searchText, this.filter);
|
this.ciphers = await this.searchService.searchCiphers(this.searchText, this.filter, null, this.deleted);
|
||||||
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);
|
this.ciphers = await this.searchService.searchCiphers(this.searchText, this.filter, null, this.deleted);
|
||||||
await this.resetPaging();
|
await this.resetPaging();
|
||||||
this.searchPending = false;
|
this.searchPending = false;
|
||||||
}, timeout);
|
}, timeout);
|
||||||
|
@ -22,9 +22,11 @@ export class GroupingsComponent {
|
|||||||
@Input() showFolders = true;
|
@Input() showFolders = true;
|
||||||
@Input() showCollections = true;
|
@Input() showCollections = true;
|
||||||
@Input() showFavorites = true;
|
@Input() showFavorites = true;
|
||||||
|
@Input() showTrash = true;
|
||||||
|
|
||||||
@Output() onAllClicked = new EventEmitter();
|
@Output() onAllClicked = new EventEmitter();
|
||||||
@Output() onFavoritesClicked = new EventEmitter();
|
@Output() onFavoritesClicked = new EventEmitter();
|
||||||
|
@Output() onTrashClicked = 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() onAddFolder = new EventEmitter();
|
||||||
@ -39,6 +41,7 @@ export class GroupingsComponent {
|
|||||||
cipherType = CipherType;
|
cipherType = CipherType;
|
||||||
selectedAll: boolean = false;
|
selectedAll: boolean = false;
|
||||||
selectedFavorites: boolean = false;
|
selectedFavorites: boolean = false;
|
||||||
|
selectedTrash: boolean = false;
|
||||||
selectedType: CipherType = null;
|
selectedType: CipherType = null;
|
||||||
selectedFolder: boolean = false;
|
selectedFolder: boolean = false;
|
||||||
selectedFolderId: string = null;
|
selectedFolderId: string = null;
|
||||||
@ -101,6 +104,12 @@ export class GroupingsComponent {
|
|||||||
this.onFavoritesClicked.emit();
|
this.onFavoritesClicked.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
selectTrash() {
|
||||||
|
this.clearSelections();
|
||||||
|
this.selectedTrash = true;
|
||||||
|
this.onTrashClicked.emit();
|
||||||
|
}
|
||||||
|
|
||||||
selectType(type: CipherType) {
|
selectType(type: CipherType) {
|
||||||
this.clearSelections();
|
this.clearSelections();
|
||||||
this.selectedType = type;
|
this.selectedType = type;
|
||||||
@ -131,6 +140,7 @@ export class GroupingsComponent {
|
|||||||
clearSelections() {
|
clearSelections() {
|
||||||
this.selectedAll = false;
|
this.selectedAll = false;
|
||||||
this.selectedFavorites = false;
|
this.selectedFavorites = false;
|
||||||
|
this.selectedTrash = false;
|
||||||
this.selectedType = null;
|
this.selectedType = null;
|
||||||
this.selectedFolder = false;
|
this.selectedFolder = false;
|
||||||
this.selectedFolderId = null;
|
this.selectedFolderId = null;
|
||||||
|
@ -19,17 +19,22 @@ export class SearchCiphersPipe implements PipeTransform {
|
|||||||
this.onlySearchName = platformUtilsService.getDevice() === DeviceType.EdgeExtension;
|
this.onlySearchName = platformUtilsService.getDevice() === DeviceType.EdgeExtension;
|
||||||
}
|
}
|
||||||
|
|
||||||
transform(ciphers: CipherView[], searchText: string): CipherView[] {
|
transform(ciphers: CipherView[], searchText: string, deleted: boolean = false): CipherView[] {
|
||||||
if (ciphers == null || ciphers.length === 0) {
|
if (ciphers == null || ciphers.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (searchText == null || searchText.length < 2) {
|
if (searchText == null || searchText.length < 2) {
|
||||||
return ciphers;
|
return ciphers.filter((c) => {
|
||||||
|
return deleted !== c.isDeleted;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
searchText = searchText.trim().toLowerCase();
|
searchText = searchText.trim().toLowerCase();
|
||||||
return ciphers.filter((c) => {
|
return ciphers.filter((c) => {
|
||||||
|
if (deleted !== c.isDeleted) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (c.name != null && c.name.toLowerCase().indexOf(searchText) > -1) {
|
if (c.name != null && c.name.toLowerCase().indexOf(searchText) > -1) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,8 @@ export enum EventType {
|
|||||||
Cipher_ClientCopiedHiddenField = 1112,
|
Cipher_ClientCopiedHiddenField = 1112,
|
||||||
Cipher_ClientCopiedCardCode = 1113,
|
Cipher_ClientCopiedCardCode = 1113,
|
||||||
Cipher_ClientAutofilled = 1114,
|
Cipher_ClientAutofilled = 1114,
|
||||||
|
Cipher_SoftDeleted = 1115,
|
||||||
|
Cipher_Restored = 1116,
|
||||||
|
|
||||||
Collection_Created = 1300,
|
Collection_Created = 1300,
|
||||||
Collection_Updated = 1301,
|
Collection_Updated = 1301,
|
||||||
|
@ -31,6 +31,7 @@ export class CipherData {
|
|||||||
attachments?: AttachmentData[];
|
attachments?: AttachmentData[];
|
||||||
passwordHistory?: PasswordHistoryData[];
|
passwordHistory?: PasswordHistoryData[];
|
||||||
collectionIds?: string[];
|
collectionIds?: string[];
|
||||||
|
deletedDate: string;
|
||||||
|
|
||||||
constructor(response?: CipherResponse, userId?: string, collectionIds?: string[]) {
|
constructor(response?: CipherResponse, userId?: string, collectionIds?: string[]) {
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
@ -49,6 +50,7 @@ export class CipherData {
|
|||||||
this.name = response.name;
|
this.name = response.name;
|
||||||
this.notes = response.notes;
|
this.notes = response.notes;
|
||||||
this.collectionIds = collectionIds != null ? collectionIds : response.collectionIds;
|
this.collectionIds = collectionIds != null ? collectionIds : response.collectionIds;
|
||||||
|
this.deletedDate = response.deletedDate;
|
||||||
|
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case CipherType.Login:
|
case CipherType.Login:
|
||||||
|
@ -34,6 +34,7 @@ export class Cipher extends Domain {
|
|||||||
fields: Field[];
|
fields: Field[];
|
||||||
passwordHistory: Password[];
|
passwordHistory: Password[];
|
||||||
collectionIds: string[];
|
collectionIds: string[];
|
||||||
|
deletedDate: Date;
|
||||||
|
|
||||||
constructor(obj?: CipherData, alreadyEncrypted: boolean = false, localData: any = null) {
|
constructor(obj?: CipherData, alreadyEncrypted: boolean = false, localData: any = null) {
|
||||||
super();
|
super();
|
||||||
@ -57,6 +58,7 @@ export class Cipher extends Domain {
|
|||||||
this.revisionDate = obj.revisionDate != null ? new Date(obj.revisionDate) : null;
|
this.revisionDate = obj.revisionDate != null ? new Date(obj.revisionDate) : null;
|
||||||
this.collectionIds = obj.collectionIds;
|
this.collectionIds = obj.collectionIds;
|
||||||
this.localData = localData;
|
this.localData = localData;
|
||||||
|
this.deletedDate = obj.deletedDate != null ? new Date(obj.deletedDate) : null;
|
||||||
|
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case CipherType.Login:
|
case CipherType.Login:
|
||||||
@ -172,6 +174,7 @@ export class Cipher extends Domain {
|
|||||||
c.revisionDate = this.revisionDate != null ? this.revisionDate.toISOString() : null;
|
c.revisionDate = this.revisionDate != null ? this.revisionDate.toISOString() : null;
|
||||||
c.type = this.type;
|
c.type = this.type;
|
||||||
c.collectionIds = this.collectionIds;
|
c.collectionIds = this.collectionIds;
|
||||||
|
c.deletedDate = this.deletedDate != null ? this.deletedDate.toISOString() : null;
|
||||||
|
|
||||||
this.buildDataModel(this, c, {
|
this.buildDataModel(this, c, {
|
||||||
name: null,
|
name: null,
|
||||||
|
7
src/models/request/cipherBulkRestoreRequest.ts
Normal file
7
src/models/request/cipherBulkRestoreRequest.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export class CipherBulkRestoreRequest {
|
||||||
|
ids: string[];
|
||||||
|
|
||||||
|
constructor(ids: string[]) {
|
||||||
|
this.ids = ids == null ? [] : ids;
|
||||||
|
}
|
||||||
|
}
|
@ -27,6 +27,7 @@ export class CipherResponse extends BaseResponse {
|
|||||||
attachments: AttachmentResponse[];
|
attachments: AttachmentResponse[];
|
||||||
passwordHistory: PasswordHistoryResponse[];
|
passwordHistory: PasswordHistoryResponse[];
|
||||||
collectionIds: string[];
|
collectionIds: string[];
|
||||||
|
deletedDate: string;
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
super(response);
|
super(response);
|
||||||
@ -41,6 +42,7 @@ export class CipherResponse extends BaseResponse {
|
|||||||
this.organizationUseTotp = this.getResponseProperty('OrganizationUseTotp');
|
this.organizationUseTotp = this.getResponseProperty('OrganizationUseTotp');
|
||||||
this.revisionDate = this.getResponseProperty('RevisionDate');
|
this.revisionDate = this.getResponseProperty('RevisionDate');
|
||||||
this.collectionIds = this.getResponseProperty('CollectionIds');
|
this.collectionIds = this.getResponseProperty('CollectionIds');
|
||||||
|
this.deletedDate = this.getResponseProperty('DeletedDate');
|
||||||
|
|
||||||
const login = this.getResponseProperty('Login');
|
const login = this.getResponseProperty('Login');
|
||||||
if (login != null) {
|
if (login != null) {
|
||||||
|
@ -31,6 +31,7 @@ export class CipherView implements View {
|
|||||||
passwordHistory: PasswordHistoryView[] = null;
|
passwordHistory: PasswordHistoryView[] = null;
|
||||||
collectionIds: string[] = null;
|
collectionIds: string[] = null;
|
||||||
revisionDate: Date = null;
|
revisionDate: Date = null;
|
||||||
|
deletedDate: Date = null;
|
||||||
|
|
||||||
constructor(c?: Cipher) {
|
constructor(c?: Cipher) {
|
||||||
if (!c) {
|
if (!c) {
|
||||||
@ -47,6 +48,7 @@ export class CipherView implements View {
|
|||||||
this.localData = c.localData;
|
this.localData = c.localData;
|
||||||
this.collectionIds = c.collectionIds;
|
this.collectionIds = c.collectionIds;
|
||||||
this.revisionDate = c.revisionDate;
|
this.revisionDate = c.revisionDate;
|
||||||
|
this.deletedDate = c.deletedDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
get subTitle(): string {
|
get subTitle(): string {
|
||||||
@ -97,4 +99,8 @@ export class CipherView implements View {
|
|||||||
}
|
}
|
||||||
return this.login.passwordRevisionDate;
|
return this.login.passwordRevisionDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isDeleted(): boolean {
|
||||||
|
return this.deletedDate != null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -434,6 +434,30 @@ export class ApiService implements ApiServiceAbstraction {
|
|||||||
return this.send('POST', '/ciphers/import-organization?organizationId=' + organizationId, request, true, false);
|
return this.send('POST', '/ciphers/import-organization?organizationId=' + organizationId, request, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
putDeleteCipher(id: string): Promise<any> {
|
||||||
|
return this.send('PUT', '/ciphers/' + id + '/delete', null, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
putDeleteCipherAdmin(id: string): Promise<any> {
|
||||||
|
return this.send('PUT', '/ciphers/' + id + '/delete-admin', null, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
putDeleteManyCiphers(request: CipherBulkDeleteRequest): Promise<any> {
|
||||||
|
return this.send('PUT', '/ciphers/delete', request, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
putRestoreCipher(id: string): Promise<any> {
|
||||||
|
return this.send('PUT', '/ciphers/' + id + '/restore', null, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
putRestoreCipherAdmin(id: string): Promise<any> {
|
||||||
|
return this.send('PUT', '/ciphers/' + id + '/restore-admin', null, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
putRestoreManyCiphers(request: CipherBulkDeleteRequest): Promise<any> {
|
||||||
|
return this.send('PUT', '/ciphers/restore', request, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
// Attachments APIs
|
// Attachments APIs
|
||||||
|
|
||||||
async postCipherAttachment(id: string, data: FormData): Promise<CipherResponse> {
|
async postCipherAttachment(id: string, data: FormData): Promise<CipherResponse> {
|
||||||
|
@ -19,6 +19,7 @@ import { SymmetricCryptoKey } from '../models/domain/symmetricCryptoKey';
|
|||||||
|
|
||||||
import { CipherBulkDeleteRequest } from '../models/request/cipherBulkDeleteRequest';
|
import { CipherBulkDeleteRequest } from '../models/request/cipherBulkDeleteRequest';
|
||||||
import { CipherBulkMoveRequest } from '../models/request/cipherBulkMoveRequest';
|
import { CipherBulkMoveRequest } from '../models/request/cipherBulkMoveRequest';
|
||||||
|
import { CipherBulkRestoreRequest } from '../models/request/cipherBulkRestoreRequest';
|
||||||
import { CipherBulkShareRequest } from '../models/request/cipherBulkShareRequest';
|
import { CipherBulkShareRequest } from '../models/request/cipherBulkShareRequest';
|
||||||
import { CipherCollectionsRequest } from '../models/request/cipherCollectionsRequest';
|
import { CipherCollectionsRequest } from '../models/request/cipherCollectionsRequest';
|
||||||
import { CipherCreateRequest } from '../models/request/cipherCreateRequest';
|
import { CipherCreateRequest } from '../models/request/cipherCreateRequest';
|
||||||
@ -790,6 +791,76 @@ export class CipherService implements CipherServiceAbstraction {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async softDelete(id: string | string[]): Promise<any> {
|
||||||
|
const userId = await this.userService.getUserId();
|
||||||
|
const ciphers = await this.storageService.get<{ [id: string]: CipherData; }>(
|
||||||
|
Keys.ciphersPrefix + userId);
|
||||||
|
if (ciphers == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const setDeletedDate = (cipherId: string) => {
|
||||||
|
if (ciphers[cipherId] == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ciphers[cipherId].deletedDate = new Date().toISOString();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof id === 'string') {
|
||||||
|
setDeletedDate(id);
|
||||||
|
} else {
|
||||||
|
(id as string[]).forEach(setDeletedDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.storageService.save(Keys.ciphersPrefix + userId, ciphers);
|
||||||
|
this.decryptedCipherCache = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async softDeleteWithServer(id: string): Promise<any> {
|
||||||
|
await this.apiService.putDeleteCipher(id);
|
||||||
|
await this.softDelete(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async softDeleteManyWithServer(ids: string[]): Promise<any> {
|
||||||
|
await this.apiService.putDeleteManyCiphers(new CipherBulkDeleteRequest(ids));
|
||||||
|
await this.softDelete(ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
async restore(id: string | string[]): Promise<any> {
|
||||||
|
const userId = await this.userService.getUserId();
|
||||||
|
const ciphers = await this.storageService.get<{ [id: string]: CipherData; }>(
|
||||||
|
Keys.ciphersPrefix + userId);
|
||||||
|
if (ciphers == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearDeletedDate = (cipherId: string) => {
|
||||||
|
if (ciphers[cipherId] == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ciphers[cipherId].deletedDate = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof id === 'string') {
|
||||||
|
clearDeletedDate(id);
|
||||||
|
} else {
|
||||||
|
(id as string[]).forEach(clearDeletedDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.storageService.save(Keys.ciphersPrefix + userId, ciphers);
|
||||||
|
this.decryptedCipherCache = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async restoreWithServer(id: string): Promise<any> {
|
||||||
|
await this.apiService.putRestoreCipher(id);
|
||||||
|
await this.restore(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async restoreManyWithServer(ids: string[]): Promise<any> {
|
||||||
|
await this.apiService.putRestoreManyCiphers(new CipherBulkRestoreRequest(ids));
|
||||||
|
await this.restore(ids);
|
||||||
|
}
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
|
|
||||||
private async shareAttachmentWithServer(attachmentView: AttachmentView, cipherId: string,
|
private async shareAttachmentWithServer(attachmentView: AttachmentView, cipherId: string,
|
||||||
|
@ -71,7 +71,8 @@ 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, filter: (cipher: CipherView) => boolean = null, ciphers: CipherView[] = null,
|
||||||
|
deleted: boolean = false):
|
||||||
Promise<CipherView[]> {
|
Promise<CipherView[]> {
|
||||||
const results: CipherView[] = [];
|
const results: CipherView[] = [];
|
||||||
if (query != null) {
|
if (query != null) {
|
||||||
@ -84,9 +85,16 @@ export class SearchService implements SearchServiceAbstraction {
|
|||||||
if (ciphers == null) {
|
if (ciphers == null) {
|
||||||
ciphers = await this.cipherService.getAllDecrypted();
|
ciphers = await this.cipherService.getAllDecrypted();
|
||||||
}
|
}
|
||||||
if (filter != null) {
|
|
||||||
ciphers = ciphers.filter(filter);
|
ciphers = ciphers.filter((c) => {
|
||||||
}
|
if (deleted !== c.isDeleted) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (filter != null) {
|
||||||
|
return filter(c);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
if (!this.isSearchable(query)) {
|
if (!this.isSearchable(query)) {
|
||||||
return ciphers;
|
return ciphers;
|
||||||
@ -138,9 +146,12 @@ export class SearchService implements SearchServiceAbstraction {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
searchCiphersBasic(ciphers: CipherView[], query: string) {
|
searchCiphersBasic(ciphers: CipherView[], query: string, deleted: boolean = false) {
|
||||||
query = query.trim().toLowerCase();
|
query = query.trim().toLowerCase();
|
||||||
return ciphers.filter((c) => {
|
return ciphers.filter((c) => {
|
||||||
|
if (deleted !== c.isDeleted) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (c.name != null && c.name.toLowerCase().indexOf(query) > -1) {
|
if (c.name != null && c.name.toLowerCase().indexOf(query) > -1) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user