Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
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";
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 { 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
Expand Down Expand Up @@ -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,
});
}
}
}
7 changes: 7 additions & 0 deletions apps/web/src/app/tools/send/send.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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,
});
}
}
}
1 change: 1 addition & 0 deletions apps/web/src/app/tools/send/shared/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { SendSuccessDrawerDialogComponent } from "./send-success-drawer-dialog.component";
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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")),
});
}
}
69 changes: 42 additions & 27 deletions apps/web/src/locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -3265,7 +3265,7 @@
"nextChargeHeader": {
"message": "Next Charge"
},
"plan": {
"plan": {
"message": "Plan"
},
"details": {
Expand Down Expand Up @@ -4481,7 +4481,6 @@
"updateBrowser": {
"message": "Update browser"
},

"generatingYourAccessIntelligence": {
"message": "Generating your Access Intelligence..."
},
Expand Down Expand Up @@ -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."
Expand Down Expand Up @@ -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.'"
},
Expand Down Expand Up @@ -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."
},
Expand Down Expand Up @@ -8886,15 +8901,15 @@
}
},
"accessedSecret": {
"message": "Accessed secret $SECRET_ID$.",
"message": "Accessed secret $SECRET_ID$.",
"placeholders": {
"secret_id": {
"content": "$1",
"example": "4d34e8a8"
}
}
},
"editedSecretWithId": {
"editedSecretWithId": {
"message": "Edited a secret with identifier: $SECRET_ID$",
"placeholders": {
"secret_id": {
Expand All @@ -8903,7 +8918,7 @@
}
}
},
"deletedSecretWithId": {
"deletedSecretWithId": {
"message": "Deleted a secret with identifier: $SECRET_ID$",
"placeholders": {
"secret_id": {
Expand All @@ -8921,7 +8936,7 @@
}
}
},
"restoredSecretWithId": {
"restoredSecretWithId": {
"message": "Restored a secret with identifier: $SECRET_ID$",
"placeholders": {
"secret_id": {
Expand All @@ -8930,7 +8945,7 @@
}
}
},
"createdSecretWithId": {
"createdSecretWithId": {
"message": "Created a new secret with identifier: $SECRET_ID$",
"placeholders": {
"secret_id": {
Expand All @@ -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",
Expand All @@ -8949,7 +8964,7 @@
}
},
"nameUnavailableProjectDeleted": {
"message": "Deleted project Id: $PROJECT_ID$",
"message": "Deleted project Id: $PROJECT_ID$",
"placeholders": {
"project_id": {
"content": "$1",
Expand All @@ -8958,7 +8973,7 @@
}
},
"nameUnavailableSecretDeleted": {
"message": "Deleted secret Id: $SECRET_ID$",
"message": "Deleted secret Id: $SECRET_ID$",
"placeholders": {
"secret_id": {
"content": "$1",
Expand All @@ -8967,15 +8982,15 @@
}
},
"nameUnavailableServiceAccountDeleted": {
"message": "Deleted machine account Id: $SERVICE_ACCOUNT_ID$",
"message": "Deleted machine account Id: $SERVICE_ACCOUNT_ID$",
"placeholders": {
"service_account_id": {
"content": "$1",
"example": "4d34e8a8"
}
}
},
"editedProjectWithId": {
"editedProjectWithId": {
"message": "Edited a project with identifier: $PROJECT_ID$",
"placeholders": {
"project_id": {
Expand Down Expand Up @@ -9054,7 +9069,7 @@
}
}
},
"deletedProjectWithId": {
"deletedProjectWithId": {
"message": "Deleted a project with identifier: $PROJECT_ID$",
"placeholders": {
"project_id": {
Expand All @@ -9063,7 +9078,7 @@
}
}
},
"createdProjectWithId": {
"createdProjectWithId": {
"message": "Created a new project with identifier: $PROJECT_ID$",
"placeholders": {
"project_id": {
Expand Down Expand Up @@ -9778,27 +9793,27 @@
"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"
},
"uriMatchWarningDialogLink": {
"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)"
},
Expand Down Expand Up @@ -12215,4 +12230,4 @@
"userVerificationFailed": {
"message": "User verification failed."
}
}
}
1 change: 1 addition & 0 deletions libs/components/src/drawer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Loading
Loading