1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-19 09:43:23 +00:00

Merge branch 'main' into autofill/pm-5189-fix-issues-present-with-inline-menu-rendering-in-iframes

This commit is contained in:
Cesar Gonzalez
2024-03-21 16:27:30 -05:00
17 changed files with 115 additions and 180 deletions

View File

@@ -150,7 +150,69 @@
"identityName": "8bitSolutionsLLC.bitwardendesktop", "identityName": "8bitSolutionsLLC.bitwardendesktop",
"publisher": "CN=14D52771-DE3C-4886-B8BF-825BA7690418", "publisher": "CN=14D52771-DE3C-4886-B8BF-825BA7690418",
"publisherDisplayName": "8bit Solutions LLC", "publisherDisplayName": "8bit Solutions LLC",
"languages": ["en-US"] "languages": [
"en-US",
"af",
"ar",
"az-latn",
"be",
"bg",
"bn",
"bs",
"ca",
"cs",
"cy",
"da",
"de",
"el",
"en-gb",
"en-in",
"es",
"et",
"eu",
"fa",
"fi",
"fil",
"fr",
"gl",
"he",
"hi",
"hr",
"hu",
"id",
"it",
"ja",
"ka",
"km",
"kn",
"ko",
"lt",
"lv",
"ml",
"mr",
"nb",
"ne",
"nl",
"nn",
"or",
"pl",
"pt-br",
"pt-pt",
"ro",
"ru",
"si",
"sk",
"sl",
"sr",
"sv",
"te",
"th",
"tr",
"uk",
"vi",
"zh-cn",
"zh-tw"
]
}, },
"deb": { "deb": {
"artifactName": "${productName}-${version}-${arch}.${ext}", "artifactName": "${productName}-${version}-${arch}.${ext}",

View File

@@ -145,7 +145,7 @@
type="button" type="button"
buttonType="danger" buttonType="danger"
class="btn-submit tw-ml-auto" class="btn-submit tw-ml-auto"
(click)="cancel()" (click)="cancelSubscription()"
[appApiAction]="cancelPromise" [appApiAction]="cancelPromise"
[disabled]="$any(cancelBtn).loading" [disabled]="$any(cancelBtn).loading"
*ngIf="subscription && !subscription.cancelled && !subscriptionMarkedForCancel" *ngIf="subscription && !subscription.cancelled && !subscriptionMarkedForCancel"

View File

@@ -1,12 +1,10 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { Router } from "@angular/router"; import { Router } from "@angular/router";
import { firstValueFrom, lastValueFrom, Observable } from "rxjs"; import { firstValueFrom, lastValueFrom } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { SubscriptionResponse } from "@bitwarden/common/billing/models/response/subscription.response"; import { SubscriptionResponse } from "@bitwarden/common/billing/models/response/subscription.response";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigServiceAbstraction as ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@@ -34,7 +32,6 @@ export class UserSubscriptionComponent implements OnInit {
cancelPromise: Promise<any>; cancelPromise: Promise<any>;
reinstatePromise: Promise<any>; reinstatePromise: Promise<any>;
presentUserWithOffboardingSurvey$: Observable<boolean>;
constructor( constructor(
private apiService: ApiService, private apiService: ApiService,
@@ -45,7 +42,6 @@ export class UserSubscriptionComponent implements OnInit {
private fileDownloadService: FileDownloadService, private fileDownloadService: FileDownloadService,
private dialogService: DialogService, private dialogService: DialogService,
private environmentService: EnvironmentService, private environmentService: EnvironmentService,
private configService: ConfigService,
private billingAccountProfileStateService: BillingAccountProfileStateService, private billingAccountProfileStateService: BillingAccountProfileStateService,
) { ) {
this.selfHosted = platformUtilsService.isSelfHost(); this.selfHosted = platformUtilsService.isSelfHost();
@@ -53,9 +49,6 @@ export class UserSubscriptionComponent implements OnInit {
async ngOnInit() { async ngOnInit() {
this.cloudWebVaultUrl = await firstValueFrom(this.environmentService.cloudWebVaultUrl$); this.cloudWebVaultUrl = await firstValueFrom(this.environmentService.cloudWebVaultUrl$);
this.presentUserWithOffboardingSurvey$ = this.configService.getFeatureFlag$<boolean>(
FeatureFlag.AC1607_PresentUserOffboardingSurvey,
);
await this.load(); await this.load();
this.firstLoaded = true; this.firstLoaded = true;
} }
@@ -105,16 +98,22 @@ export class UserSubscriptionComponent implements OnInit {
} }
} }
cancel = async () => { cancelSubscription = async () => {
const presentUserWithOffboardingSurvey = await this.configService.getFeatureFlag<boolean>( const reference = openOffboardingSurvey(this.dialogService, {
FeatureFlag.AC1607_PresentUserOffboardingSurvey, data: {
); type: "User",
},
});
if (presentUserWithOffboardingSurvey) { this.cancelPromise = lastValueFrom(reference.closed);
await this.cancelWithOffboardingSurvey();
} else { const result = await this.cancelPromise;
await this.cancelWithWarning();
if (result === OffboardingSurveyDialogResultType.Closed) {
return;
} }
await this.load();
}; };
downloadLicense() { downloadLicense() {
@@ -159,55 +158,6 @@ export class UserSubscriptionComponent implements OnInit {
} }
} }
private cancelWithOffboardingSurvey = async () => {
const reference = openOffboardingSurvey(this.dialogService, {
data: {
type: "User",
},
});
this.cancelPromise = lastValueFrom(reference.closed);
const result = await this.cancelPromise;
if (result === OffboardingSurveyDialogResultType.Closed) {
return;
}
await this.load();
};
private async cancelWithWarning() {
if (this.loading) {
return;
}
const confirmed = await this.dialogService.openSimpleDialog({
title: { key: "cancelSubscription" },
content: { key: "cancelConfirmation" },
type: "warning",
});
if (!confirmed) {
return;
}
try {
this.cancelPromise = this.apiService.postCancelPremium();
await this.cancelPromise;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("canceledSubscription"),
);
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.load();
} catch (e) {
this.logService.error(e);
}
}
get subscriptionMarkedForCancel() { get subscriptionMarkedForCancel() {
return ( return (
this.subscription != null && !this.subscription.cancelled && this.subscription.cancelAtEndDate this.subscription != null && !this.subscription.cancelled && this.subscription.cancelAtEndDate

View File

@@ -232,28 +232,9 @@
<button <button
bitButton bitButton
buttonType="danger" buttonType="danger"
[bitAction]="cancelWithWarning" (click)="cancelSubscription()"
type="button" type="button"
*ngIf=" *ngIf="subscription && !subscription.cancelled && !subscriptionMarkedForCancel"
subscription &&
!subscription.cancelled &&
!subscriptionMarkedForCancel &&
!(presentUserWithOffboardingSurvey$ | async)
"
>
{{ "cancelSubscription" | i18n }}
</button>
<button
bitButton
buttonType="danger"
(click)="cancelWithOffboardingSurvey()"
type="button"
*ngIf="
subscription &&
!subscription.cancelled &&
!subscriptionMarkedForCancel &&
(presentUserWithOffboardingSurvey$ | async)
"
> >
{{ "cancelSubscription" | i18n }} {{ "cancelSubscription" | i18n }}
</button> </button>

View File

@@ -1,6 +1,6 @@
import { Component, OnDestroy, OnInit } from "@angular/core"; import { Component, OnDestroy, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router"; import { ActivatedRoute } from "@angular/router";
import { concatMap, firstValueFrom, lastValueFrom, Observable, Subject, takeUntil } from "rxjs"; import { concatMap, firstValueFrom, lastValueFrom, Subject, takeUntil } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
@@ -11,8 +11,6 @@ import { PlanType } from "@bitwarden/common/billing/enums";
import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response"; import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response";
import { BillingSubscriptionItemResponse } from "@bitwarden/common/billing/models/response/subscription.response"; import { BillingSubscriptionItemResponse } from "@bitwarden/common/billing/models/response/subscription.response";
import { ProductType } from "@bitwarden/common/enums"; import { ProductType } from "@bitwarden/common/enums";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigServiceAbstraction as ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@@ -43,7 +41,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
showSecretsManagerSubscribe = false; showSecretsManagerSubscribe = false;
firstLoaded = false; firstLoaded = false;
loading: boolean; loading: boolean;
presentUserWithOffboardingSurvey$: Observable<boolean>;
protected readonly teamsStarter = ProductType.TeamsStarter; protected readonly teamsStarter = ProductType.TeamsStarter;
@@ -58,7 +55,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
private organizationApiService: OrganizationApiServiceAbstraction, private organizationApiService: OrganizationApiServiceAbstraction,
private route: ActivatedRoute, private route: ActivatedRoute,
private dialogService: DialogService, private dialogService: DialogService,
private configService: ConfigService,
) {} ) {}
async ngOnInit() { async ngOnInit() {
@@ -78,10 +74,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
takeUntil(this.destroy$), takeUntil(this.destroy$),
) )
.subscribe(); .subscribe();
this.presentUserWithOffboardingSurvey$ = this.configService.getFeatureFlag$<boolean>(
FeatureFlag.AC1607_PresentUserOffboardingSurvey,
);
} }
ngOnDestroy() { ngOnDestroy() {
@@ -278,7 +270,7 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
); );
} }
cancelWithOffboardingSurvey = async () => { cancelSubscription = async () => {
const reference = openOffboardingSurvey(this.dialogService, { const reference = openOffboardingSurvey(this.dialogService, {
data: { data: {
type: "Organization", type: "Organization",
@@ -295,36 +287,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
await this.load(); await this.load();
}; };
cancelWithWarning = async () => {
if (this.loading) {
return;
}
const confirmed = await this.dialogService.openSimpleDialog({
title: { key: "cancelSubscription" },
content: { key: "cancelConfirmation" },
type: "warning",
});
if (!confirmed) {
return;
}
try {
await this.organizationApiService.cancel(this.organizationId);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("canceledSubscription"),
);
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.load();
} catch (e) {
this.logService.error(e);
}
};
reinstate = async () => { reinstate = async () => {
if (this.loading) { if (this.loading) {
return; return;

View File

@@ -5,6 +5,7 @@ import { CollectionView } from "@bitwarden/common/src/vault/models/view/collecti
import { FolderView } from "@bitwarden/common/src/vault/models/view/folder.view"; import { FolderView } from "@bitwarden/common/src/vault/models/view/folder.view";
import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
import { CollectionAdminView } from "../../../../core/views/collection-admin.view";
import { import {
CipherTypeFilter, CipherTypeFilter,
CollectionFilter, CollectionFilter,
@@ -20,7 +21,6 @@ export abstract class VaultFilterService {
folderTree$: Observable<TreeNode<FolderFilter>>; folderTree$: Observable<TreeNode<FolderFilter>>;
collectionTree$: Observable<TreeNode<CollectionFilter>>; collectionTree$: Observable<TreeNode<CollectionFilter>>;
cipherTypeTree$: Observable<TreeNode<CipherTypeFilter>>; cipherTypeTree$: Observable<TreeNode<CipherTypeFilter>>;
reloadCollections: (collections: CollectionView[]) => void;
getCollectionNodeFromTree: (id: string) => Promise<TreeNode<CollectionFilter>>; getCollectionNodeFromTree: (id: string) => Promise<TreeNode<CollectionFilter>>;
setCollapsedFilterNodes: (collapsedFilterNodes: Set<string>) => Promise<void>; setCollapsedFilterNodes: (collapsedFilterNodes: Set<string>) => Promise<void>;
expandOrgFilter: () => Promise<void>; expandOrgFilter: () => Promise<void>;
@@ -30,4 +30,6 @@ export abstract class VaultFilterService {
head: CipherTypeFilter, head: CipherTypeFilter,
array: CipherTypeFilter[], array: CipherTypeFilter[],
) => Observable<TreeNode<CipherTypeFilter>>; ) => Observable<TreeNode<CipherTypeFilter>>;
// TODO: Remove this from org vault when collection admin service adopts state management
reloadCollections?: (collections: CollectionAdminView[]) => void;
} }

View File

@@ -15,6 +15,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { Utils } from "@bitwarden/common/platform/misc/utils"; import { Utils } from "@bitwarden/common/platform/misc/utils";
import { UserId } from "@bitwarden/common/types/guid"; import { UserId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view"; import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
@@ -31,8 +32,10 @@ describe("vault filter service", () => {
let cipherService: MockProxy<CipherService>; let cipherService: MockProxy<CipherService>;
let policyService: MockProxy<PolicyService>; let policyService: MockProxy<PolicyService>;
let i18nService: MockProxy<I18nService>; let i18nService: MockProxy<I18nService>;
let collectionService: MockProxy<CollectionService>;
let organizations: ReplaySubject<Organization[]>; let organizations: ReplaySubject<Organization[]>;
let folderViews: ReplaySubject<FolderView[]>; let folderViews: ReplaySubject<FolderView[]>;
let collectionViews: ReplaySubject<CollectionView[]>;
let stateProvider: FakeStateProvider; let stateProvider: FakeStateProvider;
const mockUserId = Utils.newGuid() as UserId; const mockUserId = Utils.newGuid() as UserId;
@@ -48,12 +51,15 @@ describe("vault filter service", () => {
accountService = mockAccountServiceWith(mockUserId); accountService = mockAccountServiceWith(mockUserId);
stateProvider = new FakeStateProvider(accountService); stateProvider = new FakeStateProvider(accountService);
i18nService.collator = new Intl.Collator("en-US"); i18nService.collator = new Intl.Collator("en-US");
collectionService = mock<CollectionService>();
organizations = new ReplaySubject<Organization[]>(1); organizations = new ReplaySubject<Organization[]>(1);
folderViews = new ReplaySubject<FolderView[]>(1); folderViews = new ReplaySubject<FolderView[]>(1);
collectionViews = new ReplaySubject<CollectionView[]>(1);
organizationService.memberOrganizations$ = organizations; organizationService.memberOrganizations$ = organizations;
folderService.folderViews$ = folderViews; folderService.folderViews$ = folderViews;
collectionService.decryptedCollections$ = collectionViews;
vaultFilterService = new VaultFilterService( vaultFilterService = new VaultFilterService(
organizationService, organizationService,
@@ -62,6 +68,7 @@ describe("vault filter service", () => {
policyService, policyService,
i18nService, i18nService,
stateProvider, stateProvider,
collectionService,
); );
collapsedGroupingsState = stateProvider.activeUser.getFake(COLLAPSED_GROUPINGS); collapsedGroupingsState = stateProvider.activeUser.getFake(COLLAPSED_GROUPINGS);
}); });
@@ -196,9 +203,7 @@ describe("vault filter service", () => {
createCollectionView("1", "collection 1", "org test id"), createCollectionView("1", "collection 1", "org test id"),
createCollectionView("2", "collection 2", "non matching org id"), createCollectionView("2", "collection 2", "non matching org id"),
]; ];
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. collectionViews.next(storedCollections);
// eslint-disable-next-line @typescript-eslint/no-floating-promises
vaultFilterService.reloadCollections(storedCollections);
await expect(firstValueFrom(vaultFilterService.filteredCollections$)).resolves.toEqual([ await expect(firstValueFrom(vaultFilterService.filteredCollections$)).resolves.toEqual([
createCollectionView("1", "collection 1", "org test id"), createCollectionView("1", "collection 1", "org test id"),
@@ -213,9 +218,7 @@ describe("vault filter service", () => {
createCollectionView("id-2", "Collection 1/Collection 2", "org test id"), createCollectionView("id-2", "Collection 1/Collection 2", "org test id"),
createCollectionView("id-3", "Collection 1/Collection 3", "org test id"), createCollectionView("id-3", "Collection 1/Collection 3", "org test id"),
]; ];
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. collectionViews.next(storedCollections);
// eslint-disable-next-line @typescript-eslint/no-floating-promises
vaultFilterService.reloadCollections(storedCollections);
const result = await firstValueFrom(vaultFilterService.collectionTree$); const result = await firstValueFrom(vaultFilterService.collectionTree$);
@@ -228,9 +231,7 @@ describe("vault filter service", () => {
createCollectionView("id-1", "Collection 1", "org test id"), createCollectionView("id-1", "Collection 1", "org test id"),
createCollectionView("id-3", "Collection 1/Collection 2/Collection 3", "org test id"), createCollectionView("id-3", "Collection 1/Collection 2/Collection 3", "org test id"),
]; ];
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. collectionViews.next(storedCollections);
// eslint-disable-next-line @typescript-eslint/no-floating-promises
vaultFilterService.reloadCollections(storedCollections);
const result = await firstValueFrom(vaultFilterService.collectionTree$); const result = await firstValueFrom(vaultFilterService.collectionTree$);
@@ -246,9 +247,7 @@ describe("vault filter service", () => {
createCollectionView("id-3", "Collection 1/Collection 2/Collection 3", "org test id"), createCollectionView("id-3", "Collection 1/Collection 2/Collection 3", "org test id"),
createCollectionView("id-4", "Collection 1/Collection 4", "org test id"), createCollectionView("id-4", "Collection 1/Collection 4", "org test id"),
]; ];
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. collectionViews.next(storedCollections);
// eslint-disable-next-line @typescript-eslint/no-floating-promises
vaultFilterService.reloadCollections(storedCollections);
const result = await firstValueFrom(vaultFilterService.collectionTree$); const result = await firstValueFrom(vaultFilterService.collectionTree$);
@@ -266,9 +265,7 @@ describe("vault filter service", () => {
createCollectionView("id-1", "Collection 1", "org test id"), createCollectionView("id-1", "Collection 1", "org test id"),
createCollectionView("id-3", "Collection 1/Collection 2/Collection 3", "org test id"), createCollectionView("id-3", "Collection 1/Collection 2/Collection 3", "org test id"),
]; ];
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. collectionViews.next(storedCollections);
// eslint-disable-next-line @typescript-eslint/no-floating-promises
vaultFilterService.reloadCollections(storedCollections);
const result = await firstValueFrom(vaultFilterService.collectionTree$); const result = await firstValueFrom(vaultFilterService.collectionTree$);

View File

@@ -1,13 +1,11 @@
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { import {
BehaviorSubject, BehaviorSubject,
combineLatest,
combineLatestWith, combineLatestWith,
firstValueFrom, firstValueFrom,
map, map,
Observable, Observable,
of, of,
ReplaySubject,
switchMap, switchMap,
} from "rxjs"; } from "rxjs";
@@ -18,6 +16,7 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { ActiveUserState, StateProvider } from "@bitwarden/common/platform/state"; import { ActiveUserState, StateProvider } from "@bitwarden/common/platform/state";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherType } from "@bitwarden/common/vault/enums";
import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
@@ -57,17 +56,14 @@ export class VaultFilterService implements VaultFilterServiceAbstraction {
map((folders) => this.buildFolderTree(folders)), map((folders) => this.buildFolderTree(folders)),
); );
// TODO: Remove once collections is refactored with observables filteredCollections$: Observable<CollectionView[]> =
// replace with collection service observable this.collectionService.decryptedCollections$.pipe(
private collectionViews$ = new ReplaySubject<CollectionView[]>(1); combineLatestWith(this._organizationFilter),
filteredCollections$: Observable<CollectionView[]> = combineLatest([ switchMap(([collections, org]) => {
this.collectionViews$, return this.filterCollections(collections, org);
this._organizationFilter, }),
]).pipe( );
switchMap(([collections, org]) => {
return this.filterCollections(collections, org);
}),
);
collectionTree$: Observable<TreeNode<CollectionFilter>> = this.filteredCollections$.pipe( collectionTree$: Observable<TreeNode<CollectionFilter>> = this.filteredCollections$.pipe(
map((collections) => this.buildCollectionTree(collections)), map((collections) => this.buildCollectionTree(collections)),
); );
@@ -87,12 +83,9 @@ export class VaultFilterService implements VaultFilterServiceAbstraction {
protected policyService: PolicyService, protected policyService: PolicyService,
protected i18nService: I18nService, protected i18nService: I18nService,
protected stateProvider: StateProvider, protected stateProvider: StateProvider,
protected collectionService: CollectionService,
) {} ) {}
async reloadCollections(collections: CollectionView[]) {
this.collectionViews$.next(collections);
}
async getCollectionNodeFromTree(id: string) { async getCollectionNodeFromTree(id: string) {
const collections = await firstValueFrom(this.collectionTree$); const collections = await firstValueFrom(this.collectionTree$);
return ServiceUtils.getTreeNodeObject(collections, id) as TreeNode<CollectionFilter>; return ServiceUtils.getTreeNodeObject(collections, id) as TreeNode<CollectionFilter>;

View File

@@ -406,10 +406,6 @@ export class VaultComponent implements OnInit, OnDestroy {
(filter.organizationId === undefined || filter.organizationId === Unassigned); (filter.organizationId === undefined || filter.organizationId === Unassigned);
this.isEmpty = collections?.length === 0 && ciphers?.length === 0; this.isEmpty = collections?.length === 0 && ciphers?.length === 0;
// This is a temporary fix to avoid double fetching collections.
// TODO: Remove when implementing new VVR menu
this.vaultFilterService.reloadCollections(allCollections);
this.performingInitialLoad = false; this.performingInitialLoad = false;
this.refreshing = false; this.refreshing = false;
}, },

View File

@@ -6,11 +6,11 @@ import { PolicyService } from "@bitwarden/common/admin-console/abstractions/poli
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { StateProvider } from "@bitwarden/common/platform/state"; import { StateProvider } from "@bitwarden/common/platform/state";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
import { CollectionAdminView } from "../../../vault/core/views/collection-admin.view"; import { CollectionAdminView } from "../../../vault/core/views/collection-admin.view";
import { CollectionAdminService } from "../../core/collection-admin.service";
import { VaultFilterService as BaseVaultFilterService } from "../../individual-vault/vault-filter/services/vault-filter.service"; import { VaultFilterService as BaseVaultFilterService } from "../../individual-vault/vault-filter/services/vault-filter.service";
import { CollectionFilter } from "../../individual-vault/vault-filter/shared/models/vault-filter.type"; import { CollectionFilter } from "../../individual-vault/vault-filter/shared/models/vault-filter.type";
@@ -32,7 +32,7 @@ export class VaultFilterService extends BaseVaultFilterService implements OnDest
policyService: PolicyService, policyService: PolicyService,
i18nService: I18nService, i18nService: I18nService,
stateProvider: StateProvider, stateProvider: StateProvider,
protected collectionAdminService: CollectionAdminService, collectionService: CollectionService,
) { ) {
super( super(
organizationService, organizationService,
@@ -41,6 +41,7 @@ export class VaultFilterService extends BaseVaultFilterService implements OnDest
policyService, policyService,
i18nService, i18nService,
stateProvider, stateProvider,
collectionService,
); );
} }

View File

@@ -170,7 +170,6 @@ export abstract class ApiService {
postRegister: (request: RegisterRequest) => Promise<RegisterResponse>; postRegister: (request: RegisterRequest) => Promise<RegisterResponse>;
postPremium: (data: FormData) => Promise<PaymentResponse>; postPremium: (data: FormData) => Promise<PaymentResponse>;
postReinstatePremium: () => Promise<any>; postReinstatePremium: () => Promise<any>;
postCancelPremium: () => Promise<any>;
postAccountStorage: (request: StorageRequest) => Promise<PaymentResponse>; postAccountStorage: (request: StorageRequest) => Promise<PaymentResponse>;
postAccountPayment: (request: PaymentRequest) => Promise<void>; postAccountPayment: (request: PaymentRequest) => Promise<void>;
postAccountLicense: (data: FormData) => Promise<any>; postAccountLicense: (data: FormData) => Promise<any>;

View File

@@ -51,7 +51,6 @@ export class OrganizationApiServiceAbstraction {
updateSeats: (id: string, request: SeatRequest) => Promise<PaymentResponse>; updateSeats: (id: string, request: SeatRequest) => Promise<PaymentResponse>;
updateStorage: (id: string, request: StorageRequest) => Promise<PaymentResponse>; updateStorage: (id: string, request: StorageRequest) => Promise<PaymentResponse>;
verifyBank: (id: string, request: VerifyBankRequest) => Promise<void>; verifyBank: (id: string, request: VerifyBankRequest) => Promise<void>;
cancel: (id: string) => Promise<void>;
reinstate: (id: string) => Promise<void>; reinstate: (id: string) => Promise<void>;
leave: (id: string) => Promise<void>; leave: (id: string) => Promise<void>;
delete: (id: string, request: SecretVerificationRequest) => Promise<void>; delete: (id: string, request: SecretVerificationRequest) => Promise<void>;

View File

@@ -184,10 +184,6 @@ export class OrganizationApiService implements OrganizationApiServiceAbstraction
); );
} }
async cancel(id: string): Promise<void> {
return this.apiService.send("POST", "/organizations/" + id + "/cancel", null, true, false);
}
async reinstate(id: string): Promise<void> { async reinstate(id: string): Promise<void> {
return this.apiService.send("POST", "/organizations/" + id + "/reinstate", null, true, false); return this.apiService.send("POST", "/organizations/" + id + "/reinstate", null, true, false);
} }

View File

@@ -12,7 +12,7 @@ export class BillingApiService implements BillingApiServiceAbstraction {
): Promise<void> { ): Promise<void> {
return this.apiService.send( return this.apiService.send(
"POST", "POST",
"/organizations/" + organizationId + "/churn", "/organizations/" + organizationId + "/cancel",
request, request,
true, true,
false, false,
@@ -20,7 +20,7 @@ export class BillingApiService implements BillingApiServiceAbstraction {
} }
cancelPremiumUserSubscription(request: SubscriptionCancellationRequest): Promise<void> { cancelPremiumUserSubscription(request: SubscriptionCancellationRequest): Promise<void> {
return this.apiService.send("POST", "/accounts/churn-premium", request, true, false); return this.apiService.send("POST", "/accounts/cancel", request, true, false);
} }
async getBillingStatus(id: string): Promise<OrganizationBillingStatusResponse> { async getBillingStatus(id: string): Promise<OrganizationBillingStatusResponse> {

View File

@@ -7,7 +7,6 @@ export enum FeatureFlag {
GeneratorToolsModernization = "generator-tools-modernization", GeneratorToolsModernization = "generator-tools-modernization",
KeyRotationImprovements = "key-rotation-improvements", KeyRotationImprovements = "key-rotation-improvements",
FlexibleCollectionsMigration = "flexible-collections-migration", FlexibleCollectionsMigration = "flexible-collections-migration",
AC1607_PresentUserOffboardingSurvey = "AC-1607_present-user-offboarding-survey",
ShowPaymentMethodWarningBanners = "show-payment-method-warning-banners", ShowPaymentMethodWarningBanners = "show-payment-method-warning-banners",
} }

View File

@@ -394,10 +394,6 @@ export class ApiService implements ApiServiceAbstraction {
return this.send("POST", "/accounts/reinstate-premium", null, true, false); return this.send("POST", "/accounts/reinstate-premium", null, true, false);
} }
postCancelPremium(): Promise<any> {
return this.send("POST", "/accounts/cancel-premium", null, true, false);
}
async postAccountStorage(request: StorageRequest): Promise<PaymentResponse> { async postAccountStorage(request: StorageRequest): Promise<PaymentResponse> {
const r = await this.send("POST", "/accounts/storage", request, true, true); const r = await this.send("POST", "/accounts/storage", request, true, true);
return new PaymentResponse(r); return new PaymentResponse(r);

View File

@@ -7,6 +7,8 @@ import { TreeNode } from "../models/domain/tree-node";
import { CollectionView } from "../models/view/collection.view"; import { CollectionView } from "../models/view/collection.view";
export abstract class CollectionService { export abstract class CollectionService {
decryptedCollections$: Observable<CollectionView[]>;
clearActiveUserCache: () => Promise<void>; clearActiveUserCache: () => Promise<void>;
encrypt: (model: CollectionView) => Promise<Collection>; encrypt: (model: CollectionView) => Promise<Collection>;
decryptedCollectionViews$: (ids: CollectionId[]) => Observable<CollectionView[]>; decryptedCollectionViews$: (ids: CollectionId[]) => Observable<CollectionView[]>;