From bfbad99fb733d8077aebd4da74beeddfd4602fdb Mon Sep 17 00:00:00 2001 From: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> Date: Tue, 4 Mar 2025 16:09:37 +0100 Subject: [PATCH] [PM-12681] Enable new Send Add/Edit Dialog on web (#13361) * Create web-specific new-send-dropdown component * Create web-specifc Send Add/Edit dialog * Use new-send-dropdown and replace old Send Add/Edit with new Add/Edit dialog * Delete old Send Add/Edit component * Remove unused entries from en/messages.json * Add cancel button to close dialog * Remove unused RouterLink * Fix typechecking issue * Use observable to show/hide premium badge * Add documentation * Move assignment of observable into ctor, as it no longer requires a promise for assignment --------- Co-authored-by: Daniel James Smith --- .../src/app/shared/loose-components.module.ts | 3 - .../app/tools/send/add-edit.component.html | 286 ------------------ .../src/app/tools/send/add-edit.component.ts | 102 ------- .../new-send/new-send-dropdown.component.html | 23 ++ .../new-send/new-send-dropdown.component.ts | 65 ++++ .../tools/send/send-add-edit.component.html | 35 +++ .../app/tools/send/send-add-edit.component.ts | 176 +++++++++++ .../src/app/tools/send/send.component.html | 15 +- apps/web/src/app/tools/send/send.component.ts | 54 +++- apps/web/src/locales/en/messages.json | 105 +++---- 10 files changed, 393 insertions(+), 471 deletions(-) delete mode 100644 apps/web/src/app/tools/send/add-edit.component.html delete mode 100644 apps/web/src/app/tools/send/add-edit.component.ts create mode 100644 apps/web/src/app/tools/send/new-send/new-send-dropdown.component.html create mode 100644 apps/web/src/app/tools/send/new-send/new-send-dropdown.component.ts create mode 100644 apps/web/src/app/tools/send/send-add-edit.component.html create mode 100644 apps/web/src/app/tools/send/send-add-edit.component.ts diff --git a/apps/web/src/app/shared/loose-components.module.ts b/apps/web/src/app/shared/loose-components.module.ts index bec54cf2c54..d57cc6bc1b4 100644 --- a/apps/web/src/app/shared/loose-components.module.ts +++ b/apps/web/src/app/shared/loose-components.module.ts @@ -67,7 +67,6 @@ import { ReusedPasswordsReportComponent as OrgReusedPasswordsReportComponent } f import { UnsecuredWebsitesReportComponent as OrgUnsecuredWebsitesReportComponent } from "../tools/reports/pages/organizations/unsecured-websites-report.component"; import { WeakPasswordsReportComponent as OrgWeakPasswordsReportComponent } from "../tools/reports/pages/organizations/weak-passwords-report.component"; /* eslint no-restricted-imports: "error" */ -import { AddEditComponent as SendAddEditComponent } from "../tools/send/add-edit.component"; import { PremiumBadgeComponent } from "../vault/components/premium-badge.component"; import { AddEditCustomFieldsComponent } from "../vault/individual-vault/add-edit-custom-fields.component"; import { AddEditComponent } from "../vault/individual-vault/add-edit.component"; @@ -148,7 +147,6 @@ import { SharedModule } from "./shared.module"; SecurityComponent, SecurityKeysComponent, SelectableAvatarComponent, - SendAddEditComponent, SetPasswordComponent, SponsoredFamiliesComponent, SponsoringOrgRowComponent, @@ -212,7 +210,6 @@ import { SharedModule } from "./shared.module"; SecurityComponent, SecurityKeysComponent, SelectableAvatarComponent, - SendAddEditComponent, SetPasswordComponent, SponsoredFamiliesComponent, SponsoringOrgRowComponent, diff --git a/apps/web/src/app/tools/send/add-edit.component.html b/apps/web/src/app/tools/send/add-edit.component.html deleted file mode 100644 index 7eade18a7c6..00000000000 --- a/apps/web/src/app/tools/send/add-edit.component.html +++ /dev/null @@ -1,286 +0,0 @@ -
- - - {{ title }} - - - - {{ "sendDisabledWarning" | i18n }} - - - {{ "sendOptionsPolicyInEffect" | i18n }} -
    -
  • {{ "sendDisableHideEmailInEffect" | i18n }}
  • -
-
- - {{ "name" | i18n }} - - {{ "sendNameDesc" | i18n }} - -
- - {{ "whatTypeOfSend" | i18n }} - - - - {{ o.name }} - - - - -
- - - - {{ "sendTypeText" | i18n }} - - {{ "sendTextDesc" | i18n }} - - - - {{ "textHiddenByDefault" | i18n }} - - - - -
-
- {{ "file" | i18n }} -

- {{ send.file.fileName }} ({{ send.file.sizeName }}) -

-
- - {{ "file" | i18n }} -
- - {{ selectedFile?.name ?? ("noFileChosen" | i18n) }} -
- - {{ "sendFileDesc" | i18n }} {{ "maxFileSize" | i18n }} -
-
-
-

{{ "share" | i18n }}

- - - {{ "sendLinkLabel" | i18n }} - - - - - - {{ "copySendLinkOnSave" | i18n }} - -
-

- -

-
-
-
-
- - {{ "deletionDate" | i18n }} - - - - - - - {{ "deletionDateDesc" | i18n }} - -
-
- - {{ "deletionDate" | i18n }} - - {{ "deletionDateDesc" | i18n }} - -
-
- - - {{ "expirationDate" | i18n }} - - - - - - - - {{ "expirationDateDesc" | i18n }} - -
-
- - - {{ "expirationDate" | i18n }} - - - - - {{ "expirationDateDesc" | i18n }} - -
-
-
- - {{ "maxAccessCount" | i18n }} - - {{ "maxAccessCountDesc" | i18n }} - - - {{ "currentAccessCount" | i18n }} - - -
-
- - {{ "password" | i18n }} - {{ "newPassword" | i18n }} - - - - {{ "sendPasswordDesc" | i18n }} - -
- - {{ "notes" | i18n }} - - {{ "sendNotesDesc" | i18n }} - - - - - {{ "hideEmail" | i18n }} - - - - - {{ "disableThisSend" | i18n }} - -
-
- - - - - - -
-
diff --git a/apps/web/src/app/tools/send/add-edit.component.ts b/apps/web/src/app/tools/send/add-edit.component.ts deleted file mode 100644 index 4ce126a33bc..00000000000 --- a/apps/web/src/app/tools/send/add-edit.component.ts +++ /dev/null @@ -1,102 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { DIALOG_DATA, DialogRef } from "@angular/cdk/dialog"; -import { DatePipe } from "@angular/common"; -import { Component, Inject } from "@angular/core"; -import { FormBuilder } from "@angular/forms"; - -import { AddEditComponent as BaseAddEditComponent } from "@bitwarden/angular/tools/send/add-edit.component"; -import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; -import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; -import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; -import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; -import { DialogService, ToastService } from "@bitwarden/components"; - -@Component({ - selector: "app-send-add-edit", - templateUrl: "add-edit.component.html", -}) -export class AddEditComponent extends BaseAddEditComponent { - override componentName = "app-send-add-edit"; - protected selectedFile: File; - - constructor( - i18nService: I18nService, - platformUtilsService: PlatformUtilsService, - environmentService: EnvironmentService, - datePipe: DatePipe, - sendService: SendService, - stateService: StateService, - messagingService: MessagingService, - policyService: PolicyService, - logService: LogService, - sendApiService: SendApiService, - dialogService: DialogService, - formBuilder: FormBuilder, - billingAccountProfileStateService: BillingAccountProfileStateService, - protected dialogRef: DialogRef, - @Inject(DIALOG_DATA) params: { sendId: string }, - accountService: AccountService, - toastService: ToastService, - ) { - super( - i18nService, - platformUtilsService, - environmentService, - datePipe, - sendService, - messagingService, - policyService, - logService, - stateService, - sendApiService, - dialogService, - formBuilder, - billingAccountProfileStateService, - accountService, - toastService, - ); - - this.sendId = params.sendId; - } - - async copyLinkToClipboard(link: string): Promise { - // Copy function on web depends on the modal being open or not. Since this event occurs during a transition - // of the modal closing we need to add a small delay to make sure state of the DOM is consistent. - return new Promise((resolve) => { - window.setTimeout(() => resolve(super.copyLinkToClipboard(link)), 500); - }); - } - - protected setSelectedFile(event: Event) { - const fileInputEl = event.target; - const file = fileInputEl.files.length > 0 ? fileInputEl.files[0] : null; - this.selectedFile = file; - } - - submitAndClose = async () => { - this.formGroup.markAllAsTouched(); - if (this.formGroup.invalid) { - return; - } - - const success = await this.submit(); - if (success) { - this.dialogRef.close(); - } - }; - - deleteAndClose = async () => { - const success = await this.delete(); - if (success) { - this.dialogRef.close(); - } - }; -} diff --git a/apps/web/src/app/tools/send/new-send/new-send-dropdown.component.html b/apps/web/src/app/tools/send/new-send/new-send-dropdown.component.html new file mode 100644 index 00000000000..34e28be1084 --- /dev/null +++ b/apps/web/src/app/tools/send/new-send/new-send-dropdown.component.html @@ -0,0 +1,23 @@ + + + + + {{ "sendTypeText" | i18n }} + + + + {{ "sendTypeFile" | i18n }} + + + diff --git a/apps/web/src/app/tools/send/new-send/new-send-dropdown.component.ts b/apps/web/src/app/tools/send/new-send/new-send-dropdown.component.ts new file mode 100644 index 00000000000..2fc83a5d03b --- /dev/null +++ b/apps/web/src/app/tools/send/new-send/new-send-dropdown.component.ts @@ -0,0 +1,65 @@ +import { CommonModule } from "@angular/common"; +import { Component, Input } from "@angular/core"; +import { Router } from "@angular/router"; +import { firstValueFrom, Observable, of, switchMap } from "rxjs"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions"; +import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; +import { BadgeModule, ButtonModule, DialogService, MenuModule } from "@bitwarden/components"; +import { DefaultSendFormConfigService } from "@bitwarden/send-ui"; + +import { SendAddEditComponent } from "../send-add-edit.component"; + +@Component({ + selector: "tools-new-send-dropdown", + templateUrl: "new-send-dropdown.component.html", + standalone: true, + imports: [JslibModule, CommonModule, ButtonModule, MenuModule, BadgeModule], + providers: [DefaultSendFormConfigService], +}) +/** + * A dropdown component that allows the user to create a new Send of a specific type. + */ +export class NewSendDropdownComponent { + /** If true, the plus icon will be hidden */ + @Input() hideIcon: boolean = false; + + /** SendType provided for the markup to pass back the selected type of Send */ + protected sendType = SendType; + + /** Indicates whether the user can access premium features. */ + protected canAccessPremium$: Observable; + + constructor( + private router: Router, + private billingAccountProfileStateService: BillingAccountProfileStateService, + private accountService: AccountService, + private dialogService: DialogService, + private addEditFormConfigService: DefaultSendFormConfigService, + ) { + this.canAccessPremium$ = this.accountService.activeAccount$.pipe( + switchMap((account) => + account + ? this.billingAccountProfileStateService.hasPremiumFromAnySource$(account.id) + : of(false), + ), + ); + } + + /** + * Opens the SendAddEditComponent for a new Send with the provided type. + * If has user does not have premium access and the type is File, the user will be redirected to the premium settings page. + * @param type The type of Send to create. + */ + async createSend(type: SendType) { + if (!(await firstValueFrom(this.canAccessPremium$)) && type === SendType.File) { + return await this.router.navigate(["settings/subscription/premium"]); + } + + const formConfig = await this.addEditFormConfigService.buildConfig("add", undefined, type); + + await SendAddEditComponent.open(this.dialogService, { formConfig }); + } +} diff --git a/apps/web/src/app/tools/send/send-add-edit.component.html b/apps/web/src/app/tools/send/send-add-edit.component.html new file mode 100644 index 00000000000..d3f3ebedf49 --- /dev/null +++ b/apps/web/src/app/tools/send/send-add-edit.component.html @@ -0,0 +1,35 @@ + + + {{ headerText }} + + + + + + + + + +
+ +
+
+
diff --git a/apps/web/src/app/tools/send/send-add-edit.component.ts b/apps/web/src/app/tools/send/send-add-edit.component.ts new file mode 100644 index 00000000000..490683a4b4d --- /dev/null +++ b/apps/web/src/app/tools/send/send-add-edit.component.ts @@ -0,0 +1,176 @@ +// FIXME: Update this file to be type safe and remove this and next line +// @ts-strict-ignore +import { DIALOG_DATA, DialogRef } from "@angular/cdk/dialog"; +import { CommonModule } from "@angular/common"; +import { Component, Inject } from "@angular/core"; +import { FormsModule } from "@angular/forms"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; +import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; +import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; +import { + AsyncActionsModule, + ButtonModule, + DialogService, + IconButtonModule, + SearchModule, + ToastService, + DialogModule, +} from "@bitwarden/components"; +import { SendFormConfig, SendFormMode, SendFormModule } from "@bitwarden/send-ui"; + +export interface SendItemDialogParams { + /** + * The configuration object for the dialog and form. + */ + formConfig: SendFormConfig; + + /** + * If true, the "edit" button will be disabled in the dialog. + */ + disableForm?: boolean; +} + +export enum SendItemDialogResult { + /** + * A Send was saved (created or updated). + */ + Saved = "saved", + + /** + * A Send was deleted. + */ + Deleted = "deleted", +} + +/** + * Component for adding or editing a send item. + */ +@Component({ + selector: "tools-send-add-edit", + templateUrl: "send-add-edit.component.html", + standalone: true, + imports: [ + CommonModule, + SearchModule, + JslibModule, + FormsModule, + ButtonModule, + IconButtonModule, + SendFormModule, + AsyncActionsModule, + DialogModule, + ], +}) +export class SendAddEditComponent { + /** + * The header text for the component. + */ + headerText: string; + + /** + * The configuration for the send form. + */ + config: SendFormConfig; + + constructor( + @Inject(DIALOG_DATA) protected params: SendItemDialogParams, + private dialogRef: DialogRef, + private i18nService: I18nService, + private sendApiService: SendApiService, + private toastService: ToastService, + private dialogService: DialogService, + ) { + this.config = params.formConfig; + this.headerText = this.getHeaderText(this.config.mode, this.config.sendType); + } + + /** + * Handles the event when the send is created. + */ + async onSendCreated(send: SendView) { + // FIXME Add dialogService.open send-created dialog + this.dialogRef.close(SendItemDialogResult.Saved); + return; + } + + /** + * Handles the event when the send is updated. + */ + async onSendUpdated(send: SendView) { + this.dialogRef.close(SendItemDialogResult.Saved); + } + + /** + * Handles the event when the send is deleted. + */ + async onSendDeleted() { + this.dialogRef.close(SendItemDialogResult.Deleted); + + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("deletedSend"), + }); + } + + /** + * Handles the deletion of the current Send. + */ + deleteSend = async () => { + const confirmed = await this.dialogService.openSimpleDialog({ + title: { key: "deleteSend" }, + content: { key: "deleteSendPermanentConfirmation" }, + type: "warning", + }); + + if (!confirmed) { + return; + } + + try { + await this.sendApiService.delete(this.config.originalSend?.id); + } catch (e) { + this.toastService.showToast({ + variant: "error", + title: null, + message: e.message, + }); + return; + } + + await this.onSendDeleted(); + }; + + /** + * Gets the header text based on the mode and type. + * @param mode The mode of the send form. + * @param type The type of the send + * @returns The header text. + */ + private getHeaderText(mode: SendFormMode, type: SendType) { + const headerKey = + mode === "edit" || mode === "partial-edit" ? "editItemHeader" : "newItemHeader"; + + switch (type) { + case SendType.Text: + return this.i18nService.t(headerKey, this.i18nService.t("textSend")); + case SendType.File: + return this.i18nService.t(headerKey, this.i18nService.t("fileSend")); + } + } + + /** + * Opens the send add/edit dialog. + * @param dialogService Instance of the DialogService. + * @param params The parameters for the dialog. + * @returns The dialog result. + */ + static open(dialogService: DialogService, params: SendItemDialogParams) { + return dialogService.open(SendAddEditComponent, { + data: params, + }); + } +} diff --git a/apps/web/src/app/tools/send/send.component.html b/apps/web/src/app/tools/send/send.component.html index 7aab50b33e5..6f690459bb0 100644 --- a/apps/web/src/app/tools/send/send.component.html +++ b/apps/web/src/app/tools/send/send.component.html @@ -11,11 +11,7 @@ - - + @@ -198,10 +194,11 @@ {{ "sendsNoItemsTitle" | i18n }} {{ "sendsNoItemsMessage" | i18n }} - + diff --git a/apps/web/src/app/tools/send/send.component.ts b/apps/web/src/app/tools/send/send.component.ts index 1268e4bfb50..1d4e6305bdc 100644 --- a/apps/web/src/app/tools/send/send.component.ts +++ b/apps/web/src/app/tools/send/send.component.ts @@ -1,6 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { Component, NgZone, ViewChild, OnInit, OnDestroy, ViewContainerRef } from "@angular/core"; +import { DialogRef } from "@angular/cdk/dialog"; +import { Component, NgZone, OnInit, OnDestroy } from "@angular/core"; import { lastValueFrom } from "rxjs"; import { SendComponent as BaseSendComponent } from "@bitwarden/angular/tools/send/send.component"; @@ -14,6 +15,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; +import { SendId } from "@bitwarden/common/types/guid"; import { DialogService, NoItemsModule, @@ -21,24 +23,25 @@ import { TableDataSource, ToastService, } from "@bitwarden/components"; -import { NoSendsIcon } from "@bitwarden/send-ui"; +import { DefaultSendFormConfigService, NoSendsIcon, SendFormConfig } from "@bitwarden/send-ui"; import { HeaderModule } from "../../layouts/header/header.module"; import { SharedModule } from "../../shared"; -import { AddEditComponent } from "./add-edit.component"; +import { NewSendDropdownComponent } from "./new-send/new-send-dropdown.component"; +import { SendAddEditComponent, SendItemDialogResult } from "./send-add-edit.component"; const BroadcasterSubscriptionId = "SendComponent"; @Component({ selector: "app-send", standalone: true, - imports: [SharedModule, SearchModule, NoItemsModule, HeaderModule], + imports: [SharedModule, SearchModule, NoItemsModule, HeaderModule, NewSendDropdownComponent], templateUrl: "send.component.html", + providers: [DefaultSendFormConfigService], }) export class SendComponent extends BaseSendComponent implements OnInit, OnDestroy { - @ViewChild("sendAddEdit", { read: ViewContainerRef, static: true }) - sendAddEditModalRef: ViewContainerRef; + private sendItemDialogRef?: DialogRef | undefined; noItemIcon = NoSendsIcon; override set filteredSends(filteredSends: SendView[]) { @@ -65,6 +68,7 @@ export class SendComponent extends BaseSendComponent implements OnInit, OnDestro sendApiService: SendApiService, dialogService: DialogService, toastService: ToastService, + private addEditFormConfigService: DefaultSendFormConfigService, ) { super( sendService, @@ -111,17 +115,41 @@ export class SendComponent extends BaseSendComponent implements OnInit, OnDestro return; } - await this.editSend(null); + const config = await this.addEditFormConfigService.buildConfig("add", null, 0); + + await this.openSendItemDialog(config); } async editSend(send: SendView) { - const dialog = this.dialogService.open(AddEditComponent, { - data: { - sendId: send == null ? null : send.id, - }, + const config = await this.addEditFormConfigService.buildConfig( + send == null ? "add" : "edit", + send == null ? null : (send.id as SendId), + send.type, + ); + + await this.openSendItemDialog(config); + } + + /** + * Opens the send item dialog. + * @param formConfig The form configuration. + * */ + async openSendItemDialog(formConfig: SendFormConfig) { + // Prevent multiple dialogs from being opened. + if (this.sendItemDialogRef) { + return; + } + + this.sendItemDialogRef = SendAddEditComponent.open(this.dialogService, { + formConfig, }); - await lastValueFrom(dialog.closed); - await this.load(); + const result = await lastValueFrom(this.sendItemDialogRef.closed); + this.sendItemDialogRef = undefined; + + // If the dialog was closed by deleting the cipher, refresh the vault. + if (result === SendItemDialogResult.Deleted || result === SendItemDialogResult.Saved) { + await this.load(); + } } } diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 22a67cbff28..da237a8a3ab 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -222,6 +222,9 @@ "notes": { "message": "Notes" }, + "privateNote": { + "message": "Private note" + }, "note": { "message": "Note" }, @@ -5105,12 +5108,40 @@ "requireSsoExemption": { "message": "Organization owners and admins are exempt from this policy's enforcement." }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, "sendTypeFile": { "message": "File" }, "sendTypeText": { "message": "Text" }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "createSend": { "message": "New Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -5135,19 +5166,15 @@ "message": "Delete Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "deleteSendConfirmation": { - "message": "Are you sure you want to delete this Send?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "whatTypeOfSend": { - "message": "What type of Send is this?", + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deletionDate": { "message": "Deletion date" }, - "deletionDateDesc": { - "message": "The Send will be permanently deleted on the specified date and time.", + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "expirationDate": { @@ -5160,21 +5187,6 @@ "maxAccessCount": { "message": "Maximum access count" }, - "maxAccessCountDesc": { - "message": "If set, users will no longer be able to access this Send once the maximum access count is reached.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Current access count" - }, - "sendPasswordDesc": { - "message": "Optionally require a password for users to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNotesDesc": { - "message": "Private notes about this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "disabled": { "message": "Disabled" }, @@ -5201,13 +5213,6 @@ "removePasswordConfirmation": { "message": "Are you sure you want to remove the password?" }, - "hideEmail": { - "message": "Hide my email address from recipients." - }, - "disableThisSend": { - "message": "Deactivate this Send so that no one can access it.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "allSends": { "message": "All Sends" }, @@ -5218,6 +5223,9 @@ "pendingDeletion": { "message": "Pending deletion" }, + "hideTextByDefault": { + "message": "Hide text by default" + }, "expired": { "message": "Expired" }, @@ -5439,13 +5447,6 @@ "message": "Always show member’s email address with recipients when creating or editing a Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendOptionsPolicyInEffect": { - "message": "The following organization policies are currently in effect:" - }, - "sendDisableHideEmailInEffect": { - "message": "Users are not allowed to hide their email address from recipients when creating or editing a Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "modifiedPolicyId": { "message": "Modified policy $ID$.", "placeholders": { @@ -5545,27 +5546,6 @@ "personalOwnershipCheckboxDesc": { "message": "Remove individual ownership for organization users" }, - "textHiddenByDefault": { - "message": "When accessing the Send, hide the text by default", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "A friendly name to describe this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "The text you want to Send." - }, - "sendFileDesc": { - "message": "The file you want to Send." - }, - "copySendLinkOnSave": { - "message": "Copy the link to share this Send to my clipboard upon save." - }, - "sendLinkLabel": { - "message": "Send link", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "send": { "message": "Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -5714,6 +5694,9 @@ "dateParsingError": { "message": "There was an error saving your deletion and expiration dates." }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, "webAuthnFallbackMsg": { "message": "To verify your 2FA please click the button below." }, @@ -9875,9 +9858,15 @@ "learnMoreAboutApi": { "message": "Learn more about Bitwarden's API" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" },