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:
@@ -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}",
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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$);
|
||||||
|
|
||||||
|
|||||||
@@ -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>;
|
||||||
|
|||||||
@@ -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;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>;
|
||||||
|
|||||||
@@ -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>;
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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> {
|
||||||
|
|||||||
@@ -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",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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[]>;
|
||||||
|
|||||||
Reference in New Issue
Block a user