clients/apps/browser/src/tools/popup/send-v2/send-v2.component.ts

175 lines
6.1 KiB
TypeScript

import { CommonModule } from "@angular/common";
import { Component, OnDestroy } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { combineLatest, distinctUntilChanged, map, shareReplay, switchMap } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { NoResults, NoSendsIcon } from "@bitwarden/assets/svg";
import { VaultLoadingSkeletonComponent } from "@bitwarden/browser/vault/popup/components/vault-loading-skeleton/vault-loading-skeleton.component";
import { BrowserPremiumUpgradePromptService } from "@bitwarden/browser/vault/popup/services/browser-premium-upgrade-prompt.service";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { PolicyType } from "@bitwarden/common/admin-console/enums";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service";
import { SearchService } from "@bitwarden/common/vault/abstractions/search.service";
import { skeletonLoadingDelay } from "@bitwarden/common/vault/utils/skeleton-loading.operator";
import {
ButtonModule,
CalloutModule,
NoItemsModule,
TypographyModule,
} from "@bitwarden/components";
import {
NewSendDropdownComponent,
SendItemsService,
SendListFiltersComponent,
SendListFiltersService,
SendListItemsContainerComponent,
SendSearchComponent,
} from "@bitwarden/send-ui";
import { CurrentAccountComponent } from "../../../auth/popup/account-switching/current-account.component";
import { PopOutComponent } from "../../../platform/popup/components/pop-out.component";
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component";
import { VaultFadeInOutSkeletonComponent } from "../../../vault/popup/components/vault-fade-in-out-skeleton/vault-fade-in-out-skeleton.component";
/** A state of the Send list UI. */
export const SendState = Object.freeze({
/** No sends exist for the current filter (file or text). */
Empty: "Empty",
/** Sends exist, but none match the current filter/search. */
NoResults: "NoResults",
} as const);
/** A state of the Send list UI. */
export type SendState = (typeof SendState)[keyof typeof SendState];
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@Component({
templateUrl: "send-v2.component.html",
providers: [
{
provide: PremiumUpgradePromptService,
useClass: BrowserPremiumUpgradePromptService,
},
],
imports: [
CalloutModule,
PopupPageComponent,
PopupHeaderComponent,
PopOutComponent,
CurrentAccountComponent,
NoItemsModule,
JslibModule,
CommonModule,
ButtonModule,
NewSendDropdownComponent,
SendListItemsContainerComponent,
SendListFiltersComponent,
SendSearchComponent,
TypographyModule,
VaultFadeInOutSkeletonComponent,
VaultLoadingSkeletonComponent,
],
})
export class SendV2Component implements OnDestroy {
sendType = SendType;
sendState = SendState;
protected listState: SendState | null = null;
protected sends$ = this.sendItemsService.filteredAndSortedSends$;
private skeletonFeatureFlag$ = this.configService.getFeatureFlag$(
FeatureFlag.VaultLoadingSkeletons,
);
protected sendsLoading$ = this.sendItemsService.loading$.pipe(
distinctUntilChanged(),
shareReplay({ bufferSize: 1, refCount: true }),
);
/** Spinner Loading State */
protected showSpinnerLoaders$ = combineLatest([
this.sendsLoading$,
this.skeletonFeatureFlag$,
]).pipe(map(([loading, skeletonsEnabled]) => loading && !skeletonsEnabled));
/** Skeleton Loading State */
protected showSkeletonsLoaders$ = combineLatest([
this.sendsLoading$,
this.searchService.isSendSearching$,
this.skeletonFeatureFlag$,
]).pipe(
map(
([loading, cipherSearching, skeletonsEnabled]) =>
(loading || cipherSearching) && skeletonsEnabled,
),
distinctUntilChanged(),
skeletonLoadingDelay(),
);
protected title: string = "allSends";
protected noItemIcon = NoSendsIcon;
protected noResultsIcon = NoResults;
protected sendsDisabled = false;
private readonly sendTypeTitles: Record<SendType, string> = {
[SendType.File]: "fileSends",
[SendType.Text]: "textSends",
};
constructor(
protected sendItemsService: SendItemsService,
protected sendListFiltersService: SendListFiltersService,
private policyService: PolicyService,
private accountService: AccountService,
private configService: ConfigService,
private searchService: SearchService,
) {
combineLatest([
this.sendItemsService.emptyList$,
this.sendItemsService.noFilteredResults$,
this.sendListFiltersService.filters$,
])
.pipe(takeUntilDestroyed())
.subscribe(([emptyList, noFilteredResults, currentFilter]) => {
if (currentFilter?.sendType !== null) {
this.title = this.sendTypeTitles[currentFilter.sendType] ?? "allSends";
} else {
this.title = "allSends";
}
if (emptyList) {
this.listState = SendState.Empty;
return;
}
if (noFilteredResults) {
this.listState = SendState.NoResults;
return;
}
this.listState = null;
});
this.accountService.activeAccount$
.pipe(
getUserId,
switchMap((userId) =>
this.policyService.policyAppliesToUser$(PolicyType.DisableSend, userId),
),
takeUntilDestroyed(),
)
.subscribe((sendsDisabled) => {
this.sendsDisabled = sendsDisabled;
});
}
ngOnDestroy(): void {}
}