diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json
index b0df5fadd4..6dc0a93059 100644
--- a/apps/browser/src/_locales/en/messages.json
+++ b/apps/browser/src/_locales/en/messages.json
@@ -1083,7 +1083,7 @@
"message": "1 GB encrypted storage for file attachments."
},
"premiumSignUpEmergency": {
- "message": "Emergency access"
+ "message": "Emergency access."
},
"premiumSignUpTwoStepOptions": {
"message": "Proprietary two-step login options such as YubiKey and Duo."
@@ -1115,6 +1115,9 @@
"premiumCurrentMemberThanks": {
"message": "Thank you for supporting Bitwarden."
},
+ "premiumFeatures": {
+ "message": "Upgrade to premium and receive:"
+ },
"premiumPrice": {
"message": "All for just $PRICE$ /year!",
"placeholders": {
@@ -1124,6 +1127,15 @@
}
}
},
+ "premiumPriceV2": {
+ "message": "All for just $PRICE$ per year!",
+ "placeholders": {
+ "price": {
+ "content": "$1",
+ "example": "$10"
+ }
+ }
+ },
"refreshComplete": {
"message": "Refresh complete"
},
@@ -3960,6 +3972,12 @@
"autoFillOnPageLoad": {
"message": "Autofill on page load?"
},
+ "cardExpiredTitle": {
+ "message": "Expired card"
+ },
+ "cardExpiredMessage": {
+ "message": "If you've renewed it, update the card's information"
+ },
"cardDetails": {
"message": "Card details"
},
diff --git a/apps/browser/src/billing/popup/settings/premium-v2.component.html b/apps/browser/src/billing/popup/settings/premium-v2.component.html
index 2a98cffb0e..7b781eafdb 100644
--- a/apps/browser/src/billing/popup/settings/premium-v2.component.html
+++ b/apps/browser/src/billing/popup/settings/premium-v2.component.html
@@ -9,13 +9,13 @@
{{ "premiumFeatures" | i18n }}
-
+
-
{{ "ppremiumSignUpStorage" | i18n }}
-
- {{ "ppremiumSignUpTwoStepOptions" | i18n }}
+ {{ "premiumSignUpTwoStepOptions" | i18n }}
-
{{ "premiumSignUpEmergency" | i18n }}
diff --git a/apps/browser/src/billing/popup/settings/premium-v2.component.ts b/apps/browser/src/billing/popup/settings/premium-v2.component.ts
index 456aa6dc9a..ef4c39942a 100644
--- a/apps/browser/src/billing/popup/settings/premium-v2.component.ts
+++ b/apps/browser/src/billing/popup/settings/premium-v2.component.ts
@@ -74,7 +74,7 @@ export class PremiumV2Component extends BasePremiumComponent {
const formattedPrice = this.platformUtilsService.isSafari()
? thePrice.replace("$", "$$$")
: thePrice;
- this.priceString = i18nService.t("premiumPrice", formattedPrice);
+ this.priceString = i18nService.t("premiumPriceV2", formattedPrice);
if (this.priceString.indexOf("%price%") > -1) {
this.priceString = this.priceString.replace("%price%", thePrice);
}
diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts
index 688e7e72a0..79a2df30e6 100644
--- a/apps/browser/src/popup/app-routing.module.ts
+++ b/apps/browser/src/popup/app-routing.module.ts
@@ -9,6 +9,7 @@ import {
unauthGuardFn,
} from "@bitwarden/angular/auth/guards";
import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard";
+import { extensionRefreshSwap } from "@bitwarden/angular/utils/extension-refresh-swap";
import {
AnonLayoutWrapperComponent,
AnonLayoutWrapperData,
@@ -91,7 +92,7 @@ import { SyncComponent } from "../vault/popup/settings/sync.component";
import { VaultSettingsV2Component } from "../vault/popup/settings/vault-settings-v2.component";
import { VaultSettingsComponent } from "../vault/popup/settings/vault-settings.component";
-import { extensionRefreshRedirect, extensionRefreshSwap } from "./extension-refresh-route-utils";
+import { extensionRefreshRedirect } from "./extension-refresh-route-utils";
import { debounceNavigationGuard } from "./services/debounce-navigation.service";
import { TabsV2Component } from "./tabs-v2.component";
import { TabsComponent } from "./tabs.component";
diff --git a/apps/browser/src/popup/extension-refresh-route-utils.ts b/apps/browser/src/popup/extension-refresh-route-utils.ts
index 3c2ca33f86..9d45d7d656 100644
--- a/apps/browser/src/popup/extension-refresh-route-utils.ts
+++ b/apps/browser/src/popup/extension-refresh-route-utils.ts
@@ -1,32 +1,9 @@
-import { inject, Type } from "@angular/core";
-import { Route, Router, Routes, UrlTree } from "@angular/router";
+import { inject } from "@angular/core";
+import { Router, UrlTree } from "@angular/router";
-import { componentRouteSwap } from "@bitwarden/angular/utils/component-route-swap";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
-/**
- * Helper function to swap between two components based on the ExtensionRefresh feature flag.
- * @param defaultComponent - The current non-refreshed component to render.
- * @param refreshedComponent - The new refreshed component to render.
- * @param options - The shared route options to apply to both components.
- */
-export function extensionRefreshSwap(
- defaultComponent: Type,
- refreshedComponent: Type,
- options: Route,
-): Routes {
- return componentRouteSwap(
- defaultComponent,
- refreshedComponent,
- async () => {
- const configService = inject(ConfigService);
- return configService.getFeatureFlag(FeatureFlag.ExtensionRefresh);
- },
- options,
- );
-}
-
/**
* Helper function to redirect to a new URL based on the ExtensionRefresh feature flag.
* @param redirectUrl - The URL to redirect to if the ExtensionRefresh flag is enabled.
diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.spec.ts
new file mode 100644
index 0000000000..95288f6b41
--- /dev/null
+++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.spec.ts
@@ -0,0 +1,192 @@
+import { ComponentFixture, fakeAsync, TestBed, tick } from "@angular/core/testing";
+import { ActivatedRoute, Router } from "@angular/router";
+import { mock } from "jest-mock-extended";
+import { BehaviorSubject } from "rxjs";
+
+import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
+import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
+import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
+import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
+import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
+import { CipherFormConfig, CipherFormConfigService, CipherFormMode } from "@bitwarden/vault";
+
+import { BrowserFido2UserInterfaceSession } from "../../../../../autofill/fido2/services/browser-fido2-user-interface.service";
+import BrowserPopupUtils from "../../../../../platform/popup/browser-popup-utils";
+import { PopupRouterCacheService } from "../../../../../platform/popup/view-cache/popup-router-cache.service";
+import { PopupCloseWarningService } from "../../../../../popup/services/popup-close-warning.service";
+
+import { AddEditV2Component } from "./add-edit-v2.component";
+
+// 'qrcode-parser' is used by `BrowserTotpCaptureService` but is an es6 module that jest can't compile.
+// Mock the entire module here to prevent jest from throwing an error. I wasn't able to find a way to mock the
+// `BrowserTotpCaptureService` where jest would not load the file in the first place.
+jest.mock("qrcode-parser", () => {});
+
+describe("AddEditV2Component", () => {
+ let component: AddEditV2Component;
+ let fixture: ComponentFixture;
+
+ const buildConfigResponse = { originalCipher: {} } as CipherFormConfig;
+ const buildConfig = jest.fn((mode: CipherFormMode) =>
+ Promise.resolve({ mode, ...buildConfigResponse }),
+ );
+ const queryParams$ = new BehaviorSubject({});
+ const disable = jest.fn();
+ const navigate = jest.fn();
+ const back = jest.fn().mockResolvedValue(null);
+
+ beforeEach(async () => {
+ buildConfig.mockClear();
+ disable.mockClear();
+ navigate.mockClear();
+ back.mockClear();
+
+ await TestBed.configureTestingModule({
+ imports: [AddEditV2Component],
+ providers: [
+ { provide: PlatformUtilsService, useValue: mock() },
+ { provide: ConfigService, useValue: mock() },
+ { provide: PopupRouterCacheService, useValue: { back } },
+ { provide: PopupCloseWarningService, useValue: { disable } },
+ { provide: Router, useValue: { navigate } },
+ { provide: ActivatedRoute, useValue: { queryParams: queryParams$ } },
+ { provide: I18nService, useValue: { t: (key: string) => key } },
+ ],
+ })
+ .overrideProvider(CipherFormConfigService, {
+ useValue: {
+ buildConfig,
+ },
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(AddEditV2Component);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ describe("query params", () => {
+ describe("mode", () => {
+ it("sets mode to `add` when no `cipherId` is provided", fakeAsync(() => {
+ queryParams$.next({});
+
+ tick();
+
+ expect(buildConfig.mock.lastCall[0]).toBe("add");
+ expect(component.config.mode).toBe("add");
+ }));
+
+ it("sets mode to `edit` when `params.clone` is not provided", fakeAsync(() => {
+ queryParams$.next({ cipherId: "222-333-444-5555", clone: "true" });
+
+ tick();
+
+ expect(buildConfig.mock.lastCall[0]).toBe("clone");
+ expect(component.config.mode).toBe("clone");
+ }));
+
+ it("sets mode to `edit` when `params.clone` is not provided", fakeAsync(() => {
+ buildConfigResponse.originalCipher = { edit: true } as Cipher;
+ queryParams$.next({ cipherId: "222-333-444-5555" });
+
+ tick();
+
+ expect(buildConfig.mock.lastCall[0]).toBe("edit");
+ expect(component.config.mode).toBe("edit");
+ }));
+
+ it("sets mode to `partial-edit` when `config.originalCipher.edit` is false", fakeAsync(() => {
+ buildConfigResponse.originalCipher = { edit: false } as Cipher;
+ queryParams$.next({ cipherId: "222-333-444-5555" });
+
+ tick();
+
+ expect(buildConfig.mock.lastCall[0]).toBe("edit");
+ expect(component.config.mode).toBe("partial-edit");
+ }));
+ });
+ });
+
+ describe("onCipherSaved", () => {
+ it("disables warning when in popout", async () => {
+ jest.spyOn(BrowserPopupUtils, "inPopout").mockReturnValueOnce(true);
+
+ await component.onCipherSaved({ id: "123-456-789" } as CipherView);
+
+ expect(disable).toHaveBeenCalled();
+ });
+
+ it("calls `confirmNewCredentialResponse` when in fido2 popout", async () => {
+ // @ts-expect-error - `inFido2PopoutWindow` is a private getter, mock the response here
+ // for the test rather than setting up the dependencies.
+ jest.spyOn(component, "inFido2PopoutWindow", "get").mockReturnValueOnce(true);
+
+ await component.onCipherSaved({ id: "123-456-789" } as CipherView);
+
+ expect(BrowserPopupUtils.inPopout).toHaveBeenCalled();
+ expect(navigate).not.toHaveBeenCalled();
+ });
+
+ it("closes single action popout", async () => {
+ jest.spyOn(BrowserPopupUtils, "inSingleActionPopout").mockReturnValueOnce(true);
+ jest.spyOn(BrowserPopupUtils, "closeSingleActionPopout").mockResolvedValue();
+
+ await component.onCipherSaved({ id: "123-456-789" } as CipherView);
+
+ expect(BrowserPopupUtils.closeSingleActionPopout).toHaveBeenCalled();
+ expect(navigate).not.toHaveBeenCalled();
+ });
+
+ it("navigates to view-cipher for new ciphers", async () => {
+ component.config.mode = "add";
+
+ await component.onCipherSaved({ id: "123-456-789" } as CipherView);
+
+ expect(navigate).toHaveBeenCalledWith(["/view-cipher"], {
+ replaceUrl: true,
+ queryParams: { cipherId: "123-456-789" },
+ });
+ expect(back).not.toHaveBeenCalled();
+ });
+
+ it("navigates to view-cipher for edit ciphers", async () => {
+ component.config.mode = "edit";
+
+ await component.onCipherSaved({ id: "123-456-789" } as CipherView);
+
+ expect(navigate).not.toHaveBeenCalled();
+ expect(back).toHaveBeenCalled();
+ });
+ });
+
+ describe("handleBackButton", () => {
+ it("disables warning and aborts fido2 popout", async () => {
+ // @ts-expect-error - `inFido2PopoutWindow` is a private getter, mock the response here
+ // for the test rather than setting up the dependencies.
+ jest.spyOn(component, "inFido2PopoutWindow", "get").mockReturnValueOnce(true);
+ jest.spyOn(BrowserFido2UserInterfaceSession, "abortPopout");
+
+ await component.handleBackButton();
+
+ expect(disable).toHaveBeenCalled();
+ expect(BrowserFido2UserInterfaceSession.abortPopout).toHaveBeenCalled();
+ expect(back).not.toHaveBeenCalled();
+ });
+
+ it("closes single action popout", async () => {
+ jest.spyOn(BrowserPopupUtils, "inSingleActionPopout").mockReturnValueOnce(true);
+ jest.spyOn(BrowserPopupUtils, "closeSingleActionPopout").mockResolvedValue();
+
+ await component.handleBackButton();
+
+ expect(BrowserPopupUtils.closeSingleActionPopout).toHaveBeenCalled();
+ expect(back).not.toHaveBeenCalled();
+ });
+
+ it("navigates the user backwards", async () => {
+ await component.handleBackButton();
+
+ expect(back).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts
index 6cf2ba0e7d..b830ae7504 100644
--- a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts
+++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts
@@ -151,10 +151,10 @@ export class AddEditV2Component implements OnInit {
constructor(
private route: ActivatedRoute,
- private popupRouterCacheService: PopupRouterCacheService,
private i18nService: I18nService,
private addEditFormConfigService: CipherFormConfigService,
private popupCloseWarningService: PopupCloseWarningService,
+ private popupRouterCacheService: PopupRouterCacheService,
private router: Router,
) {
this.subscribeToParams();
@@ -183,11 +183,7 @@ export class AddEditV2Component implements OnInit {
};
/**
- * Navigates to previous view or view-cipher path
- * depending on the history length.
- *
- * This can happen when history is lost due to the extension being
- * forced into a popout window.
+ * Handle back button
*/
async handleBackButton() {
if (this.inFido2PopoutWindow) {
@@ -223,10 +219,18 @@ export class AddEditV2Component implements OnInit {
return;
}
- await this.router.navigate(["/view-cipher"], {
- replaceUrl: true,
- queryParams: { cipherId: cipher.id },
- });
+ // When the cipher is in edit / partial edit, the previous page was the view-cipher page.
+ // In the case of creating a new cipher, the user should go view-cipher page but we need to also
+ // remove it from the history stack. This avoids the user having to click back twice on the
+ // view-cipher page.
+ if (this.config.mode === "edit" || this.config.mode === "partial-edit") {
+ await this.popupRouterCacheService.back();
+ } else {
+ await this.router.navigate(["/view-cipher"], {
+ replaceUrl: true,
+ queryParams: { cipherId: cipher.id },
+ });
+ }
}
subscribeToParams(): void {
diff --git a/apps/desktop/src/auth/set-password.component.ts b/apps/desktop/src/auth/set-password.component.ts
index 82ad60724a..f14434b277 100644
--- a/apps/desktop/src/auth/set-password.component.ts
+++ b/apps/desktop/src/auth/set-password.component.ts
@@ -14,6 +14,7 @@ import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@@ -54,6 +55,7 @@ export class SetPasswordComponent extends BaseSetPasswordComponent implements On
ssoLoginService: SsoLoginServiceAbstraction,
dialogService: DialogService,
kdfConfigService: KdfConfigService,
+ encryptService: EncryptService,
) {
super(
accountService,
@@ -76,6 +78,7 @@ export class SetPasswordComponent extends BaseSetPasswordComponent implements On
ssoLoginService,
dialogService,
kdfConfigService,
+ encryptService,
);
}
diff --git a/apps/web/src/app/app.component.ts b/apps/web/src/app/app.component.ts
index be3bd0860f..828fe8ea3f 100644
--- a/apps/web/src/app/app.component.ts
+++ b/apps/web/src/app/app.component.ts
@@ -2,17 +2,7 @@ import { DOCUMENT } from "@angular/common";
import { Component, Inject, NgZone, OnDestroy, OnInit } from "@angular/core";
import { NavigationEnd, Router } from "@angular/router";
import * as jq from "jquery";
-import {
- Subject,
- combineLatest,
- filter,
- firstValueFrom,
- map,
- switchMap,
- takeUntil,
- timeout,
- timer,
-} from "rxjs";
+import { Subject, filter, firstValueFrom, map, takeUntil, timeout } from "rxjs";
import { LogoutReason } from "@bitwarden/auth/common";
import { EventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service";
@@ -25,8 +15,6 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
-import { PaymentMethodWarningsServiceAbstraction as PaymentMethodWarningService } from "@bitwarden/common/billing/abstractions/payment-method-warnings-service.abstraction";
-import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
@@ -58,7 +46,6 @@ import {
const BroadcasterSubscriptionId = "AppComponent";
const IdleTimeout = 60000 * 10; // 10 minutes
-const PaymentMethodWarningsRefresh = 60000; // 1 Minute
@Component({
selector: "app-root",
@@ -69,7 +56,6 @@ export class AppComponent implements OnDestroy, OnInit {
private idleTimer: number = null;
private isIdle = false;
private destroy$ = new Subject();
- private paymentMethodWarningsRefresh$ = timer(0, PaymentMethodWarningsRefresh);
constructor(
@Inject(DOCUMENT) private document: Document,
@@ -98,7 +84,6 @@ export class AppComponent implements OnDestroy, OnInit {
private dialogService: DialogService,
private biometricStateService: BiometricStateService,
private stateEventRunnerService: StateEventRunnerService,
- private paymentMethodWarningService: PaymentMethodWarningService,
private organizationService: InternalOrganizationServiceAbstraction,
private accountService: AccountService,
) {}
@@ -252,25 +237,6 @@ export class AppComponent implements OnDestroy, OnInit {
new DisableSendPolicy(),
new SendOptionsPolicy(),
]);
-
- combineLatest([
- this.configService.getFeatureFlag$(FeatureFlag.ShowPaymentMethodWarningBanners),
- this.paymentMethodWarningsRefresh$,
- ])
- .pipe(
- filter(([showPaymentMethodWarningBanners]) => showPaymentMethodWarningBanners),
- switchMap(() => this.organizationService.memberOrganizations$),
- switchMap(
- async (organizations) =>
- await Promise.all(
- organizations.map((organization) =>
- this.paymentMethodWarningService.update(organization.id),
- ),
- ),
- ),
- takeUntil(this.destroy$),
- )
- .subscribe();
}
ngOnDestroy() {
@@ -328,7 +294,6 @@ export class AppComponent implements OnDestroy, OnInit {
this.folderService.clear(userId),
this.collectionService.clear(userId),
this.biometricStateService.logout(userId),
- this.paymentMethodWarningService.clear(),
]);
await this.stateEventRunnerService.handleEvent("logout", userId);
diff --git a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts
index 24970ee1ca..7dad7effee 100644
--- a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts
+++ b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts
@@ -14,6 +14,7 @@ import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.res
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
+import { ToastService } from "@bitwarden/components";
import { BillingSharedModule, PaymentComponent, TaxInfoComponent } from "../../shared";
@@ -75,6 +76,7 @@ export class TrialBillingStepComponent implements OnInit {
private messagingService: MessagingService,
private organizationBillingService: OrganizationBillingService,
private platformUtilsService: PlatformUtilsService,
+ private toastService: ToastService,
) {}
async ngOnInit(): Promise {
@@ -96,11 +98,11 @@ export class TrialBillingStepComponent implements OnInit {
const organizationId = await this.formPromise;
const planDescription = this.getPlanDescription();
- this.platformUtilsService.showToast(
- "success",
- this.i18nService.t("organizationCreated"),
- this.i18nService.t("organizationReadyToGo"),
- );
+ this.toastService.showToast({
+ variant: "success",
+ title: this.i18nService.t("organizationCreated"),
+ message: this.i18nService.t("organizationReadyToGo"),
+ });
this.organizationCreated.emit({
organizationId,
diff --git a/apps/web/src/app/billing/individual/premium.component.ts b/apps/web/src/app/billing/individual/premium.component.ts
index b43d3cef34..79a5c5e2ed 100644
--- a/apps/web/src/app/billing/individual/premium.component.ts
+++ b/apps/web/src/app/billing/individual/premium.component.ts
@@ -11,6 +11,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
+import { ToastService } from "@bitwarden/components";
import { PaymentComponent, TaxInfoComponent } from "../shared";
@@ -46,6 +47,7 @@ export class PremiumComponent implements OnInit {
private syncService: SyncService,
private environmentService: EnvironmentService,
private billingAccountProfileStateService: BillingAccountProfileStateService,
+ private toastService: ToastService,
) {
this.selfHosted = platformUtilsService.isSelfHost();
this.canAccessPremium$ = billingAccountProfileStateService.hasPremiumFromAnySource$;
@@ -75,11 +77,11 @@ export class PremiumComponent implements OnInit {
this.addonForm.markAllAsTouched();
if (this.selfHosted) {
if (this.licenseFile == null) {
- this.platformUtilsService.showToast(
- "error",
- this.i18nService.t("errorOccurred"),
- this.i18nService.t("selectFile"),
- );
+ this.toastService.showToast({
+ variant: "error",
+ title: this.i18nService.t("errorOccurred"),
+ message: this.i18nService.t("selectFile"),
+ });
return;
}
}
@@ -87,11 +89,11 @@ export class PremiumComponent implements OnInit {
if (this.selfHosted) {
// eslint-disable-next-line @typescript-eslint/no-misused-promises
if (!this.tokenService.getEmailVerified()) {
- this.platformUtilsService.showToast(
- "error",
- this.i18nService.t("errorOccurred"),
- this.i18nService.t("verifyEmailFirst"),
- );
+ this.toastService.showToast({
+ variant: "error",
+ title: this.i18nService.t("errorOccurred"),
+ message: this.i18nService.t("verifyEmailFirst"),
+ });
return;
}
@@ -130,7 +132,11 @@ export class PremiumComponent implements OnInit {
async finalizePremium() {
await this.apiService.refreshIdentityToken();
await this.syncService.fullSync(true);
- this.platformUtilsService.showToast("success", null, this.i18nService.t("premiumUpdated"));
+ this.toastService.showToast({
+ variant: "success",
+ title: null,
+ message: this.i18nService.t("premiumUpdated"),
+ });
await this.router.navigate(["/settings/subscription/user-subscription"]);
}
diff --git a/apps/web/src/app/billing/individual/user-subscription.component.ts b/apps/web/src/app/billing/individual/user-subscription.component.ts
index 7e564341ca..2d02cbc5bd 100644
--- a/apps/web/src/app/billing/individual/user-subscription.component.ts
+++ b/apps/web/src/app/billing/individual/user-subscription.component.ts
@@ -10,7 +10,7 @@ import { FileDownloadService } from "@bitwarden/common/platform/abstractions/fil
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import { DialogService } from "@bitwarden/components";
+import { DialogService, ToastService } from "@bitwarden/components";
import {
AdjustStorageDialogResult,
@@ -48,6 +48,7 @@ export class UserSubscriptionComponent implements OnInit {
private dialogService: DialogService,
private environmentService: EnvironmentService,
private billingAccountProfileStateService: BillingAccountProfileStateService,
+ private toastService: ToastService,
) {
this.selfHosted = platformUtilsService.isSelfHost();
}
@@ -94,7 +95,11 @@ export class UserSubscriptionComponent implements OnInit {
try {
this.reinstatePromise = this.apiService.postReinstatePremium();
await this.reinstatePromise;
- this.platformUtilsService.showToast("success", null, this.i18nService.t("reinstated"));
+ this.toastService.showToast({
+ variant: "success",
+ title: null,
+ message: this.i18nService.t("reinstated"),
+ });
// 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();
diff --git a/apps/web/src/app/billing/organizations/adjust-subscription.component.ts b/apps/web/src/app/billing/organizations/adjust-subscription.component.ts
index c98a6b97c4..226c92b45e 100644
--- a/apps/web/src/app/billing/organizations/adjust-subscription.component.ts
+++ b/apps/web/src/app/billing/organizations/adjust-subscription.component.ts
@@ -6,6 +6,7 @@ import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-conso
import { OrganizationSubscriptionUpdateRequest } from "@bitwarden/common/billing/models/request/organization-subscription-update.request";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
+import { ToastService } from "@bitwarden/components";
@Component({
selector: "app-adjust-subscription",
@@ -33,6 +34,7 @@ export class AdjustSubscription implements OnInit, OnDestroy {
private platformUtilsService: PlatformUtilsService,
private organizationApiService: OrganizationApiServiceAbstraction,
private formBuilder: FormBuilder,
+ private toastService: ToastService,
) {}
ngOnInit() {
@@ -76,7 +78,11 @@ export class AdjustSubscription implements OnInit, OnDestroy {
);
await this.organizationApiService.updatePasswordManagerSeats(this.organizationId, request);
- this.platformUtilsService.showToast("success", null, this.i18nService.t("subscriptionUpdated"));
+ this.toastService.showToast({
+ variant: "success",
+ title: null,
+ message: this.i18nService.t("subscriptionUpdated"),
+ });
this.onAdjusted.emit();
};
diff --git a/apps/web/src/app/billing/organizations/billing-sync-api-key.component.ts b/apps/web/src/app/billing/organizations/billing-sync-api-key.component.ts
index 95a29229cf..deb2c9da3e 100644
--- a/apps/web/src/app/billing/organizations/billing-sync-api-key.component.ts
+++ b/apps/web/src/app/billing/organizations/billing-sync-api-key.component.ts
@@ -12,7 +12,7 @@ import { Verification } from "@bitwarden/common/auth/types/verification";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import { DialogService } from "@bitwarden/components";
+import { DialogService, ToastService } from "@bitwarden/components";
export interface BillingSyncApiModalData {
organizationId: string;
@@ -43,6 +43,7 @@ export class BillingSyncApiKeyComponent {
private i18nService: I18nService,
private organizationApiService: OrganizationApiServiceAbstraction,
private logService: LogService,
+ private toastService: ToastService,
) {
this.organizationId = data.organizationId;
this.hasBillingToken = data.hasBillingToken;
@@ -67,11 +68,11 @@ export class BillingSyncApiKeyComponent {
});
await this.load(response);
this.showRotateScreen = false;
- this.platformUtilsService.showToast(
- "success",
- null,
- this.i18nService.t("billingSyncApiKeyRotated"),
- );
+ this.toastService.showToast({
+ variant: "success",
+ title: null,
+ message: this.i18nService.t("billingSyncApiKeyRotated"),
+ });
} else {
const response = await request.then((request) => {
return this.organizationApiService.getOrCreateApiKey(this.organizationId, request);
diff --git a/apps/web/src/app/billing/organizations/organization-plans.component.ts b/apps/web/src/app/billing/organizations/organization-plans.component.ts
index 995dcb2389..fe1c1568a9 100644
--- a/apps/web/src/app/billing/organizations/organization-plans.component.ts
+++ b/apps/web/src/app/billing/organizations/organization-plans.component.ts
@@ -37,6 +37,7 @@ import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { OrgKey } from "@bitwarden/common/types/key";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
+import { ToastService } from "@bitwarden/components";
import { OrganizationCreateModule } from "../../admin-console/organizations/create/organization-create.module";
import { BillingSharedModule, secretsManagerSubscribeFormFactory } from "../shared";
@@ -150,6 +151,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
private formBuilder: FormBuilder,
private organizationApiService: OrganizationApiServiceAbstraction,
private providerApiService: ProviderApiServiceAbstraction,
+ private toastService: ToastService,
) {
this.selfHosted = platformUtilsService.isSelfHost();
}
@@ -582,18 +584,18 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
orgId = await this.createCloudHosted(key, collectionCt, orgKeys, orgKey[1]);
}
- this.platformUtilsService.showToast(
- "success",
- this.i18nService.t("organizationCreated"),
- this.i18nService.t("organizationReadyToGo"),
- );
+ this.toastService.showToast({
+ variant: "success",
+ title: this.i18nService.t("organizationCreated"),
+ message: this.i18nService.t("organizationReadyToGo"),
+ });
} else {
orgId = await this.updateOrganization(orgId);
- this.platformUtilsService.showToast(
- "success",
- null,
- this.i18nService.t("organizationUpgraded"),
- );
+ this.toastService.showToast({
+ variant: "success",
+ title: null,
+ message: this.i18nService.t("organizationUpgraded"),
+ });
}
await this.apiService.refreshIdentityToken();
diff --git a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts
index c5ed013b1e..b8616ae1b4 100644
--- a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts
+++ b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts
@@ -16,7 +16,7 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import { DialogService } from "@bitwarden/components";
+import { DialogService, ToastService } from "@bitwarden/components";
import {
AdjustStorageDialogResult,
@@ -82,6 +82,7 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
private dialogService: DialogService,
private configService: ConfigService,
private providerService: ProviderService,
+ private toastService: ToastService,
) {}
async ngOnInit() {
@@ -378,7 +379,11 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
try {
await this.organizationApiService.reinstate(this.organizationId);
- this.platformUtilsService.showToast("success", null, this.i18nService.t("reinstated"));
+ this.toastService.showToast({
+ variant: "success",
+ title: null,
+ message: this.i18nService.t("reinstated"),
+ });
// 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();
@@ -475,11 +480,11 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
try {
await this.apiService.deleteRemoveSponsorship(this.organizationId);
- this.platformUtilsService.showToast(
- "success",
- null,
- this.i18nService.t("removeSponsorshipSuccess"),
- );
+ this.toastService.showToast({
+ variant: "success",
+ title: null,
+ message: this.i18nService.t("removeSponsorshipSuccess"),
+ });
await this.load();
} catch (e) {
this.logService.error(e);
diff --git a/apps/web/src/app/billing/organizations/organization-subscription-selfhost.component.ts b/apps/web/src/app/billing/organizations/organization-subscription-selfhost.component.ts
index f2884a4fd0..3d2aef6875 100644
--- a/apps/web/src/app/billing/organizations/organization-subscription-selfhost.component.ts
+++ b/apps/web/src/app/billing/organizations/organization-subscription-selfhost.component.ts
@@ -16,7 +16,7 @@ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/envi
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import { DialogService } from "@bitwarden/components";
+import { DialogService, ToastService } from "@bitwarden/components";
import { BillingSyncKeyComponent } from "./billing-sync-key.component";
@@ -84,6 +84,7 @@ export class OrganizationSubscriptionSelfhostComponent implements OnInit, OnDest
private i18nService: I18nService,
private environmentService: EnvironmentService,
private dialogService: DialogService,
+ private toastService: ToastService,
) {}
async ngOnInit() {
@@ -169,7 +170,11 @@ export class OrganizationSubscriptionSelfhostComponent implements OnInit, OnDest
this.load();
await this.loadOrganizationConnection();
this.messagingService.send("updatedOrgLicense");
- this.platformUtilsService.showToast("success", null, this.i18nService.t("licenseSyncSuccess"));
+ this.toastService.showToast({
+ variant: "success",
+ title: null,
+ message: this.i18nService.t("licenseSyncSuccess"),
+ });
};
get billingSyncSetUp() {
diff --git a/apps/web/src/app/billing/organizations/sm-adjust-subscription.component.ts b/apps/web/src/app/billing/organizations/sm-adjust-subscription.component.ts
index 50abcc92ba..bc8694a505 100644
--- a/apps/web/src/app/billing/organizations/sm-adjust-subscription.component.ts
+++ b/apps/web/src/app/billing/organizations/sm-adjust-subscription.component.ts
@@ -6,6 +6,7 @@ import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-conso
import { OrganizationSmSubscriptionUpdateRequest } from "@bitwarden/common/billing/models/request/organization-sm-subscription-update.request";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
+import { ToastService } from "@bitwarden/components";
export interface SecretsManagerSubscriptionOptions {
interval: "year" | "month";
@@ -100,6 +101,7 @@ export class SecretsManagerAdjustSubscriptionComponent implements OnInit, OnDest
private organizationApiService: OrganizationApiServiceAbstraction,
private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService,
+ private toastService: ToastService,
) {}
ngOnInit() {
@@ -158,11 +160,11 @@ export class SecretsManagerAdjustSubscriptionComponent implements OnInit, OnDest
request,
);
- await this.platformUtilsService.showToast(
- "success",
- null,
- this.i18nService.t("subscriptionUpdated"),
- );
+ this.toastService.showToast({
+ variant: "success",
+ title: null,
+ message: this.i18nService.t("subscriptionUpdated"),
+ });
this.onAdjusted.emit();
};
diff --git a/apps/web/src/app/billing/organizations/sm-subscribe-standalone.component.ts b/apps/web/src/app/billing/organizations/sm-subscribe-standalone.component.ts
index 1f8b70e03f..aae799d808 100644
--- a/apps/web/src/app/billing/organizations/sm-subscribe-standalone.component.ts
+++ b/apps/web/src/app/billing/organizations/sm-subscribe-standalone.component.ts
@@ -11,6 +11,7 @@ import { BillingCustomerDiscount } from "@bitwarden/common/billing/models/respon
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
+import { ToastService } from "@bitwarden/components";
import { secretsManagerSubscribeFormFactory } from "../shared";
@@ -33,6 +34,7 @@ export class SecretsManagerSubscribeStandaloneComponent {
private i18nService: I18nService,
private organizationApiService: OrganizationApiServiceAbstraction,
private organizationService: InternalOrganizationServiceAbstraction,
+ private toastService: ToastService,
) {}
submit = async () => {
@@ -60,11 +62,11 @@ export class SecretsManagerSubscribeStandaloneComponent {
*/
await this.apiService.refreshIdentityToken();
- this.platformUtilsService.showToast(
- "success",
- null,
- this.i18nService.t("subscribedToSecretsManager"),
- );
+ this.toastService.showToast({
+ variant: "success",
+ title: null,
+ message: this.i18nService.t("subscribedToSecretsManager"),
+ });
this.onSubscribe.emit();
};
diff --git a/apps/web/src/app/billing/settings/sponsored-families.component.ts b/apps/web/src/app/billing/settings/sponsored-families.component.ts
index 117f42fe39..c098b6044c 100644
--- a/apps/web/src/app/billing/settings/sponsored-families.component.ts
+++ b/apps/web/src/app/billing/settings/sponsored-families.component.ts
@@ -18,6 +18,7 @@ import { PlanSponsorshipType } from "@bitwarden/common/billing/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
+import { ToastService } from "@bitwarden/components";
interface RequestSponsorshipForm {
selectedSponsorshipOrgId: FormControl;
@@ -51,6 +52,7 @@ export class SponsoredFamiliesComponent implements OnInit, OnDestroy {
private organizationService: OrganizationService,
private formBuilder: FormBuilder,
private accountService: AccountService,
+ private toastService: ToastService,
) {
this.sponsorshipForm = this.formBuilder.group({
selectedSponsorshipOrgId: new FormControl("", {
@@ -118,7 +120,11 @@ export class SponsoredFamiliesComponent implements OnInit, OnDestroy {
);
await this.formPromise;
- this.platformUtilsService.showToast("success", null, this.i18nService.t("sponsorshipCreated"));
+ this.toastService.showToast({
+ variant: "success",
+ title: null,
+ message: this.i18nService.t("sponsorshipCreated"),
+ });
this.formPromise = null;
// 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
diff --git a/apps/web/src/app/billing/settings/sponsoring-org-row.component.ts b/apps/web/src/app/billing/settings/sponsoring-org-row.component.ts
index eff75b61b3..06dc1490e3 100644
--- a/apps/web/src/app/billing/settings/sponsoring-org-row.component.ts
+++ b/apps/web/src/app/billing/settings/sponsoring-org-row.component.ts
@@ -7,7 +7,7 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import { DialogService } from "@bitwarden/components";
+import { DialogService, ToastService } from "@bitwarden/components";
@Component({
selector: "[sponsoring-org-row]",
@@ -30,6 +30,7 @@ export class SponsoringOrgRowComponent implements OnInit {
private logService: LogService,
private platformUtilsService: PlatformUtilsService,
private dialogService: DialogService,
+ private toastService: ToastService,
) {}
async ngOnInit() {
@@ -53,7 +54,11 @@ export class SponsoringOrgRowComponent implements OnInit {
async resendEmail() {
await this.apiService.postResendSponsorshipOffer(this.sponsoringOrg.id);
- this.platformUtilsService.showToast("success", null, this.i18nService.t("emailSent"));
+ this.toastService.showToast({
+ variant: "success",
+ title: null,
+ message: this.i18nService.t("emailSent"),
+ });
}
get isSentAwaitingSync() {
@@ -73,7 +78,11 @@ export class SponsoringOrgRowComponent implements OnInit {
}
await this.apiService.deleteRevokeSponsorship(this.sponsoringOrg.id);
- this.platformUtilsService.showToast("success", null, this.i18nService.t("reclaimedFreePlan"));
+ this.toastService.showToast({
+ variant: "success",
+ title: null,
+ message: this.i18nService.t("reclaimedFreePlan"),
+ });
this.sponsorshipRemoved.emit();
}
diff --git a/apps/web/src/app/billing/shared/adjust-payment-dialog.component.ts b/apps/web/src/app/billing/shared/adjust-payment-dialog.component.ts
index 1addf42629..08c9bf2766 100644
--- a/apps/web/src/app/billing/shared/adjust-payment-dialog.component.ts
+++ b/apps/web/src/app/billing/shared/adjust-payment-dialog.component.ts
@@ -1,14 +1,11 @@
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
import { Component, Inject, ViewChild } from "@angular/core";
import { FormGroup } from "@angular/forms";
-import { firstValueFrom } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
-import { PaymentMethodWarningsServiceAbstraction as PaymentMethodWarningService } from "@bitwarden/common/billing/abstractions/payment-method-warnings-service.abstraction";
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
import { PaymentRequest } from "@bitwarden/common/billing/models/request/payment.request";
-import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { DialogService, ToastService } from "@bitwarden/components";
@@ -46,7 +43,6 @@ export class AdjustPaymentDialogComponent {
private apiService: ApiService,
private i18nService: I18nService,
private organizationApiService: OrganizationApiServiceAbstraction,
- private paymentMethodWarningService: PaymentMethodWarningService,
private configService: ConfigService,
private toastService: ToastService,
) {
@@ -78,12 +74,6 @@ export class AdjustPaymentDialogComponent {
}
});
await response;
- const showPaymentMethodWarningBanners = await firstValueFrom(
- this.configService.getFeatureFlag$(FeatureFlag.ShowPaymentMethodWarningBanners),
- );
- if (this.organizationId && showPaymentMethodWarningBanners) {
- await this.paymentMethodWarningService.removeSubscriptionRisk(this.organizationId);
- }
this.toastService.showToast({
variant: "success",
title: null,
diff --git a/apps/web/src/app/billing/shared/adjust-storage.component.ts b/apps/web/src/app/billing/shared/adjust-storage.component.ts
index fcdbc3437d..439bfec82a 100644
--- a/apps/web/src/app/billing/shared/adjust-storage.component.ts
+++ b/apps/web/src/app/billing/shared/adjust-storage.component.ts
@@ -10,7 +10,7 @@ import { StorageRequest } from "@bitwarden/common/models/request/storage.request
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import { DialogService } from "@bitwarden/components";
+import { DialogService, ToastService } from "@bitwarden/components";
import { PaymentComponent } from "./payment.component";
@@ -56,6 +56,7 @@ export class AdjustStorageComponent {
private activatedRoute: ActivatedRoute,
private logService: LogService,
private organizationApiService: OrganizationApiServiceAbstraction,
+ private toastService: ToastService,
) {
this.storageGbPrice = data.storageGbPrice;
this.add = data.add;
@@ -93,21 +94,21 @@ export class AdjustStorageComponent {
await action();
this.dialogRef.close(AdjustStorageDialogResult.Adjusted);
if (paymentFailed) {
- this.platformUtilsService.showToast(
- "warning",
- null,
- this.i18nService.t("couldNotChargeCardPayInvoice"),
- { timeout: 10000 },
- );
+ this.toastService.showToast({
+ variant: "warning",
+ title: null,
+ message: this.i18nService.t("couldNotChargeCardPayInvoice"),
+ timeout: 10000,
+ });
// 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.router.navigate(["../billing"], { relativeTo: this.activatedRoute });
} else {
- this.platformUtilsService.showToast(
- "success",
- null,
- this.i18nService.t("adjustedStorage", request.storageGbAdjustment.toString()),
- );
+ this.toastService.showToast({
+ variant: "success",
+ title: null,
+ message: this.i18nService.t("adjustedStorage", request.storageGbAdjustment.toString()),
+ });
}
};
diff --git a/apps/web/src/app/billing/shared/index.ts b/apps/web/src/app/billing/shared/index.ts
index edaf3a1199..ae28e45f78 100644
--- a/apps/web/src/app/billing/shared/index.ts
+++ b/apps/web/src/app/billing/shared/index.ts
@@ -3,4 +3,3 @@ export * from "./payment-method.component";
export * from "./payment.component";
export * from "./sm-subscribe.component";
export * from "./tax-info.component";
-export * from "./payment-method-warnings/payment-method-warnings.module";
diff --git a/apps/web/src/app/billing/shared/offboarding-survey.component.ts b/apps/web/src/app/billing/shared/offboarding-survey.component.ts
index 73a460f8c8..7ffd40e058 100644
--- a/apps/web/src/app/billing/shared/offboarding-survey.component.ts
+++ b/apps/web/src/app/billing/shared/offboarding-survey.component.ts
@@ -5,7 +5,7 @@ import { FormBuilder, Validators } from "@angular/forms";
import { BillingApiServiceAbstraction as BillingApiService } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import { DialogService } from "@bitwarden/components";
+import { DialogService, ToastService } from "@bitwarden/components";
type UserOffboardingParams = {
type: "User";
@@ -88,6 +88,7 @@ export class OffboardingSurveyComponent {
private billingApiService: BillingApiService,
private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService,
+ private toastService: ToastService,
) {}
submit = async () => {
@@ -106,11 +107,11 @@ export class OffboardingSurveyComponent {
? await this.billingApiService.cancelOrganizationSubscription(this.dialogParams.id, request)
: await this.billingApiService.cancelPremiumUserSubscription(request);
- this.platformUtilsService.showToast(
- "success",
- null,
- this.i18nService.t("canceledSubscription"),
- );
+ this.toastService.showToast({
+ variant: "success",
+ title: null,
+ message: this.i18nService.t("canceledSubscription"),
+ });
this.dialogRef.close(this.ResultType.Submitted);
};
diff --git a/apps/web/src/app/billing/shared/payment-method-warnings/payment-method-warnings.component.html b/apps/web/src/app/billing/shared/payment-method-warnings/payment-method-warnings.component.html
deleted file mode 100644
index 59dbc5f976..0000000000
--- a/apps/web/src/app/billing/shared/payment-method-warnings/payment-method-warnings.component.html
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
- {{ "maintainYourSubscription" | i18n: warning.organizationName }}
- {{ "addAPaymentMethod" | i18n }}.
-
-
diff --git a/apps/web/src/app/billing/shared/payment-method-warnings/payment-method-warnings.component.ts b/apps/web/src/app/billing/shared/payment-method-warnings/payment-method-warnings.component.ts
deleted file mode 100644
index d811961c0d..0000000000
--- a/apps/web/src/app/billing/shared/payment-method-warnings/payment-method-warnings.component.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { Component } from "@angular/core";
-import { map, Observable } from "rxjs";
-
-import { PaymentMethodWarningsServiceAbstraction as PaymentMethodWarningService } from "@bitwarden/common/billing/abstractions/payment-method-warnings-service.abstraction";
-
-type Warning = {
- organizationId: string;
- organizationName: string;
-};
-
-@Component({
- selector: "app-payment-method-warnings",
- templateUrl: "payment-method-warnings.component.html",
-})
-export class PaymentMethodWarningsComponent {
- constructor(private paymentMethodWarningService: PaymentMethodWarningService) {}
-
- protected warnings$: Observable =
- this.paymentMethodWarningService.paymentMethodWarnings$.pipe(
- map((warnings) =>
- Object.entries(warnings ?? [])
- .filter(([_, warning]) => warning.risksSubscriptionFailure && !warning.acknowledged)
- .map(([organizationId, { organizationName }]) => ({
- organizationId,
- organizationName,
- })),
- ),
- );
-
- protected async closeWarning(organizationId: string): Promise {
- await this.paymentMethodWarningService.acknowledge(organizationId);
- }
-}
diff --git a/apps/web/src/app/billing/shared/payment-method-warnings/payment-method-warnings.module.ts b/apps/web/src/app/billing/shared/payment-method-warnings/payment-method-warnings.module.ts
deleted file mode 100644
index c6303c878c..0000000000
--- a/apps/web/src/app/billing/shared/payment-method-warnings/payment-method-warnings.module.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { NgModule } from "@angular/core";
-
-import { BannerModule } from "@bitwarden/components";
-
-import { SharedModule } from "../../../shared";
-
-import { PaymentMethodWarningsComponent } from "./payment-method-warnings.component";
-
-@NgModule({
- imports: [BannerModule, SharedModule],
- declarations: [PaymentMethodWarningsComponent],
- exports: [PaymentMethodWarningsComponent],
-})
-export class PaymentMethodWarningsModule {}
diff --git a/apps/web/src/app/billing/shared/payment-method.component.ts b/apps/web/src/app/billing/shared/payment-method.component.ts
index eacc0b4739..0c089fa073 100644
--- a/apps/web/src/app/billing/shared/payment-method.component.ts
+++ b/apps/web/src/app/billing/shared/payment-method.component.ts
@@ -13,7 +13,7 @@ import { VerifyBankRequest } from "@bitwarden/common/models/request/verify-bank.
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import { DialogService } from "@bitwarden/components";
+import { DialogService, ToastService } from "@bitwarden/components";
import { AddCreditDialogResult, openAddCreditDialog } from "./add-credit-dialog.component";
import {
@@ -63,6 +63,7 @@ export class PaymentMethodComponent implements OnInit {
private route: ActivatedRoute,
private formBuilder: FormBuilder,
private dialogService: DialogService,
+ private toastService: ToastService,
) {}
async ngOnInit() {
@@ -144,13 +145,21 @@ export class PaymentMethodComponent implements OnInit {
request.amount1 = this.verifyBankForm.value.amount1;
request.amount2 = this.verifyBankForm.value.amount2;
await this.organizationApiService.verifyBank(this.organizationId, request);
- this.platformUtilsService.showToast("success", null, this.i18nService.t("verifiedBankAccount"));
+ this.toastService.showToast({
+ variant: "success",
+ title: null,
+ message: this.i18nService.t("verifiedBankAccount"),
+ });
await this.load();
};
submitTaxInfo = async () => {
await this.taxInfo.submitTaxInfo();
- this.platformUtilsService.showToast("success", null, this.i18nService.t("taxInfoUpdated"));
+ this.toastService.showToast({
+ variant: "success",
+ title: null,
+ message: this.i18nService.t("taxInfoUpdated"),
+ });
};
get isCreditBalance() {
diff --git a/apps/web/src/app/billing/shared/tax-info.component.html b/apps/web/src/app/billing/shared/tax-info.component.html
index c254ffa4a4..89bc7438a7 100644
--- a/apps/web/src/app/billing/shared/tax-info.component.html
+++ b/apps/web/src/app/billing/shared/tax-info.component.html
@@ -13,7 +13,7 @@
-
+
{{ "zipPostalCode" | i18n }}
diff --git a/apps/web/src/app/billing/shared/update-license-dialog.component.ts b/apps/web/src/app/billing/shared/update-license-dialog.component.ts
index fd9acfe786..b663445028 100644
--- a/apps/web/src/app/billing/shared/update-license-dialog.component.ts
+++ b/apps/web/src/app/billing/shared/update-license-dialog.component.ts
@@ -6,7 +6,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import { DialogService } from "@bitwarden/components";
+import { DialogService, ToastService } from "@bitwarden/components";
import { UpdateLicenseDialogResult } from "./update-license-types";
import { UpdateLicenseComponent } from "./update-license.component";
@@ -22,8 +22,16 @@ export class UpdateLicenseDialogComponent extends UpdateLicenseComponent {
platformUtilsService: PlatformUtilsService,
organizationApiService: OrganizationApiServiceAbstraction,
formBuilder: FormBuilder,
+ toastService: ToastService,
) {
- super(apiService, i18nService, platformUtilsService, organizationApiService, formBuilder);
+ super(
+ apiService,
+ i18nService,
+ platformUtilsService,
+ organizationApiService,
+ formBuilder,
+ toastService,
+ );
}
async submitLicense() {
const result = await this.submit();
diff --git a/apps/web/src/app/billing/shared/update-license.component.ts b/apps/web/src/app/billing/shared/update-license.component.ts
index 8dbb83c510..e542177684 100644
--- a/apps/web/src/app/billing/shared/update-license.component.ts
+++ b/apps/web/src/app/billing/shared/update-license.component.ts
@@ -6,6 +6,7 @@ import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-conso
import { ProductTierType } from "@bitwarden/common/billing/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
+import { ToastService } from "@bitwarden/components";
import { UpdateLicenseDialogResult } from "./update-license-types";
@@ -32,6 +33,7 @@ export class UpdateLicenseComponent implements OnInit {
private platformUtilsService: PlatformUtilsService,
private organizationApiService: OrganizationApiServiceAbstraction,
private formBuilder: FormBuilder,
+ private toastService: ToastService,
) {}
async ngOnInit() {
const org = await this.organizationApiService.get(this.organizationId);
@@ -52,11 +54,11 @@ export class UpdateLicenseComponent implements OnInit {
}
const files = this.licenseFile;
if (files == null) {
- this.platformUtilsService.showToast(
- "error",
- this.i18nService.t("errorOccurred"),
- this.i18nService.t("selectFile"),
- );
+ this.toastService.showToast({
+ variant: "error",
+ title: this.i18nService.t("errorOccurred"),
+ message: this.i18nService.t("selectFile"),
+ });
return;
}
const fd = new FormData();
@@ -74,11 +76,11 @@ export class UpdateLicenseComponent implements OnInit {
});
await this.formPromise;
- this.platformUtilsService.showToast(
- "success",
- null,
- this.i18nService.t("licenseUploadSuccess"),
- );
+ this.toastService.showToast({
+ variant: "success",
+ title: null,
+ message: this.i18nService.t("licenseUploadSuccess"),
+ });
this.onUpdated.emit();
return new Promise((resolve) => resolve(UpdateLicenseDialogResult.Updated));
};
diff --git a/apps/web/src/app/layouts/web-layout.component.html b/apps/web/src/app/layouts/web-layout.component.html
index 31a5e82688..fc318a6398 100644
--- a/apps/web/src/app/layouts/web-layout.component.html
+++ b/apps/web/src/app/layouts/web-layout.component.html
@@ -1,9 +1,4 @@
-
-
-
diff --git a/apps/web/src/app/layouts/web-layout.component.ts b/apps/web/src/app/layouts/web-layout.component.ts
index bb91e619aa..840beaa217 100644
--- a/apps/web/src/app/layouts/web-layout.component.ts
+++ b/apps/web/src/app/layouts/web-layout.component.ts
@@ -1,12 +1,8 @@
import { CommonModule } from "@angular/common";
import { Component } from "@angular/core";
-import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
-import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { LayoutComponent } from "@bitwarden/components";
-import { PaymentMethodWarningsModule } from "../billing/shared";
-
import { ProductSwitcherModule } from "./product-switcher/product-switcher.module";
import { ToggleWidthComponent } from "./toggle-width.component";
@@ -14,18 +10,8 @@ import { ToggleWidthComponent } from "./toggle-width.component";
selector: "app-layout",
templateUrl: "web-layout.component.html",
standalone: true,
- imports: [
- CommonModule,
- LayoutComponent,
- ProductSwitcherModule,
- ToggleWidthComponent,
- PaymentMethodWarningsModule,
- ],
+ imports: [CommonModule, LayoutComponent, ProductSwitcherModule, ToggleWidthComponent],
})
export class WebLayoutComponent {
- protected showPaymentMethodWarningBanners$ = this.configService.getFeatureFlag$(
- FeatureFlag.ShowPaymentMethodWarningBanners,
- );
-
- constructor(private configService: ConfigService) {}
+ constructor() {}
}
diff --git a/apps/web/src/app/shared/loose-components.module.ts b/apps/web/src/app/shared/loose-components.module.ts
index 1a1daefb44..8e49c95e11 100644
--- a/apps/web/src/app/shared/loose-components.module.ts
+++ b/apps/web/src/app/shared/loose-components.module.ts
@@ -61,7 +61,6 @@ import { VerifyEmailTokenComponent } from "../auth/verify-email-token.component"
import { VerifyRecoverDeleteComponent } from "../auth/verify-recover-delete.component";
import { SponsoredFamiliesComponent } from "../billing/settings/sponsored-families.component";
import { SponsoringOrgRowComponent } from "../billing/settings/sponsoring-org-row.component";
-import { PaymentMethodWarningsModule } from "../billing/shared";
import { DynamicAvatarComponent } from "../components/dynamic-avatar.component";
import { SelectableAvatarComponent } from "../components/selectable-avatar.component";
import { FrontendLayoutComponent } from "../layouts/frontend-layout.component";
@@ -113,7 +112,6 @@ import { SharedModule } from "./shared.module";
HeaderModule,
OrganizationLayoutComponent,
UserLayoutComponent,
- PaymentMethodWarningsModule,
VerifyRecoverDeleteOrgComponent,
VaultTimeoutInputComponent,
],
diff --git a/apps/web/src/app/vault/individual-vault/add-edit.component.html b/apps/web/src/app/vault/individual-vault/add-edit.component.html
index 6313689007..b5a53bd143 100644
--- a/apps/web/src/app/vault/individual-vault/add-edit.component.html
+++ b/apps/web/src/app/vault/individual-vault/add-edit.component.html
@@ -23,6 +23,9 @@
{{ "personalOwnershipPolicyInEffect" | i18n }}
+
+ {{ "cardExpiredMessage" | i18n }}
+
diff --git a/apps/web/src/app/vault/individual-vault/add-edit.component.ts b/apps/web/src/app/vault/individual-vault/add-edit.component.ts
index 7a96bff039..71ccaab7dd 100644
--- a/apps/web/src/app/vault/individual-vault/add-edit.component.ts
+++ b/apps/web/src/app/vault/individual-vault/add-edit.component.ts
@@ -11,6 +11,7 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { ProductTierType } from "@bitwarden/common/billing/enums";
import { EventType } from "@bitwarden/common/enums";
+import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -23,6 +24,7 @@ import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folde
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
import { CipherType } from "@bitwarden/common/vault/enums";
import { Launchable } from "@bitwarden/common/vault/interfaces/launchable";
+import { CardView } from "@bitwarden/common/vault/models/view/card.view";
import { DialogService } from "@bitwarden/components";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
import { PasswordRepromptService } from "@bitwarden/vault";
@@ -43,6 +45,7 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On
viewingPasswordHistory = false;
viewOnly = false;
showPasswordCount = false;
+ cardIsExpired: boolean = false;
protected totpInterval: number;
protected override componentName = "app-vault-add-edit";
@@ -115,6 +118,12 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On
await this.totpTick(interval);
}, 1000);
}
+
+ const extensionRefreshEnabled = await firstValueFrom(
+ this.configService.getFeatureFlag$(FeatureFlag.ExtensionRefresh),
+ );
+
+ this.cardIsExpired = extensionRefreshEnabled && this.isCardExpiryInThePast();
}
ngOnDestroy() {
@@ -226,6 +235,24 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On
this.viewingPasswordHistory = !this.viewingPasswordHistory;
}
+ isCardExpiryInThePast() {
+ if (this.cipher.card) {
+ const { expMonth, expYear }: CardView = this.cipher.card;
+
+ if (expYear && expMonth) {
+ // `Date` months are zero-indexed
+ const parsedMonth = parseInt(expMonth) - 1;
+ const parsedYear = parseInt(expYear);
+
+ // First day of the next month minus one, to get last day of the card month
+ const cardExpiry = new Date(parsedYear, parsedMonth + 1, 0);
+ const now = new Date();
+
+ return cardExpiry < now;
+ }
+ }
+ }
+
protected cleanUp() {
if (this.totpInterval) {
window.clearInterval(this.totpInterval);
diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json
index f7899eae3a..108807b153 100644
--- a/apps/web/src/locales/en/messages.json
+++ b/apps/web/src/locales/en/messages.json
@@ -194,6 +194,12 @@
"dr": {
"message": "Dr"
},
+ "cardExpiredTitle": {
+ "message": "Expired card"
+ },
+ "cardExpiredMessage": {
+ "message": "If you've renewed it, update the card's information"
+ },
"expirationMonth": {
"message": "Expiration month"
},
diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts
index 9733e91be7..37ef317360 100644
--- a/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts
+++ b/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts
@@ -7,7 +7,6 @@ import { JslibModule } from "@bitwarden/angular/jslib.module";
import { SearchModule } from "@bitwarden/components";
import { DangerZoneComponent } from "@bitwarden/web-vault/app/auth/settings/account/danger-zone.component";
import { OrganizationPlansComponent, TaxInfoComponent } from "@bitwarden/web-vault/app/billing";
-import { PaymentMethodWarningsModule } from "@bitwarden/web-vault/app/billing/shared";
import { OssModule } from "@bitwarden/web-vault/app/oss.module";
import {
@@ -53,7 +52,6 @@ import { SetupComponent } from "./setup/setup.component";
OrganizationPlansComponent,
SearchModule,
ProvidersLayoutComponent,
- PaymentMethodWarningsModule,
TaxInfoComponent,
DangerZoneComponent,
ScrollingModule,
diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.html b/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.html
index 3b81d0564c..482b85b712 100644
--- a/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.html
+++ b/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.html
@@ -1,6 +1,3 @@
-
,
+ refreshedComponent: Type,
+ options: Route,
+ altOptions?: Route,
+): Routes {
+ return componentRouteSwap(
+ defaultComponent,
+ refreshedComponent,
+ async () => {
+ const configService = inject(ConfigService);
+ return configService.getFeatureFlag(FeatureFlag.ExtensionRefresh);
+ },
+ options,
+ altOptions,
+ );
+}
diff --git a/libs/common/src/billing/abstractions/billilng-api.service.abstraction.ts b/libs/common/src/billing/abstractions/billilng-api.service.abstraction.ts
index 83db2fcd87..fd7feab4c3 100644
--- a/libs/common/src/billing/abstractions/billilng-api.service.abstraction.ts
+++ b/libs/common/src/billing/abstractions/billilng-api.service.abstraction.ts
@@ -8,7 +8,6 @@ import { PaymentInformationResponse } from "@bitwarden/common/billing/models/res
import { SubscriptionCancellationRequest } from "../../billing/models/request/subscription-cancellation.request";
import { OrganizationBillingMetadataResponse } from "../../billing/models/response/organization-billing-metadata.response";
-import { OrganizationBillingStatusResponse } from "../../billing/models/response/organization-billing-status.response";
import { PlanResponse } from "../../billing/models/response/plan.response";
import { ListResponse } from "../../models/response/list.response";
import { CreateClientOrganizationRequest } from "../models/request/create-client-organization.request";
@@ -34,8 +33,6 @@ export abstract class BillingApiServiceAbstraction {
organizationId: string,
) => Promise;
- getOrganizationBillingStatus: (id: string) => Promise;
-
getPlans: () => Promise>;
getProviderClientInvoiceReport: (providerId: string, invoiceId: string) => Promise;
diff --git a/libs/common/src/billing/abstractions/index.ts b/libs/common/src/billing/abstractions/index.ts
index 08a7a28fd9..c3ef8baca2 100644
--- a/libs/common/src/billing/abstractions/index.ts
+++ b/libs/common/src/billing/abstractions/index.ts
@@ -1,7 +1,6 @@
export * from "./account/billing-account-profile-state.service";
export * from "./billilng-api.service.abstraction";
export * from "./organization-billing.service";
-export * from "./payment-method-warnings-service.abstraction";
export * from "./payment-processors/braintree.service.abstraction";
export * from "./payment-processors/stripe.service.abstraction";
export * from "./provider-billing.service.abstraction";
diff --git a/libs/common/src/billing/abstractions/payment-method-warnings-service.abstraction.ts b/libs/common/src/billing/abstractions/payment-method-warnings-service.abstraction.ts
deleted file mode 100644
index d7ba522c60..0000000000
--- a/libs/common/src/billing/abstractions/payment-method-warnings-service.abstraction.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { Observable } from "rxjs";
-
-import { PaymentMethodWarning } from "../models/domain/payment-method-warning";
-
-export abstract class PaymentMethodWarningsServiceAbstraction {
- /**
- * An {@link Observable} record in the {@link ActiveUserState} of the user's organization IDs each mapped to their respective {@link PaymentMethodWarning}.
- */
- paymentMethodWarnings$: Observable>;
- /**
- * Updates the {@link ActiveUserState} by setting `acknowledged` to `true` for the {@link PaymentMethodWarning} represented by the provided organization ID.
- * @param organizationId - The ID of the organization whose warning you'd like to acknowledge.
- */
- acknowledge: (organizationId: string) => Promise;
- /**
- * Updates the {@link ActiveUserState} by setting `risksSubscriptionFailure` to `false` for the {@link PaymentMethodWarning} represented by the provided organization ID.
- * @param organizationId - The ID of the organization whose subscription risk you'd like to remove.
- */
- removeSubscriptionRisk: (organizationId: string) => Promise;
- /**
- * Clears the {@link PaymentMethodWarning} record from the {@link ActiveUserState}.
- */
- clear: () => Promise;
- /**
- * Tries to retrieve the {@link PaymentMethodWarning} for the provided organization ID from the {@link ActiveUserState}.
- * If the warning does not exist, or if the warning has been in state for longer than a week, fetches the current {@link OrganizationBillingStatusResponse} for the organization
- * from the API and uses it to update the warning in state.
- * @param organizationId - The ID of the organization whose {@link PaymentMethodWarning} you'd like to update.
- */
- update: (organizationId: string) => Promise;
-}
diff --git a/libs/common/src/billing/models/billing-keys.state.ts b/libs/common/src/billing/models/billing-keys.state.ts
deleted file mode 100644
index 1d1cce6d0b..0000000000
--- a/libs/common/src/billing/models/billing-keys.state.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { BILLING_DISK, UserKeyDefinition } from "../../platform/state";
-import { PaymentMethodWarning } from "../models/domain/payment-method-warning";
-
-export const PAYMENT_METHOD_WARNINGS_KEY = UserKeyDefinition.record(
- BILLING_DISK,
- "paymentMethodWarnings",
- {
- deserializer: (warnings) => ({
- ...warnings,
- savedAt: new Date(warnings.savedAt),
- }),
- clearOn: ["logout"],
- },
-);
diff --git a/libs/common/src/billing/models/domain/index.ts b/libs/common/src/billing/models/domain/index.ts
index 19be9c1aeb..66d7e29c10 100644
--- a/libs/common/src/billing/models/domain/index.ts
+++ b/libs/common/src/billing/models/domain/index.ts
@@ -1,5 +1,4 @@
export * from "./bank-account";
export * from "./masked-payment-method";
-export * from "./payment-method-warning";
export * from "./tax-information";
export * from "./tokenized-payment-method";
diff --git a/libs/common/src/billing/models/domain/payment-method-warning.ts b/libs/common/src/billing/models/domain/payment-method-warning.ts
deleted file mode 100644
index dd3f0f8581..0000000000
--- a/libs/common/src/billing/models/domain/payment-method-warning.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-export type PaymentMethodWarning = {
- organizationName: string;
- risksSubscriptionFailure: boolean;
- acknowledged: boolean;
- savedAt: Date;
-};
diff --git a/libs/common/src/billing/models/response/organization-billing-status.response.ts b/libs/common/src/billing/models/response/organization-billing-status.response.ts
deleted file mode 100644
index 916bebe4bc..0000000000
--- a/libs/common/src/billing/models/response/organization-billing-status.response.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { BaseResponse } from "../../../models/response/base.response";
-
-export class OrganizationBillingStatusResponse extends BaseResponse {
- organizationId: string;
- organizationName: string;
- risksSubscriptionFailure: boolean;
-
- constructor(response: any) {
- super(response);
-
- this.organizationId = this.getResponseProperty("OrganizationId");
- this.organizationName = this.getResponseProperty("OrganizationName");
- this.risksSubscriptionFailure = this.getResponseProperty("RisksSubscriptionFailure");
- }
-}
diff --git a/libs/common/src/billing/services/billing-api.service.ts b/libs/common/src/billing/services/billing-api.service.ts
index a5841fc5b5..822e6d1687 100644
--- a/libs/common/src/billing/services/billing-api.service.ts
+++ b/libs/common/src/billing/services/billing-api.service.ts
@@ -12,7 +12,6 @@ import { SubscriptionCancellationRequest } from "../../billing/models/request/su
import { TokenizedPaymentMethodRequest } from "../../billing/models/request/tokenized-payment-method.request";
import { VerifyBankAccountRequest } from "../../billing/models/request/verify-bank-account.request";
import { OrganizationBillingMetadataResponse } from "../../billing/models/response/organization-billing-metadata.response";
-import { OrganizationBillingStatusResponse } from "../../billing/models/response/organization-billing-status.response";
import { PaymentInformationResponse } from "../../billing/models/response/payment-information.response";
import { PlanResponse } from "../../billing/models/response/plan.response";
import { ListResponse } from "../../models/response/list.response";
@@ -72,17 +71,6 @@ export class BillingApiService implements BillingApiServiceAbstraction {
return response as string;
}
- async getOrganizationBillingStatus(id: string): Promise {
- const r = await this.apiService.send(
- "GET",
- "/organizations/" + id + "/billing-status",
- null,
- true,
- true,
- );
- return new OrganizationBillingStatusResponse(r);
- }
-
async getOrganizationBillingMetadata(
organizationId: string,
): Promise {
diff --git a/libs/common/src/billing/services/payment-method-warnings.service.spec.ts b/libs/common/src/billing/services/payment-method-warnings.service.spec.ts
deleted file mode 100644
index 6e37821ef5..0000000000
--- a/libs/common/src/billing/services/payment-method-warnings.service.spec.ts
+++ /dev/null
@@ -1,186 +0,0 @@
-import { any, mock, MockProxy } from "jest-mock-extended";
-import { firstValueFrom } from "rxjs";
-
-import { FakeAccountService, FakeStateProvider, mockAccountServiceWith } from "../../../spec";
-import { FakeActiveUserState } from "../../../spec/fake-state";
-import { Utils } from "../../platform/misc/utils";
-import { UserId } from "../../types/guid";
-import { BillingApiServiceAbstraction as BillingApiService } from "../abstractions/billilng-api.service.abstraction";
-import { PAYMENT_METHOD_WARNINGS_KEY } from "../models/billing-keys.state";
-import { PaymentMethodWarning } from "../models/domain/payment-method-warning";
-import { OrganizationBillingStatusResponse } from "../models/response/organization-billing-status.response";
-
-import { PaymentMethodWarningsService } from "./payment-method-warnings.service";
-
-describe("Payment Method Warnings Service", () => {
- let paymentMethodWarningsService: PaymentMethodWarningsService;
- let billingApiService: MockProxy;
-
- const mockUserId = Utils.newGuid() as UserId;
- let accountService: FakeAccountService;
- let stateProvider: FakeStateProvider;
- let activeUserState: FakeActiveUserState>;
-
- function getPastDate(daysAgo: number) {
- const date = new Date();
- date.setDate(date.getDate() - daysAgo);
- return date;
- }
-
- const getBillingStatusResponse = (organizationId: string) =>
- new OrganizationBillingStatusResponse({
- OrganizationId: organizationId,
- OrganizationName: "Teams Organization",
- RisksSubscriptionFailure: true,
- });
-
- beforeEach(() => {
- accountService = mockAccountServiceWith(mockUserId);
- stateProvider = new FakeStateProvider(accountService);
- activeUserState = stateProvider.activeUser.getFake(PAYMENT_METHOD_WARNINGS_KEY);
-
- billingApiService = mock();
- paymentMethodWarningsService = new PaymentMethodWarningsService(
- billingApiService,
- stateProvider,
- );
- });
-
- it("acknowledge", async () => {
- const organizationId = "1";
- const state: Record = {
- [organizationId]: {
- organizationName: "Teams Organization",
- risksSubscriptionFailure: true,
- acknowledged: false,
- savedAt: getPastDate(3),
- },
- };
- activeUserState.nextState(state);
- await paymentMethodWarningsService.acknowledge(organizationId);
- expect(await firstValueFrom(paymentMethodWarningsService.paymentMethodWarnings$)).toEqual({
- [organizationId]: {
- ...state[organizationId],
- acknowledged: true,
- },
- });
- });
-
- it("clear", async () => {
- const state: Record = {
- "1": {
- organizationName: "Teams Organization",
- risksSubscriptionFailure: true,
- acknowledged: false,
- savedAt: getPastDate(3),
- },
- };
- activeUserState.nextState(state);
- await paymentMethodWarningsService.clear();
- expect(await firstValueFrom(paymentMethodWarningsService.paymentMethodWarnings$)).toEqual({});
- });
-
- it("removeSubscriptionRisk", async () => {
- const organizationId = "1";
- const state: Record = {
- [organizationId]: {
- organizationName: "Teams Organization",
- risksSubscriptionFailure: true,
- acknowledged: false,
- savedAt: getPastDate(3),
- },
- };
- activeUserState.nextState(state);
- await paymentMethodWarningsService.removeSubscriptionRisk(organizationId);
- expect(await firstValueFrom(paymentMethodWarningsService.paymentMethodWarnings$)).toEqual({
- [organizationId]: {
- ...state[organizationId],
- risksSubscriptionFailure: false,
- },
- });
- });
-
- describe("update", () => {
- it("Does nothing if the stored payment method warning is less than a week old", async () => {
- const organizationId = "1";
- const state: Record = {
- [organizationId]: {
- organizationName: "Teams Organization",
- risksSubscriptionFailure: true,
- acknowledged: false,
- savedAt: getPastDate(3),
- },
- };
- activeUserState.nextState(state);
- await paymentMethodWarningsService.update(organizationId);
- expect(billingApiService.getOrganizationBillingStatus).not.toHaveBeenCalled();
- });
-
- it("Retrieves the billing status from the API and uses it to update the state if the state is null", async () => {
- const organizationId = "1";
- activeUserState.nextState(null);
- billingApiService.getOrganizationBillingStatus.mockResolvedValue(
- getBillingStatusResponse(organizationId),
- );
- await paymentMethodWarningsService.update(organizationId);
- expect(await firstValueFrom(paymentMethodWarningsService.paymentMethodWarnings$)).toEqual({
- [organizationId]: {
- organizationName: "Teams Organization",
- risksSubscriptionFailure: true,
- acknowledged: false,
- savedAt: any(),
- },
- });
- expect(billingApiService.getOrganizationBillingStatus).toHaveBeenCalledTimes(1);
- });
-
- it("Retrieves the billing status from the API and uses it to update the state if the stored warning is null", async () => {
- const organizationId = "1";
- activeUserState.nextState({
- [organizationId]: null,
- });
- billingApiService.getOrganizationBillingStatus.mockResolvedValue(
- getBillingStatusResponse(organizationId),
- );
- await paymentMethodWarningsService.update(organizationId);
- expect(await firstValueFrom(paymentMethodWarningsService.paymentMethodWarnings$)).toEqual({
- [organizationId]: {
- organizationName: "Teams Organization",
- risksSubscriptionFailure: true,
- acknowledged: false,
- savedAt: any(),
- },
- });
- expect(billingApiService.getOrganizationBillingStatus).toHaveBeenCalledTimes(1);
- });
-
- it("Retrieves the billing status from the API and uses it to update the state if the stored warning is older than a week", async () => {
- const organizationId = "1";
- activeUserState.nextState({
- [organizationId]: {
- organizationName: "Teams Organization",
- risksSubscriptionFailure: false,
- acknowledged: false,
- savedAt: getPastDate(10),
- },
- });
- billingApiService.getOrganizationBillingStatus.mockResolvedValue(
- new OrganizationBillingStatusResponse({
- OrganizationId: organizationId,
- OrganizationName: "Teams Organization",
- RisksSubscriptionFailure: true,
- }),
- );
- await paymentMethodWarningsService.update(organizationId);
- expect(await firstValueFrom(paymentMethodWarningsService.paymentMethodWarnings$)).toEqual({
- [organizationId]: {
- organizationName: "Teams Organization",
- risksSubscriptionFailure: true,
- acknowledged: false,
- savedAt: any(),
- },
- });
- expect(billingApiService.getOrganizationBillingStatus).toHaveBeenCalledTimes(1);
- });
- });
-});
diff --git a/libs/common/src/billing/services/payment-method-warnings.service.ts b/libs/common/src/billing/services/payment-method-warnings.service.ts
deleted file mode 100644
index 0dad48bb85..0000000000
--- a/libs/common/src/billing/services/payment-method-warnings.service.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-import { firstValueFrom, map, Observable } from "rxjs";
-
-import { ActiveUserState, StateProvider } from "../../platform/state";
-import { BillingApiServiceAbstraction as BillingApiService } from "../abstractions/billilng-api.service.abstraction";
-import { PaymentMethodWarningsServiceAbstraction } from "../abstractions/payment-method-warnings-service.abstraction";
-import { PAYMENT_METHOD_WARNINGS_KEY } from "../models/billing-keys.state";
-import { PaymentMethodWarning } from "../models/domain/payment-method-warning";
-
-export class PaymentMethodWarningsService implements PaymentMethodWarningsServiceAbstraction {
- private paymentMethodWarningsState: ActiveUserState>;
- paymentMethodWarnings$: Observable>;
-
- constructor(
- private billingApiService: BillingApiService,
- private stateProvider: StateProvider,
- ) {
- this.paymentMethodWarningsState = this.stateProvider.getActive(PAYMENT_METHOD_WARNINGS_KEY);
- this.paymentMethodWarnings$ = this.paymentMethodWarningsState.state$;
- }
-
- async acknowledge(organizationId: string): Promise {
- await this.paymentMethodWarningsState.update((state) => {
- const current = state[organizationId];
- state[organizationId] = {
- ...current,
- acknowledged: true,
- };
- return state;
- });
- }
-
- async removeSubscriptionRisk(organizationId: string): Promise {
- await this.paymentMethodWarningsState.update((state) => {
- const current = state[organizationId];
- state[organizationId] = {
- ...current,
- risksSubscriptionFailure: false,
- };
- return state;
- });
- }
-
- async clear(): Promise {
- await this.paymentMethodWarningsState.update(() => ({}));
- }
-
- async update(organizationId: string): Promise {
- const warning = await firstValueFrom(
- this.paymentMethodWarningsState.state$.pipe(
- map((state) => (!state ? null : state[organizationId])),
- ),
- );
- if (!warning || warning.savedAt < this.getOneWeekAgo()) {
- const { organizationName, risksSubscriptionFailure } =
- await this.billingApiService.getOrganizationBillingStatus(organizationId);
- await this.paymentMethodWarningsState.update((state) => {
- state ??= {};
- state[organizationId] = {
- organizationName,
- risksSubscriptionFailure,
- acknowledged: false,
- savedAt: new Date(),
- };
- return state;
- });
- }
- }
-
- private getOneWeekAgo = (): Date => {
- const date = new Date();
- date.setDate(date.getDate() - 7);
- return date;
- };
-}
diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts
index 583b699697..9edc14e7b0 100644
--- a/libs/common/src/enums/feature-flag.enum.ts
+++ b/libs/common/src/enums/feature-flag.enum.ts
@@ -7,7 +7,6 @@ export enum FeatureFlag {
BrowserFilelessImport = "browser-fileless-import",
ItemShare = "item-share",
GeneratorToolsModernization = "generator-tools-modernization",
- ShowPaymentMethodWarningBanners = "show-payment-method-warning-banners",
EnableConsolidatedBilling = "enable-consolidated-billing",
AC1795_UpdatedSubscriptionStatusSection = "AC-1795_updated-subscription-status-section",
EnableDeleteProvider = "AC-1218-delete-provider",
@@ -50,7 +49,6 @@ export const DefaultFeatureFlagValue = {
[FeatureFlag.BrowserFilelessImport]: FALSE,
[FeatureFlag.ItemShare]: FALSE,
[FeatureFlag.GeneratorToolsModernization]: FALSE,
- [FeatureFlag.ShowPaymentMethodWarningBanners]: FALSE,
[FeatureFlag.EnableConsolidatedBilling]: FALSE,
[FeatureFlag.AC1795_UpdatedSubscriptionStatusSection]: FALSE,
[FeatureFlag.EnableDeleteProvider]: FALSE,
diff --git a/libs/components/src/dialog/dialogs.mdx b/libs/components/src/dialog/dialogs.mdx
index bd6a30d7f2..9a63e1301b 100644
--- a/libs/components/src/dialog/dialogs.mdx
+++ b/libs/components/src/dialog/dialogs.mdx
@@ -1,5 +1,7 @@
import { Meta, Story, Source } from "@storybook/addon-docs";
+import * as stories from "./dialog.service.stories";
+
# Dialog
@@ -24,7 +26,7 @@ dialog should become scrollable.
A backdrop should be used to hide the content below the dialog. Use `#000000` with `30% opacity`.
-
+
## Accessibility
diff --git a/libs/components/src/form/forms.mdx b/libs/components/src/form/forms.mdx
index 6c6fa33f68..dba6a1466c 100644
--- a/libs/components/src/form/forms.mdx
+++ b/libs/components/src/form/forms.mdx
@@ -1,5 +1,14 @@
import { Meta, Story, Source } from "@storybook/addon-docs";
+import * as formStories from "./form.stories";
+import * as fieldStories from "../form-field/form-field.stories";
+import * as passwordToggleStories from "../form-field/password-input-toggle.stories";
+import * as searchStories from "../search/search.stories";
+import * as selectStories from "../select/select.stories";
+import * as multiSelectStories from "../form-field/multi-select.stories";
+import * as radioStories from "../radio-button/radio-button.stories";
+import * as checkboxStories from "../checkbox/checkbox.stories";
+
# Forms
@@ -8,9 +17,9 @@ Component Library forms should always be built using [Angular Reactive Forms][re
[ADR-0001][adr-0001] for a background to this decision. In practice this means that forms should
always use the native `form` element and bind a `formGroup`.
-
+
-
+
## Form spacing and sections
@@ -48,25 +57,25 @@ controls like email verification, number selection, and more.
#### Default with required attribute
-
+
#### Password Toggle
-
+
-#### Search
+### Search
-
+
### Selects
#### Searchable single select (default)
-
+
#### Multi-select
-
+
### Radio group
@@ -89,14 +98,11 @@ using a radio group for more than 5 options even if the options require addition
#### Block
-
+
#### Inline
-
-
-[reactive]: https://angular.io/guide/reactive-forms
-[adr-0001]: https://contributing.bitwarden.com/architecture/adr/reactive-forms
+
### Checkbox
@@ -116,7 +122,7 @@ If a checkbox group has more than 4 options a
#### Single checkbox
-
+
## Accessibility
@@ -176,3 +182,6 @@ the field’s label.
Maintain a ratio of 3:1 with the form's background.
- Error styling should not rely only on using the `danger-600`color change. Use
as a prefix to highlight the text as error text versus helper
+
+[reactive]: https://angular.io/guide/reactive-forms
+[adr-0001]: https://contributing.bitwarden.com/architecture/adr/reactive-forms
diff --git a/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts b/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts
index 58bd971679..fa78f04d23 100644
--- a/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts
+++ b/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts
@@ -64,6 +64,7 @@ export default {
skipToContent: "Skip to content",
submenu: "submenu",
toggleCollapse: "toggle collapse",
+ toggleSideNavigation: "toggle side navigation",
});
},
},
diff --git a/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.html b/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.html
index 07606add8b..626934b20e 100644
--- a/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.html
+++ b/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.html
@@ -74,6 +74,21 @@
bitPasswordInputToggle
[(toggled)]="showFilePassword"
>
+
+
{{ "exportPasswordDescription" | i18n }}
diff --git a/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.ts b/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.ts
index 8f2c6661fd..d83d189cd7 100644
--- a/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.ts
+++ b/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.ts
@@ -24,6 +24,7 @@ import { EventType } from "@bitwarden/common/enums";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
+import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
import {
@@ -38,6 +39,7 @@ import {
SelectModule,
ToastService,
} from "@bitwarden/components";
+import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
import { VaultExportServiceAbstraction } from "@bitwarden/vault-export-core";
import { EncryptedExportType } from "../enums/encrypted-export-type.enum";
@@ -157,6 +159,8 @@ export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
protected toastService: ToastService,
protected exportService: VaultExportServiceAbstraction,
protected eventCollectionService: EventCollectionService,
+ protected passwordGenerationService: PasswordGenerationServiceAbstraction,
+ private platformUtilsService: PlatformUtilsService,
private policyService: PolicyService,
private logService: LogService,
private formBuilder: UntypedFormBuilder,
@@ -272,6 +276,22 @@ export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
}
}
+ generatePassword = async () => {
+ const [options] = await this.passwordGenerationService.getOptions();
+ this.filePasswordValue = await this.passwordGenerationService.generatePassword(options);
+ this.exportForm.get("filePassword").setValue(this.filePasswordValue);
+ this.exportForm.get("confirmFilePassword").setValue(this.filePasswordValue);
+ };
+
+ copyPasswordToClipboard = async () => {
+ this.platformUtilsService.copyToClipboard(this.filePasswordValue);
+ this.toastService.showToast({
+ variant: "success",
+ title: null,
+ message: this.i18nService.t("valueCopied", this.i18nService.t("password")),
+ });
+ };
+
submit = async () => {
if (this.isFileEncryptedExport && this.filePassword != this.confirmFilePassword) {
this.toastService.showToast({
diff --git a/libs/vault/src/cipher-view/cipher-view.component.html b/libs/vault/src/cipher-view/cipher-view.component.html
index 68c80a7bd5..a675384ff9 100644
--- a/libs/vault/src/cipher-view/cipher-view.component.html
+++ b/libs/vault/src/cipher-view/cipher-view.component.html
@@ -1,4 +1,8 @@
+
+ {{ "cardExpiredMessage" | i18n }}
+
+
;
collections$: Observable;
private destroyed$: Subject = new Subject();
+ cardIsExpired: boolean = false;
constructor(
private organizationService: OrganizationService,
@@ -57,6 +60,8 @@ export class CipherViewComponent implements OnInit, OnDestroy {
async ngOnInit() {
await this.loadCipherData();
+
+ this.cardIsExpired = this.isCardExpiryInThePast();
}
ngOnDestroy(): void {
@@ -97,4 +102,24 @@ export class CipherViewComponent implements OnInit, OnDestroy {
.pipe(takeUntil(this.destroyed$));
}
}
+
+ isCardExpiryInThePast() {
+ if (this.cipher.card) {
+ const { expMonth, expYear }: CardView = this.cipher.card;
+
+ if (expYear && expMonth) {
+ // `Date` months are zero-indexed
+ const parsedMonth = parseInt(expMonth) - 1;
+ const parsedYear = parseInt(expYear);
+
+ // First day of the next month minus one, to get last day of the card month
+ const cardExpiry = new Date(parsedYear, parsedMonth + 1, 0);
+ const now = new Date();
+
+ return cardExpiry < now;
+ }
+ }
+
+ return false;
+ }
}
diff --git a/package-lock.json b/package-lock.json
index 1fdd4d7718..51220dd2b6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -120,7 +120,7 @@
"@typescript-eslint/parser": "7.16.1",
"@webcomponents/custom-elements": "1.6.0",
"@yao-pkg/pkg": "5.12.1",
- "autoprefixer": "10.4.19",
+ "autoprefixer": "10.4.20",
"babel-loader": "9.1.3",
"base64-loader": "1.0.0",
"browserslist": "4.23.2",
@@ -11194,9 +11194,9 @@
}
},
"node_modules/autoprefixer": {
- "version": "10.4.19",
- "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz",
- "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==",
+ "version": "10.4.20",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
+ "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==",
"dev": true,
"funding": [
{
@@ -11212,12 +11212,13 @@
"url": "https://github.com/sponsors/ai"
}
],
+ "license": "MIT",
"dependencies": {
- "browserslist": "^4.23.0",
- "caniuse-lite": "^1.0.30001599",
+ "browserslist": "^4.23.3",
+ "caniuse-lite": "^1.0.30001646",
"fraction.js": "^4.3.7",
"normalize-range": "^0.1.2",
- "picocolors": "^1.0.0",
+ "picocolors": "^1.0.1",
"postcss-value-parser": "^4.2.0"
},
"bin": {
@@ -11230,6 +11231,39 @@
"postcss": "^8.1.0"
}
},
+ "node_modules/autoprefixer/node_modules/browserslist": {
+ "version": "4.23.3",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz",
+ "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001646",
+ "electron-to-chromium": "^1.5.4",
+ "node-releases": "^2.0.18",
+ "update-browserslist-db": "^1.1.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
"node_modules/available-typed-arrays": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
diff --git a/package.json b/package.json
index d19f598143..89d216ee7a 100644
--- a/package.json
+++ b/package.json
@@ -82,7 +82,7 @@
"@typescript-eslint/parser": "7.16.1",
"@webcomponents/custom-elements": "1.6.0",
"@yao-pkg/pkg": "5.12.1",
- "autoprefixer": "10.4.19",
+ "autoprefixer": "10.4.20",
"babel-loader": "9.1.3",
"base64-loader": "1.0.0",
"browserslist": "4.23.2",