web-client
web-client copied to clipboard
Implement copy/paste image like github
Hi,
According to this demo, it's possible to upload images by copy/pasting directly into the React Markdown Editor used by reconmap.
Here is the discussion on the issue : https://github.com/andrerpena/react-mde/issues/189 And the PR which add the functionality : https://github.com/andrerpena/react-mde/pull/243 There is also a drag and drop support implemented here : https://github.com/andrerpena/react-mde/pull/268
I've checked the version used in reconmap (11.5) and the version where this functionality has been implemented (10.0), it's all good.
I'm not a react developer and I don't know how to correctly implement this. I think you should be more quicker than me.
It could be a killing feature. Currently, it's a pain to put image in text
https://codesandbox.io/s/react-mde-latest-forked-f9ti5?file=/src/index.js
import * as React from "react";
import ReactMde from "react-mde";
import ReactDOM from "react-dom";
import ReactMarkdown from "react-markdown";
import "./styles.css";
import "react-mde/lib/styles/css/react-mde-all.css";
[...] REDACTED
const save = async function* (data) {
// Promise that waits for "time" milliseconds
const wait = function (time) {
return new Promise((a, r) => {
setTimeout(() => a(), time);
});
};
// Upload "data" to your server
// Use XMLHttpRequest.send to send a FormData object containing
// "data"
// Check this question: https://stackoverflow.com/questions/18055422/how-to-receive-php-image-data-over-copy-n-paste-javascript-with-xmlhttprequest
await wait(2000);
// yields the URL that should be inserted in the markdown
yield "https://picsum.photos/300";
await wait(2000);
// returns true meaning that the save was successful
return true;
};
return (
<div className="container">
<ReactMde
value={value}
onChange={setValue}
selectedTab={selectedTab}
onTabChange={setSelectedTab}
generateMarkdownPreview={(markdown) =>
Promise.resolve(<ReactMarkdown source={markdown} />)
}
loadSuggestions={loadSuggestions}
childProps={{
writeButton: {
tabIndex: -1
}
}}
paste={{
saveImage: save
}}
/>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Here is the code to handle upload from PHP, but maybe this code is always available in reconmap as we can upload file in attachments section https://stackoverflow.com/questions/18055422/how-to-receive-php-image-data-over-copy-n-paste-javascript-with-xmlhttpreques
<?php
if( isset( $_FILES['file'] ) ) {
$file_contents = file_get_contents( $_FILES['file']['tmp_name'] );
header("Content-Type: " . $_FILES['file']['type'] );
die($file_contents);
}
else {
header("HTTP/1.1 400 Bad Request");
}
print_r($_FILES);
?>
<script>
document.onpaste = function (e) {
var items = e.clipboardData.items;
var files = [];
for( var i = 0, len = items.length; i < len; ++i ) {
var item = items[i];
if( item.kind === "file" ) {
submitFileForm(item.getAsFile(), "paste");
}
}
};
function submitFileForm(file, type) {
var extension = file.type.match(/\/([a-z0-9]+)/i)[1].toLowerCase();
var formData = new FormData();
formData.append('file', file, "image_file");
formData.append('extension', extension );
formData.append("mimetype", file.type );
formData.append('submission-type', type);
var xhr = new XMLHttpRequest();
xhr.responseType = "blob";
xhr.open('POST', '<?php echo basename(__FILE__); ?>');
xhr.onload = function () {
if (xhr.status == 200) {
var img = new Image();
img.src = (window.URL || window.webkitURL)
.createObjectURL( xhr.response );
document.body.appendChild(img);
}
};
xhr.send(formData);
}
</script>
I have a quick look into it, the attachments API endpoint could be used
The https://github.com/reconmap/web-client/blob/master/src/components/ui/forms/MarkdownEditor.js could look like this
import { useState } from 'react';
import ReactMarkdown from 'react-markdown';
import ReactMde from 'react-mde';
import './MarkdownEditor.css';
const MarkdownEditor = ({ name: editorName, value, onChange: onFormChange }) => {
const [selectedTab, setSelectedTab] = useState('write');
const save = async function* (data) {
// Promise that waits for "time" milliseconds
const wait = function (time) {
return new Promise((a, r) => {
setTimeout(() => a(), time);
});
};
// Upload "data" to your server
// Use XMLHttpRequest.send to send a FormData object containing
// "data"
// Check this question: https://stackoverflow.com/questions/18055422/how-to-receive-php-image-data-over-copy-n-paste-javascript-with-xmlhttprequest
await wait(2000);
// yields the URL that should be inserted in the markdown
yield "https://picsum.photos/300";
await wait(2000);
// returns true meaning that the save was successful
return true;
};
return <ReactMde
value={value}
onChange={editorValue => onFormChange({ target: { name: editorName, value: editorValue } })}
selectedTab={selectedTab}
onTabChange={setSelectedTab}
generateMarkdownPreview={markdown =>
Promise.resolve(<ReactMarkdown>{markdown}</ReactMarkdown>)
}
paste={{
saveImage: save
}}
/>
}
export default MarkdownEditor;
It miss only the upload part, which could reuse https://github.com/reconmap/web-client/blob/master/src/components/attachments/Dropzone.js, precisely this part
const onUploadButtonClick = ev => {
const formData = new FormData();
formData.append('parentType', parentType);
formData.append('parentId', parentId);
acceptedFiles.forEach(file => {
formData.append('attachment[]', file);
})
let uri = '/attachments';
if (attachmentId) {
formData.append('attachmentId', attachmentId);
uri = `/attachments/${attachmentId}`;
}
secureApiFetch(uri, {
method: 'POST',
body: formData
})
.then(() => {
setAcceptedFiles([]);
if (onUploadFinished) onUploadFinished();
})
.catch(err => console.error(err));
}
@santiagolizardo What do you think ?
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
@santiagolizardo Could you reopen this issue please and prevent the stale bot to close it Thanks