1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 15:53:27 +00:00

implement search service

This commit is contained in:
Kyle Spearrin
2018-08-13 11:53:16 -04:00
parent ada83aae8f
commit 4ab36bc8ee
10 changed files with 78 additions and 32 deletions

View File

@@ -29,6 +29,7 @@ import { LockService } from 'jslib/abstractions/lock.service';
import { MessagingService } from 'jslib/abstractions/messaging.service';
import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { SearchService } from 'jslib/abstractions/search.service';
import { SettingsService } from 'jslib/abstractions/settings.service';
import { StateService as StateServiceAbstraction } from 'jslib/abstractions/state.service';
import { StorageService } from 'jslib/abstractions/storage.service';
@@ -139,7 +140,8 @@ export function initFactory(i18nService: I18nService, storageService: StorageSer
{ provide: StorageService, useFactory: getBgService<StorageService>('storageService'), deps: [] },
{ provide: AppIdService, useFactory: getBgService<AppIdService>('appIdService'), deps: [] },
{ provide: AutofillService, useFactory: getBgService<AutofillService>('autofillService'), deps: [] },
{ provide: ExportService, useFactory: getBgService<AppIdService>('exportService'), deps: [] },
{ provide: ExportService, useFactory: getBgService<ExportService>('exportService'), deps: [] },
{ provide: SearchService, useFactory: getBgService<SearchService>('searchService'), deps: [] },
{
provide: APP_INITIALIZER,
useFactory: initFactory,

View File

@@ -7,7 +7,7 @@
</div>
<div class="search">
<input type="search" placeholder="{{searchPlaceholder || ('searchVault' | i18n)}}" id="search"
[(ngModel)]="searchText" appAutofocus>
[(ngModel)]="searchText" (input)="search(200)" appAutofocus>
<i class="fa fa-search"></i>
</div>
<div class="right" *ngIf="showAdd">
@@ -17,7 +17,7 @@
</div>
</header>
<content>
<ng-container *ngIf="(!isPaging() ? (ciphers | searchCiphers: searchText) : pagedCiphers) as filteredCiphers">
<ng-container *ngIf="(!isPaging() ? ciphers : pagedCiphers) as filteredCiphers">
<div class="no-items" *ngIf="!filteredCiphers.length">
<i class="fa fa-spinner fa-spin fa-3x" *ngIf="!loaded"></i>
<ng-container *ngIf="loaded">

View File

@@ -15,11 +15,11 @@ import {
import { BrowserApi } from '../../browser/browserApi';
import { CipherService } from 'jslib/abstractions/cipher.service';
import { CollectionService } from 'jslib/abstractions/collection.service';
import { FolderService } from 'jslib/abstractions/folder.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { SearchService } from 'jslib/abstractions/search.service';
import { StateService } from 'jslib/abstractions/state.service';
import { CipherType } from 'jslib/enums/cipherType';
@@ -40,7 +40,6 @@ const ComponentId = 'CiphersComponent';
})
export class CiphersComponent extends BaseCiphersComponent implements OnInit, OnDestroy {
groupingTitle: string;
searchText: string;
state: any;
showAdd = true;
folderId: string = null;
@@ -52,14 +51,14 @@ export class CiphersComponent extends BaseCiphersComponent implements OnInit, On
private preventSelected = false;
private pageSize = 100;
constructor(cipherService: CipherService, private route: ActivatedRoute,
constructor(searchService: SearchService, private route: ActivatedRoute,
private router: Router, private location: Location,
private ngZone: NgZone, private broadcasterService: BroadcasterService,
private changeDetectorRef: ChangeDetectorRef, private stateService: StateService,
private popupUtils: PopupUtilsService, private i18nService: I18nService,
private folderService: FolderService, private collectionService: CollectionService,
private analytics: Angulartics2, private platformUtilsService: PlatformUtilsService) {
super(cipherService);
super(searchService);
this.pageSize = platformUtilsService.isEdge() ? 25 : 100;
}
@@ -188,7 +187,7 @@ export class CiphersComponent extends BaseCiphersComponent implements OnInit, On
}
isSearching() {
return this.searchText != null && this.searchText.length > 1;
return !this.searchPending && this.searchService.isSearchable(this.searchText);
}
isPaging() {

View File

@@ -21,6 +21,7 @@ import { CipherView } from 'jslib/models/view/cipherView';
import { CipherService } from 'jslib/abstractions/cipher.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { SearchService } from 'jslib/abstractions/search.service';
import { SyncService } from 'jslib/abstractions/sync.service';
import { AutofillService } from '../../services/abstractions/autofill.service';
@@ -50,13 +51,15 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
private totpCode: string;
private totpTimeout: number;
private loadedTimeout: number;
private searchTimeout: number;
constructor(private platformUtilsService: PlatformUtilsService, private cipherService: CipherService,
private popupUtilsService: PopupUtilsService, private autofillService: AutofillService,
private analytics: Angulartics2, private toasterService: ToasterService,
private i18nService: I18nService, private router: Router,
private ngZone: NgZone, private broadcasterService: BroadcasterService,
private changeDetectorRef: ChangeDetectorRef, private syncService: SyncService) { }
private changeDetectorRef: ChangeDetectorRef, private syncService: SyncService,
private searchService: SearchService) { }
async ngOnInit() {
this.showLeftHeader = !this.platformUtilsService.isSafari();
@@ -171,10 +174,15 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
}
searchVault() {
if (this.searchText == null || this.searchText.length < 2) {
if (this.searchTimeout != null) {
clearTimeout(this.searchTimeout);
}
if (!this.searchService.isSearchable(this.searchText)) {
return;
}
this.router.navigate(['/tabs/vault'], { queryParams: { searchText: this.searchText } });
this.searchTimeout = window.setTimeout(async () => {
this.router.navigate(['/tabs/vault'], { queryParams: { searchText: this.searchText } });
}, 200);
}
private async load() {

View File

@@ -4,7 +4,7 @@
</div>
<div class="search">
<input type="search" placeholder="{{'searchVault' | i18n}}" id="search"
[(ngModel)]="searchText" appAutofocus>
[(ngModel)]="searchText" (input)="search(200)" appAutofocus>
<i class="fa fa-search"></i>
</div>
<div class="right">
@@ -14,7 +14,7 @@
</div>
</header>
<content>
<div class="no-items" *ngIf="!ciphers || !ciphers.length">
<div class="no-items" *ngIf="(!ciphers || !ciphers.length) && !showSearching()">
<i class="fa fa-spinner fa-spin fa-3x" *ngIf="!loaded"></i>
<ng-container *ngIf="loaded">
<i class="fa fa-frown-o fa-4x"></i>
@@ -22,7 +22,7 @@
<button (click)="addCipher()" class="btn block primary link">{{'addItem' | i18n}}</button>
</ng-container>
</div>
<ng-container *ngIf="ciphers && ciphers.length && (!searchText || searchText.length < 2)">
<ng-container *ngIf="ciphers && ciphers.length && !showSearching()">
<div class="box list" *ngIf="favoriteCiphers">
<div class="box-header">
{{'favorites' | i18n}}
@@ -127,14 +127,13 @@
</div>
</div>
</ng-container>
<ng-container *ngIf="ciphers && ciphers.length && searchText && searchText.length > 1 &&
(ciphers | searchCiphers: searchText) as searchedCiphers">
<div class="no-items" *ngIf="!searchedCiphers.length">
<ng-container *ngIf="showSearching()">
<div class="no-items" *ngIf="!ciphers || !ciphers.length">
<p>{{'noItemsInList' | i18n}}</p>
</div>
<div class="box list full-list" *ngIf="searchedCiphers.length > 0">
<div class="box list full-list" *ngIf="ciphers && ciphers.length > 0">
<div class="box-content">
<app-ciphers-list [ciphers]="searchedCiphers" title="{{'viewItem' | i18n}}"
<app-ciphers-list [ciphers]="ciphers" title="{{'viewItem' | i18n}}"
(onSelected)="selectCipher($event)"
(onDoubleSelected)="launchCipher($event)"></app-ciphers-list>
</div>

View File

@@ -24,6 +24,7 @@ import { CipherService } from 'jslib/abstractions/cipher.service';
import { CollectionService } from 'jslib/abstractions/collection.service';
import { FolderService } from 'jslib/abstractions/folder.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { SearchService } from 'jslib/abstractions/search.service';
import { StateService } from 'jslib/abstractions/state.service';
import { SyncService } from 'jslib/abstractions/sync.service';
@@ -51,11 +52,15 @@ export class GroupingsComponent extends BaseGroupingsComponent implements OnInit
state: any;
scopeState: any;
showLeftHeader = true;
searchPending = false;
private loadedTimeout: number;
private selectedTimeout: number;
private preventSelected = false;
private noFolderListSize = 100;
private searchTimeout: any = null;
private hasSearched = false;
private hasLoadedAllCiphers = false;
constructor(collectionService: CollectionService, folderService: FolderService,
private cipherService: CipherService, private router: Router,
@@ -63,7 +68,7 @@ export class GroupingsComponent extends BaseGroupingsComponent implements OnInit
private changeDetectorRef: ChangeDetectorRef, private route: ActivatedRoute,
private stateService: StateService, private popupUtils: PopupUtilsService,
private syncService: SyncService, private analytics: Angulartics2,
private platformUtilsService: PlatformUtilsService) {
private platformUtilsService: PlatformUtilsService, private searchService: SearchService) {
super(collectionService, folderService);
this.noFolderListSize = platformUtilsService.isEdge() ? 25 : 100;
}
@@ -146,8 +151,10 @@ export class GroupingsComponent extends BaseGroupingsComponent implements OnInit
}
async loadCiphers() {
this.ciphers = await this.cipherService.getAllDecrypted();
if (!this.hasLoadedAllCiphers) {
this.hasLoadedAllCiphers = !this.searchService.isSearchable(this.searchText);
}
await this.search(null);
let favoriteCiphers: CipherView[] = null;
let noFolderCiphers: CipherView[] = null;
const folderCounts = new Map<string, number>();
@@ -199,6 +206,28 @@ export class GroupingsComponent extends BaseGroupingsComponent implements OnInit
this.collectionCounts = collectionCounts;
}
async search(timeout: number = null) {
this.searchPending = false;
if (this.searchTimeout != null) {
clearTimeout(this.searchTimeout);
}
if (timeout == null) {
this.hasSearched = this.searchService.isSearchable(this.searchText);
this.ciphers = await this.searchService.searchCiphers(this.searchText, null);
return;
}
this.searchPending = true;
this.searchTimeout = setTimeout(async () => {
this.hasSearched = this.searchService.isSearchable(this.searchText);
if (!this.hasLoadedAllCiphers && !this.hasSearched) {
await this.loadCiphers();
} else {
this.ciphers = await this.searchService.searchCiphers(this.searchText, null);
}
this.searchPending = false;
}, timeout);
}
async selectType(type: CipherType) {
super.selectType(type);
this.router.navigate(['/ciphers'], { queryParams: { type: type } });
@@ -243,6 +272,10 @@ export class GroupingsComponent extends BaseGroupingsComponent implements OnInit
this.router.navigate(['/add-cipher']);
}
showSearching() {
return this.hasSearched || (!this.searchPending && this.searchService.isSearchable(this.searchText));
}
private async saveState() {
this.state = {
scrollY: this.popupUtils.getContentScrollY(window),