1+ import { LiveAnnouncer } from "@angular/cdk/a11y" ;
12import { CdkVirtualScrollableElement , ScrollingModule } from "@angular/cdk/scrolling" ;
23import { CommonModule } from "@angular/common" ;
34import { AfterViewInit , Component , DestroyRef , OnDestroy , OnInit , ViewChild } from "@angular/core" ;
45import { takeUntilDestroyed } from "@angular/core/rxjs-interop" ;
56import { Router , RouterModule } from "@angular/router" ;
67import {
78 combineLatest ,
9+ distinctUntilChanged ,
810 filter ,
911 firstValueFrom ,
1012 map ,
1113 Observable ,
1214 shareReplay ,
13- startWith ,
1415 switchMap ,
1516 take ,
17+ tap ,
1618} from "rxjs" ;
1719
1820import { JslibModule } from "@bitwarden/angular/jslib.module" ;
@@ -22,6 +24,8 @@ import { DeactivatedOrg, NoResults, VaultOpen } from "@bitwarden/assets/svg";
2224import { AccountService } from "@bitwarden/common/auth/abstractions/account.service" ;
2325import { getUserId } from "@bitwarden/common/auth/services/account.service" ;
2426import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum" ;
27+ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service" ;
28+ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service" ;
2529import { CipherId , CollectionId , OrganizationId , UserId } from "@bitwarden/common/types/guid" ;
2630import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service" ;
2731import { CipherType } from "@bitwarden/common/vault/enums" ;
@@ -41,11 +45,13 @@ import { PopOutComponent } from "../../../../platform/popup/components/pop-out.c
4145import { PopupHeaderComponent } from "../../../../platform/popup/layout/popup-header.component" ;
4246import { PopupPageComponent } from "../../../../platform/popup/layout/popup-page.component" ;
4347import { IntroCarouselService } from "../../services/intro-carousel.service" ;
44- import { VaultPopupCopyButtonsService } from "../../services/vault-popup-copy-buttons.service" ;
4548import { VaultPopupItemsService } from "../../services/vault-popup-items.service" ;
4649import { VaultPopupListFiltersService } from "../../services/vault-popup-list-filters.service" ;
50+ import { VaultPopupLoadingService } from "../../services/vault-popup-loading.service" ;
4751import { VaultPopupScrollPositionService } from "../../services/vault-popup-scroll-position.service" ;
4852import { AtRiskPasswordCalloutComponent } from "../at-risk-callout/at-risk-password-callout.component" ;
53+ import { VaultFadeInOutSkeletonComponent } from "../vault-fade-in-out-skeleton/vault-fade-in-out-skeleton.component" ;
54+ import { VaultLoadingSkeletonComponent } from "../vault-loading-skeleton/vault-loading-skeleton.component" ;
4955
5056import { BlockedInjectionBanner } from "./blocked-injection-banner/blocked-injection-banner.component" ;
5157import {
@@ -88,6 +94,8 @@ type VaultState = UnionOfValues<typeof VaultState>;
8894 SpotlightComponent ,
8995 RouterModule ,
9096 TypographyModule ,
97+ VaultLoadingSkeletonComponent ,
98+ VaultFadeInOutSkeletonComponent ,
9199 ] ,
92100} )
93101export class VaultV2Component implements OnInit , AfterViewInit , OnDestroy {
@@ -108,19 +116,30 @@ export class VaultV2Component implements OnInit, AfterViewInit, OnDestroy {
108116 ) ;
109117
110118 activeUserId : UserId | null = null ;
119+
120+ private loading$ = this . vaultPopupLoadingService . loading$ . pipe (
121+ distinctUntilChanged ( ) ,
122+ tap ( ( loading ) => {
123+ const key = loading ? "loadingVault" : "vaultLoaded" ;
124+ void this . liveAnnouncer . announce ( this . i18nService . translate ( key ) , "polite" ) ;
125+ } ) ,
126+ ) ;
127+ private skeletonFeatureFlag$ = this . configService . getFeatureFlag$ (
128+ FeatureFlag . VaultLoadingSkeletons ,
129+ ) ;
130+
111131 protected favoriteCiphers$ = this . vaultPopupItemsService . favoriteCiphers$ ;
112132 protected remainingCiphers$ = this . vaultPopupItemsService . remainingCiphers$ ;
113133 protected allFilters$ = this . vaultPopupListFiltersService . allFilters$ ;
114134
115- protected loading$ = combineLatest ( [
116- this . vaultPopupItemsService . loading$ ,
117- this . allFilters$ ,
118- // Added as a dependency to avoid flashing the copyActions on slower devices
119- this . vaultCopyButtonsService . showQuickCopyActions$ ,
120- ] ) . pipe (
121- map ( ( [ itemsLoading , filters ] ) => itemsLoading || ! filters ) ,
122- shareReplay ( { bufferSize : 1 , refCount : true } ) ,
123- startWith ( true ) ,
135+ /** When true, show spinner loading state */
136+ protected showSpinnerLoaders$ = combineLatest ( [ this . loading$ , this . skeletonFeatureFlag$ ] ) . pipe (
137+ map ( ( [ loading , skeletonsEnabled ] ) => loading && ! skeletonsEnabled ) ,
138+ ) ;
139+
140+ /** When true, show skeleton loading state */
141+ protected showSkeletonsLoaders$ = combineLatest ( [ this . loading$ , this . skeletonFeatureFlag$ ] ) . pipe (
142+ map ( ( [ loading , skeletonsEnabled ] ) => loading && skeletonsEnabled ) ,
124143 ) ;
125144
126145 protected newItemItemValues$ : Observable < NewItemInitialValues > =
@@ -150,14 +169,17 @@ export class VaultV2Component implements OnInit, AfterViewInit, OnDestroy {
150169 private vaultPopupItemsService : VaultPopupItemsService ,
151170 private vaultPopupListFiltersService : VaultPopupListFiltersService ,
152171 private vaultScrollPositionService : VaultPopupScrollPositionService ,
172+ private vaultPopupLoadingService : VaultPopupLoadingService ,
153173 private accountService : AccountService ,
154174 private destroyRef : DestroyRef ,
155175 private cipherService : CipherService ,
156176 private dialogService : DialogService ,
157- private vaultCopyButtonsService : VaultPopupCopyButtonsService ,
158177 private introCarouselService : IntroCarouselService ,
159178 private nudgesService : NudgesService ,
160179 private router : Router ,
180+ private liveAnnouncer : LiveAnnouncer ,
181+ private i18nService : I18nService ,
182+ private configService : ConfigService ,
161183 ) {
162184 combineLatest ( [
163185 this . vaultPopupItemsService . emptyVault$ ,
0 commit comments