diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-search/vault-v2-search.component.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-search/vault-v2-search.component.ts index 321717285a9..e6100321607 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-search/vault-v2-search.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-search/vault-v2-search.component.ts @@ -1,12 +1,14 @@ import { CommonModule } from "@angular/common"; -import { Component, Output, EventEmitter } from "@angular/core"; +import { Component } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormsModule } from "@angular/forms"; -import { Subject, debounceTime } from "rxjs"; +import { Subject, Subscription, debounceTime, filter } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { SearchModule } from "@bitwarden/components"; +import { VaultPopupItemsService } from "../../../services/vault-popup-items.service"; + const SearchTextDebounceInterval = 200; @Component({ @@ -17,19 +19,34 @@ const SearchTextDebounceInterval = 200; }) export class VaultV2SearchComponent { searchText: string; - @Output() searchTextChanged = new EventEmitter(); private searchText$ = new Subject(); - constructor() { - this.searchText$ - .pipe(debounceTime(SearchTextDebounceInterval), takeUntilDestroyed()) - .subscribe((data) => { - this.searchTextChanged.emit(data); - }); + constructor(private vaultPopupItemsService: VaultPopupItemsService) { + this.subscribeToLatestSearchText(); + this.subscribeToApplyFilter(); } onSearchTextChanged() { this.searchText$.next(this.searchText); } + + subscribeToLatestSearchText(): Subscription { + return this.vaultPopupItemsService.latestSearchText$ + .pipe( + takeUntilDestroyed(), + filter((data) => !!data), + ) + .subscribe((text) => { + this.searchText = text; + }); + } + + subscribeToApplyFilter(): Subscription { + return this.searchText$ + .pipe(debounceTime(SearchTextDebounceInterval), takeUntilDestroyed()) + .subscribe((data) => { + this.vaultPopupItemsService.applyFilter(data); + }); + } } diff --git a/apps/browser/src/vault/popup/components/vault/vault-v2.component.html b/apps/browser/src/vault/popup/components/vault/vault-v2.component.html index f99d3cbb303..9f38fd61fab 100644 --- a/apps/browser/src/vault/popup/components/vault/vault-v2.component.html +++ b/apps/browser/src/vault/popup/components/vault/vault-v2.component.html @@ -22,18 +22,15 @@ -
- - + - -
+
- + {{ "noItemsMatchSearch" | i18n }} {{ "clearFiltersOrTryAnother" | i18n }} @@ -41,7 +38,7 @@
{{ "organizationIsDeactivated" | i18n }} diff --git a/apps/browser/src/vault/popup/components/vault/vault-v2.component.ts b/apps/browser/src/vault/popup/components/vault/vault-v2.component.ts index 5e9487ac880..5e91f196a30 100644 --- a/apps/browser/src/vault/popup/components/vault/vault-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault/vault-v2.component.ts @@ -44,6 +44,7 @@ export class VaultV2Component implements OnInit, OnDestroy { protected vaultIcon = Icons.Vault; protected deactivatedIcon = Icons.DeactivatedOrg; + protected noResultsIcon = Icons.NoResults; constructor( private vaultPopupItemsService: VaultPopupItemsService, @@ -54,10 +55,6 @@ export class VaultV2Component implements OnInit, OnDestroy { ngOnDestroy(): void {} - handleSearchTextChange(searchText: string) { - this.vaultPopupItemsService.applyFilter(searchText); - } - addCipher() { // TODO: Add currently filtered organization to query params if available void this.router.navigate(["/add-cipher"], {}); diff --git a/apps/browser/src/vault/popup/services/vault-popup-items.service.ts b/apps/browser/src/vault/popup/services/vault-popup-items.service.ts index eacb8e013ef..ab98722d060 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-items.service.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-items.service.ts @@ -38,7 +38,8 @@ import { MY_VAULT_ID, VaultPopupListFiltersService } from "./vault-popup-list-fi }) export class VaultPopupItemsService { private _refreshCurrentTab$ = new Subject(); - private searchText$ = new BehaviorSubject(""); + private _searchText$ = new BehaviorSubject(""); + latestSearchText$: Observable = this._searchText$.asObservable(); /** * Observable that contains the list of other cipher types that should be shown @@ -105,7 +106,7 @@ export class VaultPopupItemsService { private _filteredCipherList$: Observable = combineLatest([ this._cipherList$, - this.searchText$, + this._searchText$, this.vaultPopupListFiltersService.filterFunction$, ]).pipe( map(([ciphers, searchText, filterFunction]): [CipherView[], string] => [ @@ -179,7 +180,7 @@ export class VaultPopupItemsService { * Observable that indicates whether a filter is currently applied to the ciphers. */ hasFilterApplied$ = combineLatest([ - this.searchText$, + this._searchText$, this.vaultPopupListFiltersService.filters$, ]).pipe( switchMap(([searchText, filters]) => { @@ -242,7 +243,7 @@ export class VaultPopupItemsService { } applyFilter(newSearchText: string) { - this.searchText$.next(newSearchText); + this._searchText$.next(newSearchText); } /** diff --git a/libs/components/src/icon/icons/index.ts b/libs/components/src/icon/icons/index.ts index 9de81f19913..ea583031f61 100644 --- a/libs/components/src/icon/icons/index.ts +++ b/libs/components/src/icon/icons/index.ts @@ -2,3 +2,4 @@ export * from "./deactivated-org"; export * from "./search"; export * from "./no-access"; export * from "./vault"; +export * from "./no-results"; diff --git a/libs/components/src/icon/icons/no-results.ts b/libs/components/src/icon/icons/no-results.ts new file mode 100644 index 00000000000..f68e67f88ce --- /dev/null +++ b/libs/components/src/icon/icons/no-results.ts @@ -0,0 +1,18 @@ +import { svgIcon } from "../icon"; + +export const NoResults = svgIcon` + + + + + + + + + + + + + + +`;