Browse Source
* Remove unneeded ApiService * Extract SendAccess for sends of type text * Migrate form and card-body * Migrate callout * Extract SendAccess for sends of type file * Converted SendAccess component to standalone * Migrated bottom message to CL * Added Send Access Password Component * Added No item component, password component and changed bootstrap classes * Updated send texts and added layout for unexpected error * Changed SendAccessTextComponent to standalone * Moved AccessComponent to oss.module.ts and removed unnecessary components from app.module * Properly set access modifiers * Using async action on download button * Updated links * Using tailwind classes * Using ng-template and ng-container * Added validation to check if status code is from a wrong password * Using Component Library Forms * using subscriber to update password on send access * Using reactive forms to show the text on send access * Updated message.json keys for changed values * Removed unnecessary components and changed classes to tailwind ones * added margin bottom on send-access-password to keep consistent with other send-access layouts * removed duplicated message key * Added error toast message on wrong password --------- Co-authored-by: Daniel James Smith <djsmith@web.de>pull/6907/head
12 changed files with 365 additions and 233 deletions
@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
|
||||
import { svgIcon } from "@bitwarden/components"; |
||||
|
||||
export const ExpiredSend = svgIcon` |
||||
<svg xmlns="http://www.w3.org/2000/svg" width="130" height="130" fill="none"> |
||||
<path class="tw-fill-secondary-500" fill-rule="evenodd" d="M22.75 29.695c0-4.991 4.074-9.037 9.1-9.037h14.3v2.582h-14.3c-3.59 0-6.5 2.89-6.5 6.455v68.428h-2.6V29.696Zm75.4 76.175V68.428h2.6v37.442c0 4.991-4.074 9.038-9.1 9.038h-53.3v-2.582h53.3c3.59 0 6.5-2.891 6.5-6.456Z" clip-rule="evenodd"/> |
||||
<path class="tw-fill-secondary-500" fill-rule="evenodd" d="M43.55 37.441c0-17.113 13.969-30.986 31.2-30.986s31.2 13.873 31.2 30.986c0 17.114-13.969 30.987-31.2 30.987s-31.2-13.873-31.2-30.986Zm31.2-33.568c-18.667 0-33.8 15.03-33.8 33.569S56.083 71.01 74.75 71.01c18.668 0 33.8-15.03 33.8-33.569S93.418 3.873 74.75 3.873Z" clip-rule="evenodd"/> |
||||
<path class="tw-fill-secondary-500" fill-rule="evenodd" d="M73.972 65.2c0 .357.291.646.65.646 15.968 0 28.925-12.71 28.925-28.404a.648.648 0 0 0-.65-.646.648.648 0 0 0-.65.646c0 14.967-12.36 27.113-27.625 27.113a.648.648 0 0 0-.65.645ZM46.347 38.087c.36 0 .65-.289.65-.645 0-14.968 12.361-27.113 27.625-27.113.36 0 .65-.29.65-.646a.648.648 0 0 0-.65-.646c-15.968 0-28.925 12.71-28.925 28.405 0 .356.291.645.65.645Z" clip-rule="evenodd"/> |
||||
<path class="tw-fill-secondary-500" fill-rule="evenodd" d="M123.729 81.869a1.926 1.926 0 0 1 0 2.739l-1.439 1.43a1.96 1.96 0 0 1-2.758 0L95.577 62.245a1.306 1.306 0 0 0-1.839 0 1.285 1.285 0 0 0 0 1.826l23.956 23.791a4.571 4.571 0 0 0 6.434 0l1.44-1.43a4.497 4.497 0 0 0 0-6.39l-23.956-23.791a1.306 1.306 0 0 0-1.838 0 1.285 1.285 0 0 0 0 1.825l23.955 23.792ZM34.45 36.797c0-.714.582-1.292 1.3-1.292h5.85c.718 0 1.3.578 1.3 1.291 0 .714-.582 1.292-1.3 1.292h-5.85c-.718 0-1.3-.578-1.3-1.291Zm0 10.973c0-.713.582-1.29 1.3-1.29h7.8c.718 0 1.3.578 1.3 1.29 0 .714-.582 1.292-1.3 1.292h-7.8c-.718 0-1.3-.578-1.3-1.291Zm0 10.975c0-.713.582-1.291 1.3-1.291H49.4c.718 0 1.3.578 1.3 1.29 0 .714-.582 1.292-1.3 1.292H35.75c-.718 0-1.3-.578-1.3-1.291Zm0 10.975c0-.714.582-1.292 1.3-1.292H72.8c.718 0 1.3.578 1.3 1.291s-.582 1.291-1.3 1.291H35.75c-.718 0-1.3-.578-1.3-1.29Zm0 10.973c0-.713.582-1.29 1.3-1.29h27.3c.718 0 1.3.577 1.3 1.29 0 .713-.582 1.291-1.3 1.291h-27.3c-.718 0-1.3-.578-1.3-1.29Zm6.5 10.975c0-.713.582-1.291 1.3-1.291H88.4c.718 0 1.3.578 1.3 1.291s-.582 1.291-1.3 1.291H42.25c-.718 0-1.3-.578-1.3-1.291Zm0 10.974c0-.713.582-1.291 1.3-1.291H88.4c.718 0 1.3.578 1.3 1.291s-.582 1.291-1.3 1.291H42.25c-.718 0-1.3-.578-1.3-1.291Z" clip-rule="evenodd"/> |
||||
<path class="tw-fill-secondary-500" fill-rule="evenodd" d="M43.664 86.742c.412.292.617.794.524 1.289l-6.366 33.964a1.305 1.305 0 0 1-1.745.968l-9.692-3.707-4.914 5.689c-.355.41-.928.557-1.438.37a1.292 1.292 0 0 1-.849-1.211v-8.444c0-.305.108-.599.306-.832l14.73-17.357a1.306 1.306 0 0 1 1.831-.156c.549.46.619 1.275.156 1.82L21.784 116.13v4.485l3.225-3.733c.358-.414.94-.56 1.454-.364l9.089 3.476 5.567-29.698-32.42 18.385 6.813 3.082c.653.296.941 1.061.643 1.71a1.303 1.303 0 0 1-1.722.64l-9.122-4.128a1.289 1.289 0 0 1-.106-2.296l37.06-21.017c.44-.249.986-.222 1.399.07Z" clip-rule="evenodd"/> |
||||
</svg> |
||||
`;
|
||||
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
<p>{{ send.file.fileName }}</p> |
||||
<button bitButton type="button" buttonType="primary" [bitAction]="download" [block]="true"> |
||||
<i class="bwi bwi-download" aria-hidden="true"></i> |
||||
{{ "downloadAttachments" | i18n }} ({{ send.file.sizeName }}) |
||||
</button> |
||||
@ -0,0 +1,67 @@
@@ -0,0 +1,67 @@
|
||||
import { Component, Input } from "@angular/core"; |
||||
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; |
||||
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; |
||||
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 { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer"; |
||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; |
||||
import { SendAccessRequest } from "@bitwarden/common/tools/send/models/request/send-access.request"; |
||||
import { SendAccessView } from "@bitwarden/common/tools/send/models/view/send-access.view"; |
||||
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; |
||||
|
||||
import { SharedModule } from "../../shared"; |
||||
|
||||
@Component({ |
||||
selector: "app-send-access-file", |
||||
templateUrl: "send-access-file.component.html", |
||||
imports: [SharedModule], |
||||
standalone: true, |
||||
}) |
||||
export class SendAccessFileComponent { |
||||
@Input() send: SendAccessView; |
||||
@Input() decKey: SymmetricCryptoKey; |
||||
@Input() accessRequest: SendAccessRequest; |
||||
constructor( |
||||
private i18nService: I18nService, |
||||
private platformUtilsService: PlatformUtilsService, |
||||
private cryptoService: CryptoService, |
||||
private fileDownloadService: FileDownloadService, |
||||
private sendApiService: SendApiService |
||||
) {} |
||||
|
||||
protected download = async () => { |
||||
if (this.send == null || this.decKey == null) { |
||||
return; |
||||
} |
||||
|
||||
const downloadData = await this.sendApiService.getSendFileDownloadData( |
||||
this.send, |
||||
this.accessRequest |
||||
); |
||||
|
||||
if (Utils.isNullOrWhitespace(downloadData.url)) { |
||||
this.platformUtilsService.showToast("error", null, this.i18nService.t("missingSendFile")); |
||||
return; |
||||
} |
||||
|
||||
const response = await fetch(new Request(downloadData.url, { cache: "no-store" })); |
||||
if (response.status !== 200) { |
||||
this.platformUtilsService.showToast("error", null, this.i18nService.t("errorOccurred")); |
||||
return; |
||||
} |
||||
|
||||
try { |
||||
const encBuf = await EncArrayBuffer.fromResponse(response); |
||||
const decBuf = await this.cryptoService.decryptFromBytes(encBuf, this.decKey); |
||||
this.fileDownloadService.download({ |
||||
fileName: this.send.file.fileName, |
||||
blobData: decBuf, |
||||
downloadMethod: "save", |
||||
}); |
||||
} catch (e) { |
||||
this.platformUtilsService.showToast("error", null, this.i18nService.t("errorOccurred")); |
||||
} |
||||
}; |
||||
} |
||||
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
<p bitTypography="body1">{{ "sendProtectedPassword" | i18n }}</p> |
||||
<p bitTypography="body1">{{ "sendProtectedPasswordDontKnow" | i18n }}</p> |
||||
<div class="tw-mb-3" [formGroup]="formGroup"> |
||||
<bit-form-field> |
||||
<bit-label>{{ "password" | i18n }}</bit-label> |
||||
<input |
||||
bitInput |
||||
type="password" |
||||
formControlName="password" |
||||
required |
||||
appInputVerbatim |
||||
appAutofocus |
||||
/> |
||||
<button type="button" bitIconButton bitSuffix bitPasswordInputToggle></button> |
||||
</bit-form-field> |
||||
<div class="tw-flex"> |
||||
<button |
||||
bitButton |
||||
bitFormButton |
||||
type="submit" |
||||
buttonType="primary" |
||||
[loading]="loading" |
||||
[block]="true" |
||||
> |
||||
<span> <i class="bwi bwi-sign-in" aria-hidden="true"></i> {{ "continue" | i18n }} </span> |
||||
</button> |
||||
</div> |
||||
</div> |
||||
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
import { Component, EventEmitter, Input, Output } from "@angular/core"; |
||||
import { FormBuilder, Validators } from "@angular/forms"; |
||||
import { Subject, takeUntil } from "rxjs"; |
||||
|
||||
import { SharedModule } from "../../shared"; |
||||
|
||||
@Component({ |
||||
selector: "app-send-access-password", |
||||
templateUrl: "send-access-password.component.html", |
||||
imports: [SharedModule], |
||||
standalone: true, |
||||
}) |
||||
export class SendAccessPasswordComponent { |
||||
private destroy$ = new Subject<void>(); |
||||
protected formGroup = this.formBuilder.group({ |
||||
password: ["", [Validators.required]], |
||||
}); |
||||
|
||||
@Input() loading: boolean; |
||||
@Output() setPasswordEvent = new EventEmitter<string>(); |
||||
|
||||
constructor(private formBuilder: FormBuilder) {} |
||||
|
||||
async ngOnInit() { |
||||
this.formGroup.controls.password.valueChanges |
||||
.pipe(takeUntil(this.destroy$)) |
||||
.subscribe((val) => { |
||||
this.setPasswordEvent.emit(val); |
||||
}); |
||||
} |
||||
|
||||
ngOnDestroy() { |
||||
this.destroy$.next(); |
||||
this.destroy$.complete(); |
||||
} |
||||
} |
||||
@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
|
||||
<bit-callout *ngIf="send.text.hidden" type="info">{{ "sendHiddenByDefault" | i18n }}</bit-callout> |
||||
<bit-form-field [formGroup]="formGroup"> |
||||
<textarea id="text" bitInput rows="8" name="Text" formControlName="sendText" readonly></textarea> |
||||
</bit-form-field> |
||||
<div class="tw-mb-3"> |
||||
<button |
||||
bitButton |
||||
type="button" |
||||
buttonType="secondary" |
||||
[block]="true" |
||||
(click)="toggleText()" |
||||
*ngIf="send.text.hidden" |
||||
> |
||||
<i |
||||
class="bwi bwi-lg" |
||||
aria-hidden="true" |
||||
[ngClass]="{ 'bwi-eye': !showText, 'bwi-eye-slash': showText }" |
||||
></i> |
||||
{{ "toggleVisibility" | i18n }} |
||||
</button> |
||||
</div> |
||||
<div class="tw-mb-3"> |
||||
<button bitButton type="button" buttonType="primary" [block]="true" (click)="copyText()"> |
||||
<i class="bwi bwi-clone" aria-hidden="true"></i> {{ "copyValue" | i18n }} |
||||
</button> |
||||
</div> |
||||
@ -0,0 +1,59 @@
@@ -0,0 +1,59 @@
|
||||
import { Component, Input } from "@angular/core"; |
||||
import { FormBuilder } from "@angular/forms"; |
||||
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; |
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; |
||||
import { SendAccessView } from "@bitwarden/common/tools/send/models/view/send-access.view"; |
||||
|
||||
import { SharedModule } from "../../shared"; |
||||
|
||||
@Component({ |
||||
selector: "app-send-access-text", |
||||
templateUrl: "send-access-text.component.html", |
||||
imports: [SharedModule], |
||||
standalone: true, |
||||
}) |
||||
export class SendAccessTextComponent { |
||||
private _send: SendAccessView = null; |
||||
protected showText = false; |
||||
|
||||
protected formGroup = this.formBuilder.group({ |
||||
sendText: [""], |
||||
}); |
||||
|
||||
constructor( |
||||
private i18nService: I18nService, |
||||
private platformUtilsService: PlatformUtilsService, |
||||
private formBuilder: FormBuilder |
||||
) {} |
||||
|
||||
get send(): SendAccessView { |
||||
return this._send; |
||||
} |
||||
|
||||
@Input() set send(value: SendAccessView) { |
||||
this._send = value; |
||||
this.showText = this.send.text != null ? !this.send.text.hidden : true; |
||||
|
||||
if (this.send == null || this.send.text == null) { |
||||
return; |
||||
} |
||||
|
||||
this.formGroup.controls.sendText.patchValue( |
||||
this.showText ? this.send.text.text : this.send.text.maskedText |
||||
); |
||||
} |
||||
|
||||
protected copyText() { |
||||
this.platformUtilsService.copyToClipboard(this.send.text.text); |
||||
this.platformUtilsService.showToast( |
||||
"success", |
||||
null, |
||||
this.i18nService.t("valueCopied", this.i18nService.t("sendTypeText")) |
||||
); |
||||
} |
||||
|
||||
protected toggleText() { |
||||
this.showText = !this.showText; |
||||
} |
||||
} |
||||
Loading…
Reference in new issue