1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-17 16:53:34 +00:00

merge main

This commit is contained in:
rr-bw
2024-08-30 11:40:21 -07:00
615 changed files with 20276 additions and 5305 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "@bitwarden/web-vault",
"version": "2024.8.0",
"version": "2024.8.1",
"scripts": {
"build:oss": "webpack",
"build:bit": "webpack -c ../../bitwarden_license/bit-web/webpack.config.js",

View File

@@ -1,5 +1,5 @@
<!doctype html>
<html lang="en" class="tw-h-full">
<html lang="en" class="tw-h-full theme_light">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
@@ -16,9 +16,9 @@
</head>
<body class="tw-min-h-screen !tw-min-w-0 tw-text-center tw-bg-background-alt tw-flex tw-flex-col">
<main class="tw-max-w-3xl tw-mx-auto tw-mb-8 tw-px-2">
<img src="images/logo.svg" width="200px" class="tw-py-16" alt="Bitwarden" />
<img class="new-logo-themed tw-m-8" alt="Bitwarden" />
<main class="tw-max-w-3xl tw-mx-auto tw-px-2 tw-my-4">
<h1 class="tw-mb-0 tw-h1">Sorry, this page isn't available.</h1>
<p class="tw-py-9 tw-mb-0">

View File

@@ -96,7 +96,7 @@ export abstract class BaseMembersComponent<UserView extends UserViewTypes> {
abstract edit(user: UserView): void;
abstract getUsers(): Promise<ListResponse<UserView> | UserView[]>;
abstract deleteUser(id: string): Promise<void>;
abstract removeUser(id: string): Promise<void>;
abstract reinviteUser(id: string): Promise<void>;
abstract confirmUser(user: UserView, publicKey: Uint8Array): Promise<void>;
@@ -132,7 +132,7 @@ export abstract class BaseMembersComponent<UserView extends UserViewTypes> {
return false;
}
this.actionPromise = this.deleteUser(user.id);
this.actionPromise = this.removeUser(user.id);
try {
await this.actionPromise;
this.toastService.showToast({

View File

@@ -8,6 +8,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 { ToastService } from "@bitwarden/components";
import { EventService } from "../../core";
import { EventExportService } from "../../tools/event-export";
@@ -34,6 +35,7 @@ export abstract class BaseEventsComponent {
protected platformUtilsService: PlatformUtilsService,
protected logService: LogService,
protected fileDownloadService: FileDownloadService,
private toastService: ToastService,
) {
const defaultDates = this.eventService.getDefaultDateFilters();
this.start = defaultDates[0];
@@ -164,11 +166,11 @@ export abstract class BaseEventsComponent {
try {
dates = this.eventService.formatDateFilters(this.start, this.end);
} catch (e) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("invalidDateRange"),
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("invalidDateRange"),
});
return null;
}
return dates;

View File

@@ -22,7 +22,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { OrganizationUserView } from "../organizations/core/views/organization-user.view";
import { UserConfirmComponent } from "../organizations/manage/user-confirm.component";
@@ -127,6 +127,7 @@ export abstract class BasePeopleComponent<
protected userNamePipe: UserNamePipe,
protected dialogService: DialogService,
protected organizationManagementPreferencesService: OrganizationManagementPreferencesService,
protected toastService: ToastService,
) {}
abstract edit(user: UserType): void;
@@ -251,11 +252,11 @@ export abstract class BasePeopleComponent<
this.actionPromise = this.deleteUser(user.id);
try {
await this.actionPromise;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("removedUserId", this.userNamePipe.transform(user)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("removedUserId", this.userNamePipe.transform(user)),
});
this.removeUser(user);
} catch (e) {
this.validationService.showError(e);
@@ -282,11 +283,11 @@ export abstract class BasePeopleComponent<
this.actionPromise = this.revokeUser(user.id);
try {
await this.actionPromise;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("revokedUserId", this.userNamePipe.transform(user)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("revokedUserId", this.userNamePipe.transform(user)),
});
await this.load();
} catch (e) {
this.validationService.showError(e);
@@ -298,11 +299,11 @@ export abstract class BasePeopleComponent<
this.actionPromise = this.restoreUser(user.id);
try {
await this.actionPromise;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("restoredUserId", this.userNamePipe.transform(user)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("restoredUserId", this.userNamePipe.transform(user)),
});
await this.load();
} catch (e) {
this.validationService.showError(e);
@@ -318,11 +319,11 @@ export abstract class BasePeopleComponent<
this.actionPromise = this.reinviteUser(user.id);
try {
await this.actionPromise;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("hasBeenReinvited", this.userNamePipe.transform(user)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("hasBeenReinvited", this.userNamePipe.transform(user)),
});
} catch (e) {
this.validationService.showError(e);
}
@@ -344,11 +345,11 @@ export abstract class BasePeopleComponent<
this.actionPromise = this.confirmUser(user, publicKey);
await this.actionPromise;
updateUser(this);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("hasBeenConfirmed", this.userNamePipe.transform(user)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("hasBeenConfirmed", this.userNamePipe.transform(user)),
});
} catch (e) {
this.validationService.showError(e);
throw e;

View File

@@ -12,7 +12,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { DialogService, TableDataSource } from "@bitwarden/components";
import { DialogService, TableDataSource, ToastService } from "@bitwarden/components";
import { EventService } from "../../../core";
import { SharedModule } from "../../../shared";
@@ -63,6 +63,7 @@ export class EntityEventsComponent implements OnInit {
private organizationUserService: OrganizationUserService,
private formBuilder: FormBuilder,
private validationService: ValidationService,
private toastService: ToastService,
) {}
async ngOnInit() {
@@ -109,11 +110,11 @@ export class EntityEventsComponent implements OnInit {
this.filterFormGroup.value.end,
);
} catch (e) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("invalidDateRange"),
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("invalidDateRange"),
});
return;
}

View File

@@ -14,6 +14,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 { ToastService } from "@bitwarden/components";
import { EventService } from "../../../core";
import { EventExportService } from "../../../tools/event-export";
@@ -22,6 +23,7 @@ import { BaseEventsComponent } from "../../common/base.events.component";
const EVENT_SYSTEM_USER_TO_TRANSLATION: Record<EventSystemUser, string> = {
[EventSystemUser.SCIM]: null, // SCIM acronym not able to be translated so just display SCIM
[EventSystemUser.DomainVerification]: "domainVerification",
[EventSystemUser.PublicApi]: "publicApi",
};
@Component({
@@ -50,6 +52,7 @@ export class EventsComponent extends BaseEventsComponent implements OnInit, OnDe
private organizationUserService: OrganizationUserService,
private providerService: ProviderService,
fileDownloadService: FileDownloadService,
toastService: ToastService,
) {
super(
eventService,
@@ -58,6 +61,7 @@ export class EventsComponent extends BaseEventsComponent implements OnInit, OnDe
platformUtilsService,
logService,
fileDownloadService,
toastService,
);
}

View File

@@ -24,7 +24,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { UserId } from "@bitwarden/common/types/guid";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { CollectionAdminService } from "../../../vault/core/collection-admin.service";
import { CollectionAdminView } from "../../../vault/core/views/collection-admin.view";
@@ -213,6 +213,7 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
private organizationService: OrganizationService,
private accountService: AccountService,
private collectionAdminService: CollectionAdminService,
private toastService: ToastService,
) {
this.tabIndex = params.initialTab ?? GroupAddEditTabType.Info;
}
@@ -280,11 +281,14 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
if (this.groupForm.invalid) {
if (this.tabIndex !== GroupAddEditTabType.Info) {
this.platformUtilsService.showToast(
"error",
null,
this.i18nService.t("fieldOnTabRequiresAttention", this.i18nService.t("groupInfo")),
);
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t(
"fieldOnTabRequiresAttention",
this.i18nService.t("groupInfo"),
),
});
}
return;
}
@@ -300,11 +304,14 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
await this.groupService.save(groupView);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t(this.editMode ? "editedGroupId" : "createdGroupId", formValue.name),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t(
this.editMode ? "editedGroupId" : "createdGroupId",
formValue.name,
),
});
this.dialogRef.close(GroupAddEditDialogResultType.Saved);
};
@@ -325,11 +332,11 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
await this.groupService.delete(this.organizationId, this.groupId);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("deletedGroupId", this.group.name),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("deletedGroupId", this.group.name),
});
this.dialogRef.close(GroupAddEditDialogResultType.Deleted);
};
}

View File

@@ -6,6 +6,7 @@ import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-conso
import { OrganizationVerifyDeleteRecoverRequest } from "@bitwarden/common/admin-console/models/request/organization-verify-delete-recover.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";
import { SharedModule } from "../../../shared/shared.module";
@@ -27,6 +28,7 @@ export class VerifyRecoverDeleteOrgComponent implements OnInit {
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private route: ActivatedRoute,
private toastService: ToastService,
) {}
async ngOnInit() {
@@ -44,11 +46,11 @@ export class VerifyRecoverDeleteOrgComponent implements OnInit {
submit = async () => {
const request = new OrganizationVerifyDeleteRecoverRequest(this.token);
await this.apiService.deleteUsingToken(this.orgId, request);
this.platformUtilsService.showToast(
"success",
this.i18nService.t("organizationDeleted"),
this.i18nService.t("organizationDeletedDesc"),
);
this.toastService.showToast({
variant: "success",
title: this.i18nService.t("organizationDeleted"),
message: this.i18nService.t("organizationDeletedDesc"),
});
await this.router.navigate(["/"]);
};
}

View File

@@ -4,7 +4,7 @@ import { Component, Inject, OnInit } from "@angular/core";
import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { DialogService, TableDataSource } from "@bitwarden/components";
import { DialogService, TableDataSource, ToastService } from "@bitwarden/components";
import { OrganizationUserView } from "../../../core";
@@ -24,6 +24,7 @@ export class BulkEnableSecretsManagerDialogComponent implements OnInit {
private organizationUserService: OrganizationUserService,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private toastService: ToastService,
) {}
ngOnInit(): void {
@@ -35,11 +36,11 @@ export class BulkEnableSecretsManagerDialogComponent implements OnInit {
this.data.orgId,
this.dataSource.data.map((u) => u.id),
);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("activatedAccessToSecretsManager"),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("activatedAccessToSecretsManager"),
});
this.dialogRef.close();
};

View File

@@ -45,7 +45,7 @@ export class BulkRemoveComponent {
submit = async () => {
this.loading = true;
try {
const response = await this.deleteUsers();
const response = await this.removeUsers();
response.data.forEach((entry) => {
const error = entry.error !== "" ? entry.error : this.i18nService.t("bulkRemovedMessage");
@@ -59,8 +59,8 @@ export class BulkRemoveComponent {
this.loading = false;
};
protected async deleteUsers() {
return await this.organizationUserService.deleteManyOrganizationUsers(
protected async removeUsers() {
return await this.organizationUserService.removeManyOrganizationUsers(
this.organizationId,
this.users.map((user) => user.id),
);

View File

@@ -26,7 +26,7 @@ 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 { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { CollectionAdminService } from "../../../../../vault/core/collection-admin.service";
import { CollectionAdminView } from "../../../../../vault/core/views/collection-admin.view";
@@ -143,6 +143,7 @@ export class MemberDialogComponent implements OnDestroy {
private dialogService: DialogService,
private accountService: AccountService,
organizationService: OrganizationService,
private toastService: ToastService,
) {
this.organization$ = organizationService
.get$(this.params.organizationId)
@@ -376,11 +377,11 @@ export class MemberDialogComponent implements OnDestroy {
) {
this.permissionsGroup.value.manageUsers = true;
(document.getElementById("manageUsers") as HTMLInputElement).checked = true;
this.platformUtilsService.showToast(
"info",
null,
this.i18nService.t("accountRecoveryManageUsers"),
);
this.toastService.showToast({
variant: "info",
title: null,
message: this.i18nService.t("accountRecoveryManageUsers"),
});
}
}
@@ -389,11 +390,11 @@ export class MemberDialogComponent implements OnDestroy {
if (this.formGroup.invalid) {
if (this.tabIndex !== MemberDialogTab.Role) {
this.platformUtilsService.showToast(
"error",
null,
this.i18nService.t("fieldOnTabRequiresAttention", this.i18nService.t("role")),
);
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t("fieldOnTabRequiresAttention", this.i18nService.t("role")),
});
}
return;
}
@@ -401,11 +402,11 @@ export class MemberDialogComponent implements OnDestroy {
const organization = await firstValueFrom(this.organization$);
if (!organization.useCustomPermissions && this.customUserTypeSelected) {
this.platformUtilsService.showToast(
"error",
null,
this.i18nService.t("customNonEnterpriseError"),
);
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t("customNonEnterpriseError"),
});
return;
}
@@ -452,11 +453,14 @@ export class MemberDialogComponent implements OnDestroy {
await this.userService.invite(emails, userView);
}
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t(this.editMode ? "editedUserId" : "invitedUsers", this.params.name),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t(
this.editMode ? "editedUserId" : "invitedUsers",
this.params.name,
),
});
this.close(MemberDialogResult.Saved);
};
@@ -487,16 +491,16 @@ export class MemberDialogComponent implements OnDestroy {
}
}
await this.organizationUserService.deleteOrganizationUser(
await this.organizationUserService.removeOrganizationUser(
this.params.organizationId,
this.params.organizationUserId,
);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("removedUserId", this.params.name),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("removedUserId", this.params.name),
});
this.close(MemberDialogResult.Deleted);
};
@@ -529,11 +533,11 @@ export class MemberDialogComponent implements OnDestroy {
this.params.organizationUserId,
);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("revokedUserId", this.params.name),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("revokedUserId", this.params.name),
});
this.isRevoked = true;
this.close(MemberDialogResult.Revoked);
};
@@ -548,11 +552,11 @@ export class MemberDialogComponent implements OnDestroy {
this.params.organizationUserId,
);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("restoredUserId", this.params.name),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("restoredUserId", this.params.name),
});
this.isRevoked = false;
this.close(MemberDialogResult.Restored);
};

View File

@@ -17,7 +17,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
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 { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
import { OrganizationUserResetPasswordService } from "../services/organization-user-reset-password/organization-user-reset-password.service";
@@ -50,6 +50,7 @@ export class ResetPasswordComponent implements OnInit, OnDestroy {
private policyService: PolicyService,
private logService: LogService,
private dialogService: DialogService,
private toastService: ToastService,
) {}
async ngOnInit() {
@@ -88,30 +89,30 @@ export class ResetPasswordComponent implements OnInit, OnDestroy {
}
this.platformUtilsService.copyToClipboard(value, { window: window });
this.platformUtilsService.showToast(
"info",
null,
this.i18nService.t("valueCopied", this.i18nService.t("password")),
);
this.toastService.showToast({
variant: "info",
title: null,
message: this.i18nService.t("valueCopied", this.i18nService.t("password")),
});
}
async submit() {
// Validation
if (this.newPassword == null || this.newPassword === "") {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("masterPasswordRequired"),
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("masterPasswordRequired"),
});
return false;
}
if (this.newPassword.length < Utils.minimumPasswordLength) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("masterPasswordMinlength", Utils.minimumPasswordLength),
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("masterPasswordMinlength", Utils.minimumPasswordLength),
});
return false;
}
@@ -123,11 +124,11 @@ export class ResetPasswordComponent implements OnInit, OnDestroy {
this.enforcedPolicyOptions,
)
) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("masterPasswordPolicyRequirementsNotMet"),
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("masterPasswordPolicyRequirementsNotMet"),
});
return;
}
@@ -151,11 +152,11 @@ export class ResetPasswordComponent implements OnInit, OnDestroy {
this.organizationId,
);
await this.formPromise;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("resetPasswordSuccess"),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("resetPasswordSuccess"),
});
this.passwordReset.emit();
} catch (e) {
this.logService.error(e);

View File

@@ -32,7 +32,7 @@ import {
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction";
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
import { isNotSelfUpgradable, ProductTierType } from "@bitwarden/common/billing/enums";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
@@ -269,8 +269,8 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
return collectionMap;
}
deleteUser(id: string): Promise<void> {
return this.organizationUserService.deleteOrganizationUser(this.organization.id, id);
removeUser(id: string): Promise<void> {
return this.organizationUserService.removeOrganizationUser(this.organization.id, id);
}
revokeUser(id: string): Promise<void> {

View File

@@ -15,7 +15,7 @@ import { PolicyRequest } from "@bitwarden/common/admin-console/models/request/po
import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response";
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 { BasePolicy, BasePolicyComponent } from "../policies";
@@ -58,6 +58,7 @@ export class PolicyEditComponent implements AfterViewInit {
private cdr: ChangeDetectorRef,
private formBuilder: FormBuilder,
private dialogRef: DialogRef<PolicyEditDialogResult>,
private toastService: ToastService,
) {}
get policy(): BasePolicy {
return this.data.policy;
@@ -95,7 +96,7 @@ export class PolicyEditComponent implements AfterViewInit {
try {
request = await this.policyComponent.buildRequest(this.data.policiesEnabledMap);
} catch (e) {
this.platformUtilsService.showToast("error", null, e.message);
this.toastService.showToast({ variant: "error", title: null, message: e.message });
return;
}
this.formPromise = this.policyApiService.putPolicy(
@@ -104,11 +105,11 @@ export class PolicyEditComponent implements AfterViewInit {
request,
);
await this.formPromise;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("editedPolicyId", this.i18nService.t(this.data.policy.name)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("editedPolicyId", this.i18nService.t(this.data.policy.name)),
});
this.dialogRef.close(PolicyEditDialogResult.Saved);
};

View File

@@ -14,7 +14,7 @@ import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.se
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { ApiKeyComponent } from "../../../auth/settings/security/api-key.component";
import { PurgeVaultComponent } from "../../../vault/settings/purge-vault.component";
@@ -77,6 +77,7 @@ export class AccountComponent implements OnInit, OnDestroy {
private organizationApiService: OrganizationApiServiceAbstraction,
private dialogService: DialogService,
private formBuilder: FormBuilder,
private toastService: ToastService,
) {}
async ngOnInit() {
@@ -167,7 +168,11 @@ export class AccountComponent implements OnInit, OnDestroy {
await this.organizationApiService.save(this.organizationId, request);
this.platformUtilsService.showToast("success", null, this.i18nService.t("organizationUpdated"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("organizationUpdated"),
});
};
submitCollectionManagement = async () => {
@@ -184,11 +189,11 @@ export class AccountComponent implements OnInit, OnDestroy {
await this.organizationApiService.updateCollectionManagement(this.organizationId, request);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("updatedCollectionManagement"),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("updatedCollectionManagement"),
});
};
async deleteOrganization() {

View File

@@ -14,7 +14,7 @@ import { Utils } from "@bitwarden/common/platform/misc/utils";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CipherType } from "@bitwarden/common/vault/enums";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { UserVerificationModule } from "../../../../auth/shared/components/user-verification";
import { SharedModule } from "../../../../shared/shared.module";
@@ -94,6 +94,7 @@ export class DeleteOrganizationDialogComponent implements OnInit, OnDestroy {
private organizationService: OrganizationService,
private organizationApiService: OrganizationApiServiceAbstraction,
private formBuilder: FormBuilder,
private toastService: ToastService,
) {}
ngOnDestroy(): void {
@@ -121,11 +122,11 @@ export class DeleteOrganizationDialogComponent implements OnInit, OnDestroy {
.buildRequest(this.formGroup.value.secret)
.then((request) => this.organizationApiService.delete(this.organization.id, request));
this.platformUtilsService.showToast(
"success",
this.i18nService.t("organizationDeleted"),
this.i18nService.t("organizationDeletedDesc"),
);
this.toastService.showToast({
variant: "success",
title: this.i18nService.t("organizationDeleted"),
message: this.i18nService.t("organizationDeletedDesc"),
});
this.dialogRef.close(DeleteOrganizationDialogResult.Deleted);
};

View File

@@ -14,7 +14,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { OrganizationPlansComponent } from "../../../billing";
import { SharedModule } from "../../../shared";
@@ -68,6 +68,7 @@ export class FamiliesForEnterpriseSetupComponent implements OnInit, OnDestroy {
private organizationService: OrganizationService,
private dialogService: DialogService,
private formBuilder: FormBuilder,
private toastService: ToastService,
) {}
async ngOnInit() {
@@ -76,12 +77,12 @@ export class FamiliesForEnterpriseSetupComponent implements OnInit, OnDestroy {
this.route.queryParams.pipe(first()).subscribe(async (qParams) => {
const error = qParams.token == null;
if (error) {
this.platformUtilsService.showToast(
"error",
null,
this.i18nService.t("sponsoredFamiliesAcceptFailed"),
{ timeout: 10000 },
);
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t("sponsoredFamiliesAcceptFailed"),
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(["/"]);
@@ -139,11 +140,11 @@ export class FamiliesForEnterpriseSetupComponent implements OnInit, OnDestroy {
request.sponsoredOrganizationId = organizationId;
await this.apiService.postRedeemSponsorship(this.token, request);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("sponsoredFamiliesOfferRedeemed"),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("sponsoredFamiliesOfferRedeemed"),
});
await this.syncService.fullSync(true);
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.

View File

@@ -8,7 +8,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { OrganizationUserResetPasswordService } from "../members/services/organization-user-reset-password/organization-user-reset-password.service";
@@ -29,6 +29,7 @@ export class EnrollMasterPasswordReset {
syncService: SyncService,
logService: LogService,
userVerificationService: UserVerificationService,
toastService: ToastService,
) {
const result = await UserVerificationDialogComponent.open(dialogService, {
title: "enrollAccountRecovery",
@@ -71,7 +72,11 @@ export class EnrollMasterPasswordReset {
// Enrollment succeeded
try {
platformUtilsService.showToast("success", null, i18nService.t("enrollPasswordResetSuccess"));
toastService.showToast({
variant: "success",
title: null,
message: i18nService.t("enrollPasswordResetSuccess"),
});
await syncService.fullSync(true);
} catch (e) {
logService.error(e);

View File

@@ -7,6 +7,7 @@ import { ProviderVerifyRecoverDeleteRequest } from "@bitwarden/common/admin-cons
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 { ToastService } from "@bitwarden/components";
@Component({
selector: "app-verify-recover-delete-provider",
@@ -27,6 +28,7 @@ export class VerifyRecoverDeleteProviderComponent implements OnInit {
private i18nService: I18nService,
private route: ActivatedRoute,
private logService: LogService,
private toastService: ToastService,
) {}
async ngOnInit() {
@@ -48,11 +50,11 @@ export class VerifyRecoverDeleteProviderComponent implements OnInit {
request,
);
await this.formPromise;
this.platformUtilsService.showToast(
"success",
this.i18nService.t("providerDeleted"),
this.i18nService.t("providerDeletedDesc"),
);
this.toastService.showToast({
variant: "success",
title: this.i18nService.t("providerDeleted"),
message: this.i18nService.t("providerDeletedDesc"),
});
await this.router.navigate(["/"]);
} catch (e) {
this.logService.error(e);

View File

@@ -8,6 +8,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.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 { ToastService } from "@bitwarden/components";
@Component({
selector: "app-hint",
@@ -30,8 +31,17 @@ export class HintComponent extends BaseHintComponent implements OnInit {
logService: LogService,
loginEmailService: LoginEmailServiceAbstraction,
private formBuilder: FormBuilder,
protected toastService: ToastService,
) {
super(router, i18nService, apiService, platformUtilsService, logService, loginEmailService);
super(
router,
i18nService,
apiService,
platformUtilsService,
logService,
loginEmailService,
toastService,
);
}
ngOnInit(): void {

View File

@@ -28,6 +28,8 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
import { UserId } from "@bitwarden/common/types/guid";
import { ToastService } from "@bitwarden/components";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
import { flagEnabled } from "../../../utils/flags";
@@ -71,6 +73,7 @@ export class LoginComponent extends BaseLoginComponent implements OnInit {
ssoLoginService: SsoLoginServiceAbstraction,
webAuthnLoginService: WebAuthnLoginServiceAbstraction,
registerRouteService: RegisterRouteService,
toastService: ToastService,
) {
super(
devicesApiService,
@@ -92,6 +95,7 @@ export class LoginComponent extends BaseLoginComponent implements OnInit {
ssoLoginService,
webAuthnLoginService,
registerRouteService,
toastService,
);
this.onSuccessfulLoginNavigate = this.goAfterLogIn;
this.showPasswordless = flagEnabled("showPasswordless");
@@ -138,7 +142,7 @@ export class LoginComponent extends BaseLoginComponent implements OnInit {
}
}
async goAfterLogIn() {
async goAfterLogIn(userId: UserId) {
const masterPassword = this.formGroup.value.masterPassword;
// Check master password against policy
@@ -159,7 +163,7 @@ export class LoginComponent extends BaseLoginComponent implements OnInit {
) {
const policiesData: { [id: string]: PolicyData } = {};
this.policies.map((p) => (policiesData[p.id] = PolicyData.fromPolicy(p)));
await this.policyService.replace(policiesData);
await this.policyService.replace(policiesData, userId);
await this.router.navigate(["update-password"]);
return;
}

View File

@@ -9,6 +9,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
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 { SharedModule } from "../../shared";
import { UserKeyRotationModule } from "../key-rotation/user-key-rotation.module";
@@ -35,6 +36,7 @@ export class MigrateFromLegacyEncryptionComponent {
private messagingService: MessagingService,
private logService: LogService,
private syncService: SyncService,
private toastService: ToastService,
) {}
submit = async () => {
@@ -59,12 +61,12 @@ export class MigrateFromLegacyEncryptionComponent {
await this.keyRotationService.rotateUserKeyAndEncryptedData(masterPassword, activeUser);
this.platformUtilsService.showToast(
"success",
this.i18nService.t("keyUpdated"),
this.i18nService.t("logBackInOthersToo"),
{ timeout: 15000 },
);
this.toastService.showToast({
variant: "success",
title: this.i18nService.t("keyUpdated"),
message: this.i18nService.t("logBackInOthersToo"),
timeout: 15000,
});
this.messagingService.send("logout");
} catch (e) {
this.logService.error(e);

View File

@@ -6,6 +6,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { DeleteRecoverRequest } from "@bitwarden/common/models/request/delete-recover.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-recover-delete",
@@ -25,6 +26,7 @@ export class RecoverDeleteComponent {
private apiService: ApiService,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private toastService: ToastService,
) {}
submit = async () => {
@@ -35,11 +37,11 @@ export class RecoverDeleteComponent {
const request = new DeleteRecoverRequest();
request.email = this.email.value.trim().toLowerCase();
await this.apiService.postAccountRecoverDelete(request);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("deleteRecoverEmailSent"),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("deleteRecoverEmailSent"),
});
await this.router.navigate(["/"]);
};

View File

@@ -8,6 +8,7 @@ import { TwoFactorRecoveryRequest } from "@bitwarden/common/auth/models/request/
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
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-recover-two-factor",
@@ -27,6 +28,7 @@ export class RecoverTwoFactorComponent {
private i18nService: I18nService,
private cryptoService: CryptoService,
private loginStrategyService: LoginStrategyServiceAbstraction,
private toastService: ToastService,
) {}
get email(): string {
@@ -53,11 +55,11 @@ export class RecoverTwoFactorComponent {
const key = await this.loginStrategyService.makePreloginKey(this.masterPassword, request.email);
request.masterPasswordHash = await this.cryptoService.hashMasterKey(this.masterPassword, key);
await this.apiService.postTwoFactorRecover(request);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("twoStepRecoverDisabled"),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("twoStepRecoverDisabled"),
});
await this.router.navigate(["/"]);
};
}

View File

@@ -17,7 +17,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
import { AcceptOrganizationInviteService } from "../organization-invite/accept-organization.service";
@@ -52,6 +52,7 @@ export class RegisterFormComponent extends BaseRegisterComponent implements OnIn
auditService: AuditService,
dialogService: DialogService,
acceptOrgInviteService: AcceptOrganizationInviteService,
toastService: ToastService,
) {
super(
formValidationErrorService,
@@ -68,6 +69,7 @@ export class RegisterFormComponent extends BaseRegisterComponent implements OnIn
logService,
auditService,
dialogService,
toastService,
);
super.modifyRegisterRequest = async (request: RegisterRequest) => {
// Org invites are deep linked. Non-existent accounts are redirected to the register page.
@@ -104,11 +106,11 @@ export class RegisterFormComponent extends BaseRegisterComponent implements OnIn
this.enforcedPolicyOptions,
)
) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("masterPasswordPolicyRequirementsNotMet"),
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("masterPasswordPolicyRequirementsNotMet"),
});
return;
}

View File

@@ -15,7 +15,7 @@ import { ProfileResponse } from "@bitwarden/common/models/response/profile.respo
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
type ChangeAvatarDialogData = {
profile: ProfileResponse;
@@ -55,6 +55,7 @@ export class ChangeAvatarDialogComponent implements OnInit, OnDestroy {
private platformUtilsService: PlatformUtilsService,
private avatarService: AvatarService,
private dialogRef: DialogRef,
private toastService: ToastService,
) {
this.profile = data.profile;
}
@@ -93,9 +94,17 @@ export class ChangeAvatarDialogComponent implements OnInit, OnDestroy {
if (Utils.validateHexColor(this.currentSelection) || this.currentSelection == null) {
await this.avatarService.setAvatarColor(this.currentSelection);
this.dialogRef.close();
this.platformUtilsService.showToast("success", null, this.i18nService.t("avatarUpdated"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("avatarUpdated"),
});
} else {
this.platformUtilsService.showToast("error", null, this.i18nService.t("errorOccurred"));
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t("errorOccurred"),
});
}
};

View File

@@ -12,6 +12,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { ToastService } from "@bitwarden/components";
@Component({
selector: "app-change-email",
@@ -39,6 +40,7 @@ export class ChangeEmailComponent implements OnInit {
private stateService: StateService,
private formBuilder: FormBuilder,
private kdfConfigService: KdfConfigService,
private toastService: ToastService,
) {}
async ngOnInit() {
@@ -100,11 +102,11 @@ export class ChangeEmailComponent implements OnInit {
try {
await this.apiService.postEmail(request);
this.reset();
this.platformUtilsService.showToast(
"success",
this.i18nService.t("emailChanged"),
this.i18nService.t("logBackIn"),
);
this.toastService.showToast({
variant: "success",
title: this.i18nService.t("emailChanged"),
message: this.i18nService.t("logBackIn"),
});
this.messagingService.send("logout");
} catch (e) {
this.logService.error(e);

View File

@@ -7,6 +7,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { LogService } from "@bitwarden/common/platform/abstractions/log.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";
@Component({
selector: "app-deauthorize-sessions",
@@ -23,6 +24,7 @@ export class DeauthorizeSessionsComponent {
private userVerificationService: UserVerificationService,
private messagingService: MessagingService,
private logService: LogService,
private toastService: ToastService,
) {}
async submit() {
@@ -31,11 +33,11 @@ export class DeauthorizeSessionsComponent {
.buildRequest(this.masterPassword)
.then((request) => this.apiService.postSecurityStamp(request));
await this.formPromise;
this.platformUtilsService.showToast(
"success",
this.i18nService.t("sessionsDeauthorized"),
this.i18nService.t("logBackIn"),
);
this.toastService.showToast({
variant: "success",
title: this.i18nService.t("sessionsDeauthorized"),
message: this.i18nService.t("logBackIn"),
});
this.messagingService.send("logout");
} catch (e) {
this.logService.error(e);

View File

@@ -7,7 +7,7 @@ import { Verification } from "@bitwarden/common/auth/types/verification";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
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";
@Component({
templateUrl: "delete-account-dialog.component.html",
@@ -24,6 +24,7 @@ export class DeleteAccountDialogComponent {
private formBuilder: FormBuilder,
private accountApiService: AccountApiService,
private dialogRef: DialogRef,
private toastService: ToastService,
) {}
submit = async () => {
@@ -31,11 +32,11 @@ export class DeleteAccountDialogComponent {
const verification = this.deleteForm.get("verification").value;
await this.accountApiService.deleteAccount(verification);
this.dialogRef.close();
this.platformUtilsService.showToast(
"success",
this.i18nService.t("accountDeleted"),
this.i18nService.t("accountDeletedDesc"),
);
this.toastService.showToast({
variant: "success",
title: this.i18nService.t("accountDeleted"),
message: this.i18nService.t("accountDeletedDesc"),
});
} catch (e) {
if (e instanceof ErrorResponse && e.statusCode === 400) {
this.invalidSecret = true;

View File

@@ -8,7 +8,7 @@ import { ProfileResponse } from "@bitwarden/common/models/response/profile.respo
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { ChangeAvatarDialogComponent } from "./change-avatar-dialog.component";
@@ -33,6 +33,7 @@ export class ProfileComponent implements OnInit, OnDestroy {
private platformUtilsService: PlatformUtilsService,
private stateService: StateService,
private dialogService: DialogService,
private toastService: ToastService,
) {}
async ngOnInit() {
@@ -64,6 +65,10 @@ export class ProfileComponent implements OnInit, OnDestroy {
submit = async () => {
const request = new UpdateProfileRequest(this.formGroup.get("name").value);
await this.apiService.putProfile(request);
this.platformUtilsService.showToast("success", null, this.i18nService.t("accountUpdated"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("accountUpdated"),
});
};
}

View File

@@ -22,7 +22,7 @@ import { UserId } from "@bitwarden/common/types/guid";
import { MasterKey, UserKey } from "@bitwarden/common/types/key";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
import { UserKeyRotationService } from "../key-rotation/user-key-rotation.service";
@@ -60,6 +60,7 @@ export class ChangePasswordComponent
kdfConfigService: KdfConfigService,
masterPasswordService: InternalMasterPasswordServiceAbstraction,
accountService: AccountService,
toastService: ToastService,
) {
super(
i18nService,
@@ -73,6 +74,7 @@ export class ChangePasswordComponent
kdfConfigService,
masterPasswordService,
accountService,
toastService,
);
}
@@ -141,11 +143,11 @@ export class ChangePasswordComponent
this.masterPasswordHint != null &&
this.masterPasswordHint.toLowerCase() === this.masterPassword.toLowerCase()
) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("hintEqualsPassword"),
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("hintEqualsPassword"),
});
return;
}
@@ -159,11 +161,11 @@ export class ChangePasswordComponent
async setupSubmitActions() {
if (this.currentMasterPassword == null || this.currentMasterPassword === "") {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("masterPasswordRequired"),
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("masterPasswordRequired"),
});
return false;
}
@@ -194,11 +196,11 @@ export class ChangePasswordComponent
const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey);
if (userKey == null) {
this.platformUtilsService.showToast(
"error",
null,
this.i18nService.t("invalidMasterPassword"),
);
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t("invalidMasterPassword"),
});
return;
}
@@ -225,14 +227,18 @@ export class ChangePasswordComponent
await this.formPromise;
this.platformUtilsService.showToast(
"success",
this.i18nService.t("masterPasswordChanged"),
this.i18nService.t("logBackIn"),
);
this.toastService.showToast({
variant: "success",
title: this.i18nService.t("masterPasswordChanged"),
message: this.i18nService.t("logBackIn"),
});
this.messagingService.send("logout");
} catch {
this.platformUtilsService.showToast("error", null, this.i18nService.t("errorOccurred"));
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t("errorOccurred"),
});
}
}

View File

@@ -5,7 +5,7 @@ import { FormBuilder, Validators } from "@angular/forms";
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 { EmergencyAccessService } from "../../emergency-access";
import { EmergencyAccessType } from "../../emergency-access/enums/emergency-access-type";
@@ -51,6 +51,7 @@ export class EmergencyAccessAddEditComponent implements OnInit {
private platformUtilsService: PlatformUtilsService,
private logService: LogService,
private dialogRef: DialogRef<EmergencyAccessAddEditDialogResult>,
private toastService: ToastService,
) {}
async ngOnInit() {
this.editMode = this.loading = this.params.emergencyAccessId != null;
@@ -104,11 +105,14 @@ export class EmergencyAccessAddEditComponent implements OnInit {
this.addEditForm.value.waitTime,
);
}
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t(this.editMode ? "editedUserId" : "invitedUsers", this.params.name),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t(
this.editMode ? "editedUserId" : "invitedUsers",
this.params.name,
),
});
this.dialogRef.close(EmergencyAccessAddEditDialogResult.Saved);
} catch (e) {
this.logService.error(e);

View File

@@ -10,7 +10,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { EmergencyAccessService } from "../../emergency-access";
import { EmergencyAccessStatusType } from "../../emergency-access/enums/emergency-access-status-type";
@@ -66,6 +66,7 @@ export class EmergencyAccessComponent implements OnInit {
protected dialogService: DialogService,
billingAccountProfileStateService: BillingAccountProfileStateService,
protected organizationManagementPreferencesService: OrganizationManagementPreferencesService,
private toastService: ToastService,
) {
this.canAccessPremium$ = billingAccountProfileStateService.hasPremiumFromAnySource$;
}
@@ -121,11 +122,11 @@ export class EmergencyAccessComponent implements OnInit {
}
this.actionPromise = this.emergencyAccessService.reinvite(contact.id);
await this.actionPromise;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("hasBeenReinvited", contact.email),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("hasBeenReinvited", contact.email),
});
this.actionPromise = null;
}
@@ -153,11 +154,11 @@ export class EmergencyAccessComponent implements OnInit {
if (result === EmergencyAccessConfirmDialogResult.Confirmed) {
await this.emergencyAccessService.confirm(contact.id, contact.granteeId);
updateUser();
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("hasBeenConfirmed", this.userNamePipe.transform(contact)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("hasBeenConfirmed", this.userNamePipe.transform(contact)),
});
}
return;
}
@@ -166,11 +167,11 @@ export class EmergencyAccessComponent implements OnInit {
await this.actionPromise;
updateUser();
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("hasBeenConfirmed", this.userNamePipe.transform(contact)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("hasBeenConfirmed", this.userNamePipe.transform(contact)),
});
this.actionPromise = null;
}
@@ -187,11 +188,11 @@ export class EmergencyAccessComponent implements OnInit {
try {
await this.emergencyAccessService.delete(details.id);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("removedUserId", this.userNamePipe.transform(details)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("removedUserId", this.userNamePipe.transform(details)),
});
if (details instanceof GranteeEmergencyAccess) {
this.removeGrantee(details);
@@ -221,11 +222,11 @@ export class EmergencyAccessComponent implements OnInit {
await this.emergencyAccessService.requestAccess(details.id);
details.status = EmergencyAccessStatusType.RecoveryInitiated;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("requestSent", this.userNamePipe.transform(details)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("requestSent", this.userNamePipe.transform(details)),
});
}
async approve(details: GranteeEmergencyAccess) {
@@ -250,22 +251,22 @@ export class EmergencyAccessComponent implements OnInit {
await this.emergencyAccessService.approve(details.id);
details.status = EmergencyAccessStatusType.RecoveryApproved;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("emergencyApproved", this.userNamePipe.transform(details)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("emergencyApproved", this.userNamePipe.transform(details)),
});
}
async reject(details: GranteeEmergencyAccess) {
await this.emergencyAccessService.reject(details.id);
details.status = EmergencyAccessStatusType.Confirmed;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("emergencyRejected", this.userNamePipe.transform(details)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("emergencyRejected", this.userNamePipe.transform(details)),
});
}
takeover = async (details: GrantorEmergencyAccess) => {
@@ -278,11 +279,11 @@ export class EmergencyAccessComponent implements OnInit {
});
const result = await lastValueFrom(dialogRef.closed);
if (result === EmergencyAccessTakeoverResultType.Done) {
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("passwordResetFor", this.userNamePipe.transform(details)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("passwordResetFor", this.userNamePipe.transform(details)),
});
}
};

View File

@@ -15,7 +15,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { KdfType } from "@bitwarden/common/platform/enums";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
import { EmergencyAccessService } from "../../../emergency-access";
@@ -64,6 +64,7 @@ export class EmergencyAccessTakeoverComponent
kdfConfigService: KdfConfigService,
masterPasswordService: InternalMasterPasswordServiceAbstraction,
accountService: AccountService,
protected toastService: ToastService,
) {
super(
i18nService,
@@ -77,6 +78,7 @@ export class EmergencyAccessTakeoverComponent
kdfConfigService,
masterPasswordService,
accountService,
toastService,
);
}
@@ -114,11 +116,11 @@ export class EmergencyAccessTakeoverComponent
);
} catch (e) {
this.logService.error(e);
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("unexpectedError"),
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("unexpectedError"),
});
}
this.dialogRef.close(EmergencyAccessTakeoverResultType.Done);
};

View File

@@ -12,6 +12,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 { KdfType } from "@bitwarden/common/platform/enums";
import { ToastService } from "@bitwarden/components";
@Component({
selector: "app-change-kdf-confirmation",
@@ -35,6 +36,7 @@ export class ChangeKdfConfirmationComponent {
private messagingService: MessagingService,
@Inject(DIALOG_DATA) params: { kdf: KdfType; kdfConfig: KdfConfig },
private accountService: AccountService,
private toastService: ToastService,
) {
this.kdfConfig = params.kdfConfig;
this.masterPassword = null;
@@ -46,11 +48,11 @@ export class ChangeKdfConfirmationComponent {
}
this.loading = true;
await this.makeKeyAndSaveAsync();
this.platformUtilsService.showToast(
"success",
this.i18nService.t("encKeySettingsChanged"),
this.i18nService.t("logBackIn"),
);
this.toastService.showToast({
variant: "success",
title: this.i18nService.t("encKeySettingsChanged"),
message: this.i18nService.t("logBackIn"),
});
this.messagingService.send("logout");
this.loading = false;
};

View File

@@ -17,7 +17,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
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 { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { TwoFactorBaseComponent } from "./two-factor-base.component";
@@ -68,6 +68,7 @@ export class TwoFactorAuthenticatorComponent
private accountService: AccountService,
dialogService: DialogService,
private configService: ConfigService,
protected toastService: ToastService,
) {
super(
apiService,
@@ -76,6 +77,7 @@ export class TwoFactorAuthenticatorComponent
logService,
userVerificationService,
dialogService,
toastService,
);
this.qrScript = window.document.createElement("script");
this.qrScript.src = "scripts/qrious.min.js";
@@ -148,7 +150,11 @@ export class TwoFactorAuthenticatorComponent
request.userVerificationToken = this.userVerificationToken;
await this.apiService.deleteTwoFactorAuthenticator(request);
this.enabled = false;
this.platformUtilsService.showToast("success", null, this.i18nService.t("twoStepDisabled"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("twoStepDisabled"),
});
this.onUpdated.emit(false);
}

View File

@@ -10,7 +10,7 @@ import { AuthResponseBase } from "@bitwarden/common/auth/types/auth-response";
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";
@Directive()
export abstract class TwoFactorBaseComponent {
@@ -33,6 +33,7 @@ export abstract class TwoFactorBaseComponent {
protected logService: LogService,
protected userVerificationService: UserVerificationService,
protected dialogService: DialogService,
protected toastService: ToastService,
) {}
protected auth(authResponse: AuthResponseBase) {
@@ -76,7 +77,11 @@ export abstract class TwoFactorBaseComponent {
}
await promise;
this.enabled = false;
this.platformUtilsService.showToast("success", null, this.i18nService.t("twoStepDisabled"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("twoStepDisabled"),
});
this.onUpdated.emit(false);
} catch (e) {
this.logService.error(e);
@@ -102,7 +107,11 @@ export abstract class TwoFactorBaseComponent {
await this.apiService.putTwoFactorDisable(request);
}
this.enabled = false;
this.platformUtilsService.showToast("success", null, this.i18nService.t("twoStepDisabled"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("twoStepDisabled"),
});
this.onUpdated.emit(false);
}

View File

@@ -11,7 +11,7 @@ import { AuthResponse } from "@bitwarden/common/auth/types/auth-response";
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 { TwoFactorBaseComponent } from "./two-factor-base.component";
@@ -40,6 +40,7 @@ export class TwoFactorDuoComponent extends TwoFactorBaseComponent implements OnI
dialogService: DialogService,
private formBuilder: FormBuilder,
private dialogRef: DialogRef,
protected toastService: ToastService,
) {
super(
apiService,
@@ -48,6 +49,7 @@ export class TwoFactorDuoComponent extends TwoFactorBaseComponent implements OnI
logService,
userVerificationService,
dialogService,
toastService,
);
}

View File

@@ -14,7 +14,7 @@ import { AuthResponse } from "@bitwarden/common/auth/types/auth-response";
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 { TwoFactorBaseComponent } from "./two-factor-base.component";
@@ -45,6 +45,7 @@ export class TwoFactorEmailComponent extends TwoFactorBaseComponent implements O
dialogService: DialogService,
private formBuilder: FormBuilder,
private dialogRef: DialogRef,
protected toastService: ToastService,
) {
super(
apiService,
@@ -53,6 +54,7 @@ export class TwoFactorEmailComponent extends TwoFactorBaseComponent implements O
logService,
userVerificationService,
dialogService,
toastService,
);
}
get token() {

View File

@@ -16,7 +16,7 @@ import { AuthResponse } from "@bitwarden/common/auth/types/auth-response";
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 { TwoFactorBaseComponent } from "./two-factor-base.component";
@@ -61,6 +61,7 @@ export class TwoFactorWebAuthnComponent extends TwoFactorBaseComponent {
logService: LogService,
userVerificationService: UserVerificationService,
dialogService: DialogService,
toastService: ToastService,
) {
super(
apiService,
@@ -69,6 +70,7 @@ export class TwoFactorWebAuthnComponent extends TwoFactorBaseComponent {
logService,
userVerificationService,
dialogService,
toastService,
);
this.auth(data);
}

View File

@@ -55,7 +55,7 @@ export class TwoFactorYubiKeyComponent extends TwoFactorBaseComponent implements
userVerificationService: UserVerificationService,
dialogService: DialogService,
private formBuilder: FormBuilder,
private toastService: ToastService,
protected toastService: ToastService,
) {
super(
apiService,
@@ -64,6 +64,7 @@ export class TwoFactorYubiKeyComponent extends TwoFactorBaseComponent implements
logService,
userVerificationService,
dialogService,
toastService,
);
}

View File

@@ -6,7 +6,13 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { AsyncActionsModule, BannerModule, ButtonModule, LinkModule } from "@bitwarden/components";
import {
AsyncActionsModule,
BannerModule,
ButtonModule,
LinkModule,
ToastService,
} from "@bitwarden/components";
@Component({
standalone: true,
@@ -25,22 +31,27 @@ export class VerifyEmailComponent {
private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService,
private tokenService: TokenService,
private toastService: ToastService,
) {}
async verifyEmail(): Promise<void> {
await this.apiService.refreshIdentityToken();
if (await this.tokenService.getEmailVerified()) {
this.onVerified.emit(true);
this.platformUtilsService.showToast("success", null, this.i18nService.t("emailVerified"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("emailVerified"),
});
return;
}
await this.apiService.postAccountVerifyEmail();
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("checkInboxForVerification"),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("checkInboxForVerification"),
});
}
send = async () => {

View File

@@ -9,7 +9,7 @@ import { ErrorResponse } from "@bitwarden/common/models/response/error.response"
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 { WebauthnLoginAdminService } from "../../../core";
import { CredentialCreateOptionsView } from "../../../core/views/credential-create-options.view";
@@ -60,6 +60,7 @@ export class CreateCredentialDialogComponent implements OnInit {
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private logService: LogService,
private toastService: ToastService,
) {}
ngOnInit(): void {
@@ -102,11 +103,11 @@ export class CreateCredentialDialogComponent implements OnInit {
this.invalidSecret = true;
} else {
this.logService?.error(error);
this.platformUtilsService.showToast(
"error",
this.i18nService.t("unexpectedError"),
error.message,
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("unexpectedError"),
message: error.message,
});
}
return;
}
@@ -162,17 +163,17 @@ export class CreateCredentialDialogComponent implements OnInit {
);
if (await firstValueFrom(this.hasPasskeys$)) {
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("passkeySaved", name),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("passkeySaved", name),
});
} else {
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("loginWithPasskeyEnabled"),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("loginWithPasskeyEnabled"),
});
}
this.dialogRef.close(CreateCredentialDialogResult.Success);

View File

@@ -8,7 +8,7 @@ import { ErrorResponse } from "@bitwarden/common/models/response/error.response"
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 { WebauthnLoginAdminService } from "../../../core";
import { WebauthnLoginCredentialView } from "../../../core/views/webauthn-login-credential.view";
@@ -38,6 +38,7 @@ export class DeleteCredentialDialogComponent implements OnInit, OnDestroy {
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private logService: LogService,
private toastService: ToastService,
) {}
ngOnInit(): void {
@@ -55,17 +56,21 @@ export class DeleteCredentialDialogComponent implements OnInit, OnDestroy {
this.dialogRef.disableClose = true;
try {
await this.webauthnService.deleteCredential(this.credential.id, this.formGroup.value.secret);
this.platformUtilsService.showToast("success", null, this.i18nService.t("passkeyRemoved"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("passkeyRemoved"),
});
} catch (error) {
if (error instanceof ErrorResponse && error.statusCode === 400) {
this.invalidSecret = true;
} else {
this.logService?.error(error);
this.platformUtilsService.showToast(
"error",
this.i18nService.t("unexpectedError"),
error.message,
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("unexpectedError"),
message: error.message,
});
}
return false;
} finally {

View File

@@ -9,7 +9,7 @@ import {
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.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";
/**
* @deprecated Jan 24, 2024: Use new libs/auth UserVerificationDialogComponent instead.
@@ -25,8 +25,17 @@ export class UserVerificationPromptComponent extends BaseUserVerificationPrompt
formBuilder: FormBuilder,
platformUtilsService: PlatformUtilsService,
i18nService: I18nService,
toastService: ToastService,
) {
super(null, data, userVerificationService, formBuilder, platformUtilsService, i18nService);
super(
null,
data,
userVerificationService,
formBuilder,
platformUtilsService,
i18nService,
toastService,
);
}
override close(success: boolean) {

View File

@@ -25,6 +25,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { ToastService } from "@bitwarden/components";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
@Component({
@@ -60,6 +61,7 @@ export class SsoComponent extends BaseSsoComponent implements OnInit {
configService: ConfigService,
masterPasswordService: InternalMasterPasswordServiceAbstraction,
accountService: AccountService,
toastService: ToastService,
) {
super(
ssoLoginService,
@@ -78,6 +80,7 @@ export class SsoComponent extends BaseSsoComponent implements OnInit {
configService,
masterPasswordService,
accountService,
toastService,
);
this.redirectUri = window.location.origin + "/sso-connector.html";
this.clientId = "web";

View File

@@ -17,7 +17,13 @@ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/envi
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 { LinkModule, TypographyModule, CheckboxModule, DialogService } from "@bitwarden/components";
import {
LinkModule,
TypographyModule,
CheckboxModule,
DialogService,
ToastService,
} from "@bitwarden/components";
import { TwoFactorAuthAuthenticatorComponent } from "../../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-authenticator.component";
import { TwoFactorAuthEmailComponent } from "../../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-email.component";
@@ -81,6 +87,7 @@ export class TwoFactorAuthComponent extends BaseTwoFactorAuthComponent {
accountService: AccountService,
formBuilder: FormBuilder,
@Inject(WINDOW) protected win: Window,
toastService: ToastService,
) {
super(
loginStrategyService,
@@ -100,6 +107,7 @@ export class TwoFactorAuthComponent extends BaseTwoFactorAuthComponent {
accountService,
formBuilder,
win,
toastService,
);
this.onSuccessfulLoginNavigate = this.goAfterLogIn;
}

View File

@@ -8,6 +8,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { ToastService } from "@bitwarden/components";
@Component({
selector: "app-verify-email-token",
@@ -23,6 +24,7 @@ export class VerifyEmailTokenComponent implements OnInit {
private apiService: ApiService,
private logService: LogService,
private stateService: StateService,
private toastService: ToastService,
) {}
ngOnInit() {
@@ -36,7 +38,11 @@ export class VerifyEmailTokenComponent implements OnInit {
if (await this.stateService.getIsAuthenticated()) {
await this.apiService.refreshIdentityToken();
}
this.platformUtilsService.showToast("success", null, this.i18nService.t("emailVerified"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("emailVerified"),
});
// 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(["/"]);
@@ -45,7 +51,11 @@ export class VerifyEmailTokenComponent implements OnInit {
this.logService.error(e);
}
}
this.platformUtilsService.showToast("error", null, this.i18nService.t("emailVerifiedFailed"));
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t("emailVerifiedFailed"),
});
// 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(["/"]);

View File

@@ -7,6 +7,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { VerifyDeleteRecoverRequest } from "@bitwarden/common/models/request/verify-delete-recover.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-verify-recover-delete",
@@ -26,6 +27,7 @@ export class VerifyRecoverDeleteComponent implements OnInit {
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private route: ActivatedRoute,
private toastService: ToastService,
) {}
ngOnInit() {
@@ -44,11 +46,11 @@ export class VerifyRecoverDeleteComponent implements OnInit {
submit = async () => {
const request = new VerifyDeleteRecoverRequest(this.userId, this.token);
await this.apiService.postAccountRecoverDeleteToken(request);
this.platformUtilsService.showToast(
"success",
this.i18nService.t("accountDeleted"),
this.i18nService.t("accountDeletedDesc"),
);
this.toastService.showToast({
variant: "success",
title: this.i18nService.t("accountDeleted"),
message: this.i18nService.t("accountDeletedDesc"),
});
await this.router.navigate(["/"]);
};
}

View File

@@ -56,4 +56,3 @@
{{ "save" | i18n }}
</button>
</form>
<app-payment [showMethods]="false"></app-payment>

View File

@@ -1,43 +1,36 @@
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core";
import { Component, EventEmitter, Input, Output } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { FormBuilder, Validators } from "@angular/forms";
import { Subject, takeUntil } from "rxjs";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
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",
templateUrl: "adjust-subscription.component.html",
})
export class AdjustSubscription implements OnInit, OnDestroy {
export class AdjustSubscription {
@Input() organizationId: string;
@Input() maxAutoscaleSeats: number;
@Input() currentSeatCount: number;
@Input() seatPrice = 0;
@Input() interval = "year";
@Output() onAdjusted = new EventEmitter();
private destroy$ = new Subject<void>();
adjustSubscriptionForm = this.formBuilder.group({
newSeatCount: [0, [Validators.min(0)]],
limitSubscription: [false],
newMaxSeats: [0, [Validators.min(0)]],
});
get limitSubscription(): boolean {
return this.adjustSubscriptionForm.value.limitSubscription;
}
constructor(
private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService,
private organizationApiService: OrganizationApiServiceAbstraction,
private formBuilder: FormBuilder,
private toastService: ToastService,
) {}
ngOnInit() {
) {
this.adjustSubscriptionForm.patchValue({
newSeatCount: this.currentSeatCount,
limitSubscription: this.maxAutoscaleSeats != null,
@@ -45,7 +38,7 @@ export class AdjustSubscription implements OnInit, OnDestroy {
});
this.adjustSubscriptionForm
.get("limitSubscription")
.valueChanges.pipe(takeUntil(this.destroy$))
.valueChanges.pipe(takeUntilDestroyed())
.subscribe((value: boolean) => {
if (value) {
this.adjustSubscriptionForm
@@ -63,10 +56,6 @@ export class AdjustSubscription implements OnInit, OnDestroy {
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
submit = async () => {
this.adjustSubscriptionForm.markAllAsTouched();
if (this.adjustSubscriptionForm.invalid) {
@@ -99,18 +88,15 @@ export class AdjustSubscription implements OnInit, OnDestroy {
: 0;
}
get additionalMaxSeatCount(): number {
return this.adjustSubscriptionForm.value.newMaxSeats
? this.adjustSubscriptionForm.value.newMaxSeats - this.currentSeatCount
: 0;
}
get maxSeatTotal(): number {
return Math.abs((this.adjustSubscriptionForm.value.newMaxSeats ?? 0) * this.seatPrice);
}
get seatTotalCost(): number {
const totalSeat = Math.abs(this.adjustSubscriptionForm.value.newSeatCount * this.seatPrice);
return totalSeat;
return Math.abs(this.adjustSubscriptionForm.value.newSeatCount * this.seatPrice);
}
get limitSubscription(): boolean {
return this.adjustSubscriptionForm.value.limitSubscription;
}
}

View File

@@ -30,6 +30,8 @@
bitIconButton="bwi-clone"
bitSuffix
type="button"
showToast
[valueLabel]="'billingSyncKey' | i18n"
[appCopyClick]="clientSecret"
[appA11yTitle]="'copyValue' | i18n"
></button>

View File

@@ -37,7 +37,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { DialogService, ToastService } from "@bitwarden/components";
import { PaymentComponent } from "../shared/payment.component";
import { PaymentComponent } from "../shared/payment/payment.component";
import { TaxInfoComponent } from "../shared/tax-info.component";
type ChangePlanDialogParams = {

View File

@@ -1,7 +1,9 @@
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { featureFlaggedRoute } from "@bitwarden/angular/platform/utils/feature-flagged-route";
import { canAccessBillingTab } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { organizationPermissionsGuard } from "../../admin-console/organizations/guards/org-permissions.guard";
import { organizationIsUnmanaged } from "../../billing/guards/organization-is-unmanaged.guard";
@@ -11,6 +13,7 @@ import { PaymentMethodComponent } from "../shared";
import { OrgBillingHistoryViewComponent } from "./organization-billing-history-view.component";
import { OrganizationSubscriptionCloudComponent } from "./organization-subscription-cloud.component";
import { OrganizationSubscriptionSelfhostComponent } from "./organization-subscription-selfhost.component";
import { OrganizationPaymentMethodComponent } from "./payment-method/organization-payment-method.component";
const routes: Routes = [
{
@@ -25,17 +28,21 @@ const routes: Routes = [
: OrganizationSubscriptionCloudComponent,
data: { titleId: "subscription" },
},
{
path: "payment-method",
component: PaymentMethodComponent,
canActivate: [
organizationPermissionsGuard((org) => org.canEditPaymentMethods),
organizationIsUnmanaged,
],
data: {
titleId: "paymentMethod",
...featureFlaggedRoute({
defaultComponent: PaymentMethodComponent,
flaggedComponent: OrganizationPaymentMethodComponent,
featureFlag: FeatureFlag.AC2476_DeprecateStripeSourcesAPI,
routeOptions: {
path: "payment-method",
canActivate: [
organizationPermissionsGuard((org) => org.canEditPaymentMethods),
organizationIsUnmanaged,
],
data: {
titleId: "paymentMethod",
},
},
},
}),
{
path: "history",
component: OrgBillingHistoryViewComponent,

View File

@@ -15,6 +15,7 @@ import { OrganizationBillingRoutingModule } from "./organization-billing-routing
import { OrganizationPlansComponent } from "./organization-plans.component";
import { OrganizationSubscriptionCloudComponent } from "./organization-subscription-cloud.component";
import { OrganizationSubscriptionSelfhostComponent } from "./organization-subscription-selfhost.component";
import { OrganizationPaymentMethodComponent } from "./payment-method/organization-payment-method.component";
import { SecretsManagerAdjustSubscriptionComponent } from "./sm-adjust-subscription.component";
import { SecretsManagerSubscribeStandaloneComponent } from "./sm-subscribe-standalone.component";
import { SubscriptionHiddenComponent } from "./subscription-hidden.component";
@@ -42,6 +43,7 @@ import { SubscriptionStatusComponent } from "./subscription-status.component";
SubscriptionHiddenComponent,
SubscriptionStatusComponent,
ChangePlanDialogComponent,
OrganizationPaymentMethodComponent,
],
})
export class OrganizationBillingModule {}

View File

@@ -41,7 +41,7 @@ import { ToastService } from "@bitwarden/components";
import { OrganizationCreateModule } from "../../admin-console/organizations/create/organization-create.module";
import { BillingSharedModule, secretsManagerSubscribeFormFactory } from "../shared";
import { PaymentComponent } from "../shared/payment.component";
import { PaymentComponent } from "../shared/payment/payment.component";
import { TaxInfoComponent } from "../shared/tax-info.component";
interface OnSuccessArgs {

View File

@@ -222,8 +222,10 @@
</ng-container>
</ng-container>
<h2 bitTypography="h2" class="tw-mt-7">{{ "selfHostingTitle" | i18n }}</h2>
<p bitTypography="body1">
<h2 bitTypography="h2" *ngIf="shownSelfHost()" class="tw-mt-7">
{{ "selfHostingTitle" | i18n }}
</h2>
<p bitTypography="body1" *ngIf="shownSelfHost()">
{{ "selfHostingEnterpriseOrganizationSectionCopy" | i18n }}
<a
href="https://bitwarden.com/help/licensing-on-premise/#retrieve-organization-license"
@@ -240,7 +242,7 @@
buttonType="secondary"
type="button"
(click)="downloadLicense()"
*ngIf="canDownloadLicense"
*ngIf="canDownloadLicense && shownSelfHost()"
[disabled]="showDownloadLicense"
>
{{ "downloadLicense" | i18n }}

View File

@@ -345,6 +345,13 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
);
}
shownSelfHost(): boolean {
return (
this.sub?.plan.productTier !== ProductTierType.Teams &&
this.sub?.plan.productTier !== ProductTierType.Free
);
}
cancelSubscription = async () => {
const reference = openOffboardingSurvey(this.dialogService, {
data: {

View File

@@ -0,0 +1,63 @@
<app-header></app-header>
<bit-container>
<ng-container *ngIf="loading">
<i
class="bwi bwi-spinner bwi-spin tw-text-muted"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
</ng-container>
<ng-container *ngIf="!loading">
<!-- Account Credit -->
<bit-section>
<h2 bitTypography="h2">
{{ accountCreditHeaderText }}
</h2>
<p class="tw-text-lg tw-font-bold">{{ Math.abs(accountCredit) | currency: "$" }}</p>
<p bitTypography="body1">{{ "creditAppliedDesc" | i18n }}</p>
<button type="button" bitButton buttonType="secondary" [bitAction]="addAccountCredit">
{{ "addCredit" | i18n }}
</button>
</bit-section>
<!-- Payment Method -->
<bit-section>
<h2 bitTypography="h2">{{ "paymentMethod" | i18n }}</h2>
<p *ngIf="!paymentSource" bitTypography="body1">{{ "noPaymentMethod" | i18n }}</p>
<ng-container *ngIf="paymentSource">
<app-verify-bank-account
*ngIf="paymentSource.needsVerification"
[onSubmit]="verifyBankAccount"
(submitted)="load()"
>
</app-verify-bank-account>
<p>
<i class="bwi bwi-fw" [ngClass]="paymentSourceClasses"></i>
{{ paymentSource.description }}
<span *ngIf="paymentSource.needsVerification">- {{ "unverified" | i18n }}</span>
</p>
</ng-container>
<button type="button" bitButton buttonType="secondary" [bitAction]="updatePaymentMethod">
{{ updatePaymentSourceButtonText }}
</button>
<p *ngIf="subscriptionIsUnpaid" bitTypography="body1">
{{ "paymentChargedWithUnpaidSubscription" | i18n }}
</p>
</bit-section>
<!-- Tax Information -->
<bit-section>
<h2 bitTypography="h2">{{ "taxInformation" | i18n }}</h2>
<p bitTypography="body1">{{ "taxInformationDesc" | i18n }}</p>
<app-tax-info></app-tax-info>
<button
type="button"
bitButton
bitFormButton
buttonType="primary"
[bitAction]="updateTaxInformation"
>
{{ "save" | i18n }}
</button>
</bit-section>
</ng-container>
</bit-container>

View File

@@ -0,0 +1,169 @@
import { Component, ViewChild } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { ActivatedRoute, Router } from "@angular/router";
import { from, lastValueFrom, switchMap } from "rxjs";
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions";
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request";
import { VerifyBankAccountRequest } from "@bitwarden/common/billing/models/request/verify-bank-account.request";
import { PaymentSourceResponse } from "@bitwarden/common/billing/models/response/payment-source.response";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { DialogService, ToastService } from "@bitwarden/components";
import { TaxInfoComponent } from "../../shared";
import {
AddCreditDialogResult,
openAddCreditDialog,
} from "../../shared/add-credit-dialog.component";
import {
AdjustPaymentDialogV2Component,
AdjustPaymentDialogV2ResultType,
} from "../../shared/adjust-payment-dialog/adjust-payment-dialog-v2.component";
@Component({
templateUrl: "./organization-payment-method.component.html",
})
export class OrganizationPaymentMethodComponent {
@ViewChild(TaxInfoComponent) taxInfoComponent: TaxInfoComponent;
organizationId: string;
accountCredit: number;
paymentSource?: PaymentSourceResponse;
subscriptionStatus?: string;
loading = true;
protected readonly Math = Math;
constructor(
private activatedRoute: ActivatedRoute,
private billingApiService: BillingApiServiceAbstraction,
private dialogService: DialogService,
private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService,
private router: Router,
private toastService: ToastService,
) {
this.activatedRoute.params
.pipe(
takeUntilDestroyed(),
switchMap(({ organizationId }) => {
if (this.platformUtilsService.isSelfHost()) {
return from(this.router.navigate(["/settings/subscription"]));
}
this.organizationId = organizationId;
return from(this.load());
}),
)
.subscribe();
}
protected addAccountCredit = async (): Promise<void> => {
const dialogRef = openAddCreditDialog(this.dialogService, {
data: {
organizationId: this.organizationId,
},
});
const result = await lastValueFrom(dialogRef.closed);
if (result === AddCreditDialogResult.Added) {
await this.load();
}
};
protected load = async (): Promise<void> => {
this.loading = true;
const { accountCredit, paymentSource, subscriptionStatus } =
await this.billingApiService.getOrganizationPaymentMethod(this.organizationId);
this.accountCredit = accountCredit;
this.paymentSource = paymentSource;
this.subscriptionStatus = subscriptionStatus;
this.loading = false;
};
protected updatePaymentMethod = async (): Promise<void> => {
const dialogRef = AdjustPaymentDialogV2Component.open(this.dialogService, {
data: {
initialPaymentMethod: this.paymentSource?.type,
organizationId: this.organizationId,
},
});
const result = await lastValueFrom(dialogRef.closed);
if (result === AdjustPaymentDialogV2ResultType.Submitted) {
await this.load();
}
};
protected updateTaxInformation = async (): Promise<void> => {
this.taxInfoComponent.taxFormGroup.updateValueAndValidity();
this.taxInfoComponent.taxFormGroup.markAllAsTouched();
if (this.taxInfoComponent.taxFormGroup.invalid) {
return;
}
const request = new ExpandedTaxInfoUpdateRequest();
request.country = this.taxInfoComponent.country;
request.postalCode = this.taxInfoComponent.postalCode;
request.taxId = this.taxInfoComponent.taxId;
request.line1 = this.taxInfoComponent.line1;
request.line2 = this.taxInfoComponent.line2;
request.city = this.taxInfoComponent.city;
request.state = this.taxInfoComponent.state;
await this.billingApiService.updateOrganizationTaxInformation(this.organizationId, request);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("taxInfoUpdated"),
});
};
protected verifyBankAccount = async (request: VerifyBankAccountRequest): Promise<void> => {
await this.billingApiService.verifyOrganizationBankAccount(this.organizationId, request);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("verifiedBankAccount"),
});
};
protected get accountCreditHeaderText(): string {
const key = this.accountCredit <= 0 ? "accountBalance" : "accountCredit";
return this.i18nService.t(key);
}
protected get paymentSourceClasses() {
if (this.paymentSource == null) {
return [];
}
switch (this.paymentSource.type) {
case PaymentMethodType.Card:
return ["bwi-credit-card"];
case PaymentMethodType.BankAccount:
return ["bwi-bank"];
case PaymentMethodType.Check:
return ["bwi-money"];
case PaymentMethodType.PayPal:
return ["bwi-paypal text-primary"];
default:
return [];
}
}
protected get subscriptionIsUnpaid(): boolean {
return this.subscriptionStatus === "unpaid";
}
protected get updatePaymentSourceButtonText(): string {
const key = this.paymentSource == null ? "addPaymentMethod" : "changePaymentMethod";
return this.i18nService.t(key);
}
}

View File

@@ -0,0 +1,24 @@
<bit-dialog dialogSize="large" [title]="dialogHeader">
<ng-container bitDialogContent>
<app-payment-v2
[showAccountCredit]="false"
[showBankAccount]="!!organizationId"
[initialPaymentMethod]="initialPaymentMethod"
></app-payment-v2>
<app-tax-info (onCountryChanged)="onCountryChanged()"></app-tax-info>
</ng-container>
<ng-container bitDialogFooter>
<button type="submit" bitButton bitFormButton buttonType="primary" [bitAction]="submit">
{{ "submit" }}
</button>
<button
type="button"
bitButton
bitFormButton
buttonType="secondary"
[bitDialogClose]="ResultType.Closed"
>
{{ "cancel" | i18n }}
</button>
</ng-container>
</bit-dialog>

View File

@@ -0,0 +1,123 @@
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
import { forwardRef, Component, Inject, ViewChild } from "@angular/core";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions";
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
import { PaymentRequest } from "@bitwarden/common/billing/models/request/payment.request";
import { UpdatePaymentMethodRequest } from "@bitwarden/common/billing/models/request/update-payment-method.request";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { DialogService, ToastService } from "@bitwarden/components";
import { TaxInfoComponent } from "../";
import { PaymentV2Component } from "../payment/payment-v2.component";
export interface AdjustPaymentDialogV2Params {
initialPaymentMethod?: PaymentMethodType;
organizationId?: string;
}
export enum AdjustPaymentDialogV2ResultType {
Closed = "closed",
Submitted = "submitted",
}
@Component({
templateUrl: "./adjust-payment-dialog-v2.component.html",
})
export class AdjustPaymentDialogV2Component {
@ViewChild(PaymentV2Component) paymentComponent: PaymentV2Component;
@ViewChild(forwardRef(() => TaxInfoComponent)) taxInfoComponent: TaxInfoComponent;
protected readonly PaymentMethodType = PaymentMethodType;
protected readonly ResultType = AdjustPaymentDialogV2ResultType;
protected dialogHeader: string;
protected initialPaymentMethod: PaymentMethodType;
protected organizationId?: string;
constructor(
private apiService: ApiService,
private billingApiService: BillingApiServiceAbstraction,
@Inject(DIALOG_DATA) protected dialogParams: AdjustPaymentDialogV2Params,
private dialogRef: DialogRef<AdjustPaymentDialogV2ResultType>,
private i18nService: I18nService,
private toastService: ToastService,
) {
const key = this.dialogParams.initialPaymentMethod ? "changePaymentMethod" : "addPaymentMethod";
this.dialogHeader = this.i18nService.t(key);
this.initialPaymentMethod = this.dialogParams.initialPaymentMethod ?? PaymentMethodType.Card;
this.organizationId = this.dialogParams.organizationId;
}
onCountryChanged = () => {
if (this.taxInfoComponent.taxInfo.country === "US") {
this.paymentComponent.showBankAccount = !!this.organizationId;
} else {
this.paymentComponent.showBankAccount = false;
if (this.paymentComponent.selected === PaymentMethodType.BankAccount) {
this.paymentComponent.select(PaymentMethodType.Card);
}
}
};
submit = async (): Promise<void> => {
this.taxInfoComponent.taxFormGroup.updateValueAndValidity();
this.taxInfoComponent.taxFormGroup.markAllAsTouched();
if (this.taxInfoComponent.taxFormGroup.invalid) {
return;
}
if (!this.organizationId) {
await this.updatePremiumUserPaymentMethod();
} else {
await this.updateOrganizationPaymentMethod();
}
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("updatedPaymentMethod"),
});
this.dialogRef.close(AdjustPaymentDialogV2ResultType.Submitted);
};
private updateOrganizationPaymentMethod = async () => {
const paymentSource = await this.paymentComponent.tokenize();
const request = new UpdatePaymentMethodRequest();
request.paymentSource = paymentSource;
request.taxInformation = {
country: this.taxInfoComponent.country,
postalCode: this.taxInfoComponent.postalCode,
taxId: this.taxInfoComponent.taxId,
line1: this.taxInfoComponent.line1,
line2: this.taxInfoComponent.line2,
city: this.taxInfoComponent.city,
state: this.taxInfoComponent.state,
};
await this.billingApiService.updateOrganizationPaymentMethod(this.organizationId, request);
};
private updatePremiumUserPaymentMethod = async () => {
const { type, token } = await this.paymentComponent.tokenize();
const request = new PaymentRequest();
request.paymentMethodType = type;
request.paymentToken = token;
request.country = this.taxInfoComponent.country;
request.postalCode = this.taxInfoComponent.postalCode;
await this.apiService.postAccountPayment(request);
};
static open = (
dialogService: DialogService,
dialogConfig: DialogConfig<AdjustPaymentDialogV2Params>,
) =>
dialogService.open<AdjustPaymentDialogV2ResultType>(
AdjustPaymentDialogV2Component,
dialogConfig,
);
}

View File

@@ -10,8 +10,8 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { DialogService, ToastService } from "@bitwarden/components";
import { PaymentComponent } from "./payment.component";
import { TaxInfoComponent } from "./tax-info.component";
import { PaymentComponent } from "../payment/payment.component";
import { TaxInfoComponent } from "../tax-info.component";
export interface AdjustPaymentDialogData {
organizationId: string;

View File

@@ -12,7 +12,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { DialogService, ToastService } from "@bitwarden/components";
import { PaymentComponent } from "./payment.component";
import { PaymentComponent } from "./payment/payment.component";
export interface AdjustStorageDialogData {
storageGbPrice: number;

View File

@@ -4,19 +4,29 @@ import { HeaderModule } from "../../layouts/header/header.module";
import { SharedModule } from "../../shared";
import { AddCreditDialogComponent } from "./add-credit-dialog.component";
import { AdjustPaymentDialogComponent } from "./adjust-payment-dialog.component";
import { AdjustPaymentDialogV2Component } from "./adjust-payment-dialog/adjust-payment-dialog-v2.component";
import { AdjustPaymentDialogComponent } from "./adjust-payment-dialog/adjust-payment-dialog.component";
import { AdjustStorageComponent } from "./adjust-storage.component";
import { BillingHistoryComponent } from "./billing-history.component";
import { OffboardingSurveyComponent } from "./offboarding-survey.component";
import { PaymentV2Component } from "./payment/payment-v2.component";
import { PaymentComponent } from "./payment/payment.component";
import { PaymentMethodComponent } from "./payment-method.component";
import { PaymentComponent } from "./payment.component";
import { SecretsManagerSubscribeComponent } from "./sm-subscribe.component";
import { TaxInfoComponent } from "./tax-info.component";
import { UpdateLicenseDialogComponent } from "./update-license-dialog.component";
import { UpdateLicenseComponent } from "./update-license.component";
import { VerifyBankAccountComponent } from "./verify-bank-account/verify-bank-account.component";
@NgModule({
imports: [SharedModule, PaymentComponent, TaxInfoComponent, HeaderModule],
imports: [
SharedModule,
PaymentComponent,
TaxInfoComponent,
HeaderModule,
PaymentV2Component,
VerifyBankAccountComponent,
],
declarations: [
AddCreditDialogComponent,
AdjustPaymentDialogComponent,
@@ -27,6 +37,7 @@ import { UpdateLicenseComponent } from "./update-license.component";
UpdateLicenseComponent,
UpdateLicenseDialogComponent,
OffboardingSurveyComponent,
AdjustPaymentDialogV2Component,
],
exports: [
SharedModule,
@@ -38,6 +49,7 @@ import { UpdateLicenseComponent } from "./update-license.component";
UpdateLicenseComponent,
UpdateLicenseDialogComponent,
OffboardingSurveyComponent,
VerifyBankAccountComponent,
],
})
export class BillingSharedModule {}

View File

@@ -1,5 +1,5 @@
export * from "./billing-shared.module";
export * from "./payment-method.component";
export * from "./payment.component";
export * from "./payment/payment.component";
export * from "./sm-subscribe.component";
export * from "./tax-info.component";

View File

@@ -2,7 +2,7 @@ import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
import { Component, Inject } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";
import { BillingApiServiceAbstraction as BillingApiService } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction";
import { BillingApiServiceAbstraction as BillingApiService } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { DialogService, ToastService } from "@bitwarden/components";

View File

@@ -19,7 +19,7 @@ import { AddCreditDialogResult, openAddCreditDialog } from "./add-credit-dialog.
import {
AdjustPaymentDialogResult,
openAdjustPaymentDialog,
} from "./adjust-payment-dialog.component";
} from "./adjust-payment-dialog/adjust-payment-dialog.component";
import { TaxInfoComponent } from "./tax-info.component";
@Component({

View File

@@ -0,0 +1,158 @@
<form [formGroup]="formGroup" [bitSubmit]="submit">
<div class="tw-mb-4 tw-text-lg">
<bit-radio-group formControlName="paymentMethod">
<bit-radio-button id="card-payment-method" [value]="PaymentMethodType.Card">
<bit-label>
<i class="bwi bwi-fw bwi-credit-card" aria-hidden="true"></i>
{{ "creditCard" | i18n }}
</bit-label>
</bit-radio-button>
<bit-radio-button
id="bank-payment-method"
[value]="PaymentMethodType.BankAccount"
*ngIf="showBankAccount"
>
<bit-label>
<i class="bwi bwi-fw bwi-bank" aria-hidden="true"></i>
{{ "bankAccount" | i18n }}
</bit-label>
</bit-radio-button>
<bit-radio-button
id="paypal-payment-method"
[value]="PaymentMethodType.PayPal"
*ngIf="showPayPal"
>
<bit-label>
<i class="bwi bwi-fw bwi-paypal" aria-hidden="true"></i>
{{ "payPal" | i18n }}
</bit-label>
</bit-radio-button>
<bit-radio-button
id="credit-payment-method"
[value]="PaymentMethodType.Credit"
*ngIf="showAccountCredit"
>
<bit-label>
<i class="bwi bwi-fw bwi-dollar" aria-hidden="true"></i>
{{ "accountCredit" | i18n }}
</bit-label>
</bit-radio-button>
</bit-radio-group>
</div>
<!-- Card -->
<ng-container *ngIf="usingCard">
<div class="tw-grid tw-grid-cols-2 tw-gap-4 tw-mb-4">
<div class="tw-col-span-1">
<label for="stripe-card-number">
{{ "number" | i18n }}
<span class="tw-text-xs tw-font-normal">({{ "required" | i18n }})</span>
</label>
<div id="stripe-card-number" class="form-control stripe-form-control"></div>
</div>
<div class="tw-col-span-1 tw-flex tw-items-end">
<img
src="../../../images/cards.png"
alt="Visa, MasterCard, Discover, AmEx, JCB, Diners Club, UnionPay"
class="tw-max-w-full"
/>
</div>
<div class="tw-col-span-1">
<label for="stripe-card-expiry">
{{ "expiration" | i18n }}
<span class="tw-text-xs tw-font-normal">({{ "required" | i18n }})</span>
</label>
<div id="stripe-card-expiry" class="form-control stripe-form-control"></div>
</div>
<div class="tw-col-span-1">
<div class="tw-flex">
<label for="stripe-card-cvc">
{{ "securityCode" | i18n }}
<span class="tw-text-xs tw-font-normal">({{ "required" | i18n }})</span>
</label>
<a
href="https://www.cvvnumber.com/cvv.html"
tabindex="-1"
target="_blank"
rel="noreferrer"
class="ml-auto"
appA11yTitle="{{ 'learnMore' | i18n }}"
>
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
</a>
</div>
<div id="stripe-card-cvc" class="form-control stripe-form-control"></div>
</div>
</div>
</ng-container>
<!-- Bank Account -->
<ng-container *ngIf="showBankAccount && usingBankAccount">
<app-callout type="warning" title="{{ 'verifyBankAccount' | i18n }}">
{{ "verifyBankAccountInitialDesc" | i18n }} {{ "verifyBankAccountFailureWarning" | i18n }}
</app-callout>
<div class="tw-grid tw-grid-cols-2 tw-gap-4 tw-mb-4" formGroupName="bankInformation">
<bit-form-field class="tw-col-span-1" disableMargin>
<bit-label>{{ "routingNumber" | i18n }}</bit-label>
<input
bitInput
id="routingNumber"
type="text"
formControlName="routingNumber"
required
appInputVerbatim
/>
</bit-form-field>
<bit-form-field class="tw-col-span-1" disableMargin>
<bit-label>{{ "accountNumber" | i18n }}</bit-label>
<input
bitInput
id="accountNumber"
type="text"
formControlName="accountNumber"
required
appInputVerbatim
/>
</bit-form-field>
<bit-form-field class="tw-col-span-1" disableMargin>
<bit-label>{{ "accountHolderName" | i18n }}</bit-label>
<input
id="accountHolderName"
bitInput
type="text"
formControlName="accountHolderName"
required
appInputVerbatim
/>
</bit-form-field>
<bit-form-field class="tw-col-span-1" disableMargin>
<bit-label>{{ "bankAccountType" | i18n }}</bit-label>
<bit-select id="accountHolderType" formControlName="accountHolderType" required>
<bit-option [value]="''" label="-- {{ 'select' | i18n }} --"></bit-option>
<bit-option
[value]="'company'"
label="{{ 'bankAccountTypeCompany' | i18n }}"
></bit-option>
<bit-option
[value]="'individual'"
label="{{ 'bankAccountTypeIndividual' | i18n }}"
></bit-option>
</bit-select>
</bit-form-field>
</div>
</ng-container>
<!-- PayPal -->
<ng-container *ngIf="showPayPal && usingPayPal">
<div class="tw-mb-3">
<div id="braintree-container" class="tw-mb-1 tw-content-center"></div>
<small class="tw-text-muted">{{ "paypalClickSubmit" | i18n }}</small>
</div>
</ng-container>
<!-- Account Credit -->
<ng-container *ngIf="showAccountCredit && usingAccountCredit">
<app-callout type="info">
{{ "makeSureEnoughCredit" | i18n }}
</app-callout>
</ng-container>
<button *ngIf="!!onSubmit" bitButton bitFormButton buttonType="primary" type="submit">
{{ "submit" | i18n }}
</button>
</form>

View File

@@ -0,0 +1,192 @@
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core";
import { FormControl, FormGroup, Validators } from "@angular/forms";
import { Subject } from "rxjs";
import { takeUntil } from "rxjs/operators";
import {
BillingApiServiceAbstraction,
BraintreeServiceAbstraction,
StripeServiceAbstraction,
} from "@bitwarden/common/billing/abstractions";
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
import { TokenizedPaymentSourceRequest } from "@bitwarden/common/billing/models/request/tokenized-payment-source.request";
import { SharedModule } from "../../../shared";
/**
* Render a form that allows the user to enter their payment method, tokenize it against one of our payment providers and,
* optionally, submit it using the {@link onSubmit} function if it is provided.
*
* This component is meant to replace the existing {@link PaymentComponent} which is using the deprecated Stripe Sources API.
*/
@Component({
selector: "app-payment-v2",
templateUrl: "./payment-v2.component.html",
standalone: true,
imports: [SharedModule],
})
export class PaymentV2Component implements OnInit, OnDestroy {
/** Show account credit as a payment option. */
@Input() showAccountCredit: boolean = true;
/** Show bank account as a payment option. */
@Input() showBankAccount: boolean = true;
/** Show PayPal as a payment option. */
@Input() showPayPal: boolean = true;
/** The payment method selected by default when the component renders. */
@Input() private initialPaymentMethod: PaymentMethodType = PaymentMethodType.Card;
/** If provided, will be invoked with the tokenized payment source during form submission. */
@Input() protected onSubmit?: (request: TokenizedPaymentSourceRequest) => Promise<void>;
@Output() submitted = new EventEmitter<PaymentMethodType>();
private destroy$ = new Subject<void>();
protected formGroup = new FormGroup({
paymentMethod: new FormControl<PaymentMethodType>(null),
bankInformation: new FormGroup({
routingNumber: new FormControl<string>("", [Validators.required]),
accountNumber: new FormControl<string>("", [Validators.required]),
accountHolderName: new FormControl<string>("", [Validators.required]),
accountHolderType: new FormControl<string>("", [Validators.required]),
}),
});
protected PaymentMethodType = PaymentMethodType;
constructor(
private billingApiService: BillingApiServiceAbstraction,
private braintreeService: BraintreeServiceAbstraction,
private stripeService: StripeServiceAbstraction,
) {}
ngOnInit(): void {
this.formGroup.controls.paymentMethod.patchValue(this.initialPaymentMethod);
this.stripeService.loadStripe(
{
cardNumber: "#stripe-card-number",
cardExpiry: "#stripe-card-expiry",
cardCvc: "#stripe-card-cvc",
},
this.initialPaymentMethod === PaymentMethodType.Card,
);
if (this.showPayPal) {
this.braintreeService.loadBraintree(
"#braintree-container",
this.initialPaymentMethod === PaymentMethodType.PayPal,
);
}
this.formGroup
.get("paymentMethod")
.valueChanges.pipe(takeUntil(this.destroy$))
.subscribe((type) => {
this.onPaymentMethodChange(type);
});
}
/** Programmatically select the provided payment method. */
select = (paymentMethod: PaymentMethodType) => {
this.formGroup.value.paymentMethod = paymentMethod;
};
protected submit = async () => {
const { type, token } = await this.tokenize();
await this.onSubmit({ type, token });
this.submitted.emit(type);
};
/**
* Tokenize the payment method information entered by the user against one of our payment providers.
*
* - {@link PaymentMethodType.Card} => [Stripe.confirmCardSetup]{@link https://docs.stripe.com/js/setup_intents/confirm_card_setup}
* - {@link PaymentMethodType.BankAccount} => [Stripe.confirmUsBankAccountSetup]{@link https://docs.stripe.com/js/setup_intents/confirm_us_bank_account_setup}
* - {@link PaymentMethodType.PayPal} => [Braintree.requestPaymentMethod]{@link https://braintree.github.io/braintree-web-drop-in/docs/current/Dropin.html#requestPaymentMethod}
* */
async tokenize(): Promise<{ type: PaymentMethodType; token: string }> {
const type = this.selected;
if (this.usingStripe) {
const clientSecret = await this.billingApiService.createSetupIntent(type);
if (this.usingBankAccount) {
const token = await this.stripeService.setupBankAccountPaymentMethod(clientSecret, {
accountHolderName: this.formGroup.value.bankInformation.accountHolderName,
routingNumber: this.formGroup.value.bankInformation.routingNumber,
accountNumber: this.formGroup.value.bankInformation.accountNumber,
accountHolderType: this.formGroup.value.bankInformation.accountHolderType,
});
return {
type,
token,
};
}
if (this.usingCard) {
const token = await this.stripeService.setupCardPaymentMethod(clientSecret);
return {
type,
token,
};
}
}
if (this.usingPayPal) {
const token = await this.braintreeService.requestPaymentMethod();
return {
type,
token,
};
}
return null;
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
this.stripeService.unloadStripe();
if (this.showPayPal) {
this.braintreeService.unloadBraintree();
}
}
private onPaymentMethodChange(type: PaymentMethodType): void {
switch (type) {
case PaymentMethodType.Card: {
this.stripeService.mountElements();
break;
}
case PaymentMethodType.PayPal: {
this.braintreeService.createDropin();
break;
}
}
}
get selected(): PaymentMethodType {
return this.formGroup.value.paymentMethod;
}
protected get usingAccountCredit(): boolean {
return this.selected === PaymentMethodType.Credit;
}
protected get usingBankAccount(): boolean {
return this.selected === PaymentMethodType.BankAccount;
}
protected get usingCard(): boolean {
return this.selected === PaymentMethodType.Card;
}
protected get usingPayPal(): boolean {
return this.selected === PaymentMethodType.PayPal;
}
private get usingStripe(): boolean {
return this.usingBankAccount || this.usingCard;
}
}

View File

@@ -7,7 +7,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { SharedModule } from "../../shared";
import { SharedModule } from "../../../shared";
@Component({
selector: "app-payment",

View File

@@ -0,0 +1,18 @@
<app-callout type="warning" title="{{ 'verifyBankAccount' | i18n }}">
<p>{{ "verifyBankAccountDesc" | i18n }} {{ "verifyBankAccountFailureWarning" | i18n }}</p>
<form [formGroup]="formGroup" [bitSubmit]="submit">
<bit-form-field class="tw-mr-2 tw-w-40">
<bit-label>{{ "amountX" | i18n: "1" }}</bit-label>
<input bitInput type="number" step="1" placeholder="xx" formControlName="amount1" />
<span bitPrefix>$0.</span>
</bit-form-field>
<bit-form-field class="tw-mr-2 tw-w-40">
<bit-label>{{ "amountX" | i18n: "2" }}</bit-label>
<input bitInput type="number" step="1" placeholder="xx" formControlName="amount2" />
<span bitPrefix>$0.</span>
</bit-form-field>
<button *ngIf="onSubmit" type="submit" bitButton bitFormButton buttonType="primary">
{{ "submit" | i18n }}
</button>
</form>
</app-callout>

View File

@@ -0,0 +1,41 @@
import { Component, EventEmitter, Input, Output } from "@angular/core";
import { FormBuilder, FormControl, Validators } from "@angular/forms";
import { VerifyBankAccountRequest } from "@bitwarden/common/billing/models/request/verify-bank-account.request";
import { SharedModule } from "../../../shared";
@Component({
selector: "app-verify-bank-account",
templateUrl: "./verify-bank-account.component.html",
standalone: true,
imports: [SharedModule],
})
export class VerifyBankAccountComponent {
@Input() onSubmit?: (request: VerifyBankAccountRequest) => Promise<void>;
@Output() submitted = new EventEmitter();
protected formGroup = this.formBuilder.group({
amount1: new FormControl<number>(null, [
Validators.required,
Validators.min(0),
Validators.max(99),
]),
amount2: new FormControl<number>(null, [
Validators.required,
Validators.min(0),
Validators.max(99),
]),
});
constructor(private formBuilder: FormBuilder) {}
submit = async () => {
const request = new VerifyBankAccountRequest(
this.formGroup.value.amount1,
this.formGroup.value.amount2,
);
await this.onSubmit(request);
this.submitted.emit();
};
}

View File

@@ -40,6 +40,7 @@ import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platfor
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service";
import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service";
import { ThemeType } from "@bitwarden/common/platform/enums";
import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service";
// eslint-disable-next-line import/no-restricted-paths -- Implementation for memory storage
@@ -62,6 +63,7 @@ import { WebSetPasswordJitService, WebRegistrationFinishService, WebLoginService
import { AcceptOrganizationInviteService } from "../auth/organization-invite/accept-organization.service";
import { HtmlStorageService } from "../core/html-storage.service";
import { I18nService } from "../core/i18n.service";
import { WebBiometricsService } from "../platform/web-biometric.service";
import { WebEnvironmentService } from "../platform/web-environment.service";
import { WebMigrationRunner } from "../platform/web-migration-runner";
import { WebStorageServiceProvider } from "../platform/web-storage-service.provider";
@@ -164,6 +166,11 @@ const safeProviders: SafeProvider[] = [
useClass: WebEnvironmentService,
deps: [WINDOW, StateProvider, AccountService],
}),
safeProvider({
provide: BiometricsService,
useClass: WebBiometricsService,
deps: [],
}),
safeProvider({
provide: ThemeStateService,
useFactory: (globalStateProvider: GlobalStateProvider) =>

View File

@@ -186,20 +186,6 @@ export class WebPlatformUtilsService implements PlatformUtilsService {
throw new Error("Cannot read from clipboard on web.");
}
supportsBiometric() {
return Promise.resolve(false);
}
authenticateBiometric() {
return Promise.resolve(false);
}
biometricsNeedsSetup: () => Promise<boolean>;
biometricsSupportsAutoSetup(): Promise<boolean> {
throw new Error("Method not implemented.");
}
biometricsSetup: () => Promise<void>;
supportsSecureStorage() {
return false;
}

View File

@@ -69,6 +69,7 @@ import { PreferencesComponent } from "./settings/preferences.component";
import { GeneratorComponent } from "./tools/generator.component";
import { ReportsModule } from "./tools/reports";
import { AccessComponent } from "./tools/send/access.component";
import { SendAccessExplainerComponent } from "./tools/send/send-access-explainer.component";
import { SendComponent } from "./tools/send/send.component";
import { VaultModule } from "./vault/individual-vault/vault.module";
@@ -146,11 +147,6 @@ const routes: Routes = [
canActivate: [unauthGuardFn()],
data: { titleId: "deleteAccount" } satisfies DataProperties,
},
{
path: "send/:sendId/:key",
component: AccessComponent,
data: { titleId: "Bitwarden Send" } satisfies DataProperties,
},
{
path: "update-temp-password",
component: UpdateTempPasswordComponent,
@@ -211,6 +207,24 @@ const routes: Routes = [
},
],
},
{
path: "send/:sendId/:key",
data: {
pageTitle: "viewSend",
showReadonlyHostname: true,
} satisfies DataProperties & AnonLayoutWrapperData,
children: [
{
path: "",
component: AccessComponent,
},
{
path: "",
outlet: "secondary",
component: SendAccessExplainerComponent,
},
],
},
{
path: "set-password-jit",
canActivate: [canAccessFeature(FeatureFlag.EmailVerification)],

View File

@@ -0,0 +1,27 @@
import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service";
export class WebBiometricsService extends BiometricsService {
async supportsBiometric(): Promise<boolean> {
return false;
}
async isBiometricUnlockAvailable(): Promise<boolean> {
return false;
}
async authenticateBiometric(): Promise<boolean> {
throw new Error("Method not implemented.");
}
async biometricsNeedsSetup(): Promise<boolean> {
throw new Error("Method not implemented.");
}
async biometricsSupportsAutoSetup(): Promise<boolean> {
throw new Error("Method not implemented.");
}
async biometricsSetup(): Promise<void> {
throw new Error("Method not implemented.");
}
}

View File

@@ -167,7 +167,10 @@ export class PreferencesComponent implements OnInit, OnDestroy {
);
return;
}
const values = this.form.value;
// must get raw value b/c the vault timeout action is disabled when a policy is applied
// which removes the timeout action property and value from the normal form.value.
const values = this.form.getRawValue();
const activeAcct = await firstValueFrom(this.accountService.activeAccount$);

View File

@@ -1,85 +1,52 @@
<form [formGroup]="formGroup" [bitSubmit]="load">
<div
class="tw-mx-auto tw-mt-5 tw-flex tw-max-w-xl tw-flex-col tw-items-center tw-justify-center tw-p-8"
>
<img class="logo logo-themed" alt="Bitwarden" />
<div class="tw-mt-5 tw-w-full">
<h2 bitTypography="h2" class="tw-mb-4 tw-text-center">View Send</h2>
</div>
<div class="tw-w-full tw-text-center" *ngIf="creatorIdentifier != null">
<p>{{ "sendAccessCreatorIdentifier" | i18n: creatorIdentifier }}</p>
</div>
<bit-callout *ngIf="hideEmail" type="warning" title="{{ 'warning' | i18n }}">
{{ "viewSendHiddenEmailWarning" | i18n }}
<a bitLink href="https://bitwarden.com/help/receive-send/" target="_blank" rel="noreferrer">{{
"learnMore" | i18n
}}</a
>.
</bit-callout>
<div
class="tw-mt-3 tw-w-10/12 tw-rounded-md tw-border tw-border-solid tw-border-secondary-300 tw-bg-background tw-p-6"
>
<ng-container *ngIf="!loading; else spinner">
<app-send-access-password
(setPasswordEvent)="setPassword($event)"
*ngIf="passwordRequired && !error"
></app-send-access-password>
<bit-no-items [icon]="expiredSendIcon" class="tw-text-main" *ngIf="unavailable">
<ng-container slot="description">{{ "sendAccessUnavailable" | i18n }}</ng-container>
</bit-no-items>
<bit-no-items [icon]="expiredSendIcon" class="tw-text-main" *ngIf="error">
<ng-container slot="description">{{ "unexpectedErrorSend" | i18n }}</ng-container>
</bit-no-items>
<div *ngIf="!passwordRequired && send && !error && !unavailable">
<p class="tw-text-center">
<b>{{ send.name }}</b>
</p>
<hr />
<!-- Text -->
<ng-container *ngIf="send.type === sendType.Text">
<app-send-access-text [send]="send"></app-send-access-text>
</ng-container>
<!-- File -->
<ng-container *ngIf="send.type === sendType.File">
<app-send-access-file
[send]="send"
[decKey]="decKey"
[accessRequest]="accessRequest"
></app-send-access-file>
</ng-container>
<p *ngIf="expirationDate" class="tw-text-center tw-text-muted">
Expires: {{ expirationDate | date: "medium" }}
</p>
</div>
<bit-callout *ngIf="hideEmail" type="warning" title="{{ 'warning' | i18n }}">
{{ "viewSendHiddenEmailWarning" | i18n }}
<a bitLink href="https://bitwarden.com/help/receive-send/" target="_blank" rel="noreferrer">{{
"learnMore" | i18n
}}</a
>.
</bit-callout>
<ng-container *ngIf="!loading; else spinner">
<app-send-access-password
(setPasswordEvent)="setPassword($event)"
*ngIf="passwordRequired && !error"
></app-send-access-password>
<bit-no-items [icon]="expiredSendIcon" class="tw-text-main" *ngIf="unavailable">
<ng-container slot="description">{{ "sendAccessUnavailable" | i18n }}</ng-container>
</bit-no-items>
<bit-no-items [icon]="expiredSendIcon" class="tw-text-main" *ngIf="error">
<ng-container slot="description">{{ "unexpectedErrorSend" | i18n }}</ng-container>
</bit-no-items>
<div *ngIf="!passwordRequired && send && !error && !unavailable">
<p class="tw-text-center">
<b>{{ send.name }}</b>
</p>
<hr />
<!-- Text -->
<ng-container *ngIf="send.type === sendType.Text">
<app-send-access-text [send]="send"></app-send-access-text>
</ng-container>
<ng-template #spinner>
<div class="tw-text-center">
<i
class="bwi bwi-spinner bwi-spin bwi-2x tw-text-muted"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
</div>
</ng-template>
</div>
<div class="tw-mt-5 tw-w-10/12 tw-text-center tw-text-muted">
<p bitTypography="body2" class="tw-mb-0">
{{ "sendAccessTaglineProductDesc" | i18n }}
{{ "sendAccessTaglineLearnMore" | i18n }}
<a
bitLink
href="https://www.bitwarden.com/products/send?source=web-vault"
target="_blank"
rel="noreferrer"
>Bitwarden Send</a
>
{{ "sendAccessTaglineOr" | i18n }}
<a bitLink [routerLink]="registerRoute$ | async" target="_blank" rel="noreferrer">{{
"sendAccessTaglineSignUp" | i18n
}}</a>
{{ "sendAccessTaglineTryToday" | i18n }}
<!-- File -->
<ng-container *ngIf="send.type === sendType.File">
<app-send-access-file
[send]="send"
[decKey]="decKey"
[accessRequest]="accessRequest"
></app-send-access-file>
</ng-container>
<p *ngIf="expirationDate" class="tw-text-center tw-text-muted">
Expires: {{ expirationDate | date: "medium" }}
</p>
</div>
</div>
</ng-container>
<ng-template #spinner>
<div class="tw-text-center">
<i
class="bwi bwi-spinner bwi-spin bwi-2x tw-text-muted"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
</div>
</ng-template>
</form>

View File

@@ -2,6 +2,7 @@ import { Component, OnInit } from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { ActivatedRoute } from "@angular/router";
import { AnonLayoutWrapperDataService } from "@bitwarden/auth/angular";
import { RegisterRouteService } from "@bitwarden/auth/common";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
@@ -70,6 +71,7 @@ export class AccessComponent implements OnInit {
private i18nService: I18nService,
private configService: ConfigService,
private registerRouteService: RegisterRouteService,
private layoutWrapperDataService: AnonLayoutWrapperDataService,
protected formBuilder: FormBuilder,
) {}
@@ -151,6 +153,15 @@ export class AccessComponent implements OnInit {
!this.passwordRequired &&
!this.loading &&
!this.unavailable;
if (this.creatorIdentifier != null) {
this.layoutWrapperDataService.setAnonLayoutWrapperData({
pageSubtitle: {
subtitle: this.i18nService.t("sendAccessCreatorIdentifier", this.creatorIdentifier),
translate: false,
},
});
}
};
protected setPassword(password: string) {

View File

@@ -0,0 +1,18 @@
<div class="tw-text-center tw-text-muted">
<p bitTypography="body2" class="tw-mb-0">
{{ "sendAccessTaglineProductDesc" | i18n }}
{{ "sendAccessTaglineLearnMore" | i18n }}
<a
bitLink
href="https://www.bitwarden.com/products/send?source=web-vault"
target="_blank"
rel="noreferrer"
>Bitwarden Send</a
>
{{ "sendAccessTaglineOr" | i18n }}
<a bitLink [routerLink]="registerRoute$ | async" target="_blank" rel="noreferrer">{{
"sendAccessTaglineSignUp" | i18n
}}</a>
{{ "sendAccessTaglineTryToday" | i18n }}
</p>
</div>

View File

@@ -0,0 +1,17 @@
import { Component } from "@angular/core";
import { RegisterRouteService } from "@bitwarden/auth/common";
import { SharedModule } from "../../shared";
@Component({
selector: "app-send-access-explainer",
templateUrl: "send-access-explainer.component.html",
standalone: true,
imports: [SharedModule],
})
export class SendAccessExplainerComponent {
// TODO: remove when email verification flag is removed
registerRoute$ = this.registerRouteService.registerRoute$();
constructor(private registerRouteService: RegisterRouteService) {}
}

View File

@@ -11,7 +11,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
@Component({
selector: "app-vault-collections",
@@ -29,6 +29,7 @@ export class CollectionsComponent extends BaseCollectionsComponent implements On
accountService: AccountService,
protected dialogRef: DialogRef,
@Inject(DIALOG_DATA) params: CollectionsDialogParams,
toastService: ToastService,
) {
super(
collectionService,
@@ -39,6 +40,7 @@ export class CollectionsComponent extends BaseCollectionsComponent implements On
logService,
configService,
accountService,
toastService,
);
this.cipherId = params?.cipherId;
}

View File

@@ -15,7 +15,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SyncService } from "@bitwarden/common/platform/sync";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { OrganizationUserResetPasswordService } from "../../../../admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service";
import { EnrollMasterPasswordReset } from "../../../../admin-console/organizations/users/enroll-master-password-reset.component";
@@ -50,6 +50,7 @@ export class OrganizationOptionsComponent implements OnInit, OnDestroy {
private dialogService: DialogService,
private resetPasswordService: OrganizationUserResetPasswordService,
private userVerificationService: UserVerificationService,
private toastService: ToastService,
) {}
async ngOnInit() {
@@ -158,6 +159,7 @@ export class OrganizationOptionsComponent implements OnInit, OnDestroy {
this.syncService,
this.logService,
this.userVerificationService,
this.toastService,
);
} else {
// Remove reset password

View File

@@ -718,12 +718,12 @@ export class VaultComponent implements OnInit, OnDestroy {
const result: ViewCipherDialogCloseResult = await lastValueFrom(dialogRef.closed);
// If the dialog was closed by deleting the cipher, refresh the vault.
if (result.action === ViewCipherDialogResult.deleted) {
if (result?.action === ViewCipherDialogResult.deleted) {
this.refresh();
}
// If the dialog was closed by any other action (close button, escape key, etc), navigate back to the vault.
if (!result.action) {
if (!result?.action) {
this.go({ cipherId: null, itemId: null, action: null });
}
}

View File

@@ -15,7 +15,7 @@ import { CipherData } from "@bitwarden/common/vault/models/data/cipher.data";
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
import { CipherCollectionsRequest } from "@bitwarden/common/vault/models/request/cipher-collections.request";
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import {
CollectionsComponent as BaseCollectionsComponent,
@@ -41,6 +41,7 @@ export class CollectionsComponent extends BaseCollectionsComponent {
accountService: AccountService,
protected dialogRef: DialogRef,
@Inject(DIALOG_DATA) params: OrgVaultCollectionsDialogParams,
toastService: ToastService,
) {
super(
collectionService,
@@ -53,6 +54,7 @@ export class CollectionsComponent extends BaseCollectionsComponent {
accountService,
dialogRef,
params,
toastService,
);
this.allowSelectNone = true;
this.collectionIds = params?.collectionIds;

View File

@@ -113,10 +113,32 @@
{{ "new" | i18n }}<i class="bwi bwi-angle-down tw-ml-2" aria-hidden="true"></i>
</button>
<bit-menu #addOptions aria-labelledby="newItemDropdown">
<button type="button" bitMenuItem (click)="addCipher()">
<i class="bwi bwi-fw bwi-globe" aria-hidden="true"></i>
{{ "item" | i18n }}
</button>
<ng-container [ngSwitch]="extensionRefreshEnabled">
<ng-container *ngSwitchCase="true">
<button type="button" bitMenuItem (click)="addCipher(CipherType.Login)">
<i class="bwi bwi-globe" slot="start" aria-hidden="true"></i>
{{ "typeLogin" | i18n }}
</button>
<button type="button" bitMenuItem (click)="addCipher(CipherType.Card)">
<i class="bwi bwi-credit-card" slot="start" aria-hidden="true"></i>
{{ "typeCard" | i18n }}
</button>
<button type="button" bitMenuItem (click)="addCipher(CipherType.Identity)">
<i class="bwi bwi-id-card" slot="start" aria-hidden="true"></i>
{{ "typeIdentity" | i18n }}
</button>
<button type="button" bitMenuItem (click)="addCipher(CipherType.SecureNote)">
<i class="bwi bwi-sticky-note" slot="start" aria-hidden="true"></i>
{{ "note" | i18n }}
</button>
</ng-container>
<ng-container *ngSwitchCase="false">
<button type="button" bitMenuItem (click)="addCipher()">
<i class="bwi bwi-fw bwi-globe" aria-hidden="true"></i>
{{ "item" | i18n }}
</button>
</ng-container>
</ng-container>
<button type="button" bitMenuItem (click)="addCollection()">
<i class="bwi bwi-fw bwi-collection" aria-hidden="true"></i>
{{ "collection" | i18n }}

View File

@@ -10,6 +10,7 @@ import { ProductTierType } from "@bitwarden/common/billing/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 { CipherType } from "@bitwarden/common/vault/enums";
import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
import {
DialogService,
@@ -67,7 +68,7 @@ export class VaultHeaderComponent implements OnInit {
@Input() searchText: string;
/** Emits an event when the new item button is clicked in the header */
@Output() onAddCipher = new EventEmitter<void>();
@Output() onAddCipher = new EventEmitter<CipherType | undefined>();
/** Emits an event when the new collection button is clicked in the header */
@Output() onAddCollection = new EventEmitter<void>();
@@ -89,6 +90,14 @@ export class VaultHeaderComponent implements OnInit {
protected restrictProviderAccessFlag = false;
/**
* Whether the extension refresh feature flag is enabled.
*/
protected extensionRefreshEnabled = false;
/** The cipher type enum. */
protected CipherType = CipherType;
constructor(
private organizationService: OrganizationService,
private i18nService: I18nService,
@@ -102,6 +111,9 @@ export class VaultHeaderComponent implements OnInit {
this.restrictProviderAccessFlag = await this.configService.getFeatureFlag(
FeatureFlag.RestrictProviderAccess,
);
this.extensionRefreshEnabled = await this.configService.getFeatureFlag(
FeatureFlag.ExtensionRefresh,
);
}
get title() {
@@ -194,8 +206,8 @@ export class VaultHeaderComponent implements OnInit {
return this.collection.node.canEdit(this.organization);
}
addCipher() {
this.onAddCipher.emit();
addCipher(cipherType?: CipherType) {
this.onAddCipher.emit(cipherType);
}
async addCollection() {

View File

@@ -4,7 +4,7 @@
[organization]="organization"
[collection]="selectedCollection"
[searchText]="currentSearchText$ | async"
(onAddCipher)="addCipher()"
(onAddCipher)="addCipher($event)"
(onAddCollection)="addCollection()"
(onEditCollection)="editCollection(selectedCollection.node, $event.tab, $event.readonly)"
(onDeleteCollection)="deleteCollection(selectedCollection.node)"

View File

@@ -54,6 +54,7 @@ import { OrganizationId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
import { CipherType } from "@bitwarden/common/vault/enums";
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
@@ -786,14 +787,14 @@ export class VaultComponent implements OnInit, OnDestroy {
}
}
async addCipher() {
async addCipher(cipherType?: CipherType) {
let collections: CollectionView[] = [];
// Admins limited to only adding items to collections they have access to.
collections = await firstValueFrom(this.editableCollections$);
await this.editCipher(null, (comp) => {
comp.type = this.activeFilter.cipherType;
comp.type = cipherType || this.activeFilter.cipherType;
comp.collections = collections;
if (this.activeFilter.collectionId) {
comp.collectionIds = [this.activeFilter.collectionId];

View File

@@ -0,0 +1,6 @@
<svg viewBox="0 0 290 45" fill="#FFF" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M69.799 10.713c3.325 0 5.911 1.248 7.811 3.848 1.9 2.549 2.85 6.033 2.85 10.453 0 4.576-.95 8.113-2.902 10.61-1.953 2.547-4.592 3.743-7.918 3.743-3.325 0-5.858-1.144-7.758-3.536h-.528l-1.003 2.444a.976.976 0 0 1-.897.572H55.23a.94.94 0 0 1-.95-.936V1.352a.94.94 0 0 1 .95-.936h5.7a.94.94 0 0 1 .95.936v8.009c0 1.144-.105 2.964-.316 5.46h.317c1.741-2.704 4.433-4.108 7.917-4.108Zm-2.428 6.084c-1.847 0-3.273.572-4.17 1.717-.844 1.144-1.32 3.068-1.32 5.668v.832c0 2.964.423 5.097 1.32 6.345.897 1.248 2.322 1.924 4.275 1.924 1.531 0 2.85-.728 3.748-2.184.897-1.404 1.372-3.537 1.372-6.189 0-2.704-.475-4.732-1.372-6.084-.95-1.352-2.27-2.029-3.853-2.029ZM93.022 38.9h-5.7a.94.94 0 0 1-.95-.936V12.221a.94.94 0 0 1 .95-.936h5.7a.94.94 0 0 1 .95.936v25.69c.053.468-.422.988-.95.988Zm20.849-5.564c1.108 0 2.428-.208 4.011-.624a.632.632 0 0 1 .792.624v4.316a.64.64 0 0 1-.37.572c-1.794.728-4.064 1.092-6.597 1.092-3.062 0-5.278-.728-6.651-2.288-1.372-1.508-2.111-3.796-2.111-6.812V16.953h-3.008c-.37 0-.634-.26-.634-.624v-2.444c0-.052.053-.104.053-.156l4.17-2.444 2.058-5.408c.106-.26.317-.417.581-.417h3.8c.369 0 .633.26.633.625v5.252h7.548c.158 0 .317.156.317.312v4.68c0 .364-.264.624-.634.624h-7.178v13.21c0 1.04.317 1.872.897 2.34.528.572 1.373.832 2.323.832Zm35.521 5.564c-.739 0-1.319-.468-1.636-1.144l-5.595-16.797c-.369-1.196-.844-3.016-1.478-5.357h-.158l-.528 1.873-1.108 3.536-5.753 16.797c-.211.676-.845 1.092-1.584 1.092a1.628 1.628 0 0 1-1.583-1.196l-7.02-24.182c-.211-.728.369-1.508 1.214-1.508h.158c.528 0 1.003.364 1.161.884l4.117 14.717c1.003 3.849 1.689 6.657 2.006 8.53h.158c.95-3.85 1.689-6.397 2.164-7.698l5.331-15.393c.211-.624.792-1.04 1.531-1.04.686 0 1.267.416 1.478 1.04l4.961 15.29c1.214 3.9 1.953 6.396 2.217 7.696h.158c.159-1.04.792-3.952 2.006-8.633l3.958-14.509c.159-.52.634-.884 1.162-.884.791 0 1.372.728 1.161 1.508l-6.651 24.182c-.211.728-.844 1.196-1.636 1.196h-.211Zm31.352 0a.962.962 0 0 1-.95-.832l-.475-3.432h-.264c-1.372 1.716-2.745 2.964-4.223 3.692-1.425.728-3.166 1.04-5.119 1.04-2.692 0-4.751-.676-6.228-2.028-1.32-1.196-2.059-2.808-2.164-4.836-.212-2.704.95-5.305 3.166-6.813 2.27-1.456 5.437-2.34 9.712-2.34l5.173-.156v-1.768c0-2.6-.528-4.473-1.637-5.773-1.108-1.3-2.744-1.924-5.067-1.924-2.216 0-4.433.52-6.756 1.612-.58.26-1.266 0-1.53-.572s0-1.248.58-1.456c2.639-1.04 5.226-1.612 7.865-1.612 3.008 0 5.225.78 6.756 2.34 1.478 1.508 2.216 3.953 2.216 7.125v16.901c-.052.312-.527.832-1.055.832Zm-10.926-1.768c2.956 0 5.226-.832 6.862-2.444 1.689-1.612 2.533-3.952 2.533-6.813v-2.6l-4.75.208c-3.853.156-6.545.78-8.234 1.768-1.636.988-2.481 2.6-2.481 4.68 0 1.665.528 3.017 1.531 3.953 1.161.78 2.639 1.248 4.539 1.248Zm31.246-25.638c.792 0 1.584.052 2.481.156a1.176 1.176 0 0 1 1.003 1.352c-.106.624-.739.988-1.372.884-.792-.104-1.584-.208-2.375-.208-2.323 0-4.223.988-5.701 2.912-1.478 1.925-2.217 4.42-2.217 7.333v13.625c0 .676-.527 1.196-1.214 1.196-.686 0-1.213-.52-1.213-1.196V13.105c0-.572.475-1.04 1.055-1.04.581 0 1.056.416 1.056.988l.211 3.848h.158c1.109-1.976 2.323-3.38 3.589-4.16 1.214-.832 2.745-1.248 4.539-1.248Zm18.579 0c1.953 0 3.695.364 5.12 1.04 1.478.676 2.745 1.924 3.853 3.64h.158a122.343 122.343 0 0 1-.158-6.084V1.612c0-.676.528-1.196 1.214-1.196.686 0 1.214.52 1.214 1.196v36.351c0 .468-.37.832-.845.832a.852.852 0 0 1-.844-.78l-.528-3.38h-.211c-2.058 3.068-5.067 4.576-8.92 4.576-3.8 0-6.598-1.144-8.656-3.484-1.953-2.34-3.008-5.668-3.008-10.089 0-4.628.95-8.165 2.955-10.66 2.006-2.237 4.856-3.485 8.656-3.485Zm0 2.236c-3.008 0-5.225 1.04-6.756 3.12-1.478 2.029-2.216 4.993-2.216 8.945 0 7.593 3.008 11.39 9.025 11.39 3.114 0 5.331-.885 6.756-2.653 1.478-1.768 2.164-4.68 2.164-8.737v-.416c0-4.16-.686-7.124-2.164-8.893-1.372-1.872-3.642-2.756-6.809-2.756Zm31.616 25.638c-3.959 0-7.02-1.196-9.289-3.64-2.217-2.392-3.326-5.772-3.326-10.089 0-4.316 1.056-7.748 3.22-10.297 2.164-2.6 5.014-3.9 8.656-3.9 3.167 0 5.753 1.092 7.548 3.276 1.9 2.184 2.797 5.2 2.797 8.997v1.976h-19.634c.052 3.692.897 6.5 2.639 8.477 1.741 1.976 4.169 2.86 7.389 2.86 1.531 0 2.956-.104 4.117-.312.844-.156 1.847-.416 3.061-.832.686-.26 1.425.26 1.425.988 0 .416-.264.832-.686.988-1.267.52-2.481.832-3.589 1.04-1.32.364-2.745.468-4.328.468Zm-.739-25.69c-2.639 0-4.75.832-6.334 2.548-1.583 1.665-2.48 4.16-2.797 7.333h16.89c0-3.068-.686-5.564-2.059-7.28-1.372-1.717-3.272-2.6-5.7-2.6ZM288.733 38.9c-.686 0-1.214-.52-1.214-1.196V21.426c0-2.704-.58-4.68-1.689-5.877-1.214-1.196-2.955-1.872-5.383-1.872-3.273 0-5.648.78-7.126 2.444-1.478 1.613-2.322 4.265-2.322 7.853V37.6c0 .676-.528 1.196-1.214 1.196-.686 0-1.214-.52-1.214-1.196V13.105c0-.624.475-1.092 1.108-1.092.581 0 1.003.416 1.109.936l.316 2.704h.159c1.794-2.808 4.908-4.212 9.448-4.212 6.175 0 9.289 3.276 9.289 9.829V37.6c-.053.727-.633 1.3-1.267 1.3ZM90.225 0c-2.48 0-4.486 1.872-4.486 4.212v.416c0 2.289 2.058 4.213 4.486 4.213s4.486-1.924 4.486-4.213v-.364C94.711 1.872 92.653 0 90.225 0Z" />
<path
d="M32.041 24.546V5.95H18.848v33.035c2.336-1.22 4.427-2.547 6.272-3.98 4.614-3.565 6.921-7.051 6.921-10.46Zm5.654-22.314v22.314c0 1.665-.329 3.317-.986 4.953-.658 1.637-1.473 3.09-2.445 4.359-.971 1.268-2.13 2.503-3.475 3.704-1.345 1.2-2.586 2.199-3.725 2.993a46.963 46.963 0 0 1-3.563 2.251c-1.237.707-2.116 1.187-2.636 1.439-.52.251-.938.445-1.252.58-.235.117-.49.175-.765.175s-.53-.058-.766-.174c-.314-.136-.731-.33-1.252-.581-.52-.252-1.398-.732-2.635-1.439a47.003 47.003 0 0 1-3.564-2.251c-1.138-.794-2.38-1.792-3.725-2.993-1.345-1.2-2.503-2.436-3.475-3.704-.972-1.27-1.787-2.722-2.444-4.359C.329 27.863 0 26.211 0 24.546V2.232c0-.504.187-.94.56-1.308A1.823 1.823 0 0 1 1.885.372H35.81c.511 0 .953.184 1.326.552.373.368.56.804.56 1.308Z" />
</svg>

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@@ -5,7 +5,7 @@
<meta name="viewport" content="width=1010" />
<meta name="theme-color" content="#175DDC" />
<title page-title>Bitwarden Web Vault</title>
<title page-title>Bitwarden Web vault</title>
<link rel="apple-touch-icon" sizes="180x180" href="images/icons/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="images/icons/favicon-32x32.png" />
@@ -15,16 +15,14 @@
</head>
<body class="layout_frontend">
<app-root>
<div class="mt-5 d-flex justify-content-center">
<div>
<img class="mb-4 logo logo-themed" alt="Bitwarden" />
<p class="text-center">
<i
class="bwi bwi-spinner bwi-spin bwi-2x text-muted"
title="Loading"
aria-hidden="true"
></i>
</p>
<div class="tw-p-8 tw-flex">
<img class="new-logo-themed" alt="Bitwarden" />
<div class="spinner-container tw-justify-center">
<i
class="bwi bwi-spinner bwi-spin bwi-3x tw-text-muted"
title="Loading"
aria-hidden="true"
></i>
</div>
</div>
</app-root>

View File

@@ -964,6 +964,12 @@
"newAccountCreated": {
"message": "U nuwe rekening is geskep! U kan nou aanteken."
},
"newAccountCreated2": {
"message": "Your new account has been created!"
},
"youHaveBeenLoggedIn": {
"message": "You have been logged in!"
},
"trialAccountCreated": {
"message": "Rekening suksesvol geskep."
},
@@ -4983,6 +4989,10 @@
}
}
},
"viewSend": {
"message": "View Send",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"viewSendHiddenEmailWarning": {
"message": "Die Bitwarden-gebruiker wat hierdie Send geskep het, het gekies om hul e-posadres te verberg. U moet verseker dat u die bron van hierdie skakel vertrou voordat u die inhoud gebruik of aflaai.",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
@@ -9008,5 +9018,12 @@
},
"secretsManagerWithFreePasswordManagerInfo": {
"message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over."
},
"publicApi": {
"message": "Public API",
"description": "The text, 'API', is an acronymn and should not be translated."
},
"additionalContentAvailable": {
"message": "Additional content is available"
}
}

View File

@@ -964,6 +964,12 @@
"newAccountCreated": {
"message": "تم إنشاء حساب جديد لك! بإمكانك الآن تسجيل الدخول."
},
"newAccountCreated2": {
"message": "Your new account has been created!"
},
"youHaveBeenLoggedIn": {
"message": "You have been logged in!"
},
"trialAccountCreated": {
"message": "تم إنشاء الحساب بنجاح."
},
@@ -4983,6 +4989,10 @@
}
}
},
"viewSend": {
"message": "View Send",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"viewSendHiddenEmailWarning": {
"message": "The Bitwarden user who created this Send has chosen to hide their email address. You should ensure you trust the source of this link before using or downloading its content.",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
@@ -9008,5 +9018,12 @@
},
"secretsManagerWithFreePasswordManagerInfo": {
"message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over."
},
"publicApi": {
"message": "Public API",
"description": "The text, 'API', is an acronymn and should not be translated."
},
"additionalContentAvailable": {
"message": "Additional content is available"
}
}

View File

@@ -964,6 +964,12 @@
"newAccountCreated": {
"message": "Yeni hesabınız yaradıldı! İndi giriş edə bilərsiniz."
},
"newAccountCreated2": {
"message": "Your new account has been created!"
},
"youHaveBeenLoggedIn": {
"message": "You have been logged in!"
},
"trialAccountCreated": {
"message": "Hesab uğurla yaradıldı."
},
@@ -4983,6 +4989,10 @@
}
}
},
"viewSend": {
"message": "\"Send\"ə bax",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"viewSendHiddenEmailWarning": {
"message": "Bu \"Send\"i yaradan Bitwarden istifadəçisi e-poçt ünvanını gizlətməyi seçib. İstifadə etməzdən və ya endirməzdən əvvəl bu keçidin mənbəyinin etibarlı olduğuna əmin olmalısınız.",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
@@ -9008,5 +9018,12 @@
},
"secretsManagerWithFreePasswordManagerInfo": {
"message": "Tamamlayıcı bir illik Parol Meneceri abunəliyiniz seçilmiş plana yüksəldiləcək. Ödənişsiz dövr bitənə qədər sizdən ödəniş alınmayacaq."
},
"publicApi": {
"message": "Hər kəsə açıq API",
"description": "The text, 'API', is an acronymn and should not be translated."
},
"additionalContentAvailable": {
"message": "Additional content is available"
}
}

View File

@@ -964,6 +964,12 @@
"newAccountCreated": {
"message": "Ваш уліковы запіс створаны! Цяпер вы можаце ўвайсці ў яго."
},
"newAccountCreated2": {
"message": "Your new account has been created!"
},
"youHaveBeenLoggedIn": {
"message": "You have been logged in!"
},
"trialAccountCreated": {
"message": "Уліковы запіс паспяхова створаны."
},
@@ -4983,6 +4989,10 @@
}
}
},
"viewSend": {
"message": "View Send",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"viewSendHiddenEmailWarning": {
"message": "Карыстальнік Bitwarden, які стварыў гэты Send, вырашыў схаваць сваю электронную пошту. Пераканайцеся, што гэта спасылка атрымана з надзейных крыніц і толькі пасля гэтага выкарыстоўвайце або пампуйце дадзенае змесціва.",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
@@ -9008,5 +9018,12 @@
},
"secretsManagerWithFreePasswordManagerInfo": {
"message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over."
},
"publicApi": {
"message": "Public API",
"description": "The text, 'API', is an acronymn and should not be translated."
},
"additionalContentAvailable": {
"message": "Additional content is available"
}
}

View File

@@ -87,7 +87,7 @@
}
},
"showMatchDetection": {
"message": "Show match detection $WEBSITE$",
"message": "Показване на откритото съвпадение $WEBSITE$",
"placeholders": {
"website": {
"content": "$1",
@@ -96,7 +96,7 @@
}
},
"hideMatchDetection": {
"message": "Hide match detection $WEBSITE$",
"message": "Скриване на откритото съвпадение $WEBSITE$",
"placeholders": {
"website": {
"content": "$1",
@@ -964,6 +964,12 @@
"newAccountCreated": {
"message": "Абонаментът ви бе създаден. Вече можете да се впишете."
},
"newAccountCreated2": {
"message": "Новата Ви регистрация беше създадена!"
},
"youHaveBeenLoggedIn": {
"message": "Вече сте вписан(а)!"
},
"trialAccountCreated": {
"message": "Регистрацията е създадена успешно."
},
@@ -4983,6 +4989,10 @@
}
}
},
"viewSend": {
"message": "Преглед на изпращането",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"viewSendHiddenEmailWarning": {
"message": "Потребителят, който е създал това изпращане, е избрал да скрие адреса на своята е-поща. Уверете се, че източникът на тази връзка е достоверен, преди да я последвате или да свалите съдържание чрез нея.",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
@@ -5653,7 +5663,7 @@
"message": "Free Bitwarden Families offer successfully redeemed"
},
"redeemed": {
"message": "Redeemed"
"message": "Използван"
},
"redeemedAccount": {
"message": "Регистрацията е присвоена"
@@ -6875,7 +6885,7 @@
"description": "Label for the expiration date of an access token."
},
"canRead": {
"message": "Can read",
"message": "Може да чете",
"description": "Label for the access level of an access token (Read only)."
},
"accessTokensNoItemsTitle": {
@@ -7200,7 +7210,7 @@
"message": "Member status filter"
},
"inviteMember": {
"message": "Invite member"
"message": "Покана на член"
},
"needsConfirmation": {
"message": "Изисква потвърждение"
@@ -9008,5 +9018,12 @@
},
"secretsManagerWithFreePasswordManagerInfo": {
"message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over."
},
"publicApi": {
"message": "Публичен ППИ",
"description": "The text, 'API', is an acronymn and should not be translated."
},
"additionalContentAvailable": {
"message": "Има налично допълнително съдържание"
}
}

Some files were not shown because too many files have changed in this diff Show More