Expiration settings, protection settings, fix drop area hover
- Front end for expiration time/date - Setting expiration via `PUT /fling/{flingId}` - Settings for protection code enabled in front-end (no backend) - Upload drop container used to loose focus when hovering over child elements. This is fixed now.
This commit is contained in:
parent
4ab3bf705e
commit
b0a7e8b443
5 changed files with 176 additions and 39 deletions
|
@ -54,18 +54,19 @@ public class FlingDto {
|
|||
|
||||
@JsonProperty("expiration")
|
||||
private void unpackExpiration(Map<String, Object> expiration) {
|
||||
String type = (String) expiration.getOrDefault("expirationType", null);
|
||||
String type = (String) expiration.getOrDefault("type", null);
|
||||
if(type == null) return;
|
||||
|
||||
switch(type) {
|
||||
case "time":
|
||||
this.expirationClicks = null;
|
||||
// json can only handle int, long must be given as string
|
||||
this.expirationTime = Instant.ofEpochMilli(Long.parseLong((String) expiration.get("value")));
|
||||
// TODO: this back and forth conversion is a bit hack-ish
|
||||
this.expirationTime = Instant.ofEpochMilli(Long.valueOf(expiration.get("value").toString()));
|
||||
break;
|
||||
case "clicks":
|
||||
this.expirationTime = null;
|
||||
this.expirationClicks = (Integer) expiration.get("value");
|
||||
this.expirationClicks = Integer.valueOf(expiration.get("value").toString());
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected value '"+type+"'");
|
||||
|
|
|
@ -65,8 +65,8 @@ public class FlingService {
|
|||
|
||||
mergeNonEmpty(flingDto::getAllowUpload, flingEntity::setAllowUpload);
|
||||
mergeNonEmpty(flingDto::getDirectDownload, flingEntity::setDirectDownload);
|
||||
mergeNonEmpty(flingDto::getExpirationClicks, flingEntity::setExpirationClicks);
|
||||
mergeNonEmpty(flingDto::getExpirationTime, flingEntity::setExpirationTime);
|
||||
mergeWithEmpty(flingDto::getExpirationClicks, flingEntity::setExpirationClicks);
|
||||
mergeWithEmpty(flingDto::getExpirationTime, flingEntity::setExpirationTime);
|
||||
mergeNonEmpty(flingDto::getName, flingEntity::setName);
|
||||
mergeNonEmpty(flingDto::getShared, flingEntity::setShared);
|
||||
mergeNonEmpty(flingDto::getShareUrl, flingEntity::setShareUrl);
|
||||
|
@ -131,4 +131,9 @@ public class FlingService {
|
|||
T r = sup.get();
|
||||
if(r != null) con.accept(r);
|
||||
}
|
||||
|
||||
private <T> void mergeWithEmpty(Supplier<T> sup, Consumer<T> con) {
|
||||
T r = sup.get();
|
||||
con.accept(r);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,14 +6,20 @@ import classNames from 'classnames';
|
|||
import {flingClient} from '../../util/flingclient';
|
||||
|
||||
export default function Settings(props) {
|
||||
let [fling, setFling] = useState({name: "", sharing: {directDownload: false, allowUpload: true, shared: true, shareUrl: ""}});
|
||||
let [fling, setFling] = useState({name: "", sharing: {directDownload: false, allowUpload: true, shared: true, shareUrl: "", authCode: ""},
|
||||
expiration: {type: "clicks", value: 0}});
|
||||
let [shareUrlUnique, setShareUrlUnique] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
if(props.activeFling) {
|
||||
flingClient.getFling(props.activeFling)
|
||||
.then(result => {
|
||||
setFling(result);
|
||||
let f = {...fling, ...result};
|
||||
let s = {...fling.sharing, ...result.sharing};
|
||||
let e = {...fling.expiration, ...result.expiration};
|
||||
f.sharing = s;
|
||||
f.expiration = e;
|
||||
setFling(f);
|
||||
});
|
||||
}
|
||||
}, [props.activeFling]);
|
||||
|
@ -80,6 +86,61 @@ export default function Settings(props) {
|
|||
});
|
||||
}
|
||||
|
||||
function setName(ev) {
|
||||
let f = {...fling};
|
||||
let value = ev.currentTarget.value;
|
||||
|
||||
f.name = value;
|
||||
setFling(f);
|
||||
}
|
||||
|
||||
function setExpirationType(ev) {
|
||||
let f = {...fling};
|
||||
let e = {...fling.expiration}; //TODO: sharing is not cloned
|
||||
let value = ev.currentTarget.value;
|
||||
|
||||
if(value === "never") {
|
||||
e = {};
|
||||
} else {
|
||||
e.type = value;
|
||||
e.value = "";
|
||||
}
|
||||
|
||||
f.expiration = e;
|
||||
setFling(f);
|
||||
}
|
||||
|
||||
function setExpirationValue(ev) {
|
||||
let f = {...fling};
|
||||
let e = {...fling.expiration}; //TODO: sharing is not cloned
|
||||
let value = e.type === "time" ? ev.currentTarget.valueAsNumber: ev.currentTarget.value;
|
||||
|
||||
e.value = value;
|
||||
|
||||
f.expiration = e;
|
||||
setFling(f);
|
||||
}
|
||||
|
||||
function formatExpirationTime() {
|
||||
if (!fling.expiration || !fling.expiration.value || fling.expiration.type !== "time")
|
||||
return "";
|
||||
|
||||
|
||||
let date = new Date(fling.expiration.value);
|
||||
let fmt = date.toISOString().split("T")[0];
|
||||
return fmt;
|
||||
}
|
||||
|
||||
function setAuthCode(ev) {
|
||||
let f = {...fling};
|
||||
let s = {...fling.sharing};
|
||||
let value = ev.currentTarget.value;
|
||||
|
||||
s.authCode = value;
|
||||
f.sharing = s;
|
||||
setFling(f);
|
||||
}
|
||||
|
||||
function handleSubmit(ev) {
|
||||
ev.preventDefault();
|
||||
log.info(fling);
|
||||
|
@ -88,37 +149,100 @@ export default function Settings(props) {
|
|||
|
||||
return(
|
||||
<div className="container">
|
||||
{log.info(props)}
|
||||
<div className="columns">
|
||||
<div className="column col-6">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="p-centered column col-xl-9 col-sm-12 col-6">
|
||||
<form className="form-horizontal" onSubmit={handleSubmit}>
|
||||
<div className="form-group">
|
||||
<div className="col-3 col-sm-12">
|
||||
<label className="form-label" htmlFor="input-name">Name</label>
|
||||
<input className="form-input" type="text" id="input-name" value={fling.name} />
|
||||
|
||||
<label className="form-label" htmlFor="input-share-url">Share</label>
|
||||
</div>
|
||||
<div className="col-9 col-sm-12">
|
||||
<input className="form-input" type="text" id="input-name" value={fling.name} onChange={setName}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<div className="col-3 col-sm-12">
|
||||
<label className="form-label" htmlFor="input-share-url">Share URL</label>
|
||||
</div>
|
||||
<div className="col-9 col-sm-12">
|
||||
<input className="form-input" type="text" id="input-share-url" value={fling.sharing.shareUrl} onChange={setShareUrl} />
|
||||
<i className={`icon icon-cross text-error ${shareUrlUnique ? "d-hide": "d-visible"}`} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label className="form-switch form-inline">
|
||||
<input type="checkbox" id="direct-download" checked={fling.sharing.directDownload} onChange={toggleSharing}/>
|
||||
<i className="form-icon" /> Direct Download
|
||||
<div className="col-3 col-sm-12">
|
||||
<label className="form-label" htmlFor="input-passcode">Passcode</label>
|
||||
</div>
|
||||
<div className="col-9 col-sm-12">
|
||||
<div className="input-group">
|
||||
<input className="form-input" type="text" value={fling.sharing.authCode} onChange={setAuthCode} />
|
||||
<label className="form-switch ml-2 popover popover-bottom">
|
||||
<input type="checkbox" checked={!!fling.sharing.authCode} readOnly />
|
||||
<i className="form-icon" /> Protected
|
||||
<div className="popover-container card">
|
||||
<div className="card-body">
|
||||
{!!fling.sharing.authCode ? "Delete the passcode to disable protection": "Set a passcode to enable protection"}
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label className="form-switch form-inline">
|
||||
<input type="checkbox" id="allow-upload" checked={fling.sharing.allowUpload} onChange={toggleSharing}/>
|
||||
<i className="form-icon" /> Allow Uploads
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<div className="col-3 col-sm-12">
|
||||
<label className="form-label">Expiration</label>
|
||||
</div>
|
||||
<div className="col-9 col-sm-12">
|
||||
<div className="form-group">
|
||||
<select className="form-select" value={fling.expiration.type} onChange={setExpirationType}>
|
||||
<option value="never">Never</option>
|
||||
<option value="time">Date</option>
|
||||
<option value="clicks">Clicks</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className={fling.expiration.type === "clicks" ? "d-visible": "d-hide"}>
|
||||
<div className="input-group">
|
||||
<span className="input-group-addon">Expire after</span>
|
||||
<input className="form-input" type="number" value={fling.expiration.value || ""} onChange={setExpirationValue} />
|
||||
<span className="input-group-addon">Clicks</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={fling.expiration.type === "time" ? "d-visible": "d-hide"}>
|
||||
<div className="input-group">
|
||||
<span className="input-group-addon">Expire after</span>
|
||||
<input className="form-input" type="date" value={formatExpirationTime()} onChange={setExpirationValue} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<div className="col-3 col-sm-12">
|
||||
<label className="form-label">Settings</label>
|
||||
</div>
|
||||
<div className="col-9 col-sm-12">
|
||||
|
||||
<label className="form-switch form-inline">
|
||||
<input type="checkbox" id="shared" checked={fling.sharing.shared} onChange={toggleSharing}/>
|
||||
<i className="form-icon" /> Shared
|
||||
</label>
|
||||
<label className="form-switch form-inline">
|
||||
<input type="checkbox" id="allow-upload" checked={fling.sharing.allowUpload} onChange={toggleSharing}/>
|
||||
<i className="form-icon" /> Uploads
|
||||
</label>
|
||||
<label className="form-switch form-inline">
|
||||
<input type="checkbox" id="direct-download" checked={fling.sharing.directDownload} onChange={toggleSharing}/>
|
||||
<i className="form-icon" /> Direct Download
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="submit" className="btn float-right" value="Save" />
|
||||
<input type="submit" className="btn btn-primary float-right" value="Save" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -169,10 +169,9 @@ export default function Upload(props) {
|
|||
onDragEnter={handleOnDragEnter}
|
||||
onDragLeave={handleOnDragLeave}>
|
||||
|
||||
<div className="dropzone c-hand py-2">
|
||||
<div className="dropzone c-hand py-2" onDragLeave={stopEvent} >
|
||||
<input className="d-hide" ref={fileInputRef} type="file" multiple
|
||||
onDragOver={stopEvent} onDragEnter={stopEvent} onDragLeave={stopEvent}
|
||||
onChange={handleFileInputChange} />
|
||||
onDragLeave={stopEvent} onChange={handleFileInputChange} />
|
||||
{zoneContent(dragging)}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -186,7 +185,7 @@ export default function Upload(props) {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="upload-command-line m-2">
|
||||
<span className="total-upload">Total Size: {totalSize()}</span>
|
||||
<button className="btn btn-primary btn-upload" onClick={handleUpload}>Upload</button>
|
||||
</div>
|
||||
|
|
|
@ -207,11 +207,6 @@ tbody td {
|
|||
|
||||
.total-upload {
|
||||
float: left;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.btn-upload {
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.my-input {
|
||||
|
@ -221,3 +216,16 @@ tbody td {
|
|||
height: 100%;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.upload-command-line {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
/************\
|
||||
| Settings |
|
||||
\************/
|
||||
|
||||
.share-settings {
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue