1
0
mirror of https://github.com/bitwarden/web synced 2025-12-27 13:43:21 +00:00

Apply Prettier (#1347)

This commit is contained in:
Oscar Hinton
2021-12-17 15:57:11 +01:00
committed by GitHub
parent 2b0a9d995e
commit 56477eb39c
414 changed files with 33390 additions and 26857 deletions

View File

@@ -1,92 +1,152 @@
<form #form (ngSubmit)="load()" [appApiAction]="formPromise" class="container" ngNativeValidate>
<div class="row justify-content-center mt-5">
<div class="col-12">
<p class="lead text-center mb-4">Bitwarden Send</p>
</div>
<div class="col-12 text-center" *ngIf="creatorIdentifier != null">
<p>{{'sendCreatorIdentifier' | i18n: creatorIdentifier }}</p>
</div>
<div class="col-8" *ngIf="hideEmail">
<app-callout type="warning" title="{{'warning' | i18n}}">
{{'viewSendHiddenEmailWarning' | i18n }}
<a href="https://bitwarden.com/help/article/receive-send/" target="_blank">{{'learnMore' | i18n}}</a>.
</app-callout>
</div>
<div class="row justify-content-center mt-5">
<div class="col-12">
<p class="lead text-center mb-4">Bitwarden Send</p>
</div>
<div class="row justify-content-center">
<div class="col-5">
<div class="card d-block">
<div class="card-body" *ngIf="loading" class="text-center">
<i class="fa fa-spinner fa-spin fa-2x text-muted" title="{{'loading' | i18n}}"
aria-hidden="true"></i>
<span class="sr-only">{{'loading' | i18n}}</span>
</div>
<div class="card-body" *ngIf="!loading && passwordRequired">
<p>{{'sendProtectedPassword' | i18n}}</p>
<p>{{'sendProtectedPasswordDontKnow' | i18n}}</p>
<div class="form-group">
<label for="password">{{'password' | i18n}}</label>
<input id="password" type="password" name="Password" class="text-monospace form-control"
[(ngModel)]="password" required appInputVerbatim appAutofocus>
</div>
<div class="d-flex">
<button type="submit" class="btn btn-primary btn-block btn-submit" [disabled]="form.loading">
<span>
<i class="fa fa-sign-in" aria-hidden="true"></i> {{'continue' | i18n}}
</span>
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}" aria-hidden="true"></i>
</button>
</div>
</div>
<div class="card-body" *ngIf="!loading && unavailable">
{{'sendAccessUnavailable' | i18n}}
</div>
<div class="card-body" *ngIf="!loading && error">
{{'unexpectedError' | i18n}}
</div>
<div class="card-body" *ngIf="!loading && !passwordRequired && send">
<p class="text-center"><b>{{send.name}}</b></p>
<hr>
<!-- Text -->
<ng-container *ngIf="send.type === sendType.Text">
<app-callout *ngIf="send.text.hidden" type="tip">{{'sendHiddenByDefault' | i18n}}</app-callout>
<div class="form-group">
<textarea id="text" rows="8" name="Text" [(ngModel)]="sendText" class="form-control"
readonly></textarea>
</div>
<button class="btn btn-block btn-link" type="button" (click)="toggleText()"
*ngIf="send.text.hidden">
<i class="fa fa-lg" aria-hidden="true"
[ngClass]="{'fa-eye': !showText, 'fa-eye-slash': showText}"></i>
{{'toggleVisibility' | i18n}}
</button>
<button class="btn btn-block btn-link" type="button" (click)="copyText()">
<i class="fa fa-copy" aria-hidden="true"></i> {{'copyValue' | i18n}}
</button>
</ng-container>
<!-- File -->
<ng-container *ngIf="send.type === sendType.File">
<p>{{send.file.fileName}}</p>
<button class="btn btn-primary btn-block" type="button" (click)="download()" *ngIf="!downloading">
<i class="fa fa-download" aria-hidden="true"></i>
{{'downloadFile' | i18n}} ({{send.file.sizeName}})</button>
<button class="btn btn-primary btn-block" type="button" *ngIf="downloading" disabled="true">
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}" aria-hidden="true"></i>
</button>
</ng-container>
<p *ngIf="expirationDate" class="text-center text-muted">Expires:
{{expirationDate | date: 'medium'}}</p>
</div>
<div class="col-12 text-center" *ngIf="creatorIdentifier != null">
<p>{{ "sendCreatorIdentifier" | i18n: creatorIdentifier }}</p>
</div>
<div class="col-8" *ngIf="hideEmail">
<app-callout type="warning" title="{{ 'warning' | i18n }}">
{{ "viewSendHiddenEmailWarning" | i18n }}
<a href="https://bitwarden.com/help/article/receive-send/" target="_blank">{{
"learnMore" | i18n
}}</a
>.
</app-callout>
</div>
</div>
<div class="row justify-content-center">
<div class="col-5">
<div class="card d-block">
<div class="card-body" *ngIf="loading" class="text-center">
<i
class="fa fa-spinner fa-spin fa-2x text-muted"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ "loading" | i18n }}</span>
</div>
<div class="card-body" *ngIf="!loading && passwordRequired">
<p>{{ "sendProtectedPassword" | i18n }}</p>
<p>{{ "sendProtectedPasswordDontKnow" | i18n }}</p>
<div class="form-group">
<label for="password">{{ "password" | i18n }}</label>
<input
id="password"
type="password"
name="Password"
class="text-monospace form-control"
[(ngModel)]="password"
required
appInputVerbatim
appAutofocus
/>
</div>
<div class="d-flex">
<button
type="submit"
class="btn btn-primary btn-block btn-submit"
[disabled]="form.loading"
>
<span>
<i class="fa fa-sign-in" aria-hidden="true"></i> {{ "continue" | i18n }}
</span>
<i
class="fa fa-spinner fa-spin"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
</button>
</div>
</div>
<div class="card-body" *ngIf="!loading && unavailable">
{{ "sendAccessUnavailable" | i18n }}
</div>
<div class="card-body" *ngIf="!loading && error">
{{ "unexpectedError" | i18n }}
</div>
<div class="card-body" *ngIf="!loading && !passwordRequired && send">
<p class="text-center">
<b>{{ send.name }}</b>
</p>
<hr />
<!-- Text -->
<ng-container *ngIf="send.type === sendType.Text">
<app-callout *ngIf="send.text.hidden" type="tip">{{
"sendHiddenByDefault" | i18n
}}</app-callout>
<div class="form-group">
<textarea
id="text"
rows="8"
name="Text"
[(ngModel)]="sendText"
class="form-control"
readonly
></textarea>
</div>
<button
class="btn btn-block btn-link"
type="button"
(click)="toggleText()"
*ngIf="send.text.hidden"
>
<i
class="fa fa-lg"
aria-hidden="true"
[ngClass]="{ 'fa-eye': !showText, 'fa-eye-slash': showText }"
></i>
{{ "toggleVisibility" | i18n }}
</button>
<button class="btn btn-block btn-link" type="button" (click)="copyText()">
<i class="fa fa-copy" aria-hidden="true"></i> {{ "copyValue" | i18n }}
</button>
</ng-container>
<!-- File -->
<ng-container *ngIf="send.type === sendType.File">
<p>{{ send.file.fileName }}</p>
<button
class="btn btn-primary btn-block"
type="button"
(click)="download()"
*ngIf="!downloading"
>
<i class="fa fa-download" aria-hidden="true"></i>
{{ "downloadFile" | i18n }} ({{ send.file.sizeName }})
</button>
<button
class="btn btn-primary btn-block"
type="button"
*ngIf="downloading"
disabled="true"
>
<i
class="fa fa-spinner fa-spin"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
</button>
</ng-container>
<p *ngIf="expirationDate" class="text-center text-muted">
Expires: {{ expirationDate | date: "medium" }}
</p>
</div>
<div class="col-12 text-center mt-5 text-muted">
<p class="mb-0">{{'sendAccessTaglineProductDesc' | i18n}}<br>
{{'sendAccessTaglineLearnMore' | i18n}} <a
href="https://www.bitwarden.com/products/send?source=web-vault" target="_blank">Bitwarden Send</a>
{{'sendAccessTaglineOr' | i18n}} <a
href="https://vault.bitwarden.com/#/register" target="_blank">{{'sendAccessTaglineSignUp' | i18n}}</a>
{{'sendAccessTaglineTryToday' | i18n}}
</p>
</div>
</div>
</div>
<div class="col-12 text-center mt-5 text-muted">
<p class="mb-0">
{{ "sendAccessTaglineProductDesc" | i18n }}<br />
{{ "sendAccessTaglineLearnMore" | i18n }}
<a href="https://www.bitwarden.com/products/send?source=web-vault" target="_blank"
>Bitwarden Send</a
>
{{ "sendAccessTaglineOr" | i18n }}
<a href="https://vault.bitwarden.com/#/register" target="_blank">{{
"sendAccessTaglineSignUp" | i18n
}}</a>
{{ "sendAccessTaglineTryToday" | i18n }}
</p>
</div>
</div>
</form>

View File

@@ -1,169 +1,184 @@
import {
Component,
OnInit,
} from '@angular/core';
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from '@angular/router';
import { ActivatedRoute } from "@angular/router";
import { ApiService } from 'jslib-common/abstractions/api.service';
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
import { CryptoFunctionService } from 'jslib-common/abstractions/cryptoFunction.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { ApiService } from "jslib-common/abstractions/api.service";
import { CryptoService } from "jslib-common/abstractions/crypto.service";
import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { Utils } from 'jslib-common/misc/utils';
import { Utils } from "jslib-common/misc/utils";
import { SendAccess } from 'jslib-common/models/domain/sendAccess';
import { SymmetricCryptoKey } from 'jslib-common/models/domain/symmetricCryptoKey';
import { SendAccess } from "jslib-common/models/domain/sendAccess";
import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey";
import { SendAccessView } from 'jslib-common/models/view/sendAccessView';
import { SendAccessView } from "jslib-common/models/view/sendAccessView";
import { SendType } from 'jslib-common/enums/sendType';
import { SendAccessRequest } from 'jslib-common/models/request/sendAccessRequest';
import { ErrorResponse } from 'jslib-common/models/response/errorResponse';
import { SendType } from "jslib-common/enums/sendType";
import { SendAccessRequest } from "jslib-common/models/request/sendAccessRequest";
import { ErrorResponse } from "jslib-common/models/response/errorResponse";
import { SendAccessResponse } from 'jslib-common/models/response/sendAccessResponse';
import { SendAccessResponse } from "jslib-common/models/response/sendAccessResponse";
@Component({
selector: 'app-send-access',
templateUrl: 'access.component.html',
selector: "app-send-access",
templateUrl: "access.component.html",
})
export class AccessComponent implements OnInit {
send: SendAccessView;
sendType = SendType;
downloading = false;
loading = true;
passwordRequired = false;
formPromise: Promise<SendAccessResponse>;
password: string;
showText = false;
unavailable = false;
error = false;
hideEmail = false;
send: SendAccessView;
sendType = SendType;
downloading = false;
loading = true;
passwordRequired = false;
formPromise: Promise<SendAccessResponse>;
password: string;
showText = false;
unavailable = false;
error = false;
hideEmail = false;
private id: string;
private key: string;
private decKey: SymmetricCryptoKey;
private accessRequest: SendAccessRequest;
private id: string;
private key: string;
private decKey: SymmetricCryptoKey;
private accessRequest: SendAccessRequest;
constructor(private i18nService: I18nService, private cryptoFunctionService: CryptoFunctionService,
private apiService: ApiService, private platformUtilsService: PlatformUtilsService,
private route: ActivatedRoute, private cryptoService: CryptoService) {
constructor(
private i18nService: I18nService,
private cryptoFunctionService: CryptoFunctionService,
private apiService: ApiService,
private platformUtilsService: PlatformUtilsService,
private route: ActivatedRoute,
private cryptoService: CryptoService
) {}
get sendText() {
if (this.send == null || this.send.text == null) {
return null;
}
return this.showText ? this.send.text.text : this.send.text.maskedText;
}
get expirationDate() {
if (this.send == null || this.send.expirationDate == null) {
return null;
}
return this.send.expirationDate;
}
get creatorIdentifier() {
if (this.send == null || this.send.creatorIdentifier == null) {
return null;
}
return this.send.creatorIdentifier;
}
ngOnInit() {
this.route.params.subscribe(async (params) => {
this.id = params.sendId;
this.key = params.key;
if (this.key == null || this.id == null) {
return;
}
await this.load();
});
}
async download() {
if (this.send == null || this.decKey == null) {
return;
}
get sendText() {
if (this.send == null || this.send.text == null) {
return null;
}
return this.showText ? this.send.text.text : this.send.text.maskedText;
if (this.downloading) {
return;
}
get expirationDate() {
if (this.send == null || this.send.expirationDate == null) {
return null;
}
return this.send.expirationDate;
const downloadData = await this.apiService.getSendFileDownloadData(
this.send,
this.accessRequest
);
if (Utils.isNullOrWhitespace(downloadData.url)) {
this.platformUtilsService.showToast("error", null, this.i18nService.t("missingSendFile"));
return;
}
get creatorIdentifier() {
if (this.send == null || this.send.creatorIdentifier == null) {
return null;
}
return this.send.creatorIdentifier;
this.downloading = true;
const response = await fetch(new Request(downloadData.url, { cache: "no-store" }));
if (response.status !== 200) {
this.platformUtilsService.showToast("error", null, this.i18nService.t("errorOccurred"));
this.downloading = false;
return;
}
ngOnInit() {
this.route.params.subscribe(async params => {
this.id = params.sendId;
this.key = params.key;
if (this.key == null || this.id == null) {
return;
}
await this.load();
});
try {
const buf = await response.arrayBuffer();
const decBuf = await this.cryptoService.decryptFromBytes(buf, this.decKey);
this.platformUtilsService.saveFile(window, decBuf, null, this.send.file.fileName);
} catch (e) {
this.platformUtilsService.showToast("error", null, this.i18nService.t("errorOccurred"));
}
async download() {
if (this.send == null || this.decKey == null) {
return;
}
this.downloading = false;
}
if (this.downloading) {
return;
}
copyText() {
this.platformUtilsService.copyToClipboard(this.send.text.text);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("valueCopied", this.i18nService.t("sendTypeText"))
);
}
toggleText() {
this.showText = !this.showText;
}
const downloadData = await this.apiService.getSendFileDownloadData(this.send, this.accessRequest);
if (Utils.isNullOrWhitespace(downloadData.url)) {
this.platformUtilsService.showToast('error', null, this.i18nService.t('missingSendFile'));
return;
}
this.downloading = true;
const response = await fetch(new Request(downloadData.url, { cache: 'no-store' }));
if (response.status !== 200) {
this.platformUtilsService.showToast('error', null, this.i18nService.t('errorOccurred'));
this.downloading = false;
return;
}
try {
const buf = await response.arrayBuffer();
const decBuf = await this.cryptoService.decryptFromBytes(buf, this.decKey);
this.platformUtilsService.saveFile(window, decBuf, null, this.send.file.fileName);
} catch (e) {
this.platformUtilsService.showToast('error', null, this.i18nService.t('errorOccurred'));
}
this.downloading = false;
async load() {
this.unavailable = false;
this.error = false;
this.hideEmail = false;
const keyArray = Utils.fromUrlB64ToArray(this.key);
this.accessRequest = new SendAccessRequest();
if (this.password != null) {
const passwordHash = await this.cryptoFunctionService.pbkdf2(
this.password,
keyArray,
"sha256",
100000
);
this.accessRequest.password = Utils.fromBufferToB64(passwordHash);
}
copyText() {
this.platformUtilsService.copyToClipboard(this.send.text.text);
this.platformUtilsService.showToast('success', null,
this.i18nService.t('valueCopied', this.i18nService.t('sendTypeText')));
}
toggleText() {
this.showText = !this.showText;
}
async load() {
this.unavailable = false;
this.error = false;
this.hideEmail = false;
const keyArray = Utils.fromUrlB64ToArray(this.key);
this.accessRequest = new SendAccessRequest();
if (this.password != null) {
const passwordHash = await this.cryptoFunctionService.pbkdf2(this.password, keyArray, 'sha256', 100000);
this.accessRequest.password = Utils.fromBufferToB64(passwordHash);
try {
let sendResponse: SendAccessResponse = null;
if (this.loading) {
sendResponse = await this.apiService.postSendAccess(this.id, this.accessRequest);
} else {
this.formPromise = this.apiService.postSendAccess(this.id, this.accessRequest);
sendResponse = await this.formPromise;
}
this.passwordRequired = false;
const sendAccess = new SendAccess(sendResponse);
this.decKey = await this.cryptoService.makeSendKey(keyArray);
this.send = await sendAccess.decrypt(this.decKey);
this.showText = this.send.text != null ? !this.send.text.hidden : true;
} catch (e) {
if (e instanceof ErrorResponse) {
if (e.statusCode === 401) {
this.passwordRequired = true;
} else if (e.statusCode === 404) {
this.unavailable = true;
} else {
this.error = true;
}
try {
let sendResponse: SendAccessResponse = null;
if (this.loading) {
sendResponse = await this.apiService.postSendAccess(this.id, this.accessRequest);
} else {
this.formPromise = this.apiService.postSendAccess(this.id, this.accessRequest);
sendResponse = await this.formPromise;
}
this.passwordRequired = false;
const sendAccess = new SendAccess(sendResponse);
this.decKey = await this.cryptoService.makeSendKey(keyArray);
this.send = await sendAccess.decrypt(this.decKey);
this.showText = this.send.text != null ? !this.send.text.hidden : true;
} catch (e) {
if (e instanceof ErrorResponse) {
if (e.statusCode === 401) {
this.passwordRequired = true;
} else if (e.statusCode === 404) {
this.unavailable = true;
} else {
this.error = true;
}
}
}
this.loading = false;
this.hideEmail = this.creatorIdentifier == null && !this.passwordRequired && !this.loading && !this.unavailable;
}
}
this.loading = false;
this.hideEmail =
this.creatorIdentifier == null &&
!this.passwordRequired &&
!this.loading &&
!this.unavailable;
}
}

View File

@@ -1,176 +1,308 @@
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="sendAddEditTitle">
<div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
<form class="modal-content" #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate
autocomplete="off">
<div class="modal-header">
<h2 class="modal-title" id="sendAddEditTitle">{{title}}</h2>
<button type="button" class="close" data-dismiss="modal" appA11yTitle="{{'close' | i18n}}">
<span aria-hidden="true">&times;</span>
</button>
<div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
<form
class="modal-content"
#form
(ngSubmit)="submit()"
[appApiAction]="formPromise"
ngNativeValidate
autocomplete="off"
>
<div class="modal-header">
<h2 class="modal-title" id="sendAddEditTitle">{{ title }}</h2>
<button
type="button"
class="close"
data-dismiss="modal"
appA11yTitle="{{ 'close' | i18n }}"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body" *ngIf="send">
<app-callout *ngIf="disableSend">
<span>{{ "sendDisabledWarning" | i18n }}</span>
</app-callout>
<app-callout *ngIf="!disableSend && disableHideEmail">
<span>{{ "sendOptionsPolicyInEffect" | i18n }}</span>
<ul class="mb-0">
<li>{{ "sendDisableHideEmailInEffect" | i18n }}</li>
</ul>
</app-callout>
<div class="row">
<div class="col-6 form-group">
<label for="name">{{ "name" | i18n }}</label>
<input
id="name"
class="form-control"
type="text"
name="Name"
[(ngModel)]="send.name"
required
[readOnly]="disableSend"
/>
<small class="form-text text-muted">{{ "sendNameDesc" | i18n }}</small>
</div>
</div>
<div class="row" *ngIf="!editMode">
<div class="col-6 form-group">
<label>{{ "whatTypeOfSend" | i18n }}</label>
<div class="form-check" *ngFor="let o of typeOptions">
<input
class="form-check-input"
type="radio"
[(ngModel)]="send.type"
name="Type_{{ o.value }}"
id="type_{{ o.value }}"
[value]="o.value"
(change)="typeChanged(o)"
[checked]="send.type === o.value"
/>
<label class="form-check-label" for="type_{{ o.value }}">
{{ o.name }}
</label>
</div>
<div class="modal-body" *ngIf="send">
<app-callout *ngIf="disableSend">
<span>{{'sendDisabledWarning' | i18n}}</span>
</app-callout>
<app-callout *ngIf="!disableSend && disableHideEmail">
<span>{{'sendOptionsPolicyInEffect' | i18n}}</span>
<ul class="mb-0">
<li>{{'sendDisableHideEmailInEffect' | i18n}}</li>
</ul>
</app-callout>
<div class="row">
<div class="col-6 form-group">
<label for="name">{{'name' | i18n}}</label>
<input id="name" class="form-control" type="text" name="Name" [(ngModel)]="send.name" required
[readOnly]="disableSend">
<small class="form-text text-muted">{{'sendNameDesc' | i18n}}</small>
</div>
</div>
<div class="row" *ngIf="!editMode">
<div class="col-6 form-group">
<label>{{'whatTypeOfSend' | i18n}}</label>
<div class="form-check" *ngFor="let o of typeOptions">
<input class="form-check-input" type="radio" [(ngModel)]="send.type" name="Type_{{o.value}}"
id="type_{{o.value}}" [value]="o.value" (change)="typeChanged(o)"
[checked]="send.type === o.value">
<label class="form-check-label" for="type_{{o.value}}">
{{o.name}}
</label>
</div>
</div>
</div>
<!-- Text -->
<ng-container *ngIf="send.type === sendType.Text">
<div class="form-group">
<label for="text">{{'sendTypeText' | i18n}}</label>
<textarea id="text" name="Text.Text" rows="6" [(ngModel)]="send.text.text" class="form-control"
[readOnly]="disableSend"></textarea>
<small class="form-text text-muted">{{'sendTextDesc' | i18n}}</small>
</div>
<div class="form-group">
<div class="form-check">
<input class="form-check-input" type="checkbox" [(ngModel)]="send.text.hidden"
id="text-hidden" name="Text.Hidden" [disabled]="disableSend">
<label class="form-check-label" for="text-hidden">{{'textHiddenByDefault' | i18n}}</label>
</div>
</div>
</ng-container>
<!-- File -->
<ng-container *ngIf="send.type === sendType.File">
<div class="form-group">
<div *ngIf="editMode">
<strong class="d-block">{{'file' | i18n}}</strong>
{{send.file.fileName}} ({{send.file.sizeName}})
</div>
<div *ngIf="!editMode">
<label for="file">{{'file' | i18n}}</label>
<input type="file" id="file" class="form-control-file" name="file" required
[disabled]="disableSend">
<small class="form-text text-muted">{{'sendFileDesc' | i18n}} {{'maxFileSize' |
i18n}}</small>
</div>
</div>
</ng-container>
<h3 class="mt-5">{{'share' | i18n}}</h3>
<div class="form-group" *ngIf="link">
<label for="link">{{'sendLinkLabel' | i18n}}</label>
<input type="text" readonly id="link" name="Link" [(ngModel)]="link" class="form-control">
</div>
<div class="form-group">
<div class="form-check">
<input class="form-check-input" type="checkbox" [(ngModel)]="copyLink" id="copy-link"
name="CopyLink">
<label class="form-check-label" for="copy-link">{{'copySendLinkOnSave' | i18n}}</label>
</div>
</div>
<div id="options-header" class="section-header d-flex flex-row align-items-center mt-5"
(click)="toggleOptions()">
<h3 class="mb-0 mr-2">{{'options' | i18n}}</h3>
<a class="mb-1" href="#" appStopClick role="button">
<i class="fa" aria-hidden="true"
[ngClass]="{'fa-chevron-down': !showOptions, 'fa-chevron-up': showOptions}"></i>
</a>
</div>
<div id="options" [hidden]="!showOptions">
<app-send-efflux-dates
[initialDeletionDate]="send.deletionDate" [initialExpirationDate]="send.expirationDate"
[editMode]="editMode" [disabled]="disableSend" (datesChanged)="setDates($event)">
</app-send-efflux-dates>
<div class="row">
<div class="col-6 form-group">
<label for="maxAccessCount">{{'maxAccessCount' | i18n}}</label>
<input id="maxAccessCount" class="form-control" type="number" name="MaxAccessCount"
[(ngModel)]="send.maxAccessCount" min="1" [readOnly]="disableSend">
<div class="form-text text-muted small">{{'maxAccessCountDesc' | i18n}}</div>
</div>
<div class="col-6 form-group" *ngIf="editMode">
<label for="accessCount">{{'currentAccessCount' | i18n}}</label>
<input id="accessCount" class="form-control" type="text" name="AccessCount" readonly
[(ngModel)]="send.accessCount">
</div>
</div>
<div class="row">
<div class="col-6 form-group">
<label for="password" *ngIf="!hasPassword">{{'password' | i18n}}</label>
<label for="password" *ngIf="hasPassword">{{'newPassword' | i18n}}</label>
<div class="input-group">
<input id="password" class="form-control text-monospace"
type="{{showPassword ? 'text' : 'password'}}" name="Password" [(ngModel)]="password"
[readOnly]="disableSend">
<div class="input-group-append">
<button type="button" class="btn btn-outline-secondary"
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePasswordVisible()">
<i class="fa fa-lg" aria-hidden="true"
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
</button>
</div>
</div>
<div class="form-text text-muted small">{{'sendPasswordDesc' | i18n}}</div>
</div>
</div>
<div class="form-group">
<label for="notes">{{'notes' | i18n}}</label>
<textarea id="notes" name="Notes" rows="6" [(ngModel)]="send.notes" class="form-control"
[readOnly]="disableSend"></textarea>
<div class="form-text text-muted small">{{'sendNotesDesc' | i18n}}</div>
</div>
<div class="form-group">
<div class="form-check">
<input class="form-check-input" type="checkbox" [(ngModel)]="send.hideEmail" id="hideEmail"
name="HideEmail" [disabled]="(disableHideEmail && !send.hideEmail) || disableSend">
<label class="form-check-label" for="hideEmail">
{{'hideEmail' | i18n}}
</label>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input class="form-check-input" type="checkbox" [(ngModel)]="send.disabled" id="disabled"
name="Disabled" [disabled]="disableSend">
<label class="form-check-label" for="disabled">{{'disableThisSend' | i18n}}</label>
</div>
</div>
</div>
</div>
</div>
<!-- Text -->
<ng-container *ngIf="send.type === sendType.Text">
<div class="form-group">
<label for="text">{{ "sendTypeText" | i18n }}</label>
<textarea
id="text"
name="Text.Text"
rows="6"
[(ngModel)]="send.text.text"
class="form-control"
[readOnly]="disableSend"
></textarea>
<small class="form-text text-muted">{{ "sendTextDesc" | i18n }}</small>
</div>
<div class="form-group">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
[(ngModel)]="send.text.hidden"
id="text-hidden"
name="Text.Hidden"
[disabled]="disableSend"
/>
<label class="form-check-label" for="text-hidden">{{
"textHiddenByDefault" | i18n
}}</label>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary btn-submit manual" [ngClass]="{loading:form.loading}"
[disabled]="form.loading || disableSend">
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}" aria-hidden="true"></i>
<span>{{'save' | i18n}}</span>
</button>
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
{{'cancel' | i18n}}
</button>
<div class="ml-auto" *ngIf="send">
<button #deleteBtn type="button" (click)="delete()" class="btn btn-outline-danger"
appA11yTitle="{{'delete' | i18n}}" *ngIf="editMode" [disabled]="deleteBtn.loading"
[appApiAction]="deletePromise">
<i class="fa fa-trash-o fa-lg fa-fw" [hidden]="deleteBtn.loading" aria-hidden="true"></i>
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!deleteBtn.loading"
title="{{'loading' | i18n}}" aria-hidden="true"></i>
</button>
</div>
</div>
</ng-container>
<!-- File -->
<ng-container *ngIf="send.type === sendType.File">
<div class="form-group">
<div *ngIf="editMode">
<strong class="d-block">{{ "file" | i18n }}</strong>
{{ send.file.fileName }} ({{ send.file.sizeName }})
</div>
</form>
</div>
<div *ngIf="!editMode">
<label for="file">{{ "file" | i18n }}</label>
<input
type="file"
id="file"
class="form-control-file"
name="file"
required
[disabled]="disableSend"
/>
<small class="form-text text-muted"
>{{ "sendFileDesc" | i18n }} {{ "maxFileSize" | i18n }}</small
>
</div>
</div>
</ng-container>
<h3 class="mt-5">{{ "share" | i18n }}</h3>
<div class="form-group" *ngIf="link">
<label for="link">{{ "sendLinkLabel" | i18n }}</label>
<input
type="text"
readonly
id="link"
name="Link"
[(ngModel)]="link"
class="form-control"
/>
</div>
<div class="form-group">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
[(ngModel)]="copyLink"
id="copy-link"
name="CopyLink"
/>
<label class="form-check-label" for="copy-link">{{
"copySendLinkOnSave" | i18n
}}</label>
</div>
</div>
<div
id="options-header"
class="section-header d-flex flex-row align-items-center mt-5"
(click)="toggleOptions()"
>
<h3 class="mb-0 mr-2">{{ "options" | i18n }}</h3>
<a class="mb-1" href="#" appStopClick role="button">
<i
class="fa"
aria-hidden="true"
[ngClass]="{ 'fa-chevron-down': !showOptions, 'fa-chevron-up': showOptions }"
></i>
</a>
</div>
<div id="options" [hidden]="!showOptions">
<app-send-efflux-dates
[initialDeletionDate]="send.deletionDate"
[initialExpirationDate]="send.expirationDate"
[editMode]="editMode"
[disabled]="disableSend"
(datesChanged)="setDates($event)"
>
</app-send-efflux-dates>
<div class="row">
<div class="col-6 form-group">
<label for="maxAccessCount">{{ "maxAccessCount" | i18n }}</label>
<input
id="maxAccessCount"
class="form-control"
type="number"
name="MaxAccessCount"
[(ngModel)]="send.maxAccessCount"
min="1"
[readOnly]="disableSend"
/>
<div class="form-text text-muted small">{{ "maxAccessCountDesc" | i18n }}</div>
</div>
<div class="col-6 form-group" *ngIf="editMode">
<label for="accessCount">{{ "currentAccessCount" | i18n }}</label>
<input
id="accessCount"
class="form-control"
type="text"
name="AccessCount"
readonly
[(ngModel)]="send.accessCount"
/>
</div>
</div>
<div class="row">
<div class="col-6 form-group">
<label for="password" *ngIf="!hasPassword">{{ "password" | i18n }}</label>
<label for="password" *ngIf="hasPassword">{{ "newPassword" | i18n }}</label>
<div class="input-group">
<input
id="password"
class="form-control text-monospace"
type="{{ showPassword ? 'text' : 'password' }}"
name="Password"
[(ngModel)]="password"
[readOnly]="disableSend"
/>
<div class="input-group-append">
<button
type="button"
class="btn btn-outline-secondary"
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
(click)="togglePasswordVisible()"
>
<i
class="fa fa-lg"
aria-hidden="true"
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
></i>
</button>
</div>
</div>
<div class="form-text text-muted small">{{ "sendPasswordDesc" | i18n }}</div>
</div>
</div>
<div class="form-group">
<label for="notes">{{ "notes" | i18n }}</label>
<textarea
id="notes"
name="Notes"
rows="6"
[(ngModel)]="send.notes"
class="form-control"
[readOnly]="disableSend"
></textarea>
<div class="form-text text-muted small">{{ "sendNotesDesc" | i18n }}</div>
</div>
<div class="form-group">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
[(ngModel)]="send.hideEmail"
id="hideEmail"
name="HideEmail"
[disabled]="(disableHideEmail && !send.hideEmail) || disableSend"
/>
<label class="form-check-label" for="hideEmail">
{{ "hideEmail" | i18n }}
</label>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
[(ngModel)]="send.disabled"
id="disabled"
name="Disabled"
[disabled]="disableSend"
/>
<label class="form-check-label" for="disabled">{{ "disableThisSend" | i18n }}</label>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button
type="submit"
class="btn btn-primary btn-submit manual"
[ngClass]="{ loading: form.loading }"
[disabled]="form.loading || disableSend"
>
<i class="fa fa-spinner fa-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
<span>{{ "save" | i18n }}</span>
</button>
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
{{ "cancel" | i18n }}
</button>
<div class="ml-auto" *ngIf="send">
<button
#deleteBtn
type="button"
(click)="delete()"
class="btn btn-outline-danger"
appA11yTitle="{{ 'delete' | i18n }}"
*ngIf="editMode"
[disabled]="deleteBtn.loading"
[appApiAction]="deletePromise"
>
<i
class="fa fa-trash-o fa-lg fa-fw"
[hidden]="deleteBtn.loading"
aria-hidden="true"
></i>
<i
class="fa fa-spinner fa-spin fa-lg fa-fw"
[hidden]="!deleteBtn.loading"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
</button>
</div>
</div>
</form>
</div>
</div>

View File

@@ -1,37 +1,52 @@
import { DatePipe } from '@angular/common';
import { DatePipe } from "@angular/common";
import { Component } from '@angular/core';
import { Component } from "@angular/core";
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { LogService } from 'jslib-common/abstractions/log.service';
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { PolicyService } from 'jslib-common/abstractions/policy.service';
import { SendService } from 'jslib-common/abstractions/send.service';
import { StateService } from 'jslib-common/abstractions/state.service';
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.service";
import { MessagingService } from "jslib-common/abstractions/messaging.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { PolicyService } from "jslib-common/abstractions/policy.service";
import { SendService } from "jslib-common/abstractions/send.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { AddEditComponent as BaseAddEditComponent } from 'jslib-angular/components/send/add-edit.component';
import { AddEditComponent as BaseAddEditComponent } from "jslib-angular/components/send/add-edit.component";
@Component({
selector: 'app-send-add-edit',
templateUrl: 'add-edit.component.html',
selector: "app-send-add-edit",
templateUrl: "add-edit.component.html",
})
export class AddEditComponent extends BaseAddEditComponent {
constructor(i18nService: I18nService, platformUtilsService: PlatformUtilsService,
environmentService: EnvironmentService, datePipe: DatePipe,
sendService: SendService, stateService: StateService,
messagingService: MessagingService, policyService: PolicyService,
logService: LogService) {
super(i18nService, platformUtilsService, environmentService, datePipe, sendService,
messagingService, policyService, logService, stateService);
}
constructor(
i18nService: I18nService,
platformUtilsService: PlatformUtilsService,
environmentService: EnvironmentService,
datePipe: DatePipe,
sendService: SendService,
stateService: StateService,
messagingService: MessagingService,
policyService: PolicyService,
logService: LogService
) {
super(
i18nService,
platformUtilsService,
environmentService,
datePipe,
sendService,
messagingService,
policyService,
logService,
stateService
);
}
async copyLinkToClipboard(link: string): Promise<void | boolean> {
// 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);
});
}
async copyLinkToClipboard(link: string): Promise<void | boolean> {
// 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);
});
}
}

View File

@@ -1,103 +1,191 @@
<div class="row" [formGroup]="datesForm">
<div class="col-6 form-group">
<label for="deletionDate">{{'deletionDate' | i18n}}</label>
<ng-template #deletionDateCustom>
<ng-container [ngSwitch]="browserPath">
<ng-container *ngSwitchCase="'firefox'">
<div class="d-flex justify-content-around">
<input id="deletionDateCustomFallback" class="form-control mt-1" type="date"
name="DeletionDateFallback" formControlName="fallbackDeletionDate" required
placeholder="MM/DD/YYYY" [readOnly]="disableSend" data-date-format="mm/dd/yyyy">
<input id="deletionTimeCustomFallback" class="form-control mt-1 ml-1" type="time"
name="DeletionTimeDate" formControlName="fallbackDeletionTime" required
placeholder="HH:MM AM/PM" [readOnly]="disableSend">
</div>
</ng-container>
<ng-container *ngSwitchCase="'safari'">
<div class="d-flex justify-content-around">
<input id="deletionDateCustomFallback" class="form-control mt-1" type="date"
name="DeletionDateFallback" formControlName="fallbackDeletionDate" required
placeholder="MM/DD/YYYY" [readOnly]="disableSend" data-date-format="mm/dd/yyyy">
<select id="deletionTimeCustomFallback" class="form-control mt-1 ml-1" [required]="!editMode"
formControlName="fallbackDeletionTime" name="SafariDeletionTime">
<option *ngFor="let o of safariDeletionTimePresetOptions" [ngValue]="o.twentyFourHour">{{o.twelveHour}}</option>
</select>
</div>
</ng-container>
<ng-container *ngSwitchDefault>
<input id="deletionDateCustom" class="form-control mt-1" type="datetime-local"
name="DeletionDate" formControlName="defaultDeletionDateTime" required
placeholder="MM/DD/YYYY HH:MM AM/PM" [readOnly]="disabled">
</ng-container>
</ng-container>
</ng-template>
<div *ngIf="!editMode">
<select id="deletionDate" name="SelectedDeletionDatePreset"
formControlName="selectedDeletionDatePreset" class="form-control" required>
<option *ngFor="let o of deletionDatePresets" [ngValue]="o.value">{{o.name}}
</option>
<div class="col-6 form-group">
<label for="deletionDate">{{ "deletionDate" | i18n }}</label>
<ng-template #deletionDateCustom>
<ng-container [ngSwitch]="browserPath">
<ng-container *ngSwitchCase="'firefox'">
<div class="d-flex justify-content-around">
<input
id="deletionDateCustomFallback"
class="form-control mt-1"
type="date"
name="DeletionDateFallback"
formControlName="fallbackDeletionDate"
required
placeholder="MM/DD/YYYY"
[readOnly]="disableSend"
data-date-format="mm/dd/yyyy"
/>
<input
id="deletionTimeCustomFallback"
class="form-control mt-1 ml-1"
type="time"
name="DeletionTimeDate"
formControlName="fallbackDeletionTime"
required
placeholder="HH:MM AM/PM"
[readOnly]="disableSend"
/>
</div>
</ng-container>
<ng-container *ngSwitchCase="'safari'">
<div class="d-flex justify-content-around">
<input
id="deletionDateCustomFallback"
class="form-control mt-1"
type="date"
name="DeletionDateFallback"
formControlName="fallbackDeletionDate"
required
placeholder="MM/DD/YYYY"
[readOnly]="disableSend"
data-date-format="mm/dd/yyyy"
/>
<select
id="deletionTimeCustomFallback"
class="form-control mt-1 ml-1"
[required]="!editMode"
formControlName="fallbackDeletionTime"
name="SafariDeletionTime"
>
<option
*ngFor="let o of safariDeletionTimePresetOptions"
[ngValue]="o.twentyFourHour"
>
{{ o.twelveHour }}
</option>
</select>
<ng-container *ngIf="selectedDeletionDatePreset.value === 0">
<ng-container *ngTemplateOutlet="deletionDateCustom">
</ng-container>
</ng-container>
</div>
<div *ngIf="editMode">
<ng-container *ngTemplateOutlet="deletionDateCustom">
</ng-container>
</div>
<div class="form-text text-muted small">{{'deletionDateDesc' | i18n}}</div>
</div>
</ng-container>
<ng-container *ngSwitchDefault>
<input
id="deletionDateCustom"
class="form-control mt-1"
type="datetime-local"
name="DeletionDate"
formControlName="defaultDeletionDateTime"
required
placeholder="MM/DD/YYYY HH:MM AM/PM"
[readOnly]="disabled"
/>
</ng-container>
</ng-container>
</ng-template>
<div *ngIf="!editMode">
<select
id="deletionDate"
name="SelectedDeletionDatePreset"
formControlName="selectedDeletionDatePreset"
class="form-control"
required
>
<option *ngFor="let o of deletionDatePresets" [ngValue]="o.value">{{ o.name }}</option>
</select>
<ng-container *ngIf="selectedDeletionDatePreset.value === 0">
<ng-container *ngTemplateOutlet="deletionDateCustom"> </ng-container>
</ng-container>
</div>
<div class="col-6 form-group">
<div class="d-flex">
<label for="expirationDate">{{'expirationDate' | i18n}}</label>
<a href="#" appStopClick (click)="clearExpiration()" class="ml-auto"
*ngIf="editMode && !disabled">
{{'clear' | i18n}}
</a>
</div>
<ng-template #expirationDateCustom>
<ng-container [ngSwitch]="browserPath">
<div *ngSwitchCase="'firefox'" class="d-flex justify-content-around">
<input id="expirationDateCustomFallback" class="form-control mt-1" type="date"
name="ExpirationDateFallback" formControlName="fallbackExpirationDate" [required]="!editMode"
placeholder="MM/DD/YYYY" [readOnly]="disabled" data-date-format="mm/dd/yyyy">
<input id="expirationTimeCustomFallback" class="form-control mt-1 ml-1" type="time"
name="ExpirationTimeFallback" formControlName="fallbackExpirationTime" [required]="!editMode"
placeholder="HH:MM AM/PM" [readOnly]="disabled">
</div>
<!-- non-default cases are not showing up -->
<div *ngSwitchCase="'safari'" class="d-flex justify-content-around">
<input id="expirationDateCustomFallback" class="form-control mt-1" type="date"
name="ExpirationDateFallback" formControlName="fallbackExpirationDate" [required]="!editMode"
placeholder="MM/DD/YYYY" [readOnly]="disabled" data-date-format="mm/dd/yyyy">
<select id="expirationTimeCustomFallback" class="form-control mt-1 ml-1" [required]="!editMode"
formControlName="fallbackExpirationTime" name="SafariExpirationTime">
<option *ngFor="let o of safariExpirationTimePresetOptions" [ngValue]="o.twentyFourHour">{{o.twelveHour}}</option>
</select>
</div>
<ng-container *ngSwitchDefault>
<input id="expirationDateCustom" class="form-control mt-1" type="datetime-local"
name="ExpirationDate" formControlName="defaultExpirationDateTime" placeholder="MM/DD/YYYY HH:MM AM/PM"
[readOnly]="disabled">
</ng-container>
</ng-container>
</ng-template>
<div *ngIf="!editMode">
<select id="expirationDate" name="SelectedExpirationDatePreset"
formControlName="selectedExpirationDatePreset" class="form-control" required>
<option *ngFor="let o of expirationDatePresets" [ngValue]="o.value">{{o.name}}
</option>
</select>
<ng-container *ngIf="selectedExpirationDatePreset.value === 0">
<ng-container *ngTemplateOutlet="expirationDateCustom">
</ng-container>
</ng-container>
</div>
<div *ngIf="editMode">
<ng-container *ngTemplateOutlet="expirationDateCustom">
</ng-container>
</div>
<div class="form-text text-muted small">{{'expirationDateDesc' | i18n}}</div>
<div *ngIf="editMode">
<ng-container *ngTemplateOutlet="deletionDateCustom"> </ng-container>
</div>
<div class="form-text text-muted small">{{ "deletionDateDesc" | i18n }}</div>
</div>
<div class="col-6 form-group">
<div class="d-flex">
<label for="expirationDate">{{ "expirationDate" | i18n }}</label>
<a
href="#"
appStopClick
(click)="clearExpiration()"
class="ml-auto"
*ngIf="editMode && !disabled"
>
{{ "clear" | i18n }}
</a>
</div>
<ng-template #expirationDateCustom>
<ng-container [ngSwitch]="browserPath">
<div *ngSwitchCase="'firefox'" class="d-flex justify-content-around">
<input
id="expirationDateCustomFallback"
class="form-control mt-1"
type="date"
name="ExpirationDateFallback"
formControlName="fallbackExpirationDate"
[required]="!editMode"
placeholder="MM/DD/YYYY"
[readOnly]="disabled"
data-date-format="mm/dd/yyyy"
/>
<input
id="expirationTimeCustomFallback"
class="form-control mt-1 ml-1"
type="time"
name="ExpirationTimeFallback"
formControlName="fallbackExpirationTime"
[required]="!editMode"
placeholder="HH:MM AM/PM"
[readOnly]="disabled"
/>
</div>
<!-- non-default cases are not showing up -->
<div *ngSwitchCase="'safari'" class="d-flex justify-content-around">
<input
id="expirationDateCustomFallback"
class="form-control mt-1"
type="date"
name="ExpirationDateFallback"
formControlName="fallbackExpirationDate"
[required]="!editMode"
placeholder="MM/DD/YYYY"
[readOnly]="disabled"
data-date-format="mm/dd/yyyy"
/>
<select
id="expirationTimeCustomFallback"
class="form-control mt-1 ml-1"
[required]="!editMode"
formControlName="fallbackExpirationTime"
name="SafariExpirationTime"
>
<option
*ngFor="let o of safariExpirationTimePresetOptions"
[ngValue]="o.twentyFourHour"
>
{{ o.twelveHour }}
</option>
</select>
</div>
<ng-container *ngSwitchDefault>
<input
id="expirationDateCustom"
class="form-control mt-1"
type="datetime-local"
name="ExpirationDate"
formControlName="defaultExpirationDateTime"
placeholder="MM/DD/YYYY HH:MM AM/PM"
[readOnly]="disabled"
/>
</ng-container>
</ng-container>
</ng-template>
<div *ngIf="!editMode">
<select
id="expirationDate"
name="SelectedExpirationDatePreset"
formControlName="selectedExpirationDatePreset"
class="form-control"
required
>
<option *ngFor="let o of expirationDatePresets" [ngValue]="o.value">{{ o.name }}</option>
</select>
<ng-container *ngIf="selectedExpirationDatePreset.value === 0">
<ng-container *ngTemplateOutlet="expirationDateCustom"> </ng-container>
</ng-container>
</div>
<div *ngIf="editMode">
<ng-container *ngTemplateOutlet="expirationDateCustom"> </ng-container>
</div>
<div class="form-text text-muted small">{{ "expirationDateDesc" | i18n }}</div>
</div>
</div>

View File

@@ -1,22 +1,25 @@
import { DatePipe } from '@angular/common';
import { DatePipe } from "@angular/common";
import { Component } from '@angular/core';
import { Component } from "@angular/core";
import { ControlContainer, NgForm } from '@angular/forms';
import { ControlContainer, NgForm } from "@angular/forms";
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { EffluxDatesComponent as BaseEffluxDatesComponent } from 'jslib-angular/components/send/efflux-dates.component';
import { EffluxDatesComponent as BaseEffluxDatesComponent } from "jslib-angular/components/send/efflux-dates.component";
@Component({
selector: 'app-send-efflux-dates',
templateUrl: 'efflux-dates.component.html',
viewProviders: [{ provide: ControlContainer, useExisting: NgForm }],
selector: "app-send-efflux-dates",
templateUrl: "efflux-dates.component.html",
viewProviders: [{ provide: ControlContainer, useExisting: NgForm }],
})
export class EffluxDatesComponent extends BaseEffluxDatesComponent {
constructor(protected i18nService: I18nService, protected platformUtilsService: PlatformUtilsService,
protected datePipe: DatePipe) {
super(i18nService, platformUtilsService, datePipe);
}
constructor(
protected i18nService: I18nService,
protected platformUtilsService: PlatformUtilsService,
protected datePipe: DatePipe
) {
super(i18nService, platformUtilsService, datePipe);
}
}

View File

@@ -1,137 +1,188 @@
<div class="container page-content">
<app-callout type="warning" title="{{'sendDisabled' | i18n}}" *ngIf="disableSend">
<span>{{'sendDisabledWarning' | i18n}}</span>
</app-callout>
<div class="row">
<div class="col-3 groupings">
<div class="card vault-filters">
<div class="card-header d-flex">
{{'filters' | i18n}}
</div>
<div class="card-body">
<input type="search" placeholder="{{searchPlaceholder || ('searchSends' | i18n)}}" id="search"
class="form-control" [(ngModel)]="searchText" (input)="searchTextChanged()" autocomplete="off"
appAutofocus>
<ul class="fa-ul card-ul">
<li [ngClass]="{active: selectedAll}">
<a href="#" appStopClick (click)="selectAll()">
<i class="fa-li fa fa-fw fa-th"></i>{{'allSends' | i18n}}
</a>
</li>
</ul>
<h3>{{'types' | i18n}}</h3>
<ul class="fa-ul card-ul">
<li [ngClass]="{active: selectedType === sendType.Text}">
<a href="#" appStopClick (click)="selectType(sendType.Text)">
<i class="fa-li fa fa-fw fa-file-text-o"></i>{{'sendTypeText' | i18n}}
</a>
</li>
<li [ngClass]="{active: selectedType === sendType.File}">
<a href="#" appStopClick (click)="selectType(sendType.File)">
<i class="fa-li fa fa-fw fa-file-o"></i>{{'sendTypeFile' | i18n}}
</a>
</li>
</ul>
</div>
</div>
<app-callout type="warning" title="{{ 'sendDisabled' | i18n }}" *ngIf="disableSend">
<span>{{ "sendDisabledWarning" | i18n }}</span>
</app-callout>
<div class="row">
<div class="col-3 groupings">
<div class="card vault-filters">
<div class="card-header d-flex">
{{ "filters" | i18n }}
</div>
<div class="col-9">
<div class="page-header d-flex">
<h1>
{{'send' | i18n}}
<small #actionSpinner [appApiAction]="actionPromise">
<ng-container *ngIf="actionSpinner.loading">
<i class="fa fa-spinner fa-spin text-muted" title="{{'loading' | i18n}}"
aria-hidden="true"></i>
<span class="sr-only">{{'loading' | i18n}}</span>
</ng-container>
</small>
</h1>
<div class="ml-auto d-flex">
<button type="button" class="btn btn-outline-primary btn-sm" (click)="addSend()"
[disabled]="disableSend">
<i class="fa fa-plus fa-fw" aria-hidden="true"></i>{{'createSend' | i18n}}
</button>
</div>
</div>
<!--Listing Table-->
<table class="table table-hover table-list" *ngIf="filteredSends && filteredSends.length">
<tbody>
<tr *ngFor="let s of filteredSends">
<td class="table-list-icon">
<div class="icon" aria-hidden="true">
<i class="fa fa-fw fa-lg fa-file-o" *ngIf="s.type == sendType.File"></i>
<i class="fa fa-fw fa-lg fa-file-text-o" *ngIf="s.type == sendType.Text"></i>
</div>
</td>
<td class="reduced-lh wrap">
<a href="#" appStopClick appStopProp (click)="editSend(s)">{{s.name}}</a>
<ng-container *ngIf="s.disabled">
<i class="fa fa-warning" appStopProp title="{{'disabled' | i18n}}"
aria-hidden="true"></i>
<span class="sr-only">{{'disabled' | i18n}}</span>
</ng-container>
<ng-container *ngIf="s.password">
<i class="fa fa-key" appStopProp title="{{'password' | i18n}}" aria-hidden="true"></i>
<span class="sr-only">{{'password' | i18n}}</span>
</ng-container>
<ng-container *ngIf="s.maxAccessCountReached">
<i class="fa fa-ban" appStopProp title="{{'maxAccessCountReached' | i18n}}"
aria-hidden="true"></i>
<span class="sr-only">{{'maxAccessCountReached' | i18n}}</span>
</ng-container>
<ng-container *ngIf="s.expired">
<i class="fa fa-clock-o" appStopProp title="{{'expired' | i18n}}"
aria-hidden="true"></i>
<span class="sr-only">{{'expired' | i18n}}</span>
</ng-container>
<ng-container *ngIf="s.pendingDelete">
<i class="fa fa-trash" appStopProp title="{{'pendingDeletion' | i18n}}"
aria-hidden="true"></i>
<span class="sr-only">{{'pendingDeletion' | i18n}}</span>
</ng-container>
<br>
<small appStopProp>{{s.deletionDate | date:'medium'}}</small>
</td>
<td class="table-list-options">
<div class="dropdown" appListDropdown>
<button class="btn btn-outline-secondary dropdown-toggle" type="button"
id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true"
aria-expanded="false" appA11yTitle="{{'options' | i18n}}">
<i class="fa fa-cog fa-lg" aria-hidden="true"></i>
</button>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
<a class="dropdown-item" href="#" appStopClick (click)="copy(s)">
<i class="fa fa-fw fa-copy" aria-hidden="true"></i>
{{'copySendLink' | i18n}}
</a>
<a class="dropdown-item" href="#" appStopClick (click)="removePassword(s)"
*ngIf="s.password && !disableSend">
<i class="fa fa-fw fa-undo" aria-hidden="true"></i>
{{'removePassword' | i18n}}
</a>
<a class="dropdown-item text-danger" href="#" appStopClick (click)="delete(s)">
<i class="fa fa-fw fa-trash-o" aria-hidden="true"></i>
{{'delete' | i18n}}
</a>
</div>
</div>
</td>
</tr>
</tbody>
</table>
<div class="no-items" *ngIf="filteredSends && !filteredSends.length">
<ng-container *ngIf="!loaded">
<i class="fa fa-spinner fa-spin text-muted" title="{{'loading' | i18n}}" aria-hidden="true"></i>
<span class="sr-only">{{'loading' | i18n}}</span>
</ng-container>
<ng-container *ngIf="loaded">
<p>{{'noSendsInList' | i18n}}</p>
<button (click)="addSend()" class="btn btn-outline-primary" [disabled]="disableSend">
<i class="fa fa-plus fa-fw"></i>{{'createSend' | i18n}}</button>
</ng-container>
</div>
<div class="card-body">
<input
type="search"
placeholder="{{ searchPlaceholder || ('searchSends' | i18n) }}"
id="search"
class="form-control"
[(ngModel)]="searchText"
(input)="searchTextChanged()"
autocomplete="off"
appAutofocus
/>
<ul class="fa-ul card-ul">
<li [ngClass]="{ active: selectedAll }">
<a href="#" appStopClick (click)="selectAll()">
<i class="fa-li fa fa-fw fa-th"></i>{{ "allSends" | i18n }}
</a>
</li>
</ul>
<h3>{{ "types" | i18n }}</h3>
<ul class="fa-ul card-ul">
<li [ngClass]="{ active: selectedType === sendType.Text }">
<a href="#" appStopClick (click)="selectType(sendType.Text)">
<i class="fa-li fa fa-fw fa-file-text-o"></i>{{ "sendTypeText" | i18n }}
</a>
</li>
<li [ngClass]="{ active: selectedType === sendType.File }">
<a href="#" appStopClick (click)="selectType(sendType.File)">
<i class="fa-li fa fa-fw fa-file-o"></i>{{ "sendTypeFile" | i18n }}
</a>
</li>
</ul>
</div>
</div>
</div>
<div class="col-9">
<div class="page-header d-flex">
<h1>
{{ "send" | i18n }}
<small #actionSpinner [appApiAction]="actionPromise">
<ng-container *ngIf="actionSpinner.loading">
<i
class="fa fa-spinner fa-spin text-muted"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ "loading" | i18n }}</span>
</ng-container>
</small>
</h1>
<div class="ml-auto d-flex">
<button
type="button"
class="btn btn-outline-primary btn-sm"
(click)="addSend()"
[disabled]="disableSend"
>
<i class="fa fa-plus fa-fw" aria-hidden="true"></i>{{ "createSend" | i18n }}
</button>
</div>
</div>
<!--Listing Table-->
<table class="table table-hover table-list" *ngIf="filteredSends && filteredSends.length">
<tbody>
<tr *ngFor="let s of filteredSends">
<td class="table-list-icon">
<div class="icon" aria-hidden="true">
<i class="fa fa-fw fa-lg fa-file-o" *ngIf="s.type == sendType.File"></i>
<i class="fa fa-fw fa-lg fa-file-text-o" *ngIf="s.type == sendType.Text"></i>
</div>
</td>
<td class="reduced-lh wrap">
<a href="#" appStopClick appStopProp (click)="editSend(s)">{{ s.name }}</a>
<ng-container *ngIf="s.disabled">
<i
class="fa fa-warning"
appStopProp
title="{{ 'disabled' | i18n }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ "disabled" | i18n }}</span>
</ng-container>
<ng-container *ngIf="s.password">
<i
class="fa fa-key"
appStopProp
title="{{ 'password' | i18n }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ "password" | i18n }}</span>
</ng-container>
<ng-container *ngIf="s.maxAccessCountReached">
<i
class="fa fa-ban"
appStopProp
title="{{ 'maxAccessCountReached' | i18n }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ "maxAccessCountReached" | i18n }}</span>
</ng-container>
<ng-container *ngIf="s.expired">
<i
class="fa fa-clock-o"
appStopProp
title="{{ 'expired' | i18n }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ "expired" | i18n }}</span>
</ng-container>
<ng-container *ngIf="s.pendingDelete">
<i
class="fa fa-trash"
appStopProp
title="{{ 'pendingDeletion' | i18n }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ "pendingDeletion" | i18n }}</span>
</ng-container>
<br />
<small appStopProp>{{ s.deletionDate | date: "medium" }}</small>
</td>
<td class="table-list-options">
<div class="dropdown" appListDropdown>
<button
class="btn btn-outline-secondary dropdown-toggle"
type="button"
id="dropdownMenuButton"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
appA11yTitle="{{ 'options' | i18n }}"
>
<i class="fa fa-cog fa-lg" aria-hidden="true"></i>
</button>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
<a class="dropdown-item" href="#" appStopClick (click)="copy(s)">
<i class="fa fa-fw fa-copy" aria-hidden="true"></i>
{{ "copySendLink" | i18n }}
</a>
<a
class="dropdown-item"
href="#"
appStopClick
(click)="removePassword(s)"
*ngIf="s.password && !disableSend"
>
<i class="fa fa-fw fa-undo" aria-hidden="true"></i>
{{ "removePassword" | i18n }}
</a>
<a class="dropdown-item text-danger" href="#" appStopClick (click)="delete(s)">
<i class="fa fa-fw fa-trash-o" aria-hidden="true"></i>
{{ "delete" | i18n }}
</a>
</div>
</div>
</td>
</tr>
</tbody>
</table>
<div class="no-items" *ngIf="filteredSends && !filteredSends.length">
<ng-container *ngIf="!loaded">
<i
class="fa fa-spinner fa-spin text-muted"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ "loading" | i18n }}</span>
</ng-container>
<ng-container *ngIf="loaded">
<p>{{ "noSendsInList" | i18n }}</p>
<button (click)="addSend()" class="btn btn-outline-primary" [disabled]="disableSend">
<i class="fa fa-plus fa-fw"></i>{{ "createSend" | i18n }}
</button>
</ng-container>
</div>
</div>
</div>
</div>
<ng-template #sendAddEdit></ng-template>

View File

@@ -1,89 +1,104 @@
import {
Component,
NgZone,
ViewChild,
ViewContainerRef,
} from '@angular/core';
import { Component, NgZone, ViewChild, ViewContainerRef } from "@angular/core";
import { SendView } from 'jslib-common/models/view/sendView';
import { SendView } from "jslib-common/models/view/sendView";
import { SendComponent as BaseSendComponent } from 'jslib-angular/components/send/send.component';
import { SendComponent as BaseSendComponent } from "jslib-angular/components/send/send.component";
import { AddEditComponent } from './add-edit.component';
import { AddEditComponent } from "./add-edit.component";
import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service';
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { LogService } from 'jslib-common/abstractions/log.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { PolicyService } from 'jslib-common/abstractions/policy.service';
import { SearchService } from 'jslib-common/abstractions/search.service';
import { SendService } from 'jslib-common/abstractions/send.service';
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { PolicyService } from "jslib-common/abstractions/policy.service";
import { SearchService } from "jslib-common/abstractions/search.service";
import { SendService } from "jslib-common/abstractions/send.service";
import { ModalService } from 'jslib-angular/services/modal.service';
import { ModalService } from "jslib-angular/services/modal.service";
const BroadcasterSubscriptionId = 'SendComponent';
const BroadcasterSubscriptionId = "SendComponent";
@Component({
selector: 'app-send',
templateUrl: 'send.component.html',
selector: "app-send",
templateUrl: "send.component.html",
})
export class SendComponent extends BaseSendComponent {
@ViewChild('sendAddEdit', { read: ViewContainerRef, static: true }) sendAddEditModalRef: ViewContainerRef;
@ViewChild("sendAddEdit", { read: ViewContainerRef, static: true })
sendAddEditModalRef: ViewContainerRef;
constructor(sendService: SendService, i18nService: I18nService,
platformUtilsService: PlatformUtilsService, environmentService: EnvironmentService,
ngZone: NgZone, searchService: SearchService,
policyService: PolicyService, private modalService: ModalService,
private broadcasterService: BroadcasterService, logService: LogService) {
super(sendService, i18nService, platformUtilsService, environmentService, ngZone, searchService,
policyService, logService);
}
constructor(
sendService: SendService,
i18nService: I18nService,
platformUtilsService: PlatformUtilsService,
environmentService: EnvironmentService,
ngZone: NgZone,
searchService: SearchService,
policyService: PolicyService,
private modalService: ModalService,
private broadcasterService: BroadcasterService,
logService: LogService
) {
super(
sendService,
i18nService,
platformUtilsService,
environmentService,
ngZone,
searchService,
policyService,
logService
);
}
async ngOnInit() {
await super.ngOnInit();
await this.load();
async ngOnInit() {
await super.ngOnInit();
await this.load();
// Broadcaster subscription - load if sync completes in the background
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
this.ngZone.run(async () => {
switch (message.command) {
case 'syncCompleted':
if (message.successfully) {
await this.load();
}
break;
}
});
});
}
ngOnDestroy() {
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
}
async addSend() {
if (this.disableSend) {
return;
// Broadcaster subscription - load if sync completes in the background
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
this.ngZone.run(async () => {
switch (message.command) {
case "syncCompleted":
if (message.successfully) {
await this.load();
}
break;
}
});
});
}
const component = await this.editSend(null);
component.type = this.type;
ngOnDestroy() {
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
}
async addSend() {
if (this.disableSend) {
return;
}
async editSend(send: SendView) {
const [modal, childComponent] = await this.modalService.openViewRef(AddEditComponent, this.sendAddEditModalRef, comp => {
comp.sendId = send == null ? null : send.id;
comp.onSavedSend.subscribe(async (s: SendView) => {
modal.close();
await this.load();
});
comp.onDeletedSend.subscribe(async (s: SendView) => {
modal.close();
await this.load();
});
const component = await this.editSend(null);
component.type = this.type;
}
async editSend(send: SendView) {
const [modal, childComponent] = await this.modalService.openViewRef(
AddEditComponent,
this.sendAddEditModalRef,
(comp) => {
comp.sendId = send == null ? null : send.id;
comp.onSavedSend.subscribe(async (s: SendView) => {
modal.close();
await this.load();
});
comp.onDeletedSend.subscribe(async (s: SendView) => {
modal.close();
await this.load();
});
}
);
return childComponent;
}
return childComponent;
}
}