Rich Text Editor (WIP)
This commit is contained in:
@@ -263,7 +263,7 @@ export function submitCompose(group, replyToId = null, router, isStandalone) {
|
||||
if (!me) return;
|
||||
|
||||
let status = getState().getIn(['compose', 'text'], '');
|
||||
const markdown = getState().getIn(['compose', 'markdown'], '');
|
||||
let markdown = getState().getIn(['compose', 'markdown'], '');
|
||||
const media = getState().getIn(['compose', 'media_attachments']);
|
||||
|
||||
// : hack :
|
||||
@@ -276,10 +276,13 @@ export function submitCompose(group, replyToId = null, router, isStandalone) {
|
||||
}
|
||||
return hasProtocol ? match : `http://${match}`
|
||||
})
|
||||
// markdown = statusMarkdown.replace(urlRegex, (match) =>{
|
||||
// const hasProtocol = match.startsWith('https://') || match.startsWith('http://')
|
||||
// return hasProtocol ? match : `http://${match}`
|
||||
// })
|
||||
markdown = markdown.replace(urlRegex, (match) =>{
|
||||
const hasProtocol = match.startsWith('https://') || match.startsWith('http://')
|
||||
if (!hasProtocol) {
|
||||
if (status.indexOf(`@${match}`) > -1) return match
|
||||
}
|
||||
return hasProtocol ? match : `http://${match}`
|
||||
})
|
||||
|
||||
const inReplyToId = getState().getIn(['compose', 'in_reply_to'], null) || replyToId
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import escapeTextContentForBrowser from 'escape-html';
|
||||
import emojify from '../../components/emoji/emoji';
|
||||
import { unescapeHTML } from '../../utils/html';
|
||||
import { expandSpoilers } from '../../initial_state';
|
||||
import escapeTextContentForBrowser from 'escape-html'
|
||||
import { markdownToDraft } from 'markdown-draft-js'
|
||||
import { Remarkable } from 'remarkable'
|
||||
import * as entities from 'entities'
|
||||
import emojify from '../../components/emoji/emoji'
|
||||
import { unescapeHTML } from '../../utils/html'
|
||||
import { expandSpoilers } from '../../initial_state'
|
||||
|
||||
const domParser = new DOMParser();
|
||||
const domParser = new DOMParser()
|
||||
|
||||
const makeEmojiMap = record => record.emojis.reduce((obj, emoji) => {
|
||||
obj[`:${emoji.shortcode}:`] = emoji;
|
||||
@@ -63,8 +66,40 @@ export function normalizeStatus(status, normalOldStatus) {
|
||||
const spoilerText = normalStatus.spoiler_text || '';
|
||||
const searchContent = [spoilerText, status.content].join('\n\n').replace(/<br\s*\/?>/g, '\n').replace(/<\/p><p>/g, '\n\n');
|
||||
const emojiMap = makeEmojiMap(normalStatus);
|
||||
const theContent = !!normalStatus.rich_content ? normalStatus.rich_content : normalStatus.content;
|
||||
|
||||
let theContent
|
||||
if (!!normalStatus.rich_content) {
|
||||
theContent = normalStatus.rich_content
|
||||
// let rawObject = markdownToDraft(theContent, {
|
||||
// preserveNewlines: true,
|
||||
// remarkablePreset: 'commonmark',
|
||||
// remarkableOptions: {
|
||||
// enable: {
|
||||
// inline: ['del', 'ins'],
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
const md = new Remarkable({
|
||||
html: false,
|
||||
breaks: true,
|
||||
})
|
||||
let html = md.render(theContent)
|
||||
html = entities.decodeHTML(html)
|
||||
|
||||
theContent = html
|
||||
|
||||
console.log("html:", html)
|
||||
console.log("theContent:", theContent)
|
||||
console.log("status:", status)
|
||||
console.log("normalStatus:", normalStatus)
|
||||
// console.log("rawObject:", rawObject)
|
||||
} else {
|
||||
theContent = normalStatus.content
|
||||
}
|
||||
// let theContent = !!normalStatus.rich_content ? normalStatus.rich_content : normalStatus.content;
|
||||
|
||||
|
||||
normalStatus.search_index = domParser.parseFromString(searchContent, 'text/html').documentElement.textContent;
|
||||
normalStatus.contentHtml = emojify(theContent, emojiMap, false, true);
|
||||
normalStatus.spoilerHtml = emojify(escapeTextContentForBrowser(spoilerText), emojiMap);
|
||||
@@ -86,3 +121,21 @@ export function normalizePoll(poll) {
|
||||
|
||||
return normalPoll;
|
||||
}
|
||||
|
||||
|
||||
// <p><h1>attention!</h1></p>
|
||||
// <p>#test @bob #nice https://bob.com http://techcrunch.com <del>strike it</del></p>
|
||||
// <p><del>https://twitter.com</del></p>
|
||||
// <p><em>@bobitalic</em></p>
|
||||
// <p><pre><code>jonincode</code></pre></p>
|
||||
|
||||
// # attention!
|
||||
// #test @bob #nice https://bob.com http://techcrunch.com ~~strike it~~
|
||||
|
||||
// ~~https://twitter.com~~
|
||||
|
||||
// _@bobitalic_
|
||||
|
||||
// ```
|
||||
// jonincode
|
||||
// ```
|
||||
@@ -109,6 +109,7 @@ export function fetchStatus(id) {
|
||||
}).then(() => {
|
||||
dispatch(fetchStatusSuccess(skipLoading));
|
||||
}, () => api(getState).get(`/api/v1/statuses/${id}`).then(response => {
|
||||
console.log("response.data:", response.data)
|
||||
dispatch(importFetchedStatus(response.data));
|
||||
dispatch(fetchStatusSuccess(skipLoading));
|
||||
})).catch(error => {
|
||||
|
||||
@@ -53,11 +53,12 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
|
||||
tokenStart: 0,
|
||||
}
|
||||
|
||||
onChange = (e, value, selectionStart, markdown) => {
|
||||
onChange = (e, value, markdown, selectionStart) => {
|
||||
if (!isObject(e)) {
|
||||
e = {
|
||||
target: {
|
||||
value,
|
||||
markdown,
|
||||
selectionStart,
|
||||
},
|
||||
}
|
||||
@@ -65,8 +66,6 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
|
||||
|
||||
const [ tokenStart, token ] = textAtCursorMatchesToken(e.target.value, e.target.selectionStart, this.props.searchTokens);
|
||||
|
||||
// console.log('onChange', e.target.value, e.target, this.textbox, tokenStart, token)
|
||||
|
||||
if (token !== null && this.state.lastToken !== token) {
|
||||
this.setState({ lastToken: token, selectedSuggestion: 0, tokenStart });
|
||||
this.props.onSuggestionsFetchRequested(token);
|
||||
@@ -75,7 +74,7 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
|
||||
this.props.onSuggestionsClearRequested();
|
||||
}
|
||||
|
||||
this.props.onChange(e, markdown);
|
||||
this.props.onChange(e);
|
||||
}
|
||||
|
||||
onKeyDown = (e) => {
|
||||
@@ -259,7 +258,7 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
|
||||
return (
|
||||
<Fragment>
|
||||
<div className={textareaContainerClasses}>
|
||||
<Textarea
|
||||
{/*<Textarea
|
||||
inputRef={this.setTextbox}
|
||||
className={textareaClasses}
|
||||
disabled={disabled}
|
||||
@@ -273,9 +272,9 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
|
||||
onBlur={this.onBlur}
|
||||
onPaste={this.onPaste}
|
||||
aria-autocomplete='list'
|
||||
/>
|
||||
/>*/}
|
||||
|
||||
{/*<Composer
|
||||
<Composer
|
||||
inputRef={this.setTextbox}
|
||||
disabled={disabled}
|
||||
placeholder={placeholder}
|
||||
@@ -288,7 +287,7 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
|
||||
onBlur={this.onBlur}
|
||||
onPaste={this.onPaste}
|
||||
small={small}
|
||||
/>*/}
|
||||
/>
|
||||
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
ContentState,
|
||||
} from 'draft-js'
|
||||
import { draftToMarkdown } from 'markdown-draft-js'
|
||||
// import draftToMarkdown from 'draftjs-to-markdown'
|
||||
import { urlRegex } from '../features/ui/util/url_regex'
|
||||
import classNames from 'classnames/bind'
|
||||
import RichTextEditorBar from './rich_text_editor_bar'
|
||||
@@ -76,7 +75,6 @@ const compositeDecorator = new CompositeDecorator([
|
||||
const HANDLE_REGEX = /\@[\w]+/g;
|
||||
const HASHTAG_REGEX = /\#[\w\u0590-\u05ff]+/g;
|
||||
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
|
||||
})
|
||||
@@ -117,7 +115,6 @@ class Composer extends PureComponent {
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
// console.log("this.props.value:", this.props.value)
|
||||
if (prevProps.value !== this.props.value) {
|
||||
// const editorState = EditorState.push(this.state.editorState, ContentState.createFromText(this.props.value));
|
||||
// this.setState({ editorState })
|
||||
@@ -126,24 +123,32 @@ class Composer extends PureComponent {
|
||||
|
||||
// EditorState.createWithContent(ContentState.createFromText('Hello'))
|
||||
|
||||
onChange = (editorState) => {
|
||||
onChange = (editorState, b, c, d) => {
|
||||
this.setState({ editorState })
|
||||
const content = this.state.editorState.getCurrentContent();
|
||||
|
||||
const content = editorState.getCurrentContent();
|
||||
const text = content.getPlainText('\u0001')
|
||||
|
||||
// const selectionState = editorState.getSelection()
|
||||
// const selectionStart = selectionState.getStartOffset()
|
||||
const selectionState = editorState.getSelection()
|
||||
const selectionStart = selectionState.getStartOffset()
|
||||
|
||||
// const rawObject = convertToRaw(content);
|
||||
// const markdownString = draftToMarkdown(rawObject);
|
||||
// const markdownString = draftToMarkdown(rawObject, {
|
||||
// trigger: '#',
|
||||
// separator: ' ',
|
||||
// });
|
||||
const rawObject = convertToRaw(content);
|
||||
const markdownString = draftToMarkdown(rawObject, {
|
||||
preserveNewlines: true,
|
||||
remarkablePreset: 'commonmark',
|
||||
remarkableOptions: {
|
||||
disable: {
|
||||
block: ['table']
|
||||
},
|
||||
enable: {
|
||||
inline: ['del', 'ins'],
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// console.log("text:", text, this.props.value)
|
||||
console.log("text:", markdownString)
|
||||
|
||||
this.props.onChange(null, text, selectionStart, markdownString)
|
||||
this.props.onChange(null, text, markdownString, selectionStart)
|
||||
}
|
||||
|
||||
// **bold**
|
||||
@@ -219,7 +224,7 @@ class Composer extends PureComponent {
|
||||
return (
|
||||
<div className={_s.default}>
|
||||
|
||||
{ /** : todo : */
|
||||
{
|
||||
!small &&
|
||||
<RichTextEditorBar
|
||||
editorState={editorState}
|
||||
@@ -241,6 +246,9 @@ class Composer extends PureComponent {
|
||||
placeholder={placeholder}
|
||||
ref={this.setRef}
|
||||
readOnly={disabled}
|
||||
onBlur={onBlur}
|
||||
onFocus={onFocus}
|
||||
stripPastedStyles
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -21,6 +21,7 @@ import PollButton from './poll_button'
|
||||
import PollForm from './poll_form'
|
||||
import SchedulePostButton from './schedule_post_button'
|
||||
import SpoilerButton from './spoiler_button'
|
||||
import RichTextEditorButton from './rich_text_editor_button'
|
||||
import StatusContainer from '../../../containers/status_container'
|
||||
import StatusVisibilityButton from './status_visibility_button'
|
||||
import UploadButton from './media_upload_button'
|
||||
@@ -92,14 +93,8 @@ class ComposeForm extends ImmutablePureComponent {
|
||||
showSearch: false,
|
||||
};
|
||||
|
||||
handleChange = (e, markdown) => {
|
||||
let position = null
|
||||
try {
|
||||
position = this.autosuggestTextarea.textbox.selectionStart
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
this.props.onChange(e.target.value, markdown, this.props.replyToId, position)
|
||||
handleChange = (e, selectionStart) => {
|
||||
this.props.onChange(e.target.value, e.target.markdown, this.props.replyToId, selectionStart)
|
||||
}
|
||||
|
||||
handleComposeFocus = () => {
|
||||
@@ -137,11 +132,11 @@ class ComposeForm extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
handleSubmit = () => {
|
||||
if (this.props.text !== this.autosuggestTextarea.textbox.value) {
|
||||
// if (this.props.text !== this.autosuggestTextarea.textbox.value) {
|
||||
// Something changed the text inside the textarea (e.g. browser extensions like Grammarly)
|
||||
// Update the state to match the current text
|
||||
this.props.onChange(this.autosuggestTextarea.textbox.value);
|
||||
}
|
||||
// this.props.onChange(this.autosuggestTextarea.textbox.value);
|
||||
// }
|
||||
|
||||
// Submit disabled:
|
||||
const { isSubmitting, isChangingUpload, isUploading, anyMedia } = this.props;
|
||||
@@ -218,6 +213,7 @@ class ComposeForm extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
handleEmojiPick = (data) => {
|
||||
// : todo : with rich text
|
||||
const { text } = this.props
|
||||
const position = this.autosuggestTextarea.textbox.selectionStart
|
||||
const needsSpace = data.custom && position > 0 && !ALLOWED_AROUND_SHORT_CODE.includes(text[position - 1])
|
||||
@@ -474,7 +470,7 @@ class ComposeForm extends ImmutablePureComponent {
|
||||
<StatusVisibilityButton />
|
||||
<SpoilerButton />
|
||||
<SchedulePostButton />
|
||||
{ /* !shouldCondense && <RichTextEditorButton /> */}
|
||||
<RichTextEditorButton />
|
||||
</div>
|
||||
|
||||
<Responsive min={BREAKPOINT_EXTRA_SMALL}>
|
||||
|
||||
Reference in New Issue
Block a user