uploading and downloading works

This commit is contained in:
or-else
2020-10-25 16:19:56 -07:00
parent 28d825c681
commit 97c8114fb7
10 changed files with 94 additions and 44 deletions
+1 -1
View File
@@ -88,7 +88,7 @@ export function asEmail(val) {
// http:/example.com - not ok. // http:/example.com - not ok.
// ' ↲ https://example.com' - not ok. (↲ means carriage return) // ' ↲ https://example.com' - not ok. (↲ means carriage return)
export function isUrlRelative(url) { export function isUrlRelative(url) {
return !/^\s*([a-z][a-z0-9+.-]*:|\/\/)/im.test(url); return url && !/^\s*([a-z][a-z0-9+.-]*:|\/\/)/im.test(url);
} }
// Ensure URL does not present an XSS risk. Optional allowedSchemes may contain an array of // Ensure URL does not present an XSS risk. Optional allowedSchemes may contain an array of
+10
View File
@@ -91,6 +91,7 @@ class MessagesView extends React.Component {
this.handleEnablePeer = this.handleEnablePeer.bind(this); this.handleEnablePeer = this.handleEnablePeer.bind(this);
this.handleAttachFile = this.handleAttachFile.bind(this); this.handleAttachFile = this.handleAttachFile.bind(this);
this.handleAttachImage = this.handleAttachImage.bind(this); this.handleAttachImage = this.handleAttachImage.bind(this);
this.handleCancelUpload = this.handleCancelUpload.bind(this);
this.postReadNotification = this.postReadNotification.bind(this); this.postReadNotification = this.postReadNotification.bind(this);
this.clearNotificationQueue = this.clearNotificationQueue.bind(this); this.clearNotificationQueue = this.clearNotificationQueue.bind(this);
@@ -761,6 +762,14 @@ class MessagesView extends React.Component {
); );
} }
handleCancelUpload(seq, uploader) {
const found = this.state.messages.find(msg => msg.seq == seq);
if (found) {
found._cancelled = true;
}
uploader.cancel();
}
render() { render() {
const {formatMessage} = this.props.intl; const {formatMessage} = this.props.intl;
@@ -862,6 +871,7 @@ class MessagesView extends React.Component {
onImagePreview={this.handleImagePostview} onImagePreview={this.handleImagePostview}
onFormResponse={this.handleFormResponse} onFormResponse={this.handleFormResponse}
onError={this.props.onError} onError={this.props.onError}
onCancelUpload={this.handleCancelUpload}
key={msg.seq} /> key={msg.seq} />
); );
} }
+26 -13
View File
@@ -19,11 +19,21 @@ export default class Attachment extends React.Component {
} }
downloadFile(url, filename, mimetype) { downloadFile(url, filename, mimetype) {
var downloader = this.props.tinode.getLargeFileHelper(); if (!url) {
this.props.onError("Invalid download URL '" + url + "'");
return;
}
const downloader = this.props.tinode.getLargeFileHelper();
this.setState({downloader: downloader}); this.setState({downloader: downloader});
downloader.download(url, filename, mimetype, (loaded) => { downloader.download(url, filename, mimetype,
this.setState({progress: loaded / this.props.size}); (loaded) => {
}).then(() => { this.setState({progress: loaded / this.props.size});
},
(err) => {
this.props.onError(err, 'err');
}
).then(() => {
this.setState({downloader: null, progress: 0}); this.setState({downloader: null, progress: 0});
}).catch((err) => { }).catch((err) => {
if (err) { if (err) {
@@ -34,7 +44,7 @@ export default class Attachment extends React.Component {
} }
handleCancel() { handleCancel() {
if (this.props.uploader) { if (this.props.uploading) {
this.props.onCancelUpload(); this.props.onCancelUpload();
} else if (this.state.downloader) { } else if (this.state.downloader) {
this.state.downloader.cancel(); this.state.downloader.cancel();
@@ -54,7 +64,7 @@ export default class Attachment extends React.Component {
// If the URL is relative use LargeFileHelper to attach authentication // If the URL is relative use LargeFileHelper to attach authentication
// credentials to the request. // credentials to the request.
let url, helperFunc; let url, helperFunc;
if (!this.props.uploader && !this.state.downloader && isUrlRelative(this.props.downloadUrl)) { if (!this.props.uploading && !this.state.downloader && isUrlRelative(this.props.downloadUrl)) {
// Relative URL. Use download helper. // Relative URL. Use download helper.
url = '#'; url = '#';
helperFunc = (e) => { helperFunc = (e) => {
@@ -62,22 +72,25 @@ export default class Attachment extends React.Component {
this.downloadFile(this.props.downloadUrl, this.props.filename, this.props.mimetype); this.downloadFile(this.props.downloadUrl, this.props.filename, this.props.mimetype);
}; };
} else { } else {
url = sanitizeUrl(this.props.downloadUrl) || 'about:blank'; url = sanitizeUrl(this.props.downloadUrl);
helperFunc = null; helperFunc = null;
} }
const downloadWidget = <><i className="material-icons">file_download</i> <FormattedMessage id="save_attachment"
defaultMessage="save" description="Call to save an attachment" /></>;
return ( return (
<div className="attachment"> <div className="attachment">
<div><i className="material-icons big gray">insert_drive_file</i></div> <div><i className="material-icons big gray">insert_drive_file</i></div>
<div className="flex-column"> <div className="flex-column">
<div>{filename} {size}</div> <div>{filename} {size}</div>
{this.props.uploader || this.state.downloader ? {this.props.uploading || this.state.downloader ?
<FileProgress progress={this.props.uploader ? this.props.progress : this.state.progress} <FileProgress progress={this.props.uploading ? this.props.progress : this.state.progress}
onCancel={this.handleCancel} /> onCancel={this.handleCancel} />
: :
<div><a href={url} download={this.props.filename} onClick={helperFunc} > <div>{url ?
<i className="material-icons">file_download</i> <FormattedMessage id="save_attachment" <a href={url} download={this.props.filename} onClick={helperFunc}>{downloadWidget}</a>
defaultMessage="save" description="Call to save an attachment" /> :
</a></div> <span className="light-gray">{downloadWidget}</span>
}</div>
} }
</div> </div>
</div> </div>
+2 -2
View File
@@ -69,7 +69,7 @@ export default class ChatMessage extends React.Component {
} }
handleCancelUpload() { handleCancelUpload() {
this.props.uploader.cancel(); this.props.onCancelUpload(this.props.seq, this.props.uploader);
} }
render() { render() {
@@ -92,7 +92,7 @@ export default class ChatMessage extends React.Component {
attachments.push(<Attachment attachments.push(<Attachment
tinode={this.props.tinode} tinode={this.props.tinode}
downloadUrl={Drafty.getDownloadUrl(att)} downloadUrl={Drafty.getDownloadUrl(att)}
filename={att.name} uploader={Drafty.isProcessing(att)} filename={att.name} uploading={Drafty.isProcessing(att)}
mimetype={att.mime} size={Drafty.getEntitySize(att)} mimetype={att.mime} size={Drafty.getEntitySize(att)}
progress={this.state.progress} progress={this.state.progress}
onCancelUpload={this.handleCancelUpload} onCancelUpload={this.handleCancelUpload}
+38 -16
View File
@@ -732,7 +732,7 @@ function asEmail(val) {
return null; return null;
} }
function isUrlRelative(url) { function isUrlRelative(url) {
return !/^\s*([a-z][a-z0-9+.-]*:|\/\/)/im.test(url); return url && !/^\s*([a-z][a-z0-9+.-]*:|\/\/)/im.test(url);
} }
function sanitizeUrl(url, allowedSchemes) { function sanitizeUrl(url, allowedSchemes) {
if (!url) { if (!url) {
@@ -2921,6 +2921,7 @@ class MessagesView extends react__WEBPACK_IMPORTED_MODULE_0___default.a.Componen
this.handleEnablePeer = this.handleEnablePeer.bind(this); this.handleEnablePeer = this.handleEnablePeer.bind(this);
this.handleAttachFile = this.handleAttachFile.bind(this); this.handleAttachFile = this.handleAttachFile.bind(this);
this.handleAttachImage = this.handleAttachImage.bind(this); this.handleAttachImage = this.handleAttachImage.bind(this);
this.handleCancelUpload = this.handleCancelUpload.bind(this);
this.postReadNotification = this.postReadNotification.bind(this); this.postReadNotification = this.postReadNotification.bind(this);
this.clearNotificationQueue = this.clearNotificationQueue.bind(this); this.clearNotificationQueue = this.clearNotificationQueue.bind(this);
this.readNotificationQueue = []; this.readNotificationQueue = [];
@@ -3611,6 +3612,16 @@ class MessagesView extends react__WEBPACK_IMPORTED_MODULE_0___default.a.Componen
}); });
} }
handleCancelUpload(seq, uploader) {
const found = this.state.messages.find(msg => msg.seq == seq);
if (found) {
found._cancelled = true;
}
uploader.cancel();
}
render() { render() {
const { const {
formatMessage formatMessage
@@ -3711,6 +3722,7 @@ class MessagesView extends react__WEBPACK_IMPORTED_MODULE_0___default.a.Componen
onImagePreview: this.handleImagePostview, onImagePreview: this.handleImagePostview,
onFormResponse: this.handleFormResponse, onFormResponse: this.handleFormResponse,
onError: this.props.onError, onError: this.props.onError,
onCancelUpload: this.handleCancelUpload,
key: msg.seq key: msg.seq
})); }));
} }
@@ -6282,7 +6294,12 @@ class Attachment extends react__WEBPACK_IMPORTED_MODULE_0___default.a.Component
} }
downloadFile(url, filename, mimetype) { downloadFile(url, filename, mimetype) {
var downloader = this.props.tinode.getLargeFileHelper(); if (!url) {
this.props.onError("Invalid download URL '" + url + "'");
return;
}
const downloader = this.props.tinode.getLargeFileHelper();
this.setState({ this.setState({
downloader: downloader downloader: downloader
}); });
@@ -6290,6 +6307,8 @@ class Attachment extends react__WEBPACK_IMPORTED_MODULE_0___default.a.Component
this.setState({ this.setState({
progress: loaded / this.props.size progress: loaded / this.props.size
}); });
}, err => {
this.props.onError(err, 'err');
}).then(() => { }).then(() => {
this.setState({ this.setState({
downloader: null, downloader: null,
@@ -6308,7 +6327,7 @@ class Attachment extends react__WEBPACK_IMPORTED_MODULE_0___default.a.Component
} }
handleCancel() { handleCancel() {
if (this.props.uploader) { if (this.props.uploading) {
this.props.onCancelUpload(); this.props.onCancelUpload();
} else if (this.state.downloader) { } else if (this.state.downloader) {
this.state.downloader.cancel(); this.state.downloader.cancel();
@@ -6327,7 +6346,7 @@ class Attachment extends react__WEBPACK_IMPORTED_MODULE_0___default.a.Component
}, "(", Object(_lib_strformat_js__WEBPACK_IMPORTED_MODULE_3__["bytesToHumanSize"])(this.props.size), ")") : null; }, "(", Object(_lib_strformat_js__WEBPACK_IMPORTED_MODULE_3__["bytesToHumanSize"])(this.props.size), ")") : null;
let url, helperFunc; let url, helperFunc;
if (!this.props.uploader && !this.state.downloader && Object(_lib_utils_js__WEBPACK_IMPORTED_MODULE_4__["isUrlRelative"])(this.props.downloadUrl)) { if (!this.props.uploading && !this.state.downloader && Object(_lib_utils_js__WEBPACK_IMPORTED_MODULE_4__["isUrlRelative"])(this.props.downloadUrl)) {
url = '#'; url = '#';
helperFunc = e => { helperFunc = e => {
@@ -6335,29 +6354,32 @@ class Attachment extends react__WEBPACK_IMPORTED_MODULE_0___default.a.Component
this.downloadFile(this.props.downloadUrl, this.props.filename, this.props.mimetype); this.downloadFile(this.props.downloadUrl, this.props.filename, this.props.mimetype);
}; };
} else { } else {
url = Object(_lib_utils_js__WEBPACK_IMPORTED_MODULE_4__["sanitizeUrl"])(this.props.downloadUrl) || 'about:blank'; url = Object(_lib_utils_js__WEBPACK_IMPORTED_MODULE_4__["sanitizeUrl"])(this.props.downloadUrl);
helperFunc = null; helperFunc = null;
} }
const downloadWidget = react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(react__WEBPACK_IMPORTED_MODULE_0___default.a.Fragment, null, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("i", {
className: "material-icons"
}, "file_download"), " ", react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__["FormattedMessage"], {
id: "save_attachment",
defaultMessage: "save"
}));
return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", {
className: "attachment" className: "attachment"
}, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", null, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("i", { }, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", null, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("i", {
className: "material-icons big gray" className: "material-icons big gray"
}, "insert_drive_file")), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { }, "insert_drive_file")), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", {
className: "flex-column" className: "flex-column"
}, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", null, filename, " ", size), this.props.uploader || this.state.downloader ? react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_file_progress_jsx__WEBPACK_IMPORTED_MODULE_2__["default"], { }, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", null, filename, " ", size), this.props.uploading || this.state.downloader ? react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_file_progress_jsx__WEBPACK_IMPORTED_MODULE_2__["default"], {
progress: this.props.uploader ? this.props.progress : this.state.progress, progress: this.props.uploading ? this.props.progress : this.state.progress,
onCancel: this.handleCancel onCancel: this.handleCancel
}) : react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", null, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("a", { }) : react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", null, url ? react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("a", {
href: url, href: url,
download: this.props.filename, download: this.props.filename,
onClick: helperFunc onClick: helperFunc
}, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("i", { }, downloadWidget) : react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("span", {
className: "material-icons" className: "light-gray"
}, "file_download"), " ", react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__["FormattedMessage"], { }, downloadWidget))));
id: "save_attachment",
defaultMessage: "save"
})))));
} }
} }
@@ -6591,7 +6613,7 @@ class ChatMessage extends react__WEBPACK_IMPORTED_MODULE_0___default.a.Component
} }
handleCancelUpload() { handleCancelUpload() {
this.props.uploader.cancel(); this.props.onCancelUpload(this.props.seq, this.props.uploader);
} }
render() { render() {
@@ -6612,7 +6634,7 @@ class ChatMessage extends react__WEBPACK_IMPORTED_MODULE_0___default.a.Component
tinode: this.props.tinode, tinode: this.props.tinode,
downloadUrl: tinode_sdk__WEBPACK_IMPORTED_MODULE_2__["Drafty"].getDownloadUrl(att), downloadUrl: tinode_sdk__WEBPACK_IMPORTED_MODULE_2__["Drafty"].getDownloadUrl(att),
filename: att.name, filename: att.name,
uploader: tinode_sdk__WEBPACK_IMPORTED_MODULE_2__["Drafty"].isProcessing(att), uploading: tinode_sdk__WEBPACK_IMPORTED_MODULE_2__["Drafty"].isProcessing(att),
mimetype: att.mime, mimetype: att.mime,
size: tinode_sdk__WEBPACK_IMPORTED_MODULE_2__["Drafty"].getEntitySize(att), size: tinode_sdk__WEBPACK_IMPORTED_MODULE_2__["Drafty"].getEntitySize(att),
progress: this.state.progress, progress: this.state.progress,
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+13 -8
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long