0.1 #1
8 changed files with 137 additions and 93 deletions
|
@ -99,11 +99,15 @@ public class FlingWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
|
||||||
.antMatchers(HttpMethod.POST, "/api/fling/{flingId}/artifact")
|
.antMatchers(HttpMethod.POST, "/api/fling/{flingId}/artifact")
|
||||||
.access("@authorizationService.allowUpload(#flingId, authentication)")
|
.access("@authorizationService.allowUpload(#flingId, authentication)")
|
||||||
.and()
|
.and()
|
||||||
// only admin can create, delete and list flings
|
// only admin can create, delete, list and modify flings
|
||||||
.authorizeRequests()
|
.authorizeRequests()
|
||||||
.antMatchers(HttpMethod.DELETE, "/api/fling/{flingId}")
|
.antMatchers(HttpMethod.DELETE, "/api/fling/{flingId}")
|
||||||
.hasAnyAuthority(FLING_ADMIN.getAuthority())
|
.hasAnyAuthority(FLING_ADMIN.getAuthority())
|
||||||
.and()
|
.and()
|
||||||
|
.authorizeRequests()
|
||||||
|
.antMatchers(HttpMethod.PUT, "/api/fling/{flingId}")
|
||||||
|
.hasAnyAuthority(FLING_ADMIN.getAuthority())
|
||||||
|
.and()
|
||||||
.authorizeRequests()
|
.authorizeRequests()
|
||||||
.antMatchers(HttpMethod.POST, "/api/fling")
|
.antMatchers(HttpMethod.POST, "/api/fling")
|
||||||
.hasAuthority(FLING_ADMIN.getAuthority())
|
.hasAuthority(FLING_ADMIN.getAuthority())
|
||||||
|
|
|
@ -121,6 +121,7 @@ public class FlingService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String hashAuthCode(String authCode) {
|
private String hashAuthCode(String authCode) {
|
||||||
|
if (!StringUtils.hasText(authCode)) return null;
|
||||||
String hash = passwordEncoder.encode(authCode);
|
String hash = passwordEncoder.encode(authCode);
|
||||||
log.debug("Hashed authentication code to {}", hash);
|
log.debug("Hashed authentication code to {}", hash);
|
||||||
return hash;
|
return hash;
|
||||||
|
@ -154,17 +155,14 @@ public class FlingService {
|
||||||
flingEntity.setId(id);
|
flingEntity.setId(id);
|
||||||
flingEntity.setAllowUpload(flingDto.getAllowUpload());
|
flingEntity.setAllowUpload(flingDto.getAllowUpload());
|
||||||
flingEntity.setDirectDownload(flingDto.getDirectDownload());
|
flingEntity.setDirectDownload(flingDto.getDirectDownload());
|
||||||
|
flingEntity.setShared(flingDto.getShared());
|
||||||
flingEntity.setExpirationClicks(flingDto.getExpirationClicks());
|
flingEntity.setExpirationClicks(flingDto.getExpirationClicks());
|
||||||
flingEntity.setExpirationTime(flingDto.getExpirationTime());
|
flingEntity.setExpirationTime(flingDto.getExpirationTime());
|
||||||
flingEntity.setName(flingDto.getName());
|
flingEntity.setName(flingDto.getName());
|
||||||
flingEntity.setShareId(flingDto.getShareId());
|
flingEntity.setShareId(flingDto.getShareId());
|
||||||
|
if (!flingDto.getAuthCode().equals(flingEntity.getAuthCode())) {
|
||||||
if(!flingEntity.getAuthCode().equals(flingDto.getAuthCode())
|
|
||||||
&& !flingEntity.getAuthCode().equals(hashAuthCode(flingDto.getAuthCode()))) {
|
|
||||||
|
|
||||||
flingEntity.setAuthCode(hashAuthCode(flingDto.getAuthCode()));
|
flingEntity.setAuthCode(hashAuthCode(flingDto.getAuthCode()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return flingMapper.map(flingEntity);
|
return flingMapper.map(flingEntity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
8
web/fling/package-lock.json
generated
8
web/fling/package-lock.json
generated
|
@ -1129,7 +1129,7 @@
|
||||||
"@fling/flingclient": {
|
"@fling/flingclient": {
|
||||||
"version": "0.1.0-snapshot",
|
"version": "0.1.0-snapshot",
|
||||||
"resolved": "https://nexus.friedl.net/repository/npm-private/@fling/flingclient/-/flingclient-0.1.0-snapshot.tgz",
|
"resolved": "https://nexus.friedl.net/repository/npm-private/@fling/flingclient/-/flingclient-0.1.0-snapshot.tgz",
|
||||||
"integrity": "sha512-P3JWlmnaYYpj5xS5EFp94OVZXSG9lJbraKlQE4SHnTctxLv3OaR4XOaO7j/FJFygJ3KhOLqVi0x8gQQEDnlMBQ==",
|
"integrity": "sha512-7paRpY2dM3d2fxCeZhZUUW+KZX8xjxyrlPxbhRcdPnyAFUY13kyczG0N5D4MFKQsquND3Dp7Js2vTKnzixPkpA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/cli": "^7.0.0",
|
"@babel/cli": "^7.0.0",
|
||||||
"superagent": "3.7.0"
|
"superagent": "3.7.0"
|
||||||
|
@ -6618,9 +6618,9 @@
|
||||||
"integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg=="
|
"integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg=="
|
||||||
},
|
},
|
||||||
"immer": {
|
"immer": {
|
||||||
"version": "7.0.5",
|
"version": "7.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/immer/-/immer-7.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/immer/-/immer-7.0.7.tgz",
|
||||||
"integrity": "sha512-TtRAKZyuqld2eYjvWgXISLJ0ZlOl1OOTzRmrmiY8SlB0dnAhZ1OiykIDL5KDFNaPHDXiLfGQFNJGtet8z8AEmg=="
|
"integrity": "sha512-Q8yYwVADJXrNfp1ZUAh4XDHkcoE3wpdpb4mC5abDSajs2EbW8+cGdPyAnglMyLnm7EF6ojD2xBFX7L5i4TIytw=="
|
||||||
},
|
},
|
||||||
"import-cwd": {
|
"import-cwd": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
"axios": "^0.19.2",
|
"axios": "^0.19.2",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"immer": "^7.0.5",
|
"immer": "^7.0.7",
|
||||||
"jwt-decode": "^2.2.0",
|
"jwt-decode": "^2.2.0",
|
||||||
"loglevel": "^1.6.8",
|
"loglevel": "^1.6.8",
|
||||||
"node-sass": "^4.14.1",
|
"node-sass": "^4.14.1",
|
||||||
|
|
|
@ -66,17 +66,9 @@ export default function New(props) {
|
||||||
|
|
||||||
const flingClient = new FlingClient();
|
const flingClient = new FlingClient();
|
||||||
flingClient.getFlingByShareId(ev.currentTarget.value)
|
flingClient.getFlingByShareId(ev.currentTarget.value)
|
||||||
.then(result => {
|
.then(result => setShareUrlUnique(false))
|
||||||
setShareUrlUnique(false);
|
.catch(error => error.status === 404 && setShareUrlUnique(true) )
|
||||||
}).catch(error => {
|
.finally(() => { s.shareUrl = value; f.sharing = s; setFling(f); });
|
||||||
if(error.status === 404) {
|
|
||||||
setShareUrlUnique(true);
|
|
||||||
}
|
|
||||||
}).finally(() => {
|
|
||||||
s.shareUrl = value;
|
|
||||||
f.sharing = s;
|
|
||||||
setFling(f);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setName(ev) {
|
function setName(ev) {
|
||||||
|
@ -157,7 +149,7 @@ export default function New(props) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flingClient.postFling({fling: flingEntity})
|
flingClient.postFling({ fling: flingEntity })
|
||||||
.then(() => handleClose())
|
.then(() => handleClose())
|
||||||
.catch(error => log.error(error))
|
.catch(error => log.error(error))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
import log from 'loglevel';
|
import log from 'loglevel';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
import _ from 'lodash';
|
|
||||||
|
|
||||||
import { useSelector, useDispatch } from 'react-redux';
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
import { Fling } from '@fling/flingclient';
|
|
||||||
|
|
||||||
import { retrieveFlings } from "../../redux/actions";
|
import { retrieveFlings } from "../../redux/actions";
|
||||||
import { FlingClient } from '../../util/fc';
|
import { FlingClient } from '../../util/fc';
|
||||||
|
@ -17,7 +15,6 @@ export default function Settings(props) {
|
||||||
* The active fling from the redux store. Treat this as immutable.
|
* The active fling from the redux store. Treat this as immutable.
|
||||||
*/
|
*/
|
||||||
let activeFling = useSelector(state => state.flings.activeFling);
|
let activeFling = useSelector(state => state.flings.activeFling);
|
||||||
let _clone = _.cloneDeep(_.toPlainObject(_.cloneDeep(activeFling)));
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deep clone the active fling from redux into a draft. Changes to the
|
* Deep clone the active fling from redux into a draft. Changes to the
|
||||||
|
@ -26,21 +23,25 @@ export default function Settings(props) {
|
||||||
*
|
*
|
||||||
* The draft, just as the activeFling, is of type Fling
|
* The draft, just as the activeFling, is of type Fling
|
||||||
*/
|
*/
|
||||||
let [draft, setDraft] = useState({ fling: _clone });
|
let [draft, setDraft] = useState(produce(activeFling, draft => draft));
|
||||||
|
|
||||||
let [shareUrlUnique, setShareUrlUnique] = useState(true);
|
let [shareUrlUnique, setShareUrlUnique] = useState(true);
|
||||||
let [authCodeChangeable, setAuthCodeChangeable] = useState(false);
|
let [authCodeChangeable, setAuthCodeChangeable] = useState(false);
|
||||||
|
let [expirationType, setExpirationType] = useState(activeFling.expirationClicks
|
||||||
|
? "clicks"
|
||||||
|
: activeFling.expirationTime ? "time" : "never");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Publishes the draft to the backend and refreshes the redux store
|
* Publishes the draft to the backend and refreshes the redux store
|
||||||
*/
|
*/
|
||||||
function publishDraft() {
|
function publishDraft() {
|
||||||
flingClient.putFling(draft).then(
|
flingClient.putFling(activeFling.id, { fling: draft })
|
||||||
|
.then(
|
||||||
success => {
|
success => {
|
||||||
log.info("Saved new settings {}", draft);
|
log.info("Saved new settings {}", draft);
|
||||||
dispatch(retrieveFlings());
|
dispatch(retrieveFlings());
|
||||||
},
|
})
|
||||||
error => log.error("Could not save new settings for {}: {}", activeFling.id, error));
|
.catch(error => log.error(`Could not save new settings for ${activeFling.id}: `, error));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -48,7 +49,7 @@ export default function Settings(props) {
|
||||||
* modifications get lost.
|
* modifications get lost.
|
||||||
*/
|
*/
|
||||||
function resetDraft() {
|
function resetDraft() {
|
||||||
setDraft(produce({}, draft => { return { fling: activeFling }; }));
|
setDraft(produce({}, draft => activeFling));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -72,29 +73,29 @@ export default function Settings(props) {
|
||||||
switch (setting) {
|
switch (setting) {
|
||||||
case "direct-download":
|
case "direct-download":
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
newDraft.fling.directDownload = true;
|
newDraft.directDownload = true;
|
||||||
newDraft.fling.shared = true;
|
newDraft.shared = true;
|
||||||
newDraft.fling.allowUpload = false;
|
newDraft.allowUpload = false;
|
||||||
} else {
|
} else {
|
||||||
newDraft.fling.directDownload = false;
|
newDraft.directDownload = false;
|
||||||
}
|
}
|
||||||
return newDraft.fling;
|
return newDraft;
|
||||||
case "allow-upload":
|
case "allow-upload":
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
newDraft.fling.allowUpload = true;
|
newDraft.allowUpload = true;
|
||||||
newDraft.fling.shared = true;
|
newDraft.shared = true;
|
||||||
newDraft.fling.directDownload = false;
|
newDraft.directDownload = false;
|
||||||
} else {
|
} else {
|
||||||
newDraft.fling.allowUpload = false;
|
newDraft.allowUpload = false;
|
||||||
}
|
}
|
||||||
return newDraft.fling;
|
return newDraft;
|
||||||
case "shared":
|
case "shared":
|
||||||
if (enabled) {
|
if (!enabled) {
|
||||||
newDraft.fling.allowUpload = false;
|
newDraft.allowUpload = false;
|
||||||
newDraft.fling.directDownload = false;
|
newDraft.directDownload = false;
|
||||||
newDraft.fling.shared = false;
|
newDraft.shared = false;
|
||||||
} else {
|
} else {
|
||||||
newDraft.fling.shared = true;
|
newDraft.shared = true;
|
||||||
}
|
}
|
||||||
return newDraft;
|
return newDraft;
|
||||||
default:
|
default:
|
||||||
|
@ -103,46 +104,78 @@ export default function Settings(props) {
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
/** Sets the Fling.name. Creates a new draft and sets it into the local state. */
|
/** Sets the Fling.name. Creates a new draft and sets it into the local state. */
|
||||||
let setName = produce((newDraft, name) => { newDraft.fling.name = name; return newDraft; });
|
let setName = _pproduce((newDraft, name) => { newDraft.name = name });
|
||||||
/** Sets the Fling.expirationTime. Creates a new draft and sets it into the local state. */
|
/** Sets the Fling.shareId. Creates a new draft and sets it into the local
|
||||||
let setExpirationTime = _pproduce((newDraft, time) => newDraft.fling.expirationTime = time);
|
* state. Sets `setShareUrlUnique`. */
|
||||||
/** Sets the Fling.expirationClicks. Creates a new draft and sets it into the local state. */
|
let setShareId = _pproduce((newDraft, shareId) => {
|
||||||
let setExpirationClicks = _pproduce((newDraft, clicks) => newDraft.fling.clicks = clicks);
|
newDraft.shareId = shareId;
|
||||||
/** Sets the Fling.shareId. Creates a new draft and sets it into the local state. */
|
|
||||||
let setShareId = _pproduce((newDraft, shareId) => newDraft.fling.shareId = shareId);
|
|
||||||
/** Sets the Fling.authCode. Creates a new draft and sets it into the local state. */
|
|
||||||
let setAuthCode = _pproduce((newDraft, authCode) => newDraft.fling.authCode = authCode);
|
|
||||||
|
|
||||||
let resetAuthCode = _pproduce((newDraft) => newDraft.fling.authCode = activeFling.authCode);
|
flingClient.getFlingByShareId(shareId)
|
||||||
|
.then(result => shareId !== activeFling.shareId && setShareUrlUnique(false))
|
||||||
|
.catch(error => error.status === 404 && setShareUrlUnique(true));
|
||||||
|
});
|
||||||
|
/** Sets the Fling.authCode. Creates a new draft and sets it into the local state. */
|
||||||
|
let setAuthCode = _pproduce((newDraft, authCode) => {
|
||||||
|
setAuthCodeChangeable(true);
|
||||||
|
if (!authCode) return newDraft;
|
||||||
|
newDraft.authCode = authCode
|
||||||
|
});
|
||||||
|
|
||||||
|
let resetAuthCode = _pproduce((newDraft) => {
|
||||||
|
setAuthCodeChangeable(true);
|
||||||
|
newDraft.authCode = "";
|
||||||
|
return newDraft;
|
||||||
|
});
|
||||||
|
|
||||||
|
let setExpiration = _pproduce((newDraft, type, value) => {
|
||||||
|
setExpirationType(type)
|
||||||
|
switch (type) {
|
||||||
|
case "clicks":
|
||||||
|
newDraft.expirationTime = "";
|
||||||
|
newDraft.expirationClicks = value;
|
||||||
|
break;
|
||||||
|
case "time":
|
||||||
|
newDraft.expirationClicks = "";
|
||||||
|
newDraft.expirationTime = value;
|
||||||
|
break;
|
||||||
|
case "never":
|
||||||
|
newDraft.expirationClicks = "";
|
||||||
|
newDraft.expirationTime = "";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log.error("Unknown expiration type");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
let resetExpiration = (draft, type) => {
|
||||||
|
setExpiration(draft, type, "");
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className="columns">
|
<div className="columns">
|
||||||
<div className="p-centered column col-xl-9 col-sm-12 col-6">
|
<div className="p-centered column col-xl-9 col-sm-12 col-6">
|
||||||
<form className="form-horizontal" onSubmit={publishDraft}>
|
<form className="form-horizontal" onSubmit={(ev) => { ev.preventDefault(); publishDraft(); }}>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<div className="col-3 col-sm-12">
|
<div className="col-3 col-sm-12">
|
||||||
<label className="form-label" htmlFor="input-name">Name</label>
|
<label className="form-label" htmlFor="input-name">Name</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-9 col-sm-12">
|
<div className="col-9 col-sm-12">
|
||||||
<input className="form-input" type="text" id="input-name"
|
<input className="form-input" type="text" id="input-name"
|
||||||
value={draft.fling.name}
|
value={draft.name}
|
||||||
onChange={(ev) => {
|
onChange={(ev) => setName(draft, ev.target.value)} />
|
||||||
ev.preventDefault();
|
|
||||||
let nd = produce(draft, newDraft => {
|
|
||||||
newDraft.fling.name = ev.target.value;
|
|
||||||
return newDraft;
|
|
||||||
})
|
|
||||||
setDraft(nd);
|
|
||||||
}} />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<div className="col-3 col-sm-12">
|
<div className="col-3 col-sm-12">
|
||||||
<label className="form-label" htmlFor="input-share-url">Share URL</label>
|
<label className="form-label" htmlFor="input-share-url">Share Id</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-9 col-sm-12">
|
<div className="col-9 col-sm-12">
|
||||||
<input className="form-input" type="text" id="input-share-url" onChange={ev => setShareId(ev.target.value)} />
|
<input className="form-input" type="text" id="input-share-url"
|
||||||
|
value={draft.shareId}
|
||||||
|
onChange={ev => setShareId(draft, ev.target.value)} />
|
||||||
<i className={`icon icon-cross text-error ${shareUrlUnique ? "d-hide" : "d-visible"}`} />
|
<i className={`icon icon-cross text-error ${shareUrlUnique ? "d-hide" : "d-visible"}`} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -153,13 +186,16 @@ export default function Settings(props) {
|
||||||
</div>
|
</div>
|
||||||
<div className="col-9 col-sm-12">
|
<div className="col-9 col-sm-12">
|
||||||
<div className="input-group">
|
<div className="input-group">
|
||||||
<input className={`form-input ${authCodeChangeable ? "d-visible" : "d-hide"}`} type="text" readOnly={!authCodeChangeable} onChange={ev => setAuthCode(ev.target.value)} />
|
<input className={`form-input ${!draft.authCode || authCodeChangeable ? "d-visible" : "d-hide"}`} type="text"
|
||||||
|
value={draft.authCode}
|
||||||
|
onChange={ev => setAuthCode(draft, ev.target.value)} />
|
||||||
|
|
||||||
<label className="form-switch ml-2 popover popover-bottom">
|
<label className="form-switch ml-2 popover popover-bottom">
|
||||||
<input type="checkbox" checked={!!draft.fling.authCode} onChange={resetAuthCode} />
|
<input type="checkbox" checked={!!draft.authCode} onChange={ev => resetAuthCode(draft)} />
|
||||||
<i className="form-icon" /> Protected
|
<i className="form-icon" /> Protected
|
||||||
<div className="popover-container card">
|
<div className="popover-container card">
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
{draft.fling.authCode ? "Click to reset passcode" : "Set passcode to enable protection"}
|
{draft.authCode ? "Click to reset passcode" : "Set passcode to enable protection"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
@ -174,25 +210,27 @@ export default function Settings(props) {
|
||||||
</div>
|
</div>
|
||||||
<div className="col-9 col-sm-12">
|
<div className="col-9 col-sm-12">
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<select className="form-select" >
|
<select className="form-select" value={expirationType} onChange={ev => resetExpiration(draft, ev.currentTarget.value)}>
|
||||||
<option value="never">Never</option>
|
<option value="never">Never</option>
|
||||||
<option value="time">Date</option>
|
<option value="time">Date</option>
|
||||||
<option value="clicks">Clicks</option>
|
<option value="clicks">Clicks</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={"clicks" === "clicks" ? "d-visible" : "d-hide"}>
|
<div className={expirationType === "clicks" ? "d-visible" : "d-hide"}>
|
||||||
<div className="input-group">
|
<div className="input-group">
|
||||||
<span className="input-group-addon">Expire after</span>
|
<span className="input-group-addon">Expire after</span>
|
||||||
<input className="form-input" type="number" />
|
<input className="form-input" type="number" value={draft.expirationClicks} onChange={ev => setExpiration(draft, "clicks", ev.target.value)} />
|
||||||
<span className="input-group-addon">Clicks</span>
|
<span className="input-group-addon">Clicks</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={"clicks" === "time" ? "d-visible" : "d-hide"}>
|
<div className={expirationType === "time" ? "d-visible" : "d-hide"}>
|
||||||
<div className="input-group">
|
<div className="input-group">
|
||||||
<span className="input-group-addon">Expire after</span>
|
<span className="input-group-addon">Expire after</span>
|
||||||
<input className="form-input" type="date" />
|
<input className="form-input" type="date"
|
||||||
|
value={draft.expirationTime ? (new Date(draft.expirationTime)).toISOString().split('T')[0]: ""}
|
||||||
|
onChange={ev => setExpiration(draft, "time", ev.target.valueAsNumber)} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -205,15 +243,18 @@ export default function Settings(props) {
|
||||||
<div className="col-9 col-sm-12">
|
<div className="col-9 col-sm-12">
|
||||||
|
|
||||||
<label className="form-switch form-inline">
|
<label className="form-switch form-inline">
|
||||||
<input type="checkbox" id="shared" checked={draft.fling.shared} onChange={toggleSharing} />
|
<input type="checkbox" id="shared" checked={draft.shared}
|
||||||
|
onChange={ev => toggleSharing(draft, ev.target.id, ev.target.checked)} />
|
||||||
<i className="form-icon" /> Shared
|
<i className="form-icon" /> Shared
|
||||||
</label>
|
</label>
|
||||||
<label className="form-switch form-inline">
|
<label className="form-switch form-inline">
|
||||||
<input type="checkbox" id="allow-upload" checked={draft.fling.allowUpload} onChange={toggleSharing} />
|
<input type="checkbox" id="allow-upload" checked={draft.allowUpload}
|
||||||
|
onChange={ev => toggleSharing(draft, ev.target.id, ev.target.checked)} />
|
||||||
<i className="form-icon" /> Uploads
|
<i className="form-icon" /> Uploads
|
||||||
</label>
|
</label>
|
||||||
<label className="form-switch form-inline">
|
<label className="form-switch form-inline">
|
||||||
<input type="checkbox" id="direct-download" checked={draft.fling.directDownload} onChange={toggleSharing} />
|
<input type="checkbox" id="direct-download" checked={draft.directDownload}
|
||||||
|
onChange={ev => toggleSharing(draft, ev.target.id, ev.target.checked)} />
|
||||||
<i className="form-icon" /> Direct Download
|
<i className="form-icon" /> Direct Download
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -56,13 +56,6 @@ function setActiveFling(id) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _purify(o) {
|
|
||||||
if(Array.isArray(o)) {
|
|
||||||
return o.map(e => toPlainObject(cloneDeep(e)));
|
|
||||||
}
|
|
||||||
return toPlainObject(cloneDeep(o));
|
|
||||||
}
|
|
||||||
|
|
||||||
function retrieveFlings() {
|
function retrieveFlings() {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const { flings: { activeFling } } = getState();
|
const { flings: { activeFling } } = getState();
|
||||||
|
@ -94,4 +87,20 @@ function deleteFling(id) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Purify the object or array `o` to a plain, simple javascript object.
|
||||||
|
*
|
||||||
|
* This is used for two reasons on the store:
|
||||||
|
* - The state itself should be kept simple. It is a mere structure of simple
|
||||||
|
* values. Any logic acting upon the state must be defined on other layers.
|
||||||
|
* - Complex objects interact in hard to understand ways with libraries
|
||||||
|
* like react and immer. A simple object is more versatile and predictable.
|
||||||
|
*/
|
||||||
|
function _purify(o) {
|
||||||
|
if(Array.isArray(o)) {
|
||||||
|
return o.map(e => toPlainObject(cloneDeep(e)));
|
||||||
|
}
|
||||||
|
return toPlainObject(cloneDeep(o));
|
||||||
|
}
|
||||||
|
|
||||||
export { retrieveFlings, setActiveFling, deleteFling };
|
export { retrieveFlings, setActiveFling, deleteFling };
|
||||||
|
|
|
@ -12,7 +12,7 @@ let clientConfig = (token) => {
|
||||||
config.basePath = process.env.REACT_APP_API.replace(/\/+$/, '');
|
config.basePath = process.env.REACT_APP_API.replace(/\/+$/, '');
|
||||||
|
|
||||||
token = token || sessionStorage.getItem('token');
|
token = token || sessionStorage.getItem('token');
|
||||||
if(token) { config.authentications['bearer'].accessToken = token; }
|
if (token) { config.authentications['bearer'].accessToken = token; }
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
};
|
};
|
||||||
|
@ -29,4 +29,4 @@ function AuthClient(token) {
|
||||||
return new fc.AuthApi(clientConfig(token));
|
return new fc.AuthApi(clientConfig(token));
|
||||||
}
|
}
|
||||||
|
|
||||||
export {FlingClient, ArtifactClient, AuthClient, fc};
|
export { FlingClient, ArtifactClient, AuthClient, fc };
|
||||||
|
|
Loading…
Reference in a new issue