1
0
mirror of https://github.com/bitwarden/browser.git synced 2025-12-05 09:14:28 +01:00
This commit is contained in:
bmbitwarden 2025-12-04 18:38:29 -06:00 committed by GitHub
commit 4015cd8cfb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 170 additions and 34 deletions

View File

@ -1,6 +1,6 @@
import { CommonModule } from "@angular/common";
import { Component, Input } from "@angular/core";
import { firstValueFrom, Observable, of, switchMap } from "rxjs";
import { firstValueFrom, Observable, of, switchMap, lastValueFrom } from "rxjs";
import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge";
import { JslibModule } from "@bitwarden/angular/jslib.module";
@ -8,7 +8,13 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions";
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
import { ButtonModule, DialogService, MenuModule } from "@bitwarden/components";
import { DefaultSendFormConfigService, SendAddEditDialogComponent } from "@bitwarden/send-ui";
import {
DefaultSendFormConfigService,
SendAddEditDialogComponent,
SendItemDialogResult,
} from "@bitwarden/send-ui";
import { SendSuccessDrawerDialogComponent } from "../shared";
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@ -57,9 +63,15 @@ export class NewSendDropdownComponent {
if (!(await firstValueFrom(this.canAccessPremium$)) && type === SendType.File) {
return;
}
const formConfig = await this.addEditFormConfigService.buildConfig("add", undefined, type);
SendAddEditDialogComponent.open(this.dialogService, { formConfig });
const dialogRef = SendAddEditDialogComponent.open(this.dialogService, { formConfig });
const result = await lastValueFrom(dialogRef.closed);
if (typeof result === "object" && result.result === SendItemDialogResult.Saved && result.send) {
this.dialogService.openDrawer(SendSuccessDrawerDialogComponent, {
data: result.send,
});
}
}
}

View File

@ -36,6 +36,7 @@ import { HeaderModule } from "../../layouts/header/header.module";
import { SharedModule } from "../../shared";
import { NewSendDropdownComponent } from "./new-send/new-send-dropdown.component";
import { SendSuccessDrawerDialogComponent } from "./shared";
const BroadcasterSubscriptionId = "SendComponent";
@ -160,5 +161,11 @@ export class SendComponent extends BaseSendComponent implements OnInit, OnDestro
if (result === SendItemDialogResult.Deleted || result === SendItemDialogResult.Saved) {
await this.load();
}
if (typeof result === "object" && result.result === SendItemDialogResult.Saved && result.send) {
this.dialogService.openDrawer(SendSuccessDrawerDialogComponent, {
data: result.send,
});
}
}
}

View File

@ -0,0 +1 @@
export { SendSuccessDrawerDialogComponent } from "./send-success-drawer-dialog.component";

View File

@ -0,0 +1,45 @@
<bit-dialog dialogSize="large" disablePadding="false" background="alt">
<ng-container bitDialogTitle>
<span>{{ dialogTitle() | i18n }}</span>
</ng-container>
<ng-container bitDialogContent>
<div
class="tw-flex tw-flex-col tw-items-center tw-justify-center tw-text-center tw-h-full tw-px-4 tw-pt-20"
>
<div class="tw-mb-6 tw-mt-8">
<div class="tw-size-[95px] tw-content-center">
<bit-icon [icon]="activeSendIcon"></bit-icon>
</div>
</div>
<h3 bitTypography="h3" class="tw-mb-2 tw-font-bold">
{{ "sendCreatedSuccessfully" | i18n }}
</h3>
<p bitTypography="body1" class="tw-mb-6 tw-max-w-sm">
{{ "sendCreatedDescription" | i18n }}
</p>
<bit-form-field class="tw-w-full tw-max-w-sm tw-mb-4">
<bit-label>{{ "sendLink" | i18n }}</bit-label>
<input bitInput disabled type="text" [value]="sendLink()" />
<button
type="button"
bitIconButton="bwi-clone"
bitSuffix
[label]="'copyLink' | i18n"
(click)="copyLink()"
></button>
</bit-form-field>
</div>
</ng-container>
<ng-container bitDialogFooter>
<button type="button" bitButton buttonType="primary" (click)="copyLink()">
{{ "copyLink" | i18n }}
</button>
<button type="button" bitButton buttonType="secondary" bitDialogClose>
{{ "close" | i18n }}
</button>
</ng-container>
</bit-dialog>

View File

@ -0,0 +1,54 @@
import { Component, ChangeDetectionStrategy, Inject, signal, computed } from "@angular/core";
import { firstValueFrom } from "rxjs";
import { ActiveSendIcon } from "@bitwarden/assets/svg";
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 { SendType } from "@bitwarden/common/tools/send/enums/send-type";
import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
import { DIALOG_DATA, DialogModule, ToastService, TypographyModule } from "@bitwarden/components";
import { SharedModule } from "@bitwarden/web-vault/app/shared";
@Component({
imports: [SharedModule, DialogModule, TypographyModule],
templateUrl: "./send-success-drawer-dialog.component.html",
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SendSuccessDrawerDialogComponent {
readonly sendLink = signal<string>("");
activeSendIcon = ActiveSendIcon;
// Computed property to get the dialog title based on send type
readonly dialogTitle = computed(() => {
return this.send.type === SendType.Text ? "newTextSend" : "newFileSend";
});
constructor(
@Inject(DIALOG_DATA) public send: SendView,
private environmentService: EnvironmentService,
private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService,
private toastService: ToastService,
) {
void this.initLink();
}
async initLink() {
const env = await firstValueFrom(this.environmentService.environment$);
this.sendLink.set(env.getSendUrl() + this.send.accessId + "/" + this.send.urlB64Key);
}
copyLink() {
const link = this.sendLink();
if (!link) {
return;
}
this.platformUtilsService.copyToClipboard(link);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("valueCopied", this.i18nService.t("sendLink")),
});
}
}

View File

@ -3265,7 +3265,7 @@
"nextChargeHeader": {
"message": "Next Charge"
},
"plan": {
"plan": {
"message": "Plan"
},
"details": {
@ -4481,7 +4481,6 @@
"updateBrowser": {
"message": "Update browser"
},
"generatingYourAccessIntelligence": {
"message": "Generating your Access Intelligence..."
},
@ -5583,6 +5582,22 @@
"message": "Send saved",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"sendCreatedSuccessfully": {
"message": "Send created successfully!",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"sendCreatedDescription": {
"message": "Copy and share this Send link. It can be viewed by the people you specified for the next N days.",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"newTextSend": {
"message": "New Text Send",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"newFileSend": {
"message": "New File Send",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"editedSend": {
"message": "Send saved",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
@ -5857,22 +5872,22 @@
"message": "credential lifecycle",
"description": "This will be used as a hyperlink"
},
"organizationDataOwnershipWarningTitle":{
"organizationDataOwnershipWarningTitle": {
"message": "Are you sure you want to proceed?"
},
"organizationDataOwnershipWarning1":{
"organizationDataOwnershipWarning1": {
"message": "will remain accessible to members"
},
"organizationDataOwnershipWarning2":{
"organizationDataOwnershipWarning2": {
"message": "will not be automatically selected when creating new items"
},
"organizationDataOwnershipWarning3":{
"organizationDataOwnershipWarning3": {
"message": "cannot be managed from the Admin Console until the user is offboarded"
},
"organizationDataOwnershipWarningContentTop":{
"organizationDataOwnershipWarningContentTop": {
"message": "By turning this policy off, the default collection: "
},
"organizationDataOwnershipWarningContentBottom":{
"organizationDataOwnershipWarningContentBottom": {
"message": "Learn more about the ",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Learn more about the credential lifecycle.'"
},
@ -5996,7 +6011,7 @@
"uriMatchDetectionOptionsLabel": {
"message": "Default URI match detection"
},
"invalidUriMatchDefaultPolicySetting": {
"invalidUriMatchDefaultPolicySetting": {
"message": "Please select a valid URI match detection option.",
"description": "Error message displayed when a user attempts to save URI match detection policy settings with an invalid selection."
},
@ -8886,7 +8901,7 @@
}
},
"accessedSecret": {
"message": "Accessed secret $SECRET_ID$.",
"message": "Accessed secret $SECRET_ID$.",
"placeholders": {
"secret_id": {
"content": "$1",
@ -8894,7 +8909,7 @@
}
}
},
"editedSecretWithId": {
"editedSecretWithId": {
"message": "Edited a secret with identifier: $SECRET_ID$",
"placeholders": {
"secret_id": {
@ -8903,7 +8918,7 @@
}
}
},
"deletedSecretWithId": {
"deletedSecretWithId": {
"message": "Deleted a secret with identifier: $SECRET_ID$",
"placeholders": {
"secret_id": {
@ -8921,7 +8936,7 @@
}
}
},
"restoredSecretWithId": {
"restoredSecretWithId": {
"message": "Restored a secret with identifier: $SECRET_ID$",
"placeholders": {
"secret_id": {
@ -8930,7 +8945,7 @@
}
}
},
"createdSecretWithId": {
"createdSecretWithId": {
"message": "Created a new secret with identifier: $SECRET_ID$",
"placeholders": {
"secret_id": {
@ -8940,7 +8955,7 @@
}
},
"accessedProjectWithIdentifier": {
"message": "Accessed a project with identifier: $PROJECT_ID$.",
"message": "Accessed a project with identifier: $PROJECT_ID$.",
"placeholders": {
"project_id": {
"content": "$1",
@ -8949,7 +8964,7 @@
}
},
"nameUnavailableProjectDeleted": {
"message": "Deleted project Id: $PROJECT_ID$",
"message": "Deleted project Id: $PROJECT_ID$",
"placeholders": {
"project_id": {
"content": "$1",
@ -8958,7 +8973,7 @@
}
},
"nameUnavailableSecretDeleted": {
"message": "Deleted secret Id: $SECRET_ID$",
"message": "Deleted secret Id: $SECRET_ID$",
"placeholders": {
"secret_id": {
"content": "$1",
@ -8967,7 +8982,7 @@
}
},
"nameUnavailableServiceAccountDeleted": {
"message": "Deleted machine account Id: $SERVICE_ACCOUNT_ID$",
"message": "Deleted machine account Id: $SERVICE_ACCOUNT_ID$",
"placeholders": {
"service_account_id": {
"content": "$1",
@ -8975,7 +8990,7 @@
}
}
},
"editedProjectWithId": {
"editedProjectWithId": {
"message": "Edited a project with identifier: $PROJECT_ID$",
"placeholders": {
"project_id": {
@ -9054,7 +9069,7 @@
}
}
},
"deletedProjectWithId": {
"deletedProjectWithId": {
"message": "Deleted a project with identifier: $PROJECT_ID$",
"placeholders": {
"project_id": {
@ -9063,7 +9078,7 @@
}
}
},
"createdProjectWithId": {
"createdProjectWithId": {
"message": "Created a new project with identifier: $PROJECT_ID$",
"placeholders": {
"project_id": {
@ -9778,15 +9793,15 @@
"message": "Common formats",
"description": "Label indicating the most common import formats"
},
"uriMatchDefaultStrategyHint": {
"uriMatchDefaultStrategyHint": {
"message": "URI match detection is how Bitwarden identifies autofill suggestions.",
"description": "Explains to the user that URI match detection determines how Bitwarden suggests autofill options, and clarifies that this default strategy applies when no specific match detection is set for a login item."
},
"regExAdvancedOptionWarning": {
"regExAdvancedOptionWarning": {
"message": "\"Regular expression\" is an advanced option with increased risk of exposing credentials.",
"description": "Content for dialog which warns a user when selecting 'regular expression' matching strategy as a cipher match strategy"
},
"startsWithAdvancedOptionWarning": {
"startsWithAdvancedOptionWarning": {
"message": "\"Starts with\" is an advanced option with increased risk of exposing credentials.",
"description": "Content for dialog which warns a user when selecting 'starts with' matching strategy as a cipher match strategy"
},
@ -9794,11 +9809,11 @@
"message": "More about match detection",
"description": "Link to match detection docs on warning dialog for advance match strategy"
},
"uriAdvancedOption":{
"uriAdvancedOption": {
"message": "Advanced options",
"description": "Advanced option placeholder for uri option component"
},
"warningCapitalized": {
"warningCapitalized": {
"message": "Warning",
"description": "Warning (should maintain locale-relevant capitalization)"
},
@ -12215,4 +12230,4 @@
"userVerificationFailed": {
"message": "User verification failed."
}
}
}

View File

@ -3,3 +3,4 @@ export * from "./drawer.component";
export * from "./drawer-body.component";
export * from "./drawer-close.directive";
export * from "./drawer-header.component";
export * from "./drawer.service";

View File

@ -44,8 +44,9 @@ export const SendItemDialogResult = Object.freeze({
} as const);
/** A result of the Send add/edit dialog. */
export type SendItemDialogResult = (typeof SendItemDialogResult)[keyof typeof SendItemDialogResult];
export type SendItemDialogResult =
| (typeof SendItemDialogResult)[keyof typeof SendItemDialogResult]
| { result: typeof SendItemDialogResult.Saved; send: SendView };
/**
* Component for adding or editing a send item.
*/
@ -93,7 +94,7 @@ export class SendAddEditDialogComponent {
*/
async onSendCreated(send: SendView) {
// FIXME Add dialogService.open send-created dialog
this.dialogRef.close(SendItemDialogResult.Saved);
this.dialogRef.close({ result: SendItemDialogResult.Saved, send });
return;
}