uploading and downloading works
This commit is contained in:
+1
-1
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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>
|
||||||
|
|||||||
@@ -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
@@ -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,
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+13
-8
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user