1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-28 15:23:53 +00:00

Changes attachment modal to remove choose file button and changes upload button to close button if the user doesn't have edit rights to the cipher.

This commit is contained in:
Jackson Engstrom
2026-01-16 10:01:21 -08:00
parent 6397b4225a
commit 456d012d52
6 changed files with 80 additions and 51 deletions

View File

@@ -496,6 +496,7 @@ export class VaultV2Component<C extends CipherViewLike>
}
const dialogRef = AttachmentsV2Component.open(this.dialogService, {
cipherId: this.cipherId as CipherId,
canEditCipher: this.cipher().edit,
});
const result = await firstValueFrom(dialogRef.closed).catch((): any => null);
if (

View File

@@ -925,6 +925,7 @@ export class VaultComponent<C extends CipherViewLike> implements OnInit, OnDestr
const dialogRef = AttachmentsV2Component.open(this.dialogService, {
cipherId: cipher.id as CipherId,
organizationId: cipher.organizationId as OrganizationId,
canEditCipher: cipher.edit,
});
const result: AttachmentDialogCloseResult = await lastValueFrom(dialogRef.closed);

View File

@@ -38,14 +38,16 @@
</button>
}
</bit-item-action>
<bit-item-action>
<app-delete-attachment
[admin]="admin() && organization()?.canEditAllCiphers"
[cipherId]="cipher().id"
[attachment]="attachment"
(onDeletionSuccess)="removeAttachment(attachment)"
></app-delete-attachment>
</bit-item-action>
@if (cipher().edit) {
<bit-item-action>
<app-delete-attachment
[admin]="admin() && organization()?.canEditAllCiphers"
[cipherId]="cipher().id"
[attachment]="attachment"
(onDeletionSuccess)="removeAttachment(attachment)"
></app-delete-attachment>
</bit-item-action>
}
</ng-container>
</bit-item>
</li>
@@ -54,46 +56,48 @@
}
<form [id]="attachmentFormId" [formGroup]="attachmentForm" [bitSubmit]="submit">
<bit-card>
<label for="file" bitTypography="body2" class="tw-block tw-text-muted tw-px-1 tw-pb-1.5">
{{ "addAttachment" | i18n }}
</label>
<div class="tw-relative">
<!-- Input elements are notoriously difficult to style, --->
<!-- The native `<input>` will be used for screen readers -->
<!-- Visual & keyboard users will interact with the styled button element -->
<input
#fileInput
class="tw-sr-only"
type="file"
id="file"
name="file"
aria-describedby="fileHelp"
tabindex="-1"
required
(change)="onFileChange($event)"
/>
<div class="tw-flex tw-gap-2 tw-items-center" aria-hidden="true">
<button
bitButton
buttonType="secondary"
type="button"
(click)="fileInput.click()"
class="tw-whitespace-nowrap"
>
{{ "chooseFile" | i18n }}
</button>
<p bitTypography="body2" class="tw-text-muted tw-mb-0">
{{
this.attachmentForm.controls.file?.value
? this.attachmentForm.controls.file.value.name
: ("noFileChosen" | i18n)
}}
</p>
@if (cipher()?.edit) {
<bit-card>
<label for="file" bitTypography="body2" class="tw-block tw-text-muted tw-px-1 tw-pb-1.5">
{{ "addAttachment" | i18n }}
</label>
<div class="tw-relative">
<!-- Input elements are notoriously difficult to style, --->
<!-- The native `<input>` will be used for screen readers -->
<!-- Visual & keyboard users will interact with the styled button element -->
<input
#fileInput
class="tw-sr-only"
type="file"
id="file"
name="file"
aria-describedby="fileHelp"
tabindex="-1"
required
(change)="onFileChange($event)"
/>
<div class="tw-flex tw-gap-2 tw-items-center" aria-hidden="true">
<button
bitButton
buttonType="secondary"
type="button"
(click)="fileInput.click()"
class="tw-whitespace-nowrap"
>
{{ "chooseFile" | i18n }}
</button>
<p bitTypography="body2" class="tw-text-muted tw-mb-0">
{{
this.attachmentForm.controls.file?.value
? this.attachmentForm.controls.file.value.name
: ("noFileChosen" | i18n)
}}
</p>
</div>
</div>
</div>
<p id="fileHelp" bitTypography="helper" class="tw-text-muted tw-px-1 tw-pt-1 tw-mb-0">
{{ "maxFileSizeSansPunctuation" | i18n }}
</p>
</bit-card>
<p id="fileHelp" bitTypography="helper" class="tw-text-muted tw-px-1 tw-pt-1 tw-mb-0">
{{ "maxFileSizeSansPunctuation" | i18n }}
</p>
</bit-card>
}
</form>

View File

@@ -105,6 +105,8 @@ export class CipherAttachmentsComponent {
/** Emits after a file has been successfully removed */
readonly onRemoveSuccess = output<void>();
readonly onCloseButtonPress = output<void>();
protected readonly organization = signal<Organization | null>(null);
protected readonly cipher = signal<CipherView | null>(null);
@@ -154,7 +156,7 @@ export class CipherAttachmentsComponent {
// Update the initial state of the submit button
const btn = this.submitBtn();
if (btn) {
btn.disabled.set(!this.attachmentForm.valid);
btn.disabled.set(!this.attachmentForm.valid && this.cipher().edit);
}
});
@@ -192,6 +194,12 @@ export class CipherAttachmentsComponent {
/** Save the attachments to the cipher */
submit = async () => {
//user can't edit cipher and will close the bit-dialog
if (!this.cipher().edit) {
this.onCloseButtonPress.emit();
return;
}
this.onUploadStarted.emit();
const file = this.attachmentForm.value.file;

View File

@@ -13,11 +13,12 @@
(onUploadSuccess)="uploadSuccessful()"
(onUploadFailed)="uploadFailed()"
(onRemoveSuccess)="removalSuccessful()"
(onCloseButtonPress)="closeButtonPressed()"
></app-cipher-attachments>
</ng-container>
<ng-container bitDialogFooter>
<button bitButton type="submit" buttonType="primary" [attr.form]="attachmentFormId" #submitBtn>
{{ "upload" | i18n }}
{{ buttonText }}
</button>
</ng-container>
</bit-dialog>

View File

@@ -3,6 +3,7 @@
import { CommonModule } from "@angular/common";
import { Component, HostListener, Inject } from "@angular/core";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { CipherId, OrganizationId } from "@bitwarden/common/types/guid";
import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values";
import {
@@ -18,6 +19,7 @@ import { CipherAttachmentsComponent } from "../../cipher-form/components/attachm
export interface AttachmentsDialogParams {
cipherId: CipherId;
canEditCipher?: boolean;
admin?: boolean;
organizationId?: OrganizationId;
}
@@ -51,7 +53,9 @@ export class AttachmentsV2Component {
cipherId: CipherId;
admin: boolean = false;
organizationId?: OrganizationId;
canEditCipher: boolean;
attachmentFormId = CipherAttachmentsComponent.attachmentFormID;
buttonText: string;
private isUploading = false;
/**
@@ -62,10 +66,14 @@ export class AttachmentsV2Component {
constructor(
private dialogRef: DialogRef<AttachmentDialogCloseResult>,
@Inject(DIALOG_DATA) public params: AttachmentsDialogParams,
private i18nService: I18nService,
) {
this.cipherId = params.cipherId;
this.organizationId = params.organizationId;
this.admin = params.admin ?? false;
this.canEditCipher = params?.canEditCipher ?? false;
this.buttonText =
this.canEditCipher || this.admin ? this.i18nService.t("upload") : this.i18nService.t("close");
}
/**
@@ -140,4 +148,10 @@ export class AttachmentsV2Component {
action: AttachmentDialogResult.Removed,
});
}
closeButtonPressed() {
this.dialogRef.close({
action: AttachmentDialogResult.Closed,
});
}
}