Commit b1be24a7 authored by Maxym Borodenko's avatar Maxym Borodenko Committed by Matija Obreza
Browse files

Fix: pasting image instead of text from Word on Mac OS

- Paste Markdown from Word
parent 5a49140c
......@@ -73,7 +73,11 @@
"title": "Title",
"true": "True",
"UUID": "UUID",
"yes": "Yes"
"yes": "Yes",
"basicMarkdown": "Basic markdown supported",
"fullMarkdown": "Full markdown supported",
"previewMarkdown": "Preview markdown",
"editMarkdown": "Edit Markdown"
},
"paginate": {
"numberOfItems": "{{count, number}} {{what, lowercase}}",
......
import RepositoryFile from 'model/repository/RepositoryFile';
import RepositoryService from 'service/genesys/RepositoryService';
import { log } from 'utilities/debug';
import * as UUIDv4 from 'uuid/v4';
export const uploadMarkdownAttachment = (file: File) => (dispatch, getState) => {
const metadata: RepositoryFile = new RepositoryFile();
const dummy: string = UUIDv4().replace(/\-/g, '');
metadata.originalFilename = `${dummy}_${file.name}`;
metadata.contentType = file.type;
console.log(`Uploading Markdown`, metadata);
return RepositoryService.uploadFile('/content/markdown', file, metadata)
.then((repoFile: RepositoryFile) => {
return repoFile;
}).catch((error) => {
log('Upload error', error);
});
};
......@@ -17,7 +17,7 @@ import Loading from 'ui/common/Loading';
import PrettyDate from 'ui/common/time/PrettyDate';
import ActionButton from 'ui/common/buttons/ActionButton';
import BackButton from 'ui/common/buttons/BackButton';
import Markdown from 'ui/catalog/markdown';
import Markdown from 'ui/common/markdown';
interface IDisplayPageProps extends React.ClassAttributes<any> {
t: any;
......
......@@ -277,10 +277,8 @@ class FileRepositoryService {
// console.log(`Fetching from ${apiUrl}`);
const data = new FormData();
data.append('file', file);
if (metadata) {
data.append('metadata', JSON.stringify(metadata));
}
// data.append('metadata', new Blob([ JSON.stringify(metadata) ], { type : 'application/json' }));
// data.append('metadata', JSON.stringify(metadata));
data.append('metadata', new Blob([ JSON.stringify(metadata) ], { type : 'application/json' }));
const content = { data };
return axiosBackend.request({
......
......@@ -6,7 +6,7 @@ import {SUBSET_FORM} from 'subsets/constants';
import Validators from 'utilities/Validators';
import {TextField} from 'ui/common/text-field';
import MarkdownField from 'ui/common/markdown/MarkdownField';
import {MarkdownFieldWithAttach} from 'ui/common/markdown/MarkdownFieldWithAttach';
import CropSelector from 'crop/ui/c/CropSelector';
interface ILoginContainerProps extends React.ClassAttributes<any> {
......@@ -43,7 +43,7 @@ class BasicInfoStep extends React.Component<ILoginContainerProps, any> {
/>
<Field
name="description"
component={ MarkdownField }
component={ MarkdownFieldWithAttach }
label={ t('subsets.dashboard.p.stepper.basicInfo.subsetDescription') }
placeholder={ t('subsets.dashboard.p.stepper.basicInfo.subsetDescriptionPlaceholder') }
/>
......
import * as React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import Markdown from 'ui/common/markdown';
import OriginalMarkdownField from 'ui/common/markdown/MarkdownField';
import FormControl from 'ui/common/forms/FormControl';
import Input from '@material-ui/core/Input';
import FormHelperText from '@material-ui/core/FormHelperText';
class MarkdownField extends OriginalMarkdownField {
// ref to input textarea
private noReactUpdates: boolean = false;
public constructor(props: any) {
super(props);
this.stopReactUpdate = this.stopReactUpdate.bind(this);
this.resumeReactUpdate = this.resumeReactUpdate.bind(this);
}
private stopReactUpdate = () => {
console.log('Stopping React updates');
this.noReactUpdates = true;
}
private resumeReactUpdate = () => {
console.log('Resuming React updates');
this.noReactUpdates = false;
}
// We need to use this while dragging so that component is not rerendered
public shouldComponentUpdate(nextProps, nextState) {
// console.log(`shouldComponentUpdate ${! this.noReactUpdates}`); // , this.props, nextProps, this.state, nextState);
return ! this.noReactUpdates;
}
public render() {
const {classes, basicMarkdown, input, label, required, meta, meta: {touched, error}, ...custom} = this.props;
const basic: boolean = basicMarkdown === undefined || null ? false : basicMarkdown;
if (basic) {
return (
<FormControl fullWidth required={ required } meta={ meta } label={ label }>
<Input error={ touched && error } { ...input } { ...custom } />
<FormHelperText>
<span>Basic markdown supported: * **</span>
</FormHelperText>
</FormControl>
);
}
return (
<div>
{ ! this.state.previewMode ?
<FormControl fullWidth required={ required } meta={ meta } label={ label }>
<Input error={ touched && error } multiline { ...input } { ...custom } />
<FormHelperText><a onClick={ this.onChangePreviewMode }>Preview Markdown</a> <span> Full markdown supported</span></FormHelperText>
</FormControl>
:
<FormControl fullWidth required={ required } meta={ meta } label={ label }>
<div style={ { paddingTop: '1.5rem' } }>
<Markdown source={ input.value } />
</div>
<FormHelperText><a onClick={ this.onChangePreviewMode }>Edit Markdown</a></FormHelperText>
</FormControl>
}
</div>
);
}
}
const mapDispatchToProps = (dispatch) => bindActionCreators({
}, dispatch);
const _markdownField = connect(null, mapDispatchToProps)(MarkdownField);
export { Markdown as default, _markdownField as MarkdownField };
import * as React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import {translate} from 'react-i18next';
import { uploadMarkdownAttachment } from 'actions/repository';
import Markdown from 'ui/common/markdown';
import OriginalMarkdownField from 'ui/common/markdown/MarkdownField';
import FormControl from 'ui/common/forms/FormControl';
import Input from '@material-ui/core/Input';
import FormHelperText from '@material-ui/core/FormHelperText';
import {insertAtCaret} from 'utilities';
class MarkdownField extends OriginalMarkdownField {
// ref to input textarea
private textarea: any;
private noReactUpdates: boolean = false;
public constructor(props: any) {
super(props);
this.setRef = this.setRef.bind(this);
this.dataDropped = this.dataDropped.bind(this);
this.dataPasted = this.dataPasted.bind(this);
this.stopReactUpdate = this.stopReactUpdate.bind(this);
this.resumeReactUpdate = this.resumeReactUpdate.bind(this);
}
// Bind event listeners
private setRef = (textarea): void => {
this.textarea = textarea;
if (textarea) {
this.textarea.addEventListener('drop', this.dataDropped, false);
this.textarea.addEventListener('dragenter', this.stopReactUpdate, false);
this.textarea.addEventListener('dragexit', this.resumeReactUpdate, false);
this.textarea.addEventListener('paste', this.dataPasted, false);
}
}
// Unbind event listeners
public componentWillUnmount() {
if (this.textarea) {
this.textarea.removeEventListener('drop', this.dataDropped, false);
this.textarea.removeEventListener('dragenter', this.stopReactUpdate, false);
this.textarea.removeEventListener('dragexit', this.resumeReactUpdate, false);
this.textarea.removeEventListener('paste', this.dataPasted, false);
}
}
private stopReactUpdate = () => {
console.log('Stopping React updates');
this.noReactUpdates = true;
}
private resumeReactUpdate = () => {
console.log('Resuming React updates');
this.noReactUpdates = false;
}
private dataDropped = (e) => {
const { input } = this.props;
e.preventDefault();
const { dataTransfer: { items, files } } = e;
const list = [ ...items ].filter((i) => i.kind === 'file').map((i) => i.getAsFile()) || files;
const p = [];
for (const f of list) {
const result = this.addFile(f);
p.push(result);
}
Promise.all(p).then(() => {
input.onChange(this.textarea.value);
this.resumeReactUpdate();
});
}
private dataPasted = async (e) => {
const { clipboardData: { files } } = e;
const types: string[] = e.clipboardData.types;
if (files.length && (types.indexOf('text/plain') === -1)) {
e.preventDefault();
for (const f of files) {
await this.addFile(f);
}
} else {
// NOOP
}
}
private addFile = (file: File) => {
const { uploadMarkdownAttachment } = this.props;
const textarea = this.textarea;
// only trigger the upload in case of a real picture
if (! file.type.startsWith('image/')) {
return;
}
return uploadMarkdownAttachment(file)
.then((r) => {
if (r.contentType.startsWith('image/')) {
insertAtCaret(textarea, `![${file.name}](/proxy/uploads/${r.uuid})`);
} else {
insertAtCaret(textarea, `[${file.name}](/proxy/uploads/${r.uuid})`);
}
return r;
});
}
// We need to use this while dragging so that component is not rerendered
public shouldComponentUpdate(nextProps, nextState) {
return ! this.noReactUpdates;
}
public render() {
const {classes, t, basicMarkdown, input, label, required, meta, ...custom} = this.props;
const basic: boolean = basicMarkdown === undefined || null ? false : basicMarkdown;
if (basic) {
return (
<FormControl fullWidth required={ required } meta={ meta } label={ label }>
<Input error={ meta && meta.touched && meta.error } { ...input } { ...custom } />
<FormHelperText>
<span>{ t('common:label.basicMarkdown') }: <code>*</code> <code>**</code></span>
</FormHelperText>
</FormControl>
);
}
return (
<div>
{ ! this.state.previewMode ?
<FormControl fullWidth required={ required } meta={ meta } label={ label }>
<Input error={ meta && meta.touched && meta.error } inputRef={ this.setRef } multiline { ...input } { ...custom } />
<FormHelperText>
<a onClick={ this.onChangePreviewMode }>{ t('common:label.previewMarkdown') }</a> <span>{ t('common:label.fullMarkdown') }</span>
</FormHelperText>
</FormControl>
:
<FormControl fullWidth required={ required } meta={ meta } label={ label }>
<div style={ { paddingTop: '1.5rem' } }>
<Markdown source={ input.value } />
</div>
<FormHelperText><a onClick={ this.onChangePreviewMode }>{ t('common:label.editMarkdown') }</a></FormHelperText>
</FormControl>
}
</div>
);
}
}
const mapDispatchToProps = (dispatch) => bindActionCreators({
uploadMarkdownAttachment,
}, dispatch);
export const MarkdownFieldWithAttach = translate()(connect(null, mapDispatchToProps)(MarkdownField));
......@@ -8,7 +8,7 @@ import { navigateTo } from 'actions/navigation';
import ContentHeader from 'ui/common/heading/ContentHeader';
import Grid from '@material-ui/core/Grid';
import Markdown from 'ui/catalog/markdown';
import Markdown from 'ui/common/markdown';
import { Link } from 'react-router-dom';
import Input from '@material-ui/core/Input';
import InputAdornment from '@material-ui/core/InputAdornment';
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment