mirror of
https://github.com/bitwarden/browser
synced 2026-02-13 06:54:07 +00:00
Merge branch 'main' into ps/extension-refresh
This commit is contained in:
@@ -4296,5 +4296,26 @@
|
||||
},
|
||||
"additionalContentAvailable": {
|
||||
"message": "Additional content is available"
|
||||
},
|
||||
"itemsInTrash": {
|
||||
"message": "Items in trash"
|
||||
},
|
||||
"noItemsInTrash": {
|
||||
"message": "No items in trash"
|
||||
},
|
||||
"noItemsInTrashDesc": {
|
||||
"message": "Items you delete will appear here and be permanently deleted after 30 days"
|
||||
},
|
||||
"trashWarning": {
|
||||
"message": "Items that have been in trash more than 30 days will automatically be deleted"
|
||||
},
|
||||
"restore": {
|
||||
"message": "Restore"
|
||||
},
|
||||
"deleteForever": {
|
||||
"message": "Delete forever"
|
||||
},
|
||||
"noEditPermissions": {
|
||||
"message": "You don't have permission to edit this item"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute, Data, NavigationEnd, Router, RouterModule } from "@angular/router";
|
||||
import { Subject, filter, firstValueFrom, switchMap, takeUntil, tap } from "rxjs";
|
||||
import { Subject, filter, switchMap, takeUntil, tap } from "rxjs";
|
||||
|
||||
import {
|
||||
AnonLayoutComponent,
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
AnonLayoutWrapperDataService,
|
||||
} from "@bitwarden/auth/angular";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||
import { Icon, IconModule } from "@bitwarden/components";
|
||||
|
||||
import { PopOutComponent } from "../../../platform/popup/components/pop-out.component";
|
||||
@@ -17,10 +16,7 @@ import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-heade
|
||||
import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component";
|
||||
import { CurrentAccountComponent } from "../account-switching/current-account.component";
|
||||
|
||||
import {
|
||||
ExtensionBitwardenLogoPrimary,
|
||||
ExtensionBitwardenLogoWhite,
|
||||
} from "./extension-bitwarden-logo.icon";
|
||||
import { ExtensionBitwardenLogo } from "./extension-bitwarden-logo.icon";
|
||||
|
||||
export interface ExtensionAnonLayoutWrapperData extends AnonLayoutWrapperData {
|
||||
showAcctSwitcher?: boolean;
|
||||
@@ -56,14 +52,13 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy {
|
||||
protected maxWidth: "md" | "3xl";
|
||||
|
||||
protected theme: string;
|
||||
protected logo: Icon;
|
||||
protected logo = ExtensionBitwardenLogo;
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private route: ActivatedRoute,
|
||||
private i18nService: I18nService,
|
||||
private extensionAnonLayoutWrapperDataService: AnonLayoutWrapperDataService,
|
||||
private themeStateService: ThemeStateService,
|
||||
) {}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
@@ -73,14 +68,6 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy {
|
||||
// Listen for page changes and update the page data appropriately
|
||||
this.listenForPageDataChanges();
|
||||
this.listenForServiceDataChanges();
|
||||
|
||||
this.theme = await firstValueFrom(this.themeStateService.selectedTheme$);
|
||||
|
||||
if (this.theme === "dark") {
|
||||
this.logo = ExtensionBitwardenLogoWhite;
|
||||
} else {
|
||||
this.logo = ExtensionBitwardenLogoPrimary;
|
||||
}
|
||||
}
|
||||
|
||||
private listenForPageDataChanges() {
|
||||
|
||||
@@ -22,8 +22,6 @@ import {
|
||||
} from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { ThemeType } from "@bitwarden/common/platform/enums";
|
||||
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { ButtonModule, I18nMockService } from "@bitwarden/components";
|
||||
|
||||
@@ -47,7 +45,6 @@ const decorators = (options: {
|
||||
applicationVersion?: string;
|
||||
clientType?: ClientType;
|
||||
hostName?: string;
|
||||
themeType?: ThemeType;
|
||||
}) => {
|
||||
return [
|
||||
componentWrapperDecorator(
|
||||
@@ -120,12 +117,6 @@ const decorators = (options: {
|
||||
getClientType: () => options.clientType || ClientType.Web,
|
||||
} as Partial<PlatformUtilsService>,
|
||||
},
|
||||
{
|
||||
provide: ThemeStateService,
|
||||
useValue: {
|
||||
selectedTheme$: of(options.themeType || ThemeType.Light),
|
||||
} as Partial<ThemeStateService>,
|
||||
},
|
||||
{
|
||||
provide: I18nService,
|
||||
useFactory: () => {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -41,7 +41,7 @@ export class HomeComponent implements OnInit, OnDestroy {
|
||||
) {}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
const email = this.loginEmailService.getEmail();
|
||||
const email = await firstValueFrom(this.loginEmailService.loginEmail$);
|
||||
const rememberEmail = this.loginEmailService.getRememberEmail();
|
||||
|
||||
if (email != null) {
|
||||
@@ -93,7 +93,7 @@ export class HomeComponent implements OnInit, OnDestroy {
|
||||
async setLoginEmailValues() {
|
||||
// Note: Browser saves email settings here instead of the login component
|
||||
this.loginEmailService.setRememberEmail(this.formGroup.value.rememberEmail);
|
||||
this.loginEmailService.setEmail(this.formGroup.value.email);
|
||||
await this.loginEmailService.setLoginEmail(this.formGroup.value.email);
|
||||
await this.loginEmailService.saveEmailSettings();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, NgZone } from "@angular/core";
|
||||
import { Component, NgZone, OnInit } from "@angular/core";
|
||||
import { FormBuilder } from "@angular/forms";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
@@ -31,7 +31,7 @@ import { flagEnabled } from "../../platform/flags";
|
||||
selector: "app-login",
|
||||
templateUrl: "login.component.html",
|
||||
})
|
||||
export class LoginComponent extends BaseLoginComponent {
|
||||
export class LoginComponent extends BaseLoginComponent implements OnInit {
|
||||
showPasswordless = false;
|
||||
constructor(
|
||||
devicesApiService: DevicesApiServiceAbstraction,
|
||||
@@ -83,13 +83,14 @@ export class LoginComponent extends BaseLoginComponent {
|
||||
};
|
||||
super.successRoute = "/tabs/vault";
|
||||
this.showPasswordless = flagEnabled("showPasswordless");
|
||||
}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
if (this.showPasswordless) {
|
||||
this.formGroup.controls.email.setValue(this.loginEmailService.getEmail());
|
||||
const loginEmail = await firstValueFrom(this.loginEmailService.loginEmail$);
|
||||
this.formGroup.controls.email.setValue(loginEmail);
|
||||
this.formGroup.controls.rememberEmail.setValue(this.loginEmailService.getRememberEmail());
|
||||
// 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.validateEmail();
|
||||
await this.validateEmail();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ export class AutoFillConstants {
|
||||
"mfacode",
|
||||
"otc",
|
||||
"otc-code",
|
||||
"otp",
|
||||
"otp-code",
|
||||
"otpcode",
|
||||
"pin",
|
||||
|
||||
@@ -105,7 +105,11 @@ export async function sendExtensionMessage(
|
||||
command: string,
|
||||
options: Record<string, any> = {},
|
||||
): Promise<any> {
|
||||
if (typeof browser !== "undefined") {
|
||||
if (
|
||||
typeof browser !== "undefined" &&
|
||||
typeof browser.runtime !== "undefined" &&
|
||||
typeof browser.runtime.sendMessage !== "undefined"
|
||||
) {
|
||||
return browser.runtime.sendMessage({ command, ...options });
|
||||
}
|
||||
|
||||
|
||||
@@ -401,6 +401,8 @@ export default class MainBackground {
|
||||
const logoutCallback = async (logoutReason: LogoutReason, userId?: UserId) =>
|
||||
await this.logout(logoutReason, userId);
|
||||
|
||||
const runtimeNativeMessagingBackground = () => this.nativeMessagingBackground;
|
||||
|
||||
const refreshAccessTokenErrorCallback = () => {
|
||||
// Send toast to popup
|
||||
this.messagingService.send("showToast", {
|
||||
@@ -616,7 +618,9 @@ export default class MainBackground {
|
||||
|
||||
this.i18nService = new I18nService(BrowserApi.getUILanguage(), this.globalStateProvider);
|
||||
|
||||
this.biometricsService = new BackgroundBrowserBiometricsService(this.nativeMessagingBackground);
|
||||
this.biometricsService = new BackgroundBrowserBiometricsService(
|
||||
runtimeNativeMessagingBackground,
|
||||
);
|
||||
|
||||
this.kdfConfigService = new KdfConfigService(this.stateProvider);
|
||||
|
||||
|
||||
@@ -6,20 +6,20 @@ import { BrowserBiometricsService } from "./browser-biometrics.service";
|
||||
|
||||
@Injectable()
|
||||
export class BackgroundBrowserBiometricsService extends BrowserBiometricsService {
|
||||
constructor(private nativeMessagingBackground: NativeMessagingBackground) {
|
||||
constructor(private nativeMessagingBackground: () => NativeMessagingBackground) {
|
||||
super();
|
||||
}
|
||||
|
||||
async authenticateBiometric(): Promise<boolean> {
|
||||
const responsePromise = this.nativeMessagingBackground.getResponse();
|
||||
await this.nativeMessagingBackground.send({ command: "biometricUnlock" });
|
||||
const responsePromise = this.nativeMessagingBackground().getResponse();
|
||||
await this.nativeMessagingBackground().send({ command: "biometricUnlock" });
|
||||
const response = await responsePromise;
|
||||
return response.response === "unlocked";
|
||||
}
|
||||
|
||||
async isBiometricUnlockAvailable(): Promise<boolean> {
|
||||
const responsePromise = this.nativeMessagingBackground.getResponse();
|
||||
await this.nativeMessagingBackground.send({ command: "biometricUnlockAvailable" });
|
||||
const responsePromise = this.nativeMessagingBackground().getResponse();
|
||||
await this.nativeMessagingBackground().send({ command: "biometricUnlockAvailable" });
|
||||
const response = await responsePromise;
|
||||
return response.response === "available";
|
||||
}
|
||||
|
||||
@@ -199,6 +199,9 @@ export const routerTransition = trigger("routerTransition", [
|
||||
transition("vault-settings => sync", inSlideLeft),
|
||||
transition("sync => vault-settings", outSlideRight),
|
||||
|
||||
transition("vault-settings => trash", inSlideLeft),
|
||||
transition("trash => vault-settings", outSlideRight),
|
||||
|
||||
// Appearance settings
|
||||
transition("tabs => appearance", inSlideLeft),
|
||||
transition("appearance => tabs", outSlideRight),
|
||||
|
||||
@@ -91,6 +91,7 @@ import { FolderAddEditComponent } from "../vault/popup/settings/folder-add-edit.
|
||||
import { FoldersV2Component } from "../vault/popup/settings/folders-v2.component";
|
||||
import { FoldersComponent } from "../vault/popup/settings/folders.component";
|
||||
import { SyncComponent } from "../vault/popup/settings/sync.component";
|
||||
import { TrashComponent } from "../vault/popup/settings/trash.component";
|
||||
import { VaultSettingsV2Component } from "../vault/popup/settings/vault-settings-v2.component";
|
||||
import { VaultSettingsComponent } from "../vault/popup/settings/vault-settings.component";
|
||||
|
||||
@@ -496,6 +497,12 @@ const routes: Routes = [
|
||||
component: AccountSwitcherComponent,
|
||||
data: { state: "account-switcher", doNotSaveUrl: true },
|
||||
},
|
||||
{
|
||||
path: "trash",
|
||||
component: TrashComponent,
|
||||
canActivate: [authGuard],
|
||||
data: { state: "trash" },
|
||||
},
|
||||
];
|
||||
|
||||
@Injectable()
|
||||
|
||||
@@ -312,13 +312,13 @@ export class AddEditV2Component implements OnInit {
|
||||
|
||||
switch (type) {
|
||||
case CipherType.Login:
|
||||
return this.i18nService.t(partOne, this.i18nService.t("typeLogin"));
|
||||
return this.i18nService.t(partOne, this.i18nService.t("typeLogin").toLocaleLowerCase());
|
||||
case CipherType.Card:
|
||||
return this.i18nService.t(partOne, this.i18nService.t("typeCard"));
|
||||
return this.i18nService.t(partOne, this.i18nService.t("typeCard").toLocaleLowerCase());
|
||||
case CipherType.Identity:
|
||||
return this.i18nService.t(partOne, this.i18nService.t("typeIdentity"));
|
||||
return this.i18nService.t(partOne, this.i18nService.t("typeIdentity").toLocaleLowerCase());
|
||||
case CipherType.SecureNote:
|
||||
return this.i18nService.t(partOne, this.i18nService.t("note"));
|
||||
return this.i18nService.t(partOne, this.i18nService.t("note").toLocaleLowerCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,13 @@
|
||||
<button type="button" bitMenuItem appCopyField="username" [cipher]="cipher">
|
||||
{{ "copyUsername" | i18n }}
|
||||
</button>
|
||||
<button type="button" bitMenuItem appCopyField="password" [cipher]="cipher">
|
||||
<button
|
||||
*ngIf="cipher.viewPassword"
|
||||
type="button"
|
||||
bitMenuItem
|
||||
appCopyField="password"
|
||||
[cipher]="cipher"
|
||||
>
|
||||
{{ "copyPassword" | i18n }}
|
||||
</button>
|
||||
<button type="button" bitMenuItem appCopyField="totp" [cipher]="cipher">
|
||||
|
||||
@@ -5,13 +5,30 @@
|
||||
|
||||
<app-cipher-view *ngIf="cipher" [cipher]="cipher"></app-cipher-view>
|
||||
|
||||
<popup-footer slot="footer">
|
||||
<button buttonType="primary" type="button" bitButton (click)="editCipher()">
|
||||
<popup-footer slot="footer" *ngIf="showFooter()">
|
||||
<button
|
||||
*ngIf="!cipher.isDeleted"
|
||||
buttonType="primary"
|
||||
type="button"
|
||||
bitButton
|
||||
(click)="editCipher()"
|
||||
>
|
||||
{{ "edit" | i18n }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
*ngIf="cipher.isDeleted && cipher.edit"
|
||||
buttonType="primary"
|
||||
type="button"
|
||||
bitButton
|
||||
[bitAction]="restore"
|
||||
>
|
||||
{{ "restore" | i18n }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
slot="end"
|
||||
*ngIf="cipher && cipher.edit"
|
||||
*ngIf="cipher.edit"
|
||||
[bitAction]="delete"
|
||||
type="button"
|
||||
buttonType="danger"
|
||||
|
||||
@@ -162,9 +162,28 @@ export class ViewV2Component {
|
||||
return true;
|
||||
};
|
||||
|
||||
restore = async (): Promise<void> => {
|
||||
try {
|
||||
await this.cipherService.restoreWithServer(this.cipher.id);
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
|
||||
await this.router.navigate(["/vault"]);
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("restoredItem"),
|
||||
});
|
||||
};
|
||||
|
||||
protected deleteCipher() {
|
||||
return this.cipher.isDeleted
|
||||
? this.cipherService.deleteWithServer(this.cipher.id)
|
||||
: this.cipherService.softDeleteWithServer(this.cipher.id);
|
||||
}
|
||||
|
||||
protected showFooter(): boolean {
|
||||
return this.cipher && (!this.cipher.isDeleted || (this.cipher.isDeleted && this.cipher.edit));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
|
||||
import { ToastService } from "@bitwarden/components";
|
||||
|
||||
@Component({
|
||||
selector: "app-vault-collections",
|
||||
@@ -30,6 +31,7 @@ export class CollectionsComponent extends BaseCollectionsComponent implements On
|
||||
logService: LogService,
|
||||
configService: ConfigService,
|
||||
accountService: AccountService,
|
||||
toastService: ToastService,
|
||||
) {
|
||||
super(
|
||||
collectionService,
|
||||
@@ -40,6 +42,7 @@ export class CollectionsComponent extends BaseCollectionsComponent implements On
|
||||
logService,
|
||||
configService,
|
||||
accountService,
|
||||
toastService,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -358,6 +358,24 @@ describe("VaultPopupItemsService", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("deletedCiphers$", () => {
|
||||
it("should return deleted ciphers", (done) => {
|
||||
const ciphers = [
|
||||
{ id: "1", type: CipherType.Login, name: "Login 1", isDeleted: true },
|
||||
{ id: "2", type: CipherType.Login, name: "Login 2", isDeleted: true },
|
||||
{ id: "3", type: CipherType.Login, name: "Login 3", isDeleted: true },
|
||||
{ id: "4", type: CipherType.Login, name: "Login 4", isDeleted: false },
|
||||
] as CipherView[];
|
||||
|
||||
cipherServiceMock.getAllDecrypted.mockResolvedValue(ciphers);
|
||||
|
||||
service.deletedCiphers$.subscribe((deletedCiphers) => {
|
||||
expect(deletedCiphers.length).toBe(3);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("hasFilterApplied$", () => {
|
||||
it("should return true if the search term provided is searchable", (done) => {
|
||||
searchService.isSearchable.mockImplementation(async () => true);
|
||||
|
||||
@@ -76,7 +76,7 @@ export class VaultPopupItemsService {
|
||||
* Observable that contains the list of all decrypted ciphers.
|
||||
* @private
|
||||
*/
|
||||
private _cipherList$: Observable<PopupCipherView[]> = merge(
|
||||
private _allDecryptedCiphers$: Observable<CipherView[]> = merge(
|
||||
this.cipherService.ciphers$,
|
||||
this.cipherService.localData$,
|
||||
).pipe(
|
||||
@@ -84,6 +84,10 @@ export class VaultPopupItemsService {
|
||||
tap(() => this._ciphersLoading$.next()),
|
||||
waitUntilSync(this.syncService),
|
||||
switchMap(() => Utils.asyncToObservable(() => this.cipherService.getAllDecrypted())),
|
||||
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||
);
|
||||
|
||||
private _activeCipherList$: Observable<PopupCipherView[]> = this._allDecryptedCiphers$.pipe(
|
||||
switchMap((ciphers) =>
|
||||
combineLatest([
|
||||
this.organizationService.organizations$,
|
||||
@@ -105,11 +109,10 @@ export class VaultPopupItemsService {
|
||||
}),
|
||||
),
|
||||
),
|
||||
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||
);
|
||||
|
||||
private _filteredCipherList$: Observable<PopupCipherView[]> = combineLatest([
|
||||
this._cipherList$,
|
||||
this._activeCipherList$,
|
||||
this._searchText$,
|
||||
this.vaultPopupListFiltersService.filterFunction$,
|
||||
]).pipe(
|
||||
@@ -208,7 +211,9 @@ export class VaultPopupItemsService {
|
||||
/**
|
||||
* Observable that indicates whether the user's vault is empty.
|
||||
*/
|
||||
emptyVault$: Observable<boolean> = this._cipherList$.pipe(map((ciphers) => !ciphers.length));
|
||||
emptyVault$: Observable<boolean> = this._activeCipherList$.pipe(
|
||||
map((ciphers) => !ciphers.length),
|
||||
);
|
||||
|
||||
/**
|
||||
* Observable that indicates whether there are no ciphers to show with the current filter.
|
||||
@@ -232,6 +237,14 @@ export class VaultPopupItemsService {
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* Observable that contains the list of ciphers that have been deleted.
|
||||
*/
|
||||
deletedCiphers$: Observable<CipherView[]> = this._allDecryptedCiphers$.pipe(
|
||||
map((ciphers) => ciphers.filter((c) => c.isDeleted)),
|
||||
shareReplay({ refCount: false, bufferSize: 1 }),
|
||||
);
|
||||
|
||||
constructor(
|
||||
private cipherService: CipherService,
|
||||
private vaultSettingsService: VaultSettingsService,
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<bit-section *ngIf="ciphers?.length">
|
||||
<bit-section-header>
|
||||
<h2 bitTypography="h6">
|
||||
{{ headerText }}
|
||||
</h2>
|
||||
<span bitTypography="body1" slot="end">{{ ciphers.length }}</span>
|
||||
</bit-section-header>
|
||||
<bit-item-group>
|
||||
<bit-item *ngFor="let cipher of ciphers">
|
||||
<a
|
||||
bit-item-content
|
||||
[appA11yTitle]="'viewItemTitle' | i18n: cipher.name"
|
||||
(click)="onViewCipher(cipher)"
|
||||
>
|
||||
<app-vault-icon slot="start" [cipher]="cipher"></app-vault-icon>
|
||||
<span data-testid="item-name">{{ cipher.name }}</span>
|
||||
</a>
|
||||
<ng-container slot="end" *ngIf="cipher.edit">
|
||||
<bit-item-action>
|
||||
<button
|
||||
type="button"
|
||||
bitIconButton="bwi-ellipsis-v"
|
||||
size="small"
|
||||
[attr.aria-label]="'moreOptionsLabel' | i18n: cipher.name"
|
||||
[title]="'moreOptionsTitle' | i18n: cipher.name"
|
||||
[bitMenuTriggerFor]="moreOptions"
|
||||
></button>
|
||||
<bit-menu #moreOptions>
|
||||
<button type="button" bitMenuItem (click)="restore(cipher)">
|
||||
{{ "restore" | i18n }}
|
||||
</button>
|
||||
<button type="button" bitMenuItem (click)="delete(cipher)">
|
||||
{{ "deleteForever" | i18n }}
|
||||
</button>
|
||||
</bit-menu>
|
||||
</bit-item-action>
|
||||
</ng-container>
|
||||
</bit-item>
|
||||
</bit-item-group>
|
||||
</bit-section>
|
||||
@@ -0,0 +1,107 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, Input } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import {
|
||||
DialogService,
|
||||
IconButtonModule,
|
||||
ItemModule,
|
||||
MenuModule,
|
||||
SectionComponent,
|
||||
SectionHeaderComponent,
|
||||
ToastService,
|
||||
} from "@bitwarden/components";
|
||||
import { PasswordRepromptService } from "@bitwarden/vault";
|
||||
|
||||
@Component({
|
||||
selector: "app-trash-list-items-container",
|
||||
templateUrl: "trash-list-items-container.component.html",
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
ItemModule,
|
||||
JslibModule,
|
||||
SectionComponent,
|
||||
SectionHeaderComponent,
|
||||
MenuModule,
|
||||
IconButtonModule,
|
||||
],
|
||||
})
|
||||
export class TrashListItemsContainerComponent {
|
||||
/**
|
||||
* The list of trashed items to display.
|
||||
*/
|
||||
@Input()
|
||||
ciphers: CipherView[] = [];
|
||||
|
||||
@Input()
|
||||
headerText: string;
|
||||
|
||||
constructor(
|
||||
private cipherService: CipherService,
|
||||
private logService: LogService,
|
||||
private toastService: ToastService,
|
||||
private i18nService: I18nService,
|
||||
private dialogService: DialogService,
|
||||
private passwordRepromptService: PasswordRepromptService,
|
||||
private router: Router,
|
||||
) {}
|
||||
|
||||
async restore(cipher: CipherView) {
|
||||
try {
|
||||
await this.cipherService.restoreWithServer(cipher.id);
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("restoredItem"),
|
||||
});
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
async delete(cipher: CipherView) {
|
||||
const repromptPassed = await this.passwordRepromptService.passwordRepromptCheck(cipher);
|
||||
|
||||
if (!repromptPassed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmed = await this.dialogService.openSimpleDialog({
|
||||
title: { key: "deleteItem" },
|
||||
content: { key: "permanentlyDeleteItemConfirmation" },
|
||||
type: "warning",
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.cipherService.deleteWithServer(cipher.id);
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("deletedItem"),
|
||||
});
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
async onViewCipher(cipher: CipherView) {
|
||||
const repromptPassed = await this.passwordRepromptService.passwordRepromptCheck(cipher);
|
||||
if (!repromptPassed) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.router.navigate(["/view-cipher"], {
|
||||
queryParams: { cipherId: cipher.id, type: cipher.type },
|
||||
});
|
||||
}
|
||||
}
|
||||
33
apps/browser/src/vault/popup/settings/trash.component.html
Normal file
33
apps/browser/src/vault/popup/settings/trash.component.html
Normal file
@@ -0,0 +1,33 @@
|
||||
<popup-page>
|
||||
<popup-header slot="header" [pageTitle]="'trash' | i18n" showBackButton>
|
||||
<ng-container slot="end">
|
||||
<app-pop-out></app-pop-out>
|
||||
</ng-container>
|
||||
</popup-header>
|
||||
<ng-container *ngIf="deletedCiphers$ | async as deletedItems">
|
||||
<bit-callout *ngIf="deletedItems.length" type="warning" title="{{ 'warning' | i18n }}">
|
||||
{{ "trashWarning" | i18n }}
|
||||
</bit-callout>
|
||||
|
||||
<ng-container *ngIf="deletedItems.length; else noDeletedItems">
|
||||
<app-trash-list-items-container
|
||||
[headerText]="'itemsInTrash' | i18n"
|
||||
[ciphers]="deletedItems"
|
||||
></app-trash-list-items-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #noDeletedItems>
|
||||
<bit-no-items
|
||||
[icon]="emptyTrashIcon"
|
||||
class="tw-flex tw-h-full tw-items-center tw-justify-center"
|
||||
>
|
||||
<ng-container slot="title">
|
||||
{{ "noItemsInTrash" | i18n }}
|
||||
</ng-container>
|
||||
<ng-container slot="description">
|
||||
{{ "noItemsInTrashDesc" | i18n }}
|
||||
</ng-container>
|
||||
</bit-no-items>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</popup-page>
|
||||
37
apps/browser/src/vault/popup/settings/trash.component.ts
Normal file
37
apps/browser/src/vault/popup/settings/trash.component.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { CalloutModule, NoItemsModule } from "@bitwarden/components";
|
||||
import { VaultIcons } from "@bitwarden/vault";
|
||||
|
||||
import { PopOutComponent } from "../../../platform/popup/components/pop-out.component";
|
||||
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
|
||||
import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component";
|
||||
import { VaultListItemsContainerComponent } from "../components/vault-v2/vault-list-items-container/vault-list-items-container.component";
|
||||
import { VaultPopupItemsService } from "../services/vault-popup-items.service";
|
||||
|
||||
import { TrashListItemsContainerComponent } from "./trash-list-items-container/trash-list-items-container.component";
|
||||
|
||||
@Component({
|
||||
templateUrl: "trash.component.html",
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
JslibModule,
|
||||
PopupPageComponent,
|
||||
PopupHeaderComponent,
|
||||
PopOutComponent,
|
||||
VaultListItemsContainerComponent,
|
||||
TrashListItemsContainerComponent,
|
||||
CalloutModule,
|
||||
NoItemsModule,
|
||||
],
|
||||
})
|
||||
export class TrashComponent {
|
||||
protected deletedCiphers$ = this.vaultPopupItemsService.deletedCiphers$;
|
||||
|
||||
protected emptyTrashIcon = VaultIcons.EmptyTrash;
|
||||
|
||||
constructor(private vaultPopupItemsService: VaultPopupItemsService) {}
|
||||
}
|
||||
@@ -24,6 +24,12 @@
|
||||
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>
|
||||
</a>
|
||||
</bit-item>
|
||||
<bit-item>
|
||||
<a bit-item-content routerLink="/trash">
|
||||
{{ "trash" | i18n }}
|
||||
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>
|
||||
</a>
|
||||
</bit-item>
|
||||
<bit-item>
|
||||
<button type="button" bit-item-content (click)="sync()">
|
||||
{{ "syncVaultNow" | i18n }}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
|
||||
import { ToastService } from "@bitwarden/components";
|
||||
|
||||
@Component({
|
||||
selector: "app-vault-collections",
|
||||
@@ -24,6 +25,7 @@ export class CollectionsComponent extends BaseCollectionsComponent {
|
||||
logService: LogService,
|
||||
configService: ConfigService,
|
||||
accountService: AccountService,
|
||||
toastService: ToastService,
|
||||
) {
|
||||
super(
|
||||
collectionService,
|
||||
@@ -34,6 +36,7 @@ export class CollectionsComponent extends BaseCollectionsComponent {
|
||||
logService,
|
||||
configService,
|
||||
accountService,
|
||||
toastService,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
@@ -51,6 +52,7 @@ export class EventsComponent extends BaseEventsComponent implements OnInit, OnDe
|
||||
private organizationUserService: OrganizationUserService,
|
||||
private providerService: ProviderService,
|
||||
fileDownloadService: FileDownloadService,
|
||||
toastService: ToastService,
|
||||
) {
|
||||
super(
|
||||
eventService,
|
||||
@@ -59,6 +61,7 @@ export class EventsComponent extends BaseEventsComponent implements OnInit, OnDe
|
||||
platformUtilsService,
|
||||
logService,
|
||||
fileDownloadService,
|
||||
toastService,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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(["/"]);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -44,8 +44,8 @@ export class HintComponent extends BaseHintComponent implements OnInit {
|
||||
);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
async ngOnInit(): Promise<void> {
|
||||
await super.ngOnInit();
|
||||
this.emailFormControl.setValue(this.email);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,4 +56,3 @@
|
||||
{{ "save" | i18n }}
|
||||
</button>
|
||||
</form>
|
||||
<app-payment [showMethods]="false"></app-payment>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
</button>
|
||||
<button type="button" bitMenuItem (click)="access(false)">
|
||||
<i class="bwi bwi-fw bwi-users" aria-hidden="true"></i>
|
||||
{{ "access" | i18n }}
|
||||
{{ "editAccess" | i18n }}
|
||||
</button>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!canEditCollection && canViewCollectionInfo">
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
(click)="bulkEditCollectionAccess()"
|
||||
>
|
||||
<i class="bwi bwi-fw bwi-users" aria-hidden="true"></i>
|
||||
{{ "access" | i18n }}
|
||||
{{ "editAccess" | i18n }}
|
||||
</button>
|
||||
<button
|
||||
*ngIf="
|
||||
@@ -81,14 +81,7 @@
|
||||
>
|
||||
<span class="tw-text-danger">
|
||||
<i class="bwi bwi-fw bwi-trash" aria-hidden="true"></i>
|
||||
{{
|
||||
(showBulkTrashOptions
|
||||
? "permanentlyDeleteSelected"
|
||||
: vaultBulkManagementActionEnabled
|
||||
? "delete"
|
||||
: "deleteSelected"
|
||||
) | i18n
|
||||
}}
|
||||
{{ (showBulkTrashOptions ? "permanentlyDeleteSelected" : "delete") | i18n }}
|
||||
</span>
|
||||
</button>
|
||||
</bit-menu>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<bit-dialog dialogSize="large">
|
||||
<bit-dialog dialogSize="large" background="alt">
|
||||
<span bitDialogTitle>
|
||||
{{ cipherTypeString }}
|
||||
</span>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -24,5 +24,16 @@ describe("CollectionUtils Service", () => {
|
||||
expect(result[0].node.name).toBe("Parent");
|
||||
expect(result[0].children[0].node.name).toBe("Child");
|
||||
});
|
||||
|
||||
it("should return an empty array if no collections are provided", () => {
|
||||
// Arrange
|
||||
const collections: CollectionView[] = [];
|
||||
|
||||
// Act
|
||||
const result = getNestedCollectionTree(collections);
|
||||
|
||||
// Assert
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,6 +14,10 @@ export function getNestedCollectionTree(collections: CollectionView[]): TreeNode
|
||||
export function getNestedCollectionTree(
|
||||
collections: (CollectionView | CollectionAdminView)[],
|
||||
): TreeNode<CollectionView | CollectionAdminView>[] {
|
||||
if (!collections) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Collections need to be cloned because ServiceUtils.nestedTraverse actively
|
||||
// modifies the names of collections.
|
||||
// These changes risk affecting collections store in StateService.
|
||||
|
||||
@@ -9025,5 +9025,8 @@
|
||||
},
|
||||
"additionalContentAvailable": {
|
||||
"message": "Additional content is available"
|
||||
},
|
||||
"editAccess": {
|
||||
"message": "Edit access"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,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 { TableDataSource, NoItemsModule } from "@bitwarden/components";
|
||||
import { TableDataSource, NoItemsModule, ToastService } from "@bitwarden/components";
|
||||
import { Devices } from "@bitwarden/web-vault/app/admin-console/icons";
|
||||
import { LooseComponentsModule } from "@bitwarden/web-vault/app/shared";
|
||||
import { SharedModule } from "@bitwarden/web-vault/app/shared/shared.module";
|
||||
@@ -54,6 +54,7 @@ export class DeviceApprovalsComponent implements OnInit, OnDestroy {
|
||||
private logService: LogService,
|
||||
private validationService: ValidationService,
|
||||
private configService: ConfigService,
|
||||
private toastService: ToastService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@@ -84,17 +85,17 @@ export class DeviceApprovalsComponent implements OnInit, OnDestroy {
|
||||
authRequest,
|
||||
);
|
||||
|
||||
this.platformUtilsService.showToast(
|
||||
"success",
|
||||
null,
|
||||
this.i18nService.t("loginRequestApproved"),
|
||||
);
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("loginRequestApproved"),
|
||||
});
|
||||
} catch (error) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
null,
|
||||
this.i18nService.t("resetPasswordDetailsError"),
|
||||
);
|
||||
this.toastService.showToast({
|
||||
variant: "error",
|
||||
title: null,
|
||||
message: this.i18nService.t("resetPasswordDetailsError"),
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -109,18 +110,22 @@ export class DeviceApprovalsComponent implements OnInit, OnDestroy {
|
||||
this.organizationId,
|
||||
this.tableDataSource.data,
|
||||
);
|
||||
this.platformUtilsService.showToast(
|
||||
"success",
|
||||
null,
|
||||
this.i18nService.t("allLoginRequestsApproved"),
|
||||
);
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("allLoginRequestsApproved"),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async denyRequest(requestId: string) {
|
||||
await this.performAsyncAction(async () => {
|
||||
await this.organizationAuthRequestService.denyPendingRequests(this.organizationId, requestId);
|
||||
this.platformUtilsService.showToast("error", null, this.i18nService.t("loginRequestDenied"));
|
||||
this.toastService.showToast({
|
||||
variant: "error",
|
||||
title: null,
|
||||
message: this.i18nService.t("loginRequestDenied"),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -134,11 +139,11 @@ export class DeviceApprovalsComponent implements OnInit, OnDestroy {
|
||||
this.organizationId,
|
||||
...this.tableDataSource.data.map((r) => r.id),
|
||||
);
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
null,
|
||||
this.i18nService.t("allLoginRequestsDenied"),
|
||||
);
|
||||
this.toastService.showToast({
|
||||
variant: "error",
|
||||
title: null,
|
||||
message: this.i18nService.t("allLoginRequestsDenied"),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitw
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
import { DialogService, ToastService } from "@bitwarden/components";
|
||||
|
||||
import { domainNameValidator } from "./validators/domain-name.validator";
|
||||
import { uniqueInArrayValidator } from "./validators/unique-in-array.validator";
|
||||
@@ -66,6 +66,7 @@ export class DomainAddEditDialogComponent implements OnInit, OnDestroy {
|
||||
private orgDomainService: OrgDomainServiceAbstraction,
|
||||
private validationService: ValidationService,
|
||||
private dialogService: DialogService,
|
||||
private toastService: ToastService,
|
||||
) {}
|
||||
|
||||
// Angular Method Implementations
|
||||
@@ -112,7 +113,11 @@ export class DomainAddEditDialogComponent implements OnInit, OnDestroy {
|
||||
// Creates a new domain record. The DNS TXT Record will be generated server-side and returned in the response.
|
||||
saveDomain = async (): Promise<void> => {
|
||||
if (this.domainForm.invalid) {
|
||||
this.platformUtilsService.showToast("error", null, this.i18nService.t("domainFormInvalid"));
|
||||
this.toastService.showToast({
|
||||
variant: "error",
|
||||
title: null,
|
||||
message: this.i18nService.t("domainFormInvalid"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -126,7 +131,11 @@ export class DomainAddEditDialogComponent implements OnInit, OnDestroy {
|
||||
this.data.orgDomain = await this.orgDomainApiService.post(this.data.organizationId, request);
|
||||
// Patch the DNS TXT Record that was generated server-side
|
||||
this.domainForm.controls.txt.patchValue(this.data.orgDomain.txt);
|
||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("domainSaved"));
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("domainSaved"),
|
||||
});
|
||||
} catch (e) {
|
||||
this.handleDomainSaveError(e);
|
||||
}
|
||||
@@ -177,7 +186,11 @@ export class DomainAddEditDialogComponent implements OnInit, OnDestroy {
|
||||
verifyDomain = async (): Promise<void> => {
|
||||
if (this.domainForm.invalid) {
|
||||
// Note: shouldn't be possible, but going to leave this to be safe.
|
||||
this.platformUtilsService.showToast("error", null, this.i18nService.t("domainFormInvalid"));
|
||||
this.toastService.showToast({
|
||||
variant: "error",
|
||||
title: null,
|
||||
message: this.i18nService.t("domainFormInvalid"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -188,7 +201,11 @@ export class DomainAddEditDialogComponent implements OnInit, OnDestroy {
|
||||
);
|
||||
|
||||
if (this.data.orgDomain.verifiedDate) {
|
||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("domainVerified"));
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("domainVerified"),
|
||||
});
|
||||
this.dialogRef.close();
|
||||
} else {
|
||||
this.domainNameCtrl.setErrors({
|
||||
@@ -250,7 +267,11 @@ export class DomainAddEditDialogComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
await this.orgDomainApiService.delete(this.data.organizationId, this.data.orgDomain.id);
|
||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("domainRemoved"));
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("domainRemoved"),
|
||||
});
|
||||
|
||||
this.dialogRef.close();
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@ 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 { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
import { DialogService, ToastService } from "@bitwarden/components";
|
||||
|
||||
import {
|
||||
DomainAddEditDialogComponent,
|
||||
@@ -37,6 +37,7 @@ export class DomainVerificationComponent implements OnInit, OnDestroy {
|
||||
private orgDomainService: OrgDomainServiceAbstraction,
|
||||
private dialogService: DialogService,
|
||||
private validationService: ValidationService,
|
||||
private toastService: ToastService,
|
||||
) {}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
@@ -110,13 +111,17 @@ export class DomainVerificationComponent implements OnInit, OnDestroy {
|
||||
);
|
||||
|
||||
if (orgDomain.verifiedDate) {
|
||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("domainVerified"));
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("domainVerified"),
|
||||
});
|
||||
} else {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
null,
|
||||
this.i18nService.t("domainNotVerified", domainName),
|
||||
);
|
||||
this.toastService.showToast({
|
||||
variant: "error",
|
||||
title: null,
|
||||
message: this.i18nService.t("domainNotVerified", domainName),
|
||||
});
|
||||
// Update this item so the last checked date gets updated.
|
||||
await this.updateOrgDomain(orgDomainId);
|
||||
}
|
||||
@@ -138,11 +143,11 @@ export class DomainVerificationComponent implements OnInit, OnDestroy {
|
||||
switch (errorResponse.statusCode) {
|
||||
case HttpStatusCode.Conflict:
|
||||
if (errorResponse.message.includes("The domain is not available to be claimed")) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
null,
|
||||
this.i18nService.t("domainNotAvailable", domainName),
|
||||
);
|
||||
this.toastService.showToast({
|
||||
variant: "error",
|
||||
title: null,
|
||||
message: this.i18nService.t("domainNotAvailable", domainName),
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -166,7 +171,11 @@ export class DomainVerificationComponent implements OnInit, OnDestroy {
|
||||
|
||||
await this.orgDomainApiService.delete(this.organizationId, orgDomainId);
|
||||
|
||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("domainRemoved"));
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("domainRemoved"),
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
|
||||
@@ -17,7 +17,7 @@ import { OrganizationConnectionResponse } from "@bitwarden/common/admin-console/
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
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({
|
||||
selector: "app-org-manage-scim",
|
||||
@@ -46,6 +46,7 @@ export class ScimComponent implements OnInit {
|
||||
private environmentService: EnvironmentService,
|
||||
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||
private dialogService: DialogService,
|
||||
private toastService: ToastService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@@ -104,7 +105,11 @@ export class ScimComponent implements OnInit {
|
||||
endpointUrl: await this.getScimEndpointUrl(),
|
||||
clientSecret: response.apiKey,
|
||||
});
|
||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("scimApiKeyRotated"));
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("scimApiKeyRotated"),
|
||||
});
|
||||
};
|
||||
|
||||
copyScimKey = async () => {
|
||||
@@ -131,7 +136,11 @@ export class ScimComponent implements OnInit {
|
||||
}
|
||||
|
||||
await this.setConnectionFormValues(response);
|
||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("scimSettingsSaved"));
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("scimSettingsSaved"),
|
||||
});
|
||||
};
|
||||
|
||||
async getScimEndpointUrl() {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Provider } from "@bitwarden/common/admin-console/models/domain/provider
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
import { DialogService, ToastService } from "@bitwarden/components";
|
||||
|
||||
import { WebProviderService } from "../services/web-provider.service";
|
||||
|
||||
@@ -32,6 +32,7 @@ export class AddOrganizationComponent implements OnInit {
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private validationService: ValidationService,
|
||||
private dialogService: DialogService,
|
||||
private toastService: ToastService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@@ -73,11 +74,11 @@ export class AddOrganizationComponent implements OnInit {
|
||||
return;
|
||||
}
|
||||
|
||||
this.platformUtilsService.showToast(
|
||||
"success",
|
||||
null,
|
||||
this.i18nService.t("organizationJoinedProvider"),
|
||||
);
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("organizationJoinedProvider"),
|
||||
});
|
||||
|
||||
this.dialogRef.close(true);
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ import { ProviderService } from "@bitwarden/common/admin-console/abstractions/pr
|
||||
import { ProviderUserType } from "@bitwarden/common/admin-console/enums";
|
||||
import { Provider } from "@bitwarden/common/admin-console/models/domain/provider";
|
||||
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 { providerPermissionsGuard } from "./provider-permissions.guard";
|
||||
|
||||
@@ -39,7 +39,7 @@ describe("Provider Permissions Guard", () => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
{ provide: ProviderService, useValue: providerService },
|
||||
{ provide: PlatformUtilsService, useValue: mock<PlatformUtilsService>() },
|
||||
{ provide: ToastService, useValue: mock<ToastService>() },
|
||||
{ provide: I18nService, useValue: mock<I18nService>() },
|
||||
{ provide: Router, useValue: mock<Router>() },
|
||||
],
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
|
||||
import { Provider } from "@bitwarden/common/admin-console/models/domain/provider";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { ToastService } from "@bitwarden/components";
|
||||
|
||||
/**
|
||||
* `CanActivateFn` that asserts the logged in user has permission to access
|
||||
@@ -36,8 +36,8 @@ export function providerPermissionsGuard(
|
||||
return async (route: ActivatedRouteSnapshot, _state: RouterStateSnapshot) => {
|
||||
const providerService = inject(ProviderService);
|
||||
const router = inject(Router);
|
||||
const platformUtilsService = inject(PlatformUtilsService);
|
||||
const i18nService = inject(I18nService);
|
||||
const toastService = inject(ToastService);
|
||||
|
||||
const provider = await providerService.get(route.params.providerId);
|
||||
if (provider == null) {
|
||||
@@ -45,14 +45,22 @@ export function providerPermissionsGuard(
|
||||
}
|
||||
|
||||
if (!provider.isProviderAdmin && !provider.enabled) {
|
||||
platformUtilsService.showToast("error", null, i18nService.t("providerIsDisabled"));
|
||||
toastService.showToast({
|
||||
variant: "error",
|
||||
title: null,
|
||||
message: i18nService.t("providerIsDisabled"),
|
||||
});
|
||||
return router.createUrlTree(["/"]);
|
||||
}
|
||||
|
||||
const hasSpecifiedPermissions = permissionsCallback == null || permissionsCallback(provider);
|
||||
|
||||
if (!hasSpecifiedPermissions) {
|
||||
platformUtilsService.showToast("error", null, i18nService.t("accessDenied"));
|
||||
toastService.showToast({
|
||||
variant: "error",
|
||||
title: null,
|
||||
message: i18nService.t("accessDenied"),
|
||||
});
|
||||
return router.createUrlTree(["/providers", provider.id]);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,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 { BaseEventsComponent } from "@bitwarden/web-vault/app/admin-console/common/base.events.component";
|
||||
import { EventService } from "@bitwarden/web-vault/app/core";
|
||||
import { EventExportService } from "@bitwarden/web-vault/app/tools/event-export";
|
||||
@@ -37,6 +38,7 @@ export class EventsComponent extends BaseEventsComponent implements OnInit {
|
||||
logService: LogService,
|
||||
private userNamePipe: UserNamePipe,
|
||||
fileDownloadService: FileDownloadService,
|
||||
toastService: ToastService,
|
||||
) {
|
||||
super(
|
||||
eventService,
|
||||
@@ -45,6 +47,7 @@ export class EventsComponent extends BaseEventsComponent implements OnInit {
|
||||
platformUtilsService,
|
||||
logService,
|
||||
fileDownloadService,
|
||||
toastService,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -190,7 +190,7 @@ export class MembersComponent extends BaseMembersComponent<ProviderUser> {
|
||||
await this.apiService.postProviderUserConfirm(this.providerId, user.id, request);
|
||||
}
|
||||
|
||||
deleteUser = (id: string): Promise<void> =>
|
||||
removeUser = (id: string): Promise<void> =>
|
||||
this.apiService.deleteProviderUser(this.providerId, id);
|
||||
|
||||
edit = async (user: ProviderUser | null): Promise<void> => {
|
||||
|
||||
@@ -21,7 +21,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 } from "@bitwarden/components";
|
||||
import { DialogService, ToastService } from "@bitwarden/components";
|
||||
import { BasePeopleComponent } from "@bitwarden/web-vault/app/admin-console/common/base.people.component";
|
||||
import { openEntityEventsDialog } from "@bitwarden/web-vault/app/admin-console/organizations/manage/entity-events.component";
|
||||
import { BulkStatusComponent } from "@bitwarden/web-vault/app/admin-console/organizations/members/components/bulk/bulk-status.component";
|
||||
@@ -75,6 +75,7 @@ export class PeopleComponent
|
||||
dialogService: DialogService,
|
||||
organizationManagementPreferencesService: OrganizationManagementPreferencesService,
|
||||
private configService: ConfigService,
|
||||
protected toastService: ToastService,
|
||||
) {
|
||||
super(
|
||||
apiService,
|
||||
@@ -89,6 +90,7 @@ export class PeopleComponent
|
||||
userNamePipe,
|
||||
dialogService,
|
||||
organizationManagementPreferencesService,
|
||||
toastService,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -213,11 +215,11 @@ export class PeopleComponent
|
||||
const filteredUsers = users.filter((u) => u.status === ProviderUserStatusType.Invited);
|
||||
|
||||
if (filteredUsers.length <= 0) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
this.i18nService.t("errorOccurred"),
|
||||
this.i18nService.t("noSelectedUsersApplicable"),
|
||||
);
|
||||
this.toastService.showToast({
|
||||
variant: "error",
|
||||
title: this.i18nService.t("errorOccurred"),
|
||||
message: this.i18nService.t("noSelectedUsersApplicable"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { ProviderUserUpdateRequest } from "@bitwarden/common/admin-console/model
|
||||
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";
|
||||
|
||||
/**
|
||||
* @deprecated Please use the {@link MembersDialogComponent} instead.
|
||||
@@ -42,6 +42,7 @@ export class UserAddEditComponent implements OnInit {
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private logService: LogService,
|
||||
private dialogService: DialogService,
|
||||
private toastService: ToastService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@@ -80,11 +81,11 @@ export class UserAddEditComponent implements OnInit {
|
||||
this.formPromise = this.apiService.postProviderUserInvite(this.providerId, request);
|
||||
}
|
||||
await this.formPromise;
|
||||
this.platformUtilsService.showToast(
|
||||
"success",
|
||||
null,
|
||||
this.i18nService.t(this.editMode ? "editedUserId" : "invitedUsers", this.name),
|
||||
);
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t(this.editMode ? "editedUserId" : "invitedUsers", this.name),
|
||||
});
|
||||
this.savedUser.emit();
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
@@ -109,11 +110,11 @@ export class UserAddEditComponent implements OnInit {
|
||||
try {
|
||||
this.deletePromise = this.apiService.deleteProviderUser(this.providerId, this.providerUserId);
|
||||
await this.deletePromise;
|
||||
this.platformUtilsService.showToast(
|
||||
"success",
|
||||
null,
|
||||
this.i18nService.t("removedUserId", this.name),
|
||||
);
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("removedUserId", this.name),
|
||||
});
|
||||
this.deletedUser.emit();
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
|
||||
@@ -14,7 +14,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";
|
||||
|
||||
@Component({
|
||||
selector: "provider-account",
|
||||
@@ -49,6 +49,7 @@ export class AccountComponent implements OnDestroy, OnInit {
|
||||
private providerApiService: ProviderApiServiceAbstraction,
|
||||
private formBuilder: FormBuilder,
|
||||
private router: Router,
|
||||
private toastService: ToastService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@@ -86,7 +87,11 @@ export class AccountComponent implements OnDestroy, OnInit {
|
||||
await this.providerApiService.putProvider(this.providerId, request);
|
||||
await this.syncService.fullSync(true);
|
||||
this.provider = await this.providerApiService.getProvider(this.providerId);
|
||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("providerUpdated"));
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("providerUpdated"),
|
||||
});
|
||||
};
|
||||
|
||||
async deleteProvider() {
|
||||
@@ -110,11 +115,11 @@ export class AccountComponent implements OnDestroy, OnInit {
|
||||
|
||||
try {
|
||||
await this.providerApiService.deleteProvider(this.providerId);
|
||||
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"),
|
||||
});
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,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 { BaseEventsComponent } from "@bitwarden/web-vault/app/admin-console/common/base.events.component";
|
||||
import { EventService } from "@bitwarden/web-vault/app/core";
|
||||
import { EventExportService } from "@bitwarden/web-vault/app/tools/event-export";
|
||||
@@ -33,6 +34,7 @@ export class ServiceAccountEventsComponent
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
logService: LogService,
|
||||
fileDownloadService: FileDownloadService,
|
||||
toastService: ToastService,
|
||||
) {
|
||||
super(
|
||||
eventService,
|
||||
@@ -41,6 +43,7 @@ export class ServiceAccountEventsComponent
|
||||
platformUtilsService,
|
||||
logService,
|
||||
fileDownloadService,
|
||||
toastService,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import { CollectionService } from "@bitwarden/common/vault/abstractions/collecti
|
||||
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
|
||||
import { ToastService } from "@bitwarden/components";
|
||||
|
||||
@Directive()
|
||||
export class CollectionsComponent implements OnInit {
|
||||
@@ -39,6 +40,7 @@ export class CollectionsComponent implements OnInit {
|
||||
private logService: LogService,
|
||||
private configService: ConfigService,
|
||||
private accountService: AccountService,
|
||||
private toastService: ToastService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@@ -82,11 +84,11 @@ export class CollectionsComponent implements OnInit {
|
||||
})
|
||||
.map((c) => c.id);
|
||||
if (!this.allowSelectNone && selectedCollectionIds.length === 0) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
this.i18nService.t("errorOccurred"),
|
||||
this.i18nService.t("selectOneCollection"),
|
||||
);
|
||||
this.toastService.showToast({
|
||||
variant: "error",
|
||||
title: this.i18nService.t("errorOccurred"),
|
||||
message: this.i18nService.t("selectOneCollection"),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
this.cipherDomain.collectionIds = selectedCollectionIds;
|
||||
@@ -94,10 +96,18 @@ export class CollectionsComponent implements OnInit {
|
||||
this.formPromise = this.saveCollections();
|
||||
await this.formPromise;
|
||||
this.onSavedCollections.emit();
|
||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("editedItem"));
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("editedItem"),
|
||||
});
|
||||
return true;
|
||||
} catch (e) {
|
||||
this.platformUtilsService.showToast("error", this.i18nService.t("errorOccurred"), e.message);
|
||||
this.toastService.showToast({
|
||||
variant: "error",
|
||||
title: this.i18nService.t("errorOccurred"),
|
||||
message: e.message,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,12 +251,12 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
|
||||
return;
|
||||
}
|
||||
|
||||
this.loginEmailService.setEmail(this.data.userEmail);
|
||||
this.loginEmailService.setLoginEmail(this.data.userEmail);
|
||||
await this.router.navigate(["/login-with-device"]);
|
||||
}
|
||||
|
||||
async requestAdminApproval() {
|
||||
this.loginEmailService.setEmail(this.data.userEmail);
|
||||
this.loginEmailService.setLoginEmail(this.data.userEmail);
|
||||
await this.router.navigate(["/admin-approval-requested"]);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Directive, OnInit } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { LoginEmailServiceAbstraction } from "@bitwarden/auth/common";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
@@ -27,8 +28,8 @@ export class HintComponent implements OnInit {
|
||||
protected toastService: ToastService,
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.email = this.loginEmailService.getEmail() ?? "";
|
||||
async ngOnInit(): Promise<void> {
|
||||
this.email = (await firstValueFrom(this.loginEmailService.loginEmail$)) ?? "";
|
||||
}
|
||||
|
||||
async submit() {
|
||||
|
||||
@@ -93,13 +93,6 @@ export class LoginViaAuthRequestComponent
|
||||
) {
|
||||
super(environmentService, i18nService, platformUtilsService, toastService);
|
||||
|
||||
// TODO: I don't know why this is necessary.
|
||||
// Why would the existence of the email depend on the navigation?
|
||||
const navigation = this.router.getCurrentNavigation();
|
||||
if (navigation) {
|
||||
this.email = this.loginEmailService.getEmail();
|
||||
}
|
||||
|
||||
// Gets signalR push notification
|
||||
// Only fires on approval to prevent enumeration
|
||||
this.authRequestService.authRequestPushNotification$
|
||||
@@ -118,6 +111,7 @@ export class LoginViaAuthRequestComponent
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.email = await firstValueFrom(this.loginEmailService.loginEmail$);
|
||||
this.userAuthNStatus = await this.authService.getAuthStatus();
|
||||
|
||||
const matchOptions: IsActiveMatchOptions = {
|
||||
@@ -165,7 +159,7 @@ export class LoginViaAuthRequestComponent
|
||||
} else {
|
||||
// Standard auth request
|
||||
// TODO: evaluate if we can remove the setting of this.email in the constructor
|
||||
this.email = this.loginEmailService.getEmail();
|
||||
this.email = await firstValueFrom(this.loginEmailService.loginEmail$);
|
||||
|
||||
if (!this.email) {
|
||||
this.toastService.showToast({
|
||||
|
||||
@@ -304,7 +304,7 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit,
|
||||
|
||||
private async loadEmailSettings() {
|
||||
// Try to load from memory first
|
||||
const email = this.loginEmailService.getEmail();
|
||||
const email = await firstValueFrom(this.loginEmailService.loginEmail$);
|
||||
const rememberEmail = this.loginEmailService.getRememberEmail();
|
||||
if (email) {
|
||||
this.formGroup.controls.email.setValue(email);
|
||||
@@ -321,7 +321,7 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit,
|
||||
}
|
||||
|
||||
protected async saveEmailSettings() {
|
||||
this.loginEmailService.setEmail(this.formGroup.value.email);
|
||||
this.loginEmailService.setLoginEmail(this.formGroup.value.email);
|
||||
this.loginEmailService.setRememberEmail(this.formGroup.value.rememberEmail);
|
||||
await this.loginEmailService.saveEmailSettings();
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@ import {
|
||||
Environment,
|
||||
} from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { ThemeType } from "@bitwarden/common/platform/enums";
|
||||
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||
import { ButtonModule } from "@bitwarden/components";
|
||||
|
||||
// FIXME: remove `/apps` import from `/libs`
|
||||
@@ -40,7 +38,6 @@ const decorators = (options: {
|
||||
applicationVersion?: string;
|
||||
clientType?: ClientType;
|
||||
hostName?: string;
|
||||
themeType?: ThemeType;
|
||||
}) => {
|
||||
return [
|
||||
componentWrapperDecorator(
|
||||
@@ -84,12 +81,6 @@ const decorators = (options: {
|
||||
getClientType: () => options.clientType || ClientType.Web,
|
||||
} as Partial<PlatformUtilsService>,
|
||||
},
|
||||
{
|
||||
provide: ThemeStateService,
|
||||
useValue: {
|
||||
selectedTheme$: of(options.themeType || ThemeType.Light),
|
||||
} as Partial<ThemeStateService>,
|
||||
},
|
||||
],
|
||||
}),
|
||||
applicationConfig({
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<main
|
||||
class="tw-flex tw-min-h-screen tw-w-full tw-mx-auto tw-flex-col tw-gap-7 tw-bg-background-alt tw-px-8 tw-pb-4 tw-text-main"
|
||||
[ngClass]="{ 'tw-pt-0': decreaseTopPadding, 'tw-pt-8': !decreaseTopPadding }"
|
||||
[ngClass]="{
|
||||
'tw-pt-0': decreaseTopPadding,
|
||||
'tw-pt-8': !decreaseTopPadding,
|
||||
'tw-relative tw-top-0': clientType === 'browser',
|
||||
}"
|
||||
>
|
||||
<bit-icon *ngIf="!hideLogo" [icon]="logo" class="tw-w-[128px] [&>*]:tw-align-top"></bit-icon>
|
||||
|
||||
|
||||
@@ -5,13 +5,11 @@ import { firstValueFrom } from "rxjs";
|
||||
import { ClientType } from "@bitwarden/common/enums";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||
|
||||
import { IconModule, Icon } from "../../../../components/src/icon";
|
||||
import { SharedModule } from "../../../../components/src/shared";
|
||||
import { TypographyModule } from "../../../../components/src/typography";
|
||||
import { BitwardenLogoPrimary, BitwardenLogoWhite } from "../icons";
|
||||
import { BitwardenShieldPrimary, BitwardenShieldWhite } from "../icons/bitwarden-shield.icon";
|
||||
import { BitwardenLogo, BitwardenShield } from "../icons";
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
@@ -34,20 +32,17 @@ export class AnonLayoutComponent implements OnInit, OnChanges {
|
||||
*/
|
||||
@Input() maxWidth: "md" | "3xl" = "md";
|
||||
|
||||
protected logo: Icon;
|
||||
|
||||
protected logo = BitwardenLogo;
|
||||
protected year = "2024";
|
||||
protected clientType: ClientType;
|
||||
protected hostname: string;
|
||||
protected version: string;
|
||||
protected theme: string;
|
||||
|
||||
protected hideYearAndVersion = false;
|
||||
|
||||
constructor(
|
||||
private environmentService: EnvironmentService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private themeStateService: ThemeStateService,
|
||||
) {
|
||||
this.year = new Date().getFullYear().toString();
|
||||
this.clientType = this.platformUtilsService.getClientType();
|
||||
@@ -56,41 +51,18 @@ export class AnonLayoutComponent implements OnInit, OnChanges {
|
||||
|
||||
async ngOnInit() {
|
||||
this.maxWidth = this.maxWidth ?? "md";
|
||||
|
||||
this.theme = await firstValueFrom(this.themeStateService.selectedTheme$);
|
||||
|
||||
if (this.theme === "dark") {
|
||||
this.logo = BitwardenLogoWhite;
|
||||
} else {
|
||||
this.logo = BitwardenLogoPrimary;
|
||||
}
|
||||
|
||||
await this.updateIcon(this.theme);
|
||||
|
||||
this.hostname = (await firstValueFrom(this.environmentService.environment$)).getHostname();
|
||||
this.version = await this.platformUtilsService.getApplicationVersion();
|
||||
|
||||
// If there is no icon input, then use the default icon
|
||||
if (this.icon == null) {
|
||||
this.icon = BitwardenShield;
|
||||
}
|
||||
}
|
||||
|
||||
async ngOnChanges(changes: SimpleChanges) {
|
||||
if (changes.icon) {
|
||||
const theme = await firstValueFrom(this.themeStateService.selectedTheme$);
|
||||
await this.updateIcon(theme);
|
||||
}
|
||||
|
||||
if (changes.maxWidth) {
|
||||
this.maxWidth = changes.maxWidth.currentValue ?? "md";
|
||||
}
|
||||
}
|
||||
|
||||
private async updateIcon(theme: string) {
|
||||
if (this.icon == null) {
|
||||
if (theme === "dark") {
|
||||
this.icon = BitwardenShieldWhite;
|
||||
}
|
||||
|
||||
if (theme !== "dark") {
|
||||
this.icon = BitwardenShieldPrimary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { Meta, StoryObj, moduleMetadata } from "@storybook/angular";
|
||||
import { BehaviorSubject, of } from "rxjs";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import { ClientType } from "@bitwarden/common/enums";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||
|
||||
import { ButtonModule } from "../../../../components/src/button";
|
||||
import { I18nMockService } from "../../../../components/src/utils/i18n-mock.service";
|
||||
@@ -47,12 +46,6 @@ export default {
|
||||
}).asObservable(),
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: ThemeStateService,
|
||||
useValue: {
|
||||
selectedTheme$: of("light"),
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -1,17 +1,9 @@
|
||||
import { svgIcon } from "@bitwarden/components";
|
||||
|
||||
export const BitwardenLogoPrimary = svgIcon`
|
||||
<svg viewBox="0 0 290 45" fill="#175DDC" xmlns="http://www.w3.org/2000/svg">
|
||||
export const BitwardenLogo = svgIcon`
|
||||
<svg viewBox="0 0 290 45" xmlns="http://www.w3.org/2000/svg">
|
||||
<title>Bitwarden</title>
|
||||
<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>
|
||||
`;
|
||||
|
||||
export const BitwardenLogoWhite = svgIcon`
|
||||
<svg viewBox="0 0 290 45" fill="#FFF" xmlns="http://www.w3.org/2000/svg">
|
||||
<title>Bitwarden</title>
|
||||
<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" />
|
||||
<path class="tw-fill-marketing-logo" 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 class="tw-fill-marketing-logo" 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>
|
||||
`;
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
import { svgIcon } from "@bitwarden/components";
|
||||
|
||||
export const BitwardenShieldPrimary = svgIcon`
|
||||
<svg viewBox="0 0 120 132" fill="#175DDC" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M82.2944 69.1899V37.2898H60V93.9624C63.948 91.869 67.4812 89.5927 70.5998 87.1338C78.3962 81.0196 82.2944 75.0383 82.2944 69.1899ZM91.8491 30.9097V69.1899C91.8491 72.0477 91.2934 74.8805 90.182 77.6883C89.0706 80.4962 87.6938 82.9884 86.0516 85.1649C84.4094 87.3415 82.452 89.4598 80.1794 91.5201C77.9068 93.5803 75.8084 95.2916 73.8842 96.654C71.96 98.0164 69.9528 99.304 67.8627 100.517C65.7726 101.73 64.288 102.552 63.4088 102.984C62.5297 103.416 61.8247 103.748 61.2939 103.981C60.8958 104.18 60.4645 104.28 60 104.28C59.5355 104.28 59.1042 104.18 58.7061 103.981C58.1753 103.748 57.4703 103.416 56.5911 102.984C55.712 102.552 54.2273 101.73 52.1372 100.517C50.0471 99.304 48.04 98.0164 46.1158 96.654C44.1916 95.2916 42.0932 93.5803 39.8206 91.5201C37.548 89.4598 35.5906 87.3415 33.9484 85.1649C32.3062 82.9884 30.9294 80.4962 29.818 77.6883C28.7066 74.8805 28.1509 72.0477 28.1509 69.1899V30.9097C28.1509 30.0458 28.4661 29.2981 29.0964 28.6668C29.7267 28.0354 30.4732 27.7197 31.3358 27.7197H88.6642C89.5268 27.7197 90.2732 28.0354 90.9036 28.6668C91.5339 29.2981 91.8491 30.0458 91.8491 30.9097Z" fill="#175DDC"/>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
export const BitwardenShieldWhite = svgIcon`
|
||||
<svg viewBox="0 0 120 132" fill="#FFF" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M82.2944 69.1899V37.2898H60V93.9624C63.948 91.869 67.4812 89.5927 70.5998 87.1338C78.3962 81.0196 82.2944 75.0383 82.2944 69.1899ZM91.8491 30.9097V69.1899C91.8491 72.0477 91.2934 74.8805 90.182 77.6883C89.0706 80.4962 87.6938 82.9884 86.0516 85.1649C84.4094 87.3415 82.452 89.4598 80.1794 91.5201C77.9068 93.5803 75.8084 95.2916 73.8842 96.654C71.96 98.0164 69.9528 99.304 67.8627 100.517C65.7726 101.73 64.288 102.552 63.4088 102.984C62.5297 103.416 61.8247 103.748 61.2939 103.981C60.8958 104.18 60.4645 104.28 60 104.28C59.5355 104.28 59.1042 104.18 58.7061 103.981C58.1753 103.748 57.4703 103.416 56.5911 102.984C55.712 102.552 54.2273 101.73 52.1372 100.517C50.0471 99.304 48.04 98.0164 46.1158 96.654C44.1916 95.2916 42.0932 93.5803 39.8206 91.5201C37.548 89.4598 35.5906 87.3415 33.9484 85.1649C32.3062 82.9884 30.9294 80.4962 29.818 77.6883C28.7066 74.8805 28.1509 72.0477 28.1509 69.1899V30.9097C28.1509 30.0458 28.4661 29.2981 29.0964 28.6668C29.7267 28.0354 30.4732 27.7197 31.3358 27.7197H88.6642C89.5268 27.7197 90.2732 28.0354 90.9036 28.6668C91.5339 29.2981 91.8491 30.0458 91.8491 30.9097Z" fill="#175DDC"/>
|
||||
export const BitwardenShield = svgIcon`
|
||||
<svg viewBox="0 0 120 132" xmlns="http://www.w3.org/2000/svg">
|
||||
<path class="tw-fill-marketing-logo" d="M82.2944 69.1899V37.2898H60V93.9624C63.948 91.869 67.4812 89.5927 70.5998 87.1338C78.3962 81.0196 82.2944 75.0383 82.2944 69.1899ZM91.8491 30.9097V69.1899C91.8491 72.0477 91.2934 74.8805 90.182 77.6883C89.0706 80.4962 87.6938 82.9884 86.0516 85.1649C84.4094 87.3415 82.452 89.4598 80.1794 91.5201C77.9068 93.5803 75.8084 95.2916 73.8842 96.654C71.96 98.0164 69.9528 99.304 67.8627 100.517C65.7726 101.73 64.288 102.552 63.4088 102.984C62.5297 103.416 61.8247 103.748 61.2939 103.981C60.8958 104.18 60.4645 104.28 60 104.28C59.5355 104.28 59.1042 104.18 58.7061 103.981C58.1753 103.748 57.4703 103.416 56.5911 102.984C55.712 102.552 54.2273 101.73 52.1372 100.517C50.0471 99.304 48.04 98.0164 46.1158 96.654C44.1916 95.2916 42.0932 93.5803 39.8206 91.5201C37.548 89.4598 35.5906 87.3415 33.9484 85.1649C32.3062 82.9884 30.9294 80.4962 29.818 77.6883C28.7066 74.8805 28.1509 72.0477 28.1509 69.1899V30.9097C28.1509 30.0458 28.4661 29.2981 29.0964 28.6668C29.7267 28.0354 30.4732 27.7197 31.3358 27.7197H88.6642C89.5268 27.7197 90.2732 28.0354 90.9036 28.6668C91.5339 29.2981 91.8491 30.0458 91.8491 30.9097Z" />
|
||||
</svg>
|
||||
`;
|
||||
|
||||
@@ -1,29 +1,28 @@
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
export abstract class LoginEmailServiceAbstraction {
|
||||
/**
|
||||
* An observable that monitors the loginEmail in memory.
|
||||
* The loginEmail is the email that is being used in the current login process.
|
||||
*/
|
||||
loginEmail$: Observable<string | null>;
|
||||
/**
|
||||
* An observable that monitors the storedEmail on disk.
|
||||
* This will return null if an account is being added.
|
||||
*/
|
||||
storedEmail$: Observable<string | null>;
|
||||
/**
|
||||
* Gets the current email being used in the login process from memory.
|
||||
* @returns A string of the email.
|
||||
* Sets the loginEmail in memory.
|
||||
* The loginEmail is the email that is being used in the current login process.
|
||||
*/
|
||||
getEmail: () => string;
|
||||
/**
|
||||
* Sets the current email being used in the login process in memory.
|
||||
* @param email The email to be set.
|
||||
*/
|
||||
setEmail: (email: string) => void;
|
||||
setLoginEmail: (email: string) => Promise<void>;
|
||||
/**
|
||||
* Gets from memory whether or not the email should be stored on disk when `saveEmailSettings` is called.
|
||||
* @returns A boolean stating whether or not the email should be stored on disk.
|
||||
*/
|
||||
getRememberEmail: () => boolean;
|
||||
/**
|
||||
* Sets in memory whether or not the email should be stored on disk when
|
||||
* `saveEmailSettings` is called.
|
||||
* Sets in memory whether or not the email should be stored on disk when `saveEmailSettings` is called.
|
||||
*/
|
||||
setRememberEmail: (value: boolean) => void;
|
||||
/**
|
||||
|
||||
@@ -43,7 +43,7 @@ describe("LoginEmailService", () => {
|
||||
|
||||
describe("storedEmail$", () => {
|
||||
it("returns the stored email when not adding an account", async () => {
|
||||
sut.setEmail("userEmail@bitwarden.com");
|
||||
await sut.setLoginEmail("userEmail@bitwarden.com");
|
||||
sut.setRememberEmail(true);
|
||||
await sut.saveEmailSettings();
|
||||
|
||||
@@ -53,7 +53,7 @@ describe("LoginEmailService", () => {
|
||||
});
|
||||
|
||||
it("returns the stored email when not adding an account and the user has just logged in", async () => {
|
||||
sut.setEmail("userEmail@bitwarden.com");
|
||||
await sut.setLoginEmail("userEmail@bitwarden.com");
|
||||
sut.setRememberEmail(true);
|
||||
await sut.saveEmailSettings();
|
||||
|
||||
@@ -66,7 +66,7 @@ describe("LoginEmailService", () => {
|
||||
});
|
||||
|
||||
it("returns null when adding an account", async () => {
|
||||
sut.setEmail("userEmail@bitwarden.com");
|
||||
await sut.setLoginEmail("userEmail@bitwarden.com");
|
||||
sut.setRememberEmail(true);
|
||||
await sut.saveEmailSettings();
|
||||
|
||||
@@ -83,7 +83,7 @@ describe("LoginEmailService", () => {
|
||||
|
||||
describe("saveEmailSettings", () => {
|
||||
it("saves the email when not adding an account", async () => {
|
||||
sut.setEmail("userEmail@bitwarden.com");
|
||||
await sut.setLoginEmail("userEmail@bitwarden.com");
|
||||
sut.setRememberEmail(true);
|
||||
await sut.saveEmailSettings();
|
||||
|
||||
@@ -95,7 +95,7 @@ describe("LoginEmailService", () => {
|
||||
it("clears the email when not adding an account and rememberEmail is false", async () => {
|
||||
storedEmailState.stateSubject.next("initialEmail@bitwarden.com");
|
||||
|
||||
sut.setEmail("userEmail@bitwarden.com");
|
||||
await sut.setLoginEmail("userEmail@bitwarden.com");
|
||||
sut.setRememberEmail(false);
|
||||
await sut.saveEmailSettings();
|
||||
|
||||
@@ -110,7 +110,7 @@ describe("LoginEmailService", () => {
|
||||
["OtherUserId" as UserId]: AuthenticationStatus.Locked,
|
||||
});
|
||||
|
||||
sut.setEmail("userEmail@bitwarden.com");
|
||||
await sut.setLoginEmail("userEmail@bitwarden.com");
|
||||
sut.setRememberEmail(true);
|
||||
await sut.saveEmailSettings();
|
||||
|
||||
@@ -127,7 +127,7 @@ describe("LoginEmailService", () => {
|
||||
["OtherUserId" as UserId]: AuthenticationStatus.Locked,
|
||||
});
|
||||
|
||||
sut.setEmail("userEmail@bitwarden.com");
|
||||
await sut.setLoginEmail("userEmail@bitwarden.com");
|
||||
sut.setRememberEmail(false);
|
||||
await sut.saveEmailSettings();
|
||||
|
||||
@@ -140,11 +140,11 @@ describe("LoginEmailService", () => {
|
||||
it("does not clear the email and rememberEmail after saving", async () => {
|
||||
// Browser uses these values to maintain the email between login and 2fa components so
|
||||
// we do not want to clear them too early.
|
||||
sut.setEmail("userEmail@bitwarden.com");
|
||||
await sut.setLoginEmail("userEmail@bitwarden.com");
|
||||
sut.setRememberEmail(true);
|
||||
await sut.saveEmailSettings();
|
||||
|
||||
const result = sut.getEmail();
|
||||
const result = await firstValueFrom(sut.loginEmail$);
|
||||
|
||||
expect(result).toBe("userEmail@bitwarden.com");
|
||||
});
|
||||
|
||||
@@ -8,21 +8,28 @@ import {
|
||||
GlobalState,
|
||||
KeyDefinition,
|
||||
LOGIN_EMAIL_DISK,
|
||||
LOGIN_EMAIL_MEMORY,
|
||||
StateProvider,
|
||||
} from "../../../../../common/src/platform/state";
|
||||
import { LoginEmailServiceAbstraction } from "../../abstractions/login-email.service";
|
||||
|
||||
export const LOGIN_EMAIL = new KeyDefinition<string>(LOGIN_EMAIL_MEMORY, "loginEmail", {
|
||||
deserializer: (value: string) => value,
|
||||
});
|
||||
|
||||
export const STORED_EMAIL = new KeyDefinition<string>(LOGIN_EMAIL_DISK, "storedEmail", {
|
||||
deserializer: (value: string) => value,
|
||||
});
|
||||
|
||||
export class LoginEmailService implements LoginEmailServiceAbstraction {
|
||||
private email: string | null;
|
||||
private rememberEmail: boolean;
|
||||
|
||||
// True if an account is currently being added through account switching
|
||||
private readonly addingAccount$: Observable<boolean>;
|
||||
|
||||
private readonly loginEmailState: GlobalState<string>;
|
||||
loginEmail$: Observable<string | null>;
|
||||
|
||||
private readonly storedEmailState: GlobalState<string>;
|
||||
storedEmail$: Observable<string | null>;
|
||||
|
||||
@@ -31,6 +38,7 @@ export class LoginEmailService implements LoginEmailServiceAbstraction {
|
||||
private authService: AuthService,
|
||||
private stateProvider: StateProvider,
|
||||
) {
|
||||
this.loginEmailState = this.stateProvider.getGlobal(LOGIN_EMAIL);
|
||||
this.storedEmailState = this.stateProvider.getGlobal(STORED_EMAIL);
|
||||
|
||||
// In order to determine if an account is being added, we check if any account is not logged out
|
||||
@@ -46,6 +54,8 @@ export class LoginEmailService implements LoginEmailServiceAbstraction {
|
||||
}),
|
||||
);
|
||||
|
||||
this.loginEmail$ = this.loginEmailState.state$;
|
||||
|
||||
this.storedEmail$ = this.storedEmailState.state$.pipe(
|
||||
switchMap(async (storedEmail) => {
|
||||
// When adding an account, we don't show the stored email
|
||||
@@ -57,12 +67,8 @@ export class LoginEmailService implements LoginEmailServiceAbstraction {
|
||||
);
|
||||
}
|
||||
|
||||
getEmail() {
|
||||
return this.email;
|
||||
}
|
||||
|
||||
setEmail(email: string) {
|
||||
this.email = email;
|
||||
async setLoginEmail(email: string) {
|
||||
await this.loginEmailState.update((_) => email);
|
||||
}
|
||||
|
||||
getRememberEmail() {
|
||||
@@ -76,25 +82,27 @@ export class LoginEmailService implements LoginEmailServiceAbstraction {
|
||||
// Note: only clear values on successful login or you are sure they are not needed.
|
||||
// Browser uses these values to maintain the email between login and 2fa components so
|
||||
// we do not want to clear them too early.
|
||||
clearValues() {
|
||||
this.email = null;
|
||||
async clearValues() {
|
||||
await this.setLoginEmail(null);
|
||||
this.rememberEmail = false;
|
||||
}
|
||||
|
||||
async saveEmailSettings() {
|
||||
const addingAccount = await firstValueFrom(this.addingAccount$);
|
||||
const email = await firstValueFrom(this.loginEmail$);
|
||||
|
||||
await this.storedEmailState.update((storedEmail) => {
|
||||
// If we're adding an account, only overwrite the stored email when rememberEmail is true
|
||||
if (addingAccount) {
|
||||
if (this.rememberEmail) {
|
||||
return this.email;
|
||||
return email;
|
||||
}
|
||||
return storedEmail;
|
||||
}
|
||||
|
||||
// Saving with rememberEmail set to false will clear the stored email
|
||||
if (this.rememberEmail) {
|
||||
return this.email;
|
||||
return email;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
@@ -210,19 +210,19 @@ export abstract class OrganizationUserService {
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
* Delete an organization user
|
||||
* Remove an organization user
|
||||
* @param organizationId - Identifier for the organization the user belongs to
|
||||
* @param id - Organization user identifier
|
||||
*/
|
||||
abstract deleteOrganizationUser(organizationId: string, id: string): Promise<void>;
|
||||
abstract removeOrganizationUser(organizationId: string, id: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Delete many organization users
|
||||
* Remove many organization users
|
||||
* @param organizationId - Identifier for the organization the users belongs to
|
||||
* @param ids - List of organization user identifiers to delete
|
||||
* @return List of user ids, including both those that were successfully deleted and those that had an error
|
||||
* @param ids - List of organization user identifiers to remove
|
||||
* @return List of user ids, including both those that were successfully removed and those that had an error
|
||||
*/
|
||||
abstract deleteManyOrganizationUsers(
|
||||
abstract removeManyOrganizationUsers(
|
||||
organizationId: string,
|
||||
ids: string[],
|
||||
): Promise<ListResponse<OrganizationUserBulkResponse>>;
|
||||
|
||||
@@ -274,7 +274,7 @@ export class OrganizationUserServiceImplementation implements OrganizationUserSe
|
||||
);
|
||||
}
|
||||
|
||||
deleteOrganizationUser(organizationId: string, id: string): Promise<any> {
|
||||
removeOrganizationUser(organizationId: string, id: string): Promise<any> {
|
||||
return this.apiService.send(
|
||||
"DELETE",
|
||||
"/organizations/" + organizationId + "/users/" + id,
|
||||
@@ -284,7 +284,7 @@ export class OrganizationUserServiceImplementation implements OrganizationUserSe
|
||||
);
|
||||
}
|
||||
|
||||
async deleteManyOrganizationUsers(
|
||||
async removeManyOrganizationUsers(
|
||||
organizationId: string,
|
||||
ids: string[],
|
||||
): Promise<ListResponse<OrganizationUserBulkResponse>> {
|
||||
|
||||
@@ -53,6 +53,7 @@ export const KEY_CONNECTOR_DISK = new StateDefinition("keyConnector", "disk");
|
||||
export const LOGIN_EMAIL_DISK = new StateDefinition("loginEmail", "disk", {
|
||||
web: "disk-local",
|
||||
});
|
||||
export const LOGIN_EMAIL_MEMORY = new StateDefinition("loginEmail", "memory");
|
||||
export const LOGIN_STRATEGY_MEMORY = new StateDefinition("loginStrategy", "memory");
|
||||
export const MASTER_PASSWORD_DISK = new StateDefinition("masterPassword", "disk");
|
||||
export const MASTER_PASSWORD_MEMORY = new StateDefinition("masterPassword", "memory");
|
||||
|
||||
@@ -44,6 +44,8 @@
|
||||
--color-text-code: 192 17 118;
|
||||
--color-text-headers: 2 15 102;
|
||||
|
||||
--color-marketing-logo: 23 93 220;
|
||||
|
||||
--tw-ring-offset-color: #ffffff;
|
||||
}
|
||||
|
||||
@@ -95,6 +97,8 @@
|
||||
--color-text-code: 240 141 199;
|
||||
--color-text-headers: 226 227 228;
|
||||
|
||||
--color-marketing-logo: 255 255 255;
|
||||
|
||||
--tw-ring-offset-color: #1f242e;
|
||||
}
|
||||
|
||||
@@ -134,6 +138,8 @@
|
||||
--color-text-alt2: 255 255 255;
|
||||
--color-text-code: 219 177 211;
|
||||
|
||||
--color-marketing-logo: 255 255 255;
|
||||
|
||||
--tw-ring-offset-color: #434c5e;
|
||||
}
|
||||
|
||||
@@ -173,6 +179,8 @@
|
||||
--color-text-alt2: 255 255 255;
|
||||
--color-text-code: 240 141 199;
|
||||
|
||||
--color-marketing-logo: 255 255 255;
|
||||
|
||||
--tw-ring-offset-color: #002b36;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ module.exports = {
|
||||
alt3: rgba("--color-background-alt3"),
|
||||
alt4: rgba("--color-background-alt4"),
|
||||
},
|
||||
"marketing-logo": rgba("--color-marketing-logo"),
|
||||
},
|
||||
textColor: {
|
||||
main: rgba("--color-text-main"),
|
||||
|
||||
113
libs/importer/spec/msecure-csv-importer.spec.ts
Normal file
113
libs/importer/spec/msecure-csv-importer.spec.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
|
||||
import { MSecureCsvImporter } from "../src/importers/msecure-csv-importer";
|
||||
|
||||
describe("MSecureCsvImporter.parse", () => {
|
||||
let importer: MSecureCsvImporter;
|
||||
beforeEach(() => {
|
||||
importer = new MSecureCsvImporter();
|
||||
});
|
||||
|
||||
it("should correctly parse credit card entries as Secret Notes", async () => {
|
||||
const mockCsvData =
|
||||
`myCreditCard|155089404,Credit Card,,,Card Number|12|41111111111111111,Expiration Date|11|05/2026,Security Code|9|123,Name on Card|0|John Doe,PIN|9|1234,Issuing Bank|0|Visa,Phone Number|4|,Billing Address|0|,`.trim();
|
||||
const result = await importer.parse(mockCsvData);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.ciphers.length).toBe(1);
|
||||
const cipher = result.ciphers[0];
|
||||
expect(cipher.name).toBe("myCreditCard");
|
||||
expect(cipher.type).toBe(CipherType.Card);
|
||||
expect(cipher.card.number).toBe("41111111111111111");
|
||||
expect(cipher.card.expiration).toBe("05 / 2026");
|
||||
expect(cipher.card.code).toBe("123");
|
||||
expect(cipher.card.cardholderName).toBe("John Doe");
|
||||
expect(cipher.card.brand).toBe("Visa");
|
||||
});
|
||||
|
||||
it("should correctly parse login entries", async () => {
|
||||
const mockCsvData = `
|
||||
Bitwarden|810974637,Login,,,Website|2|bitwarden.com,Username|7|bitwarden user,Password|8|bitpassword,
|
||||
`.trim();
|
||||
|
||||
const result = await importer.parse(mockCsvData);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.ciphers.length).toBe(1);
|
||||
const cipher = result.ciphers[0];
|
||||
expect(cipher.name).toBe("Bitwarden");
|
||||
expect(cipher.type).toBe(CipherType.Login);
|
||||
expect(cipher.login.username).toBe("bitwarden user");
|
||||
expect(cipher.login.password).toBe("bitpassword");
|
||||
expect(cipher.login.uris[0].uri).toContain("bitwarden.com");
|
||||
});
|
||||
|
||||
it("should correctly parse login entries with notes", async () => {
|
||||
const mockCsvData =
|
||||
`Example|188987444,Login,,This is a note |,Website|2|example2.com,Username|7|username || lol,Password|8|this is a password,`.trim();
|
||||
|
||||
const result = await importer.parse(mockCsvData);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.ciphers.length).toBe(1);
|
||||
const cipher = result.ciphers[0];
|
||||
expect(cipher.name).toBe("Example");
|
||||
expect(cipher.type).toBe(CipherType.Login);
|
||||
expect(cipher.login.username).toBe("username || lol");
|
||||
expect(cipher.login.password).toBe("this is a password");
|
||||
expect(cipher.login.uris[0].uri).toContain("example2.com");
|
||||
expect(cipher.notes).toBe("This is a note |");
|
||||
});
|
||||
|
||||
it("should correctly parse login entries with a tag", async () => {
|
||||
const mockCsvData = `
|
||||
Website with a tag|1401978655,Login,tag holding it,,Website|2|johndoe.com,Username|7|JohnDoeWebsite,Password|8|JohnDoePassword,
|
||||
`.trim();
|
||||
|
||||
const result = await importer.parse(mockCsvData);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.ciphers.length).toBe(1);
|
||||
const cipher = result.ciphers[0];
|
||||
expect(cipher.name).toBe("Website with a tag");
|
||||
expect(cipher.type).toBe(CipherType.Login);
|
||||
expect(cipher.login.username).toBe("JohnDoeWebsite");
|
||||
expect(cipher.login.password).toBe("JohnDoePassword");
|
||||
expect(cipher.login.uris[0].uri).toContain("johndoe.com");
|
||||
expect(cipher.notes).toBeNull();
|
||||
expect(result.folders[0].name).toContain("tag holding it");
|
||||
});
|
||||
|
||||
it("should handle multiple entries correctly", async () => {
|
||||
const mockCsvData =
|
||||
`myCreditCard|155089404,Credit Card,,,Card Number|12|41111111111111111,Expiration Date|11|05/2026,Security Code|9|123,Name on Card|0|John Doe,PIN|9|1234,Issuing Bank|0|Visa,Phone Number|4|,Billing Address|0|,
|
||||
Bitwarden|810974637,Login,,,Website|2|bitwarden.com,Username|7|bitwarden user,Password|8|bitpassword,
|
||||
Example|188987444,Login,,This is a note |,Website|2|example2.com,Username|7|username || lol,Password|8|this is a password,
|
||||
Website with a tag|1401978655,Login,tag holding it,,Website|2|johndoe.com,Username|7|JohnDoeWebsite,Password|8|JohnDoePassword,`.trim();
|
||||
|
||||
const result = await importer.parse(mockCsvData);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.ciphers.length).toBe(4);
|
||||
|
||||
// Check first entry (Credit Card)
|
||||
const cipher1 = result.ciphers[0];
|
||||
expect(cipher1.name).toBe("myCreditCard");
|
||||
expect(cipher1.type).toBe(CipherType.Card);
|
||||
|
||||
// Check second entry (Login - Bitwarden)
|
||||
const cipher2 = result.ciphers[1];
|
||||
expect(cipher2.name).toBe("Bitwarden");
|
||||
expect(cipher2.type).toBe(CipherType.Login);
|
||||
|
||||
// Check third entry (Login with note - Example)
|
||||
const cipher3 = result.ciphers[2];
|
||||
expect(cipher3.name).toBe("Example");
|
||||
expect(cipher3.type).toBe(CipherType.Login);
|
||||
|
||||
// Check fourth entry (Login with tag - Website with a tag)
|
||||
const cipher4 = result.ciphers[3];
|
||||
expect(cipher4.name).toBe("Website with a tag");
|
||||
expect(cipher4.type).toBe(CipherType.Login);
|
||||
});
|
||||
});
|
||||
@@ -9,7 +9,7 @@ import { Importer } from "./importer";
|
||||
export class MSecureCsvImporter extends BaseImporter implements Importer {
|
||||
parse(data: string): Promise<ImportResult> {
|
||||
const result = new ImportResult();
|
||||
const results = this.parseCsv(data, false);
|
||||
const results = this.parseCsv(data, false, { delimiter: "," });
|
||||
if (results == null) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
@@ -21,17 +21,43 @@ export class MSecureCsvImporter extends BaseImporter implements Importer {
|
||||
}
|
||||
|
||||
const folderName =
|
||||
this.getValueOrDefault(value[0], "Unassigned") !== "Unassigned" ? value[0] : null;
|
||||
this.getValueOrDefault(value[2], "Unassigned") !== "Unassigned" ? value[2] : null;
|
||||
this.processFolder(result, folderName);
|
||||
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.name = this.getValueOrDefault(value[2], "--");
|
||||
cipher.name = this.getValueOrDefault(value[0].split("|")[0], "--");
|
||||
|
||||
if (value[1] === "Web Logins" || value[1] === "Login") {
|
||||
cipher.login.uris = this.makeUriArray(value[4]);
|
||||
cipher.login.username = this.getValueOrDefault(value[5]);
|
||||
cipher.login.password = this.getValueOrDefault(value[6]);
|
||||
cipher.login.username = this.getValueOrDefault(this.splitValueRetainingLastPart(value[5]));
|
||||
cipher.login.uris = this.makeUriArray(this.splitValueRetainingLastPart(value[4]));
|
||||
cipher.login.password = this.getValueOrDefault(this.splitValueRetainingLastPart(value[6]));
|
||||
cipher.notes = !this.isNullOrWhitespace(value[3]) ? value[3].split("\\n").join("\n") : null;
|
||||
} else if (value[1] === "Credit Card") {
|
||||
cipher.type = CipherType.Card;
|
||||
cipher.card.number = this.getValueOrDefault(this.splitValueRetainingLastPart(value[4]));
|
||||
|
||||
const [month, year] = this.getValueOrDefault(
|
||||
this.splitValueRetainingLastPart(value[5]),
|
||||
).split("/");
|
||||
cipher.card.expMonth = month.trim();
|
||||
cipher.card.expYear = year.trim();
|
||||
cipher.card.code = this.getValueOrDefault(this.splitValueRetainingLastPart(value[6]));
|
||||
cipher.card.cardholderName = this.getValueOrDefault(
|
||||
this.splitValueRetainingLastPart(value[7]),
|
||||
);
|
||||
cipher.card.brand = this.getValueOrDefault(this.splitValueRetainingLastPart(value[9]));
|
||||
cipher.notes =
|
||||
this.getValueOrDefault(value[8].split("|")[0]) +
|
||||
": " +
|
||||
this.getValueOrDefault(this.splitValueRetainingLastPart(value[8]), "") +
|
||||
"\n" +
|
||||
this.getValueOrDefault(value[10].split("|")[0]) +
|
||||
": " +
|
||||
this.getValueOrDefault(this.splitValueRetainingLastPart(value[10]), "") +
|
||||
"\n" +
|
||||
this.getValueOrDefault(value[11].split("|")[0]) +
|
||||
": " +
|
||||
this.getValueOrDefault(this.splitValueRetainingLastPart(value[11]), "");
|
||||
} else if (value.length > 3) {
|
||||
cipher.type = CipherType.SecureNote;
|
||||
cipher.secureNote = new SecureNoteView();
|
||||
@@ -43,7 +69,11 @@ export class MSecureCsvImporter extends BaseImporter implements Importer {
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.isNullOrWhitespace(value[1]) && cipher.type !== CipherType.Login) {
|
||||
if (
|
||||
!this.isNullOrWhitespace(value[1]) &&
|
||||
cipher.type !== CipherType.Login &&
|
||||
cipher.type !== CipherType.Card
|
||||
) {
|
||||
cipher.name = value[1] + ": " + cipher.name;
|
||||
}
|
||||
|
||||
@@ -58,4 +88,11 @@ export class MSecureCsvImporter extends BaseImporter implements Importer {
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
// mSecure returns values separated by "|" where after the second separator is the value
|
||||
// like "Password|8|myPassword", we want to keep the "myPassword" but also ensure that if
|
||||
// the value contains any "|" it works fine
|
||||
private splitValueRetainingLastPart(value: string) {
|
||||
return value.split("|").slice(0, 2).concat(value.split("|").slice(2).join("|")).pop();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,15 @@
|
||||
{{ "cardExpiredMessage" | i18n }}
|
||||
</bit-callout>
|
||||
|
||||
<!-- HELPER TEXT -->
|
||||
<p
|
||||
class="tw-text-sm tw-text-muted"
|
||||
bitTypography="helper"
|
||||
*ngIf="cipher?.isDeleted && !cipher?.edit"
|
||||
>
|
||||
{{ "noEditPermissions" | i18n }}
|
||||
</p>
|
||||
|
||||
<!-- ITEM DETAILS -->
|
||||
<app-item-details-v2
|
||||
[cipher]="cipher"
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
</p>
|
||||
<a
|
||||
*ngIf="cipher.hasPasswordHistory && isLogin"
|
||||
bitLink
|
||||
class="tw-font-bold tw-no-underline"
|
||||
routerLink="/cipher-password-history"
|
||||
[queryParams]="{ cipherId: cipher.id }"
|
||||
|
||||
@@ -7,6 +7,7 @@ import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import {
|
||||
CardComponent,
|
||||
LinkModule,
|
||||
SectionComponent,
|
||||
SectionHeaderComponent,
|
||||
TypographyModule,
|
||||
@@ -24,6 +25,7 @@ import {
|
||||
SectionComponent,
|
||||
SectionHeaderComponent,
|
||||
TypographyModule,
|
||||
LinkModule,
|
||||
],
|
||||
})
|
||||
export class ItemHistoryV2Component {
|
||||
|
||||
@@ -106,10 +106,9 @@
|
||||
readonly
|
||||
bitInput
|
||||
[type]="!(isPremium$ | async) ? 'password' : 'text'"
|
||||
[value]="totpCopyCode || '*** ***'"
|
||||
[value]="totpCodeCopyObj?.totpCodeFormatted || '*** ***'"
|
||||
aria-readonly="true"
|
||||
data-testid="login-totp"
|
||||
[disabled]="!(isPremium$ | async)"
|
||||
/>
|
||||
<button
|
||||
*ngIf="isPremium$ | async"
|
||||
@@ -124,7 +123,7 @@
|
||||
bitIconButton="bwi-clone"
|
||||
bitSuffix
|
||||
type="button"
|
||||
[appCopyClick]="totpCopyCode"
|
||||
[appCopyClick]="totpCodeCopyObj?.totpCode"
|
||||
[valueLabel]="'verificationCodeTotp' | i18n"
|
||||
showToast
|
||||
[appA11yTitle]="'copyValue' | i18n"
|
||||
|
||||
@@ -20,6 +20,11 @@ import {
|
||||
|
||||
import { BitTotpCountdownComponent } from "../../components/totp-countdown/totp-countdown.component";
|
||||
|
||||
type TotpCodeValues = {
|
||||
totpCode: string;
|
||||
totpCodeFormatted?: string;
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: "app-login-credentials-view",
|
||||
templateUrl: "login-credentials-view.component.html",
|
||||
@@ -47,7 +52,7 @@ export class LoginCredentialsViewComponent {
|
||||
);
|
||||
showPasswordCount: boolean = false;
|
||||
passwordRevealed: boolean = false;
|
||||
totpCopyCode: string;
|
||||
totpCodeCopyObj: TotpCodeValues;
|
||||
private datePipe = inject(DatePipe);
|
||||
|
||||
constructor(
|
||||
@@ -77,7 +82,7 @@ export class LoginCredentialsViewComponent {
|
||||
this.showPasswordCount = !this.showPasswordCount;
|
||||
}
|
||||
|
||||
setTotpCopyCode(e: any) {
|
||||
this.totpCopyCode = e;
|
||||
setTotpCopyCode(e: TotpCodeValues) {
|
||||
this.totpCodeCopyObj = e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,13 +44,16 @@ export class BitTotpCountdownComponent implements OnInit {
|
||||
if (this.totpCode != null) {
|
||||
if (this.totpCode.length > 4) {
|
||||
this.totpCodeFormatted = this.formatTotpCode();
|
||||
this.sendCopyCode.emit(this.totpCodeFormatted);
|
||||
this.sendCopyCode.emit({
|
||||
totpCode: this.totpCode,
|
||||
totpCodeFormatted: this.totpCodeFormatted,
|
||||
});
|
||||
} else {
|
||||
this.totpCodeFormatted = this.totpCode;
|
||||
}
|
||||
} else {
|
||||
this.totpCodeFormatted = null;
|
||||
this.sendCopyCode.emit(this.totpCodeFormatted);
|
||||
this.sendCopyCode.emit({ totpCode: null, totpCodeFormatted: null });
|
||||
this.clearTotp();
|
||||
}
|
||||
}
|
||||
|
||||
18
libs/vault/src/icons/empty-trash.ts
Normal file
18
libs/vault/src/icons/empty-trash.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { svgIcon } from "@bitwarden/components";
|
||||
|
||||
export const EmptyTrash = svgIcon`
|
||||
<svg width="174" height="100" viewBox="0 0 174 100" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M113.938 95.7919L121.802 25.2171C121.882 24.4997 121.32 23.8721 120.599 23.8721H52.8158C52.0939 23.8721 51.5324 24.4997 51.6123 25.2171L59.4759 95.7919C59.5442 96.405 60.0625 96.8687 60.6794 96.8687H112.735C113.352 96.8687 113.87 96.405 113.938 95.7919Z" fill="none"/>
|
||||
<path class="tw-fill-info-600" fill-rule="evenodd" clip-rule="evenodd" d="M70.9462 38.4568C71.1965 38.44 71.4141 38.6291 71.4323 38.8793L74.2991 78.3031C74.3173 78.5532 74.1292 78.7696 73.879 78.7865C73.6288 78.8033 73.4112 78.6142 73.393 78.364L70.5261 38.9402C70.5079 38.6901 70.696 38.4737 70.9462 38.4568Z"/>
|
||||
<path class="tw-fill-info-600" fill-rule="evenodd" clip-rule="evenodd" d="M87.4314 38.4082C87.6822 38.4082 87.8855 38.6115 87.8855 38.8623L87.8855 78.3824C87.8855 78.6332 87.6822 78.8365 87.4314 78.8365C87.1806 78.8365 86.9773 78.6332 86.9773 78.3824L86.9773 38.8623C86.9773 38.6115 87.1806 38.4082 87.4314 38.4082Z"/>
|
||||
<path class="tw-fill-info-600" fill-rule="evenodd" clip-rule="evenodd" d="M103.917 38.4572C104.167 38.474 104.355 38.6905 104.337 38.9406L101.47 78.3644C101.452 78.6145 101.234 78.8037 100.984 78.7868C100.734 78.77 100.546 78.5536 100.564 78.3035L103.431 38.8797C103.449 38.6295 103.667 38.4404 103.917 38.4572Z"/>
|
||||
<path class="tw-fill-text-headers" fill-rule="evenodd" clip-rule="evenodd" d="M52.8159 24.7803C52.6354 24.7803 52.4951 24.9372 52.515 25.1165L59.3506 86.4648H76.54C76.7908 86.4648 76.9941 86.6682 76.9941 86.9189C76.9941 87.1697 76.7908 87.373 76.54 87.373H59.4518L60.3786 95.6913C60.3957 95.8446 60.5252 95.9605 60.6795 95.9605H112.735C112.889 95.9605 113.019 95.8446 113.036 95.6913L120.353 30.0186L58.2399 30.0186C57.9891 30.0186 57.7858 29.8152 57.7858 29.5645C57.7858 29.3137 57.9891 29.1104 58.2399 29.1104L120.455 29.1104L120.9 25.1165C120.919 24.9372 120.779 24.7803 120.599 24.7803H52.8159ZM50.7098 25.3177C50.5699 24.0621 51.5526 22.9639 52.8159 22.9639H120.599C121.862 22.9639 122.845 24.0622 122.705 25.3177L114.841 95.8924C114.722 96.9654 113.815 97.7769 112.735 97.7769H60.6795C59.5999 97.7769 58.6929 96.9654 58.5734 95.8924L50.7098 25.3177Z"/>
|
||||
<path class="tw-fill-info-600" fill-rule="evenodd" clip-rule="evenodd" d="M88.4499 0.527344C88.9515 0.527344 89.3581 0.933958 89.3581 1.43554V11.2051C89.3581 11.7067 88.9515 12.1133 88.4499 12.1133C87.9484 12.1133 87.5417 11.7067 87.5417 11.2051V1.43554C87.5417 0.933958 87.9484 0.527344 88.4499 0.527344Z"/>
|
||||
<path class="tw-fill-info-600" fill-rule="evenodd" clip-rule="evenodd" d="M56.8137 6.2397C57.2694 6.03014 57.8774 6.18948 58.1718 6.59559L64.3048 15.0563C64.5992 15.4624 64.4684 15.9615 64.0127 16.1711C63.557 16.3806 62.9489 16.2213 62.6545 15.8152L56.5215 7.35447C56.2272 6.94836 56.358 6.44926 56.8137 6.2397Z"/>
|
||||
<path class="tw-fill-info-600" fill-rule="evenodd" clip-rule="evenodd" d="M73.1704 2.01822C73.6671 1.94846 74.1576 2.28892 74.266 2.77864L76.396 12.3998C76.5044 12.8895 76.1896 13.3431 75.6929 13.4129C75.1962 13.4826 74.7057 13.1422 74.5973 12.6524L72.4673 3.03126C72.3589 2.54153 72.6737 2.08798 73.1704 2.01822Z"/>
|
||||
<path class="tw-fill-info-600" fill-rule="evenodd" clip-rule="evenodd" d="M104.344 2.13682C104.835 2.24151 105.103 2.71177 104.943 3.18717L101.768 12.6239C101.609 13.0993 101.081 13.3998 100.591 13.2951C100.1 13.1904 99.8321 12.7202 99.9921 12.2448L103.167 2.80806C103.327 2.33266 103.854 2.03213 104.344 2.13682Z"/>
|
||||
<path class="tw-fill-info-600" fill-rule="evenodd" clip-rule="evenodd" d="M120.085 6.23979C120.541 6.44935 120.672 6.94845 120.378 7.35456L114.245 15.8153C113.95 16.2214 113.342 16.3807 112.886 16.1712C112.431 15.9616 112.3 15.4625 112.594 15.0564L118.727 6.59568C119.022 6.18957 119.63 6.03023 120.085 6.23979Z"/>
|
||||
<path d="M129.384 27.2001L124.272 27.9646C123.505 28.0793 123.059 28.8635 123.353 29.579L150.626 95.9087C150.908 96.5946 151.738 96.888 152.38 96.5285L156.79 94.0573C157.31 93.766 157.526 93.1391 157.297 92.5833L130.726 27.9604C130.509 27.4321 129.95 27.1155 129.384 27.2001Z" fill="none"/>
|
||||
<path class="tw-fill-text-headers" fill-rule="evenodd" clip-rule="evenodd" d="M144.4 49.2028C145.911 49.8345 146.573 50.261 147.061 50.8557C147.593 51.504 147.976 52.4111 148.726 54.2353L151.93 62.0272C152.68 63.8513 153.045 64.7652 153.118 65.587C153.185 66.3407 153.004 67.0854 152.349 68.5355C152.26 68.732 152.174 68.9115 152.09 69.0865C151.969 69.3389 151.852 69.5821 151.738 69.8536C151.527 70.3581 151.273 71.0631 150.824 72.4643C150.693 72.8741 150.581 73.2651 150.49 73.6452L139.404 46.6825C139.741 46.9012 140.101 47.1138 140.489 47.3276C141.814 48.0582 142.501 48.408 143.015 48.6385C143.292 48.7625 143.55 48.864 143.818 48.9693C144.004 49.0424 144.195 49.1173 144.4 49.2028ZM134.933 40.574C134.938 40.5882 134.943 40.6024 134.949 40.6166C134.99 40.7164 135.031 40.8147 135.072 40.9115L151.431 80.6977C151.47 80.7949 151.51 80.8934 151.551 80.9931C151.557 81.0072 151.563 81.0211 151.569 81.0349L156.449 92.9041C156.507 93.043 156.453 93.1998 156.323 93.2726L151.912 95.7438C151.752 95.8337 151.544 95.7603 151.474 95.5888L124.201 29.2592C124.127 29.0803 124.239 28.8843 124.431 28.8556L129.543 28.0911C129.685 28.0699 129.824 28.1491 129.879 28.2812L134.933 40.574ZM136.764 40.2619C137.429 41.8455 137.981 42.8653 138.622 43.6471C139.287 44.4581 140.092 45.0652 141.355 45.7612C142.672 46.4872 143.303 46.8056 143.742 47.0027C144.006 47.1212 144.177 47.1875 144.389 47.2695C144.566 47.338 144.771 47.4175 145.082 47.5476C146.656 48.2055 147.682 48.778 148.476 49.7453C149.205 50.6349 149.689 51.8128 150.366 53.4594L150.422 53.5946L153.626 61.3866L153.681 61.5218C154.359 63.1683 154.843 64.3461 154.943 65.4735C155.051 66.6995 154.708 67.7893 154.026 69.2998C153.891 69.5983 153.797 69.7904 153.717 69.9561L153.717 69.9563C153.621 70.1545 153.543 70.3148 153.434 70.5741C153.253 71.0054 153.019 71.6508 152.572 73.0431C152.144 74.3778 151.988 75.3479 152.079 76.3759C152.166 77.3668 152.489 78.4733 153.13 80.066L158.145 92.2635C158.545 93.2361 158.168 94.3331 157.258 94.8429L152.847 97.3142C151.724 97.9433 150.271 97.4298 149.778 96.2295L122.505 29.8998C121.99 28.6476 122.771 27.2752 124.113 27.0746L129.225 26.3101C130.216 26.162 131.194 26.716 131.574 27.6406L136.764 40.2619Z"/>
|
||||
</svg>
|
||||
`;
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from "./deactivated-org";
|
||||
export * from "./no-folders";
|
||||
export * from "./vault";
|
||||
export * from "./empty-trash";
|
||||
|
||||
@@ -62,12 +62,6 @@ describe("CopyCipherFieldService", () => {
|
||||
expect(platformUtilsService.copyToClipboard).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should return early when cipher.viewPassword is false", async () => {
|
||||
cipher.viewPassword = false;
|
||||
await service.copy(valueToCopy, actionType, cipher, skipReprompt);
|
||||
expect(platformUtilsService.copyToClipboard).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should copy value to clipboard", async () => {
|
||||
await service.copy(valueToCopy, actionType, cipher, skipReprompt);
|
||||
expect(platformUtilsService.copyToClipboard).toHaveBeenCalledWith(valueToCopy);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user