1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-22 04:14:04 +00:00

Merge branch 'main' into km/tde-offboarding-fix

This commit is contained in:
Bernd Schoolmann
2025-01-20 13:19:21 +01:00
committed by GitHub
305 changed files with 2066 additions and 2792 deletions

View File

@@ -72,6 +72,10 @@ export class ManageTaxInformationComponent implements OnInit, OnDestroy {
}
}
markAllAsTouched() {
this.formGroup.markAllAsTouched();
}
async ngOnInit() {
if (this.startWith) {
this.formGroup.controls.country.setValue(this.startWith.country);

View File

@@ -1,37 +0,0 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Directive, ElementRef, Input, OnInit, Renderer2 } from "@angular/core";
@Directive({
selector: "[appA11yTitle]",
})
export class A11yTitleDirective implements OnInit {
@Input() set appA11yTitle(title: string) {
this.title = title;
this.setAttributes();
}
private title: string;
private originalTitle: string | null;
private originalAriaLabel: string | null;
constructor(
private el: ElementRef,
private renderer: Renderer2,
) {}
ngOnInit() {
this.originalTitle = this.el.nativeElement.getAttribute("title");
this.originalAriaLabel = this.el.nativeElement.getAttribute("aria-label");
this.setAttributes();
}
private setAttributes() {
if (this.originalTitle === null) {
this.renderer.setAttribute(this.el.nativeElement, "title", this.title);
}
if (this.originalAriaLabel === null) {
this.renderer.setAttribute(this.el.nativeElement, "aria-label", this.title);
}
}
}

View File

@@ -1,114 +0,0 @@
import { Component, ElementRef, ViewChild } from "@angular/core";
import { ComponentFixture, TestBed } from "@angular/core/testing";
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 { CopyClickDirective } from "./copy-click.directive";
@Component({
template: `
<button appCopyClick="no toast shown" #noToast></button>
<button appCopyClick="info toast shown" showToast="info" #infoToast></button>
<button appCopyClick="success toast shown" showToast #successToast></button>
<button appCopyClick="toast with label" showToast valueLabel="Content" #toastWithLabel></button>
`,
})
class TestCopyClickComponent {
@ViewChild("noToast") noToastButton: ElementRef<HTMLButtonElement>;
@ViewChild("infoToast") infoToastButton: ElementRef<HTMLButtonElement>;
@ViewChild("successToast") successToastButton: ElementRef<HTMLButtonElement>;
@ViewChild("toastWithLabel") toastWithLabelButton: ElementRef<HTMLButtonElement>;
}
describe("CopyClickDirective", () => {
let fixture: ComponentFixture<TestCopyClickComponent>;
const copyToClipboard = jest.fn();
const showToast = jest.fn();
beforeEach(async () => {
copyToClipboard.mockClear();
showToast.mockClear();
await TestBed.configureTestingModule({
declarations: [CopyClickDirective, TestCopyClickComponent],
providers: [
{
provide: I18nService,
useValue: {
t: (key: string, ...rest: string[]) => {
if (rest?.length) {
return `${key} ${rest.join("")}`;
}
return key;
},
},
},
{ provide: PlatformUtilsService, useValue: { copyToClipboard } },
{ provide: ToastService, useValue: { showToast } },
],
}).compileComponents();
fixture = TestBed.createComponent(TestCopyClickComponent);
fixture.detectChanges();
});
it("copies the the value for all variants of toasts ", () => {
const noToastButton = fixture.componentInstance.noToastButton.nativeElement;
noToastButton.click();
expect(copyToClipboard).toHaveBeenCalledWith("no toast shown");
const infoToastButton = fixture.componentInstance.infoToastButton.nativeElement;
infoToastButton.click();
expect(copyToClipboard).toHaveBeenCalledWith("info toast shown");
const successToastButton = fixture.componentInstance.successToastButton.nativeElement;
successToastButton.click();
expect(copyToClipboard).toHaveBeenCalledWith("success toast shown");
});
it("does not show a toast when showToast is not present", () => {
const noToastButton = fixture.componentInstance.noToastButton.nativeElement;
noToastButton.click();
expect(showToast).not.toHaveBeenCalled();
});
it("shows a success toast when showToast is present", () => {
const successToastButton = fixture.componentInstance.successToastButton.nativeElement;
successToastButton.click();
expect(showToast).toHaveBeenCalledWith({
message: "copySuccessful",
title: null,
variant: "success",
});
});
it("shows the toast variant when set with showToast", () => {
const infoToastButton = fixture.componentInstance.infoToastButton.nativeElement;
infoToastButton.click();
expect(showToast).toHaveBeenCalledWith({
message: "copySuccessful",
title: null,
variant: "info",
});
});
it('includes label in toast message when "copyLabel" is set', () => {
const toastWithLabelButton = fixture.componentInstance.toastWithLabelButton.nativeElement;
toastWithLabelButton.click();
expect(showToast).toHaveBeenCalledWith({
message: "valueCopied Content",
title: null,
variant: "success",
});
});
});

View File

@@ -1,68 +0,0 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Directive, HostListener, Input } from "@angular/core";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ToastService, ToastVariant } from "@bitwarden/components";
@Directive({
selector: "[appCopyClick]",
})
export class CopyClickDirective {
private _showToast = false;
private toastVariant: ToastVariant = "success";
constructor(
private platformUtilsService: PlatformUtilsService,
private toastService: ToastService,
private i18nService: I18nService,
) {}
@Input("appCopyClick") valueToCopy = "";
/**
* When set, the toast displayed will show `<valueLabel> copied`
* instead of the default messaging.
*/
@Input() valueLabel: string;
/**
* When set without a value, a success toast will be shown when the value is copied
* @example
* ```html
* <app-component [appCopyClick]="value to copy" showToast/></app-component>
* ```
* When set with a value, a toast with the specified variant will be shown when the value is copied
*
* @example
* ```html
* <app-component [appCopyClick]="value to copy" showToast="info"/></app-component>
* ```
*/
@Input() set showToast(value: ToastVariant | "") {
// When the `showToast` is set without a value, an empty string will be passed
if (value === "") {
this._showToast = true;
} else {
this._showToast = true;
this.toastVariant = value;
}
}
@HostListener("click") onClick() {
this.platformUtilsService.copyToClipboard(this.valueToCopy);
if (this._showToast) {
const message = this.valueLabel
? this.i18nService.t("valueCopied", this.valueLabel)
: this.i18nService.t("copySuccessful");
this.toastService.showToast({
variant: this.toastVariant,
title: null,
message,
});
}
}
}

View File

@@ -25,15 +25,15 @@ import {
TableModule,
ToastModule,
TypographyModule,
CopyClickDirective,
A11yTitleDirective,
} from "@bitwarden/components";
import { TwoFactorIconComponent } from "./auth/components/two-factor-icon.component";
import { DeprecatedCalloutComponent } from "./components/callout.component";
import { A11yInvalidDirective } from "./directives/a11y-invalid.directive";
import { A11yTitleDirective } from "./directives/a11y-title.directive";
import { ApiActionDirective } from "./directives/api-action.directive";
import { BoxRowDirective } from "./directives/box-row.directive";
import { CopyClickDirective } from "./directives/copy-click.directive";
import { CopyTextDirective } from "./directives/copy-text.directive";
import { FallbackSrcDirective } from "./directives/fallback-src.directive";
import { IfFeatureDirective } from "./directives/if-feature.directive";
@@ -83,10 +83,11 @@ import { IconComponent } from "./vault/components/icon.component";
LinkModule,
IconModule,
TextDragDirective,
CopyClickDirective,
A11yTitleDirective,
],
declarations: [
A11yInvalidDirective,
A11yTitleDirective,
ApiActionDirective,
AutofocusDirective,
BoxRowDirective,
@@ -105,7 +106,6 @@ import { IconComponent } from "./vault/components/icon.component";
StopClickDirective,
StopPropDirective,
TrueFalseValueDirective,
CopyClickDirective,
LaunchClickDirective,
UserNamePipe,
PasswordStrengthComponent,

View File

@@ -2,6 +2,9 @@ import { Pipe, PipeTransform } from "@angular/core";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
/**
* @deprecated: Please use the I18nPipe from @bitwarden/ui-common
*/
@Pipe({
name: "i18n",
})

View File

@@ -1,138 +1,4 @@
import { Provider } from "@angular/core";
import { Constructor, Opaque } from "type-fest";
import { SafeInjectionToken } from "../../services/injection-tokens";
/**
* The return type of the {@link safeProvider} helper function.
* Used to distinguish a type safe provider definition from a non-type safe provider definition.
* @deprecated: Please use the SafeProvider & safeProvider from @bitwarden/ui-common
*/
export type SafeProvider = Opaque<Provider>;
// TODO: type-fest also provides a type like this when we upgrade >= 3.7.0
type AbstractConstructor<T> = abstract new (...args: any) => T;
type MapParametersToDeps<T> = {
[K in keyof T]: AbstractConstructor<T[K]> | SafeInjectionToken<T[K]>;
};
type SafeInjectionTokenType<T> = T extends SafeInjectionToken<infer J> ? J : never;
/**
* Gets the instance type from a constructor, abstract constructor, or SafeInjectionToken
*/
type ProviderInstanceType<T> =
T extends SafeInjectionToken<any>
? InstanceType<SafeInjectionTokenType<T>>
: T extends Constructor<any> | AbstractConstructor<any>
? InstanceType<T>
: never;
/**
* Represents a dependency provided with the useClass option.
*/
type SafeClassProvider<
A extends AbstractConstructor<any> | SafeInjectionToken<any>,
I extends Constructor<ProviderInstanceType<A>>,
D extends MapParametersToDeps<ConstructorParameters<I>>,
> = {
provide: A;
useClass: I;
deps: D;
};
/**
* Represents a dependency provided with the useValue option.
*/
type SafeValueProvider<A extends SafeInjectionToken<any>, V extends SafeInjectionTokenType<A>> = {
provide: A;
useValue: V;
};
/**
* Represents a dependency provided with the useFactory option.
*/
type SafeFactoryProvider<
A extends AbstractConstructor<any> | SafeInjectionToken<any>,
I extends (...args: any) => ProviderInstanceType<A>,
D extends MapParametersToDeps<Parameters<I>>,
> = {
provide: A;
useFactory: I;
deps: D;
multi?: boolean;
};
/**
* Represents a dependency provided with the useExisting option.
*/
type SafeExistingProvider<
A extends Constructor<any> | AbstractConstructor<any> | SafeInjectionToken<any>,
I extends Constructor<ProviderInstanceType<A>> | AbstractConstructor<ProviderInstanceType<A>>,
> = {
provide: A;
useExisting: I;
};
/**
* Represents a dependency where there is no abstract token, the token is the implementation
*/
type SafeConcreteProvider<
I extends Constructor<any>,
D extends MapParametersToDeps<ConstructorParameters<I>>,
> = {
provide: I;
deps: D;
};
/**
* If useAngularDecorators: true is specified, do not require a deps array.
* This is a manual override for where @Injectable decorators are used
*/
type UseAngularDecorators<T extends { deps: any }> = Omit<T, "deps"> & {
useAngularDecorators: true;
};
/**
* Represents a type with a deps array that may optionally be overridden with useAngularDecorators
*/
type AllowAngularDecorators<T extends { deps: any }> = T | UseAngularDecorators<T>;
/**
* A factory function that creates a provider for the ngModule providers array.
* This (almost) guarantees type safety for your provider definition. It does nothing at runtime.
* Warning: the useAngularDecorators option provides an override where your class uses the Injectable decorator,
* however this cannot be enforced by the type system and will not cause an error if the decorator is not used.
* @example safeProvider({ provide: MyService, useClass: DefaultMyService, deps: [AnotherService] })
* @param provider Your provider object in the usual shape (e.g. using useClass, useValue, useFactory, etc.)
* @returns The exact same object without modification (pass-through).
*/
export const safeProvider = <
// types for useClass
AClass extends AbstractConstructor<any> | SafeInjectionToken<any>,
IClass extends Constructor<ProviderInstanceType<AClass>>,
DClass extends MapParametersToDeps<ConstructorParameters<IClass>>,
// types for useValue
AValue extends SafeInjectionToken<any>,
VValue extends SafeInjectionTokenType<AValue>,
// types for useFactory
AFactory extends AbstractConstructor<any> | SafeInjectionToken<any>,
IFactory extends (...args: any) => ProviderInstanceType<AFactory>,
DFactory extends MapParametersToDeps<Parameters<IFactory>>,
// types for useExisting
AExisting extends Constructor<any> | AbstractConstructor<any> | SafeInjectionToken<any>,
IExisting extends
| Constructor<ProviderInstanceType<AExisting>>
| AbstractConstructor<ProviderInstanceType<AExisting>>,
// types for no token
IConcrete extends Constructor<any>,
DConcrete extends MapParametersToDeps<ConstructorParameters<IConcrete>>,
>(
provider:
| AllowAngularDecorators<SafeClassProvider<AClass, IClass, DClass>>
| SafeValueProvider<AValue, VValue>
| AllowAngularDecorators<SafeFactoryProvider<AFactory, IFactory, DFactory>>
| SafeExistingProvider<AExisting, IExisting>
| AllowAngularDecorators<SafeConcreteProvider<IConcrete, DConcrete>>
| Constructor<unknown>,
): SafeProvider => provider as SafeProvider;
export { SafeProvider, safeProvider } from "@bitwarden/ui-common";

View File

@@ -1,111 +0,0 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
// This rule bans @ts-expect-error comments without explanation. In this file, we use it to test our types, and
// explanation is provided in header comments before each test.
import { safeProvider } from "./safe-provider";
class FooFactory {
create() {
return "thing";
}
}
abstract class FooService {
createFoo: (str: string) => string;
}
class DefaultFooService implements FooService {
constructor(private factory: FooFactory) {}
createFoo(str: string) {
return str ?? this.factory.create();
}
}
class BarFactory {
create() {
return 5;
}
}
abstract class BarService {
createBar: (num: number) => number;
}
class DefaultBarService implements BarService {
constructor(private factory: BarFactory) {}
createBar(num: number) {
return num ?? this.factory.create();
}
}
abstract class FooBarService {}
class DefaultFooBarService {
constructor(
private fooFactory: FooFactory,
private barFactory: BarFactory,
) {}
}
// useClass happy path with deps
safeProvider({
provide: FooService,
useClass: DefaultFooService,
deps: [FooFactory],
});
// useClass happy path with useAngularDecorators
safeProvider({
provide: FooService,
useClass: DefaultFooService,
useAngularDecorators: true,
});
// useClass: expect error if implementation does not match abstraction
safeProvider({
provide: FooService,
// @ts-expect-error
useClass: DefaultBarService,
deps: [BarFactory],
});
// useClass: expect error if deps type does not match
safeProvider({
provide: FooService,
useClass: DefaultFooService,
// @ts-expect-error
deps: [BarFactory],
});
// useClass: expect error if not enough deps specified
safeProvider({
provide: FooService,
useClass: DefaultFooService,
// @ts-expect-error
deps: [],
});
// useClass: expect error if too many deps specified
safeProvider({
provide: FooService,
useClass: DefaultFooService,
// @ts-expect-error
deps: [FooFactory, BarFactory],
});
// useClass: expect error if deps are in the wrong order
safeProvider({
provide: FooBarService,
useClass: DefaultFooBarService,
// @ts-expect-error
deps: [BarFactory, FooFactory],
});
// useClass: expect error if no deps specified and not using Angular decorators
// @ts-expect-error
safeProvider({
provide: FooService,
useClass: DefaultFooService,
});

View File

@@ -1,6 +1,5 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { InjectionToken } from "@angular/core";
import { Observable, Subject } from "rxjs";
import { LogoutReason } from "@bitwarden/auth/common";
@@ -14,17 +13,9 @@ import { Theme } from "@bitwarden/common/platform/enums";
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
import { Message } from "@bitwarden/common/platform/messaging";
import { VaultTimeout } from "@bitwarden/common/types/vault-timeout.type";
declare const tag: unique symbol;
/**
* A (more) typesafe version of InjectionToken which will more strictly enforce the generic type parameter.
* @remarks The default angular implementation does not use the generic type to define the structure of the object,
* so the structural type system will not complain about a mismatch in the type parameter.
* This is solved by assigning T to an arbitrary private property.
*/
export class SafeInjectionToken<T> extends InjectionToken<T> {
private readonly [tag]: T;
}
import { SafeInjectionToken } from "@bitwarden/ui-common";
// Re-export the SafeInjectionToken from ui-common
export { SafeInjectionToken } from "@bitwarden/ui-common";
export const WINDOW = new SafeInjectionToken<Window>("WINDOW");
export const OBSERVABLE_MEMORY_STORAGE = new SafeInjectionToken<

View File

@@ -293,6 +293,7 @@ import {
UserAsymmetricKeysRegenerationApiService,
DefaultUserAsymmetricKeysRegenerationApiService,
} from "@bitwarden/key-management";
import { SafeInjectionToken } from "@bitwarden/ui-common";
import { PasswordRepromptService } from "@bitwarden/vault";
import {
VaultExportService,
@@ -323,7 +324,6 @@ import {
MEMORY_STORAGE,
OBSERVABLE_DISK_STORAGE,
OBSERVABLE_MEMORY_STORAGE,
SafeInjectionToken,
SECURE_STORAGE,
STATE_FACTORY,
SUPPORTS_SECURE_STORAGE,

View File

@@ -372,11 +372,11 @@ export class AddEditComponent implements OnInit, OnDestroy {
}
if (this.cipher.name == null || this.cipher.name === "") {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("nameRequired"),
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("nameRequired"),
});
return false;
}
@@ -385,11 +385,11 @@ export class AddEditComponent implements OnInit, OnDestroy {
!this.allowPersonal &&
this.cipher.organizationId == null
) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("personalOwnershipSubmitError"),
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("personalOwnershipSubmitError"),
});
return false;
}
@@ -424,11 +424,11 @@ export class AddEditComponent implements OnInit, OnDestroy {
this.formPromise = this.saveCipher(cipher);
await this.formPromise;
this.cipher.id = cipher.id;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t(this.editMode && !this.cloneMode ? "editedItem" : "addedItem"),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t(this.editMode && !this.cloneMode ? "editedItem" : "addedItem"),
});
this.onSavedCipher.emit(this.cipher);
this.messagingService.send(this.editMode && !this.cloneMode ? "editedCipher" : "addedCipher");
return true;
@@ -514,11 +514,13 @@ export class AddEditComponent implements OnInit, OnDestroy {
try {
this.deletePromise = this.deleteCipher();
await this.deletePromise;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t(this.cipher.isDeleted ? "permanentlyDeletedItem" : "deletedItem"),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t(
this.cipher.isDeleted ? "permanentlyDeletedItem" : "deletedItem",
),
});
this.onDeletedCipher.emit(this.cipher);
this.messagingService.send(
this.cipher.isDeleted ? "permanentlyDeletedCipher" : "deletedCipher",
@@ -538,7 +540,11 @@ export class AddEditComponent implements OnInit, OnDestroy {
try {
this.restorePromise = this.restoreCipher();
await this.restorePromise;
this.platformUtilsService.showToast("success", null, this.i18nService.t("restoredItem"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("restoredItem"),
});
this.onRestoredCipher.emit(this.cipher);
this.messagingService.send("restoredCipher");
} catch (e) {
@@ -679,13 +685,17 @@ export class AddEditComponent implements OnInit, OnDestroy {
this.checkPasswordPromise = null;
if (matches > 0) {
this.platformUtilsService.showToast(
"warning",
null,
this.i18nService.t("passwordExposed", matches.toString()),
);
this.toastService.showToast({
variant: "warning",
title: null,
message: this.i18nService.t("passwordExposed", matches.toString()),
});
} else {
this.platformUtilsService.showToast("success", null, this.i18nService.t("passwordSafe"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("passwordSafe"),
});
}
}
@@ -779,11 +789,11 @@ export class AddEditComponent implements OnInit, OnDestroy {
const copyOptions = this.win != null ? { window: this.win } : null;
this.platformUtilsService.copyToClipboard(value, copyOptions);
this.platformUtilsService.showToast(
"info",
null,
this.i18nService.t("valueCopied", this.i18nService.t(typeI18nKey)),
);
this.toastService.showToast({
variant: "info",
title: null,
message: this.i18nService.t("valueCopied", this.i18nService.t(typeI18nKey)),
});
if (typeI18nKey === "password") {
void this.eventCollectionService.collectMany(EventType.Cipher_ClientCopiedPassword, [

View File

@@ -64,21 +64,21 @@ export class AttachmentsComponent implements OnInit {
const fileEl = document.getElementById("file") as HTMLInputElement;
const files = fileEl.files;
if (files == null || files.length === 0) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("selectFile"),
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("selectFile"),
});
return;
}
if (files[0].size > 524288000) {
// 500 MB
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("maxFileSize"),
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("maxFileSize"),
});
return;
}
@@ -91,7 +91,11 @@ export class AttachmentsComponent implements OnInit {
this.cipher = await this.cipherDomain.decrypt(
await this.cipherService.getKeyForCipherKeyDecryption(this.cipherDomain, activeUserId),
);
this.platformUtilsService.showToast("success", null, this.i18nService.t("attachmentSaved"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("attachmentSaved"),
});
this.onUploadedAttachment.emit();
} catch (e) {
this.logService.error(e);
@@ -122,7 +126,11 @@ export class AttachmentsComponent implements OnInit {
try {
this.deletePromises[attachment.id] = this.deleteCipherAttachment(attachment.id);
await this.deletePromises[attachment.id];
this.platformUtilsService.showToast("success", null, this.i18nService.t("deletedAttachment"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("deletedAttachment"),
});
const i = this.cipher.attachments.indexOf(attachment);
if (i > -1) {
this.cipher.attachments.splice(i, 1);
@@ -142,11 +150,11 @@ export class AttachmentsComponent implements OnInit {
}
if (!this.canAccessAttachments) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("premiumRequired"),
this.i18nService.t("premiumRequiredDesc"),
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("premiumRequired"),
message: this.i18nService.t("premiumRequiredDesc"),
});
return;
}
@@ -171,7 +179,11 @@ export class AttachmentsComponent implements OnInit {
a.downloading = true;
const response = await fetch(new Request(url, { cache: "no-store" }));
if (response.status !== 200) {
this.platformUtilsService.showToast("error", null, this.i18nService.t("errorOccurred"));
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t("errorOccurred"),
});
a.downloading = false;
return;
}
@@ -195,7 +207,11 @@ export class AttachmentsComponent implements OnInit {
// FIXME: Remove when updating file. Eslint update
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
this.platformUtilsService.showToast("error", null, this.i18nService.t("errorOccurred"));
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t("errorOccurred"),
});
}
a.downloading = false;
@@ -243,7 +259,11 @@ export class AttachmentsComponent implements OnInit {
a.downloading = true;
const response = await fetch(new Request(attachment.url, { cache: "no-store" }));
if (response.status !== 200) {
this.platformUtilsService.showToast("error", null, this.i18nService.t("errorOccurred"));
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t("errorOccurred"),
});
a.downloading = false;
return;
}
@@ -281,16 +301,20 @@ export class AttachmentsComponent implements OnInit {
}
}
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("attachmentSaved"),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("attachmentSaved"),
});
this.onReuploadedAttachment.emit();
// FIXME: Remove when updating file. Eslint update
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
this.platformUtilsService.showToast("error", null, this.i18nService.t("errorOccurred"));
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t("errorOccurred"),
});
}
a.downloading = false;

View File

@@ -11,7 +11,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { KeyService } from "@bitwarden/key-management";
@Directive()
@@ -43,6 +43,7 @@ export class FolderAddEditComponent implements OnInit {
protected logService: LogService,
protected dialogService: DialogService,
protected formBuilder: FormBuilder,
protected toastService: ToastService,
) {}
async ngOnInit() {
@@ -52,11 +53,11 @@ export class FolderAddEditComponent implements OnInit {
async submit(): Promise<boolean> {
this.folder.name = this.formGroup.controls.name.value;
if (this.folder.name == null || this.folder.name === "") {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("nameRequired"),
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("nameRequired"),
});
return false;
}
@@ -66,11 +67,11 @@ export class FolderAddEditComponent implements OnInit {
const folder = await this.folderService.encrypt(this.folder, userKey);
this.formPromise = this.folderApiService.save(folder, activeUserId);
await this.formPromise;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t(this.editMode ? "editedFolder" : "addedFolder"),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t(this.editMode ? "editedFolder" : "addedFolder"),
});
this.onSavedFolder.emit(this.folder);
return true;
} catch (e) {
@@ -95,7 +96,11 @@ export class FolderAddEditComponent implements OnInit {
const activeUserId = await firstValueFrom(this.activeUserId$);
this.deletePromise = this.folderApiService.delete(this.folder.id, activeUserId);
await this.deletePromise;
this.platformUtilsService.showToast("success", null, this.i18nService.t("deletedFolder"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("deletedFolder"),
});
this.onDeletedFolder.emit(this.folder);
} catch (e) {
this.logService.error(e);

View File

@@ -8,6 +8,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { PasswordHistoryView } from "@bitwarden/common/vault/models/view/password-history.view";
import { ToastService } from "@bitwarden/components";
@Directive()
export class PasswordHistoryComponent implements OnInit {
@@ -20,6 +21,7 @@ export class PasswordHistoryComponent implements OnInit {
protected i18nService: I18nService,
protected accountService: AccountService,
private win: Window,
private toastService: ToastService,
) {}
async ngOnInit() {
@@ -29,11 +31,11 @@ export class PasswordHistoryComponent implements OnInit {
copy(password: string) {
const copyOptions = this.win != null ? { window: this.win } : null;
this.platformUtilsService.copyToClipboard(password, copyOptions);
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")),
});
}
protected async init() {

View File

@@ -12,7 +12,7 @@ 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 { DialogService, SimpleDialogOptions } from "@bitwarden/components";
import { DialogService, SimpleDialogOptions, ToastService } from "@bitwarden/components";
@Directive()
export class PremiumComponent implements OnInit {
@@ -31,6 +31,7 @@ export class PremiumComponent implements OnInit {
protected dialogService: DialogService,
private environmentService: EnvironmentService,
billingAccountProfileStateService: BillingAccountProfileStateService,
private toastService: ToastService,
accountService: AccountService,
) {
this.isPremium$ = accountService.activeAccount$.pipe(
@@ -51,7 +52,11 @@ export class PremiumComponent implements OnInit {
try {
this.refreshPromise = this.apiService.refreshIdentityToken();
await this.refreshPromise;
this.platformUtilsService.showToast("success", null, this.i18nService.t("refreshComplete"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("refreshComplete"),
});
} catch (e) {
this.logService.error(e);
}

View File

@@ -40,7 +40,7 @@ import { AttachmentView } from "@bitwarden/common/vault/models/view/attachment.v
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { KeyService } from "@bitwarden/key-management";
import { PasswordRepromptService } from "@bitwarden/vault";
@@ -115,6 +115,7 @@ export class ViewComponent implements OnDestroy, OnInit {
protected datePipe: DatePipe,
protected accountService: AccountService,
private billingAccountProfileStateService: BillingAccountProfileStateService,
protected toastService: ToastService,
private cipherAuthorizationService: CipherAuthorizationService,
) {}
@@ -246,11 +247,13 @@ export class ViewComponent implements OnDestroy, OnInit {
try {
await this.deleteCipher();
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t(this.cipher.isDeleted ? "permanentlyDeletedItem" : "deletedItem"),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t(
this.cipher.isDeleted ? "permanentlyDeletedItem" : "deletedItem",
),
});
this.onDeletedCipher.emit(this.cipher);
} catch (e) {
this.logService.error(e);
@@ -266,7 +269,11 @@ export class ViewComponent implements OnDestroy, OnInit {
try {
await this.restoreCipher();
this.platformUtilsService.showToast("success", null, this.i18nService.t("restoredItem"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("restoredItem"),
});
this.onRestoredCipher.emit(this.cipher);
} catch (e) {
this.logService.error(e);
@@ -349,13 +356,17 @@ export class ViewComponent implements OnDestroy, OnInit {
const matches = await this.checkPasswordPromise;
if (matches > 0) {
this.platformUtilsService.showToast(
"warning",
null,
this.i18nService.t("passwordExposed", matches.toString()),
);
this.toastService.showToast({
variant: "warning",
title: null,
message: this.i18nService.t("passwordExposed", matches.toString()),
});
} else {
this.platformUtilsService.showToast("success", null, this.i18nService.t("passwordSafe"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("passwordSafe"),
});
}
}
@@ -385,11 +396,11 @@ export class ViewComponent implements OnDestroy, OnInit {
const copyOptions = this.win != null ? { window: this.win } : null;
this.platformUtilsService.copyToClipboard(value, copyOptions);
this.platformUtilsService.showToast(
"info",
null,
this.i18nService.t("valueCopied", this.i18nService.t(typeI18nKey)),
);
this.toastService.showToast({
variant: "info",
title: null,
message: this.i18nService.t("valueCopied", this.i18nService.t(typeI18nKey)),
});
if (typeI18nKey === "password") {
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
@@ -422,11 +433,11 @@ export class ViewComponent implements OnDestroy, OnInit {
}
if (this.cipher.organizationId == null && !this.canAccessPremium) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("premiumRequired"),
this.i18nService.t("premiumRequiredDesc"),
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("premiumRequired"),
message: this.i18nService.t("premiumRequiredDesc"),
});
return;
}
@@ -450,7 +461,11 @@ export class ViewComponent implements OnDestroy, OnInit {
a.downloading = true;
const response = await fetch(new Request(url, { cache: "no-store" }));
if (response.status !== 200) {
this.platformUtilsService.showToast("error", null, this.i18nService.t("errorOccurred"));
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t("errorOccurred"),
});
a.downloading = false;
return;
}
@@ -469,7 +484,11 @@ export class ViewComponent implements OnDestroy, OnInit {
// FIXME: Remove when updating file. Eslint update
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
this.platformUtilsService.showToast("error", null, this.i18nService.t("errorOccurred"));
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t("errorOccurred"),
});
}
a.downloading = false;

View File

@@ -16,6 +16,7 @@
"@bitwarden/importer/core": ["../importer/src"],
"@bitwarden/key-management": ["../key-management/src"],
"@bitwarden/platform": ["../platform/src"],
"@bitwarden/ui-common": ["../ui/common/src"],
"@bitwarden/vault-export-core": ["../tools/export/vault-export/vault-export-core/src"],
"@bitwarden/vault": ["../vault/src"]
}