Commit b93ecc70 authored by mgabdev's avatar mgabdev

Rich Text Editor (WIP) x4

parent 1a506327
......@@ -17,6 +17,7 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
static propTypes = {
value: PropTypes.string,
valueMarkdown: PropTypes.string,
suggestions: ImmutablePropTypes.list,
disabled: PropTypes.bool,
placeholder: PropTypes.string,
......@@ -214,6 +215,7 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
onKeyUp,
autoFocus,
children,
valueMarkdown,
className,
id,
maxLength,
......@@ -280,6 +282,7 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
placeholder={placeholder}
autoFocus={autoFocus}
value={value}
valueMarkdown={valueMarkdown}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
onKeyUp={onKeyUp}
......
......@@ -4,9 +4,11 @@ import {
CompositeDecorator,
RichUtils,
convertToRaw,
convertFromRaw,
ContentState,
} from 'draft-js'
import draftToMarkdown from '../features/ui/util/draft-to-markdown'
import markdownToDraft from '../features/ui/util/markdown-to-draft'
import { urlRegex } from '../features/ui/util/url_regex'
import classNames from 'classnames/bind'
import RichTextEditorBar from './rich_text_editor_bar'
......@@ -81,6 +83,7 @@ export default class Composer extends PureComponent {
disabled: PropTypes.bool,
placeholder: PropTypes.string,
value: PropTypes.string,
valueMarkdown: PropTypes.string,
onChange: PropTypes.func,
onKeyDown: PropTypes.func,
onKeyUp: PropTypes.func,
......@@ -95,6 +98,24 @@ export default class Composer extends PureComponent {
plainText: this.props.value,
}
componentDidMount() {
if (this.props.valueMarkdown) {
const rawData = markdownToDraft(this.props.valueMarkdown)
const contentState = convertFromRaw(rawData)
const editorState = EditorState.createWithContent(contentState)
this.setState({
editorState,
plainText: this.props.value,
})
} else if (this.props.value) {
editorState = EditorState.push(this.state.editorState, ContentState.createFromText(this.props.value))
this.setState({
editorState,
plainText: this.props.value,
})
}
}
componentDidUpdate() {
if (this.state.plainText !== this.props.value) {
let editorState
......
......@@ -45,7 +45,7 @@ class ComposeForm extends ImmutablePureComponent {
static contextTypes = {
router: PropTypes.object,
}
state = {
composeFocused: false,
}
......@@ -55,6 +55,7 @@ class ComposeForm extends ImmutablePureComponent {
edit: PropTypes.bool,
isMatch: PropTypes.bool,
text: PropTypes.string.isRequired,
markdown: PropTypes.string,
suggestions: ImmutablePropTypes.list,
account: ImmutablePropTypes.map.isRequired,
status: ImmutablePropTypes.map,
......@@ -133,9 +134,9 @@ class ComposeForm extends ImmutablePureComponent {
handleSubmit = () => {
// 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);
// 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);
// }
// Submit disabled:
......@@ -244,6 +245,7 @@ class ComposeForm extends ImmutablePureComponent {
isSubmitting,
selectedGifSrc,
} = this.props
const disabled = isSubmitting
const text = [this.props.spoilerText, countableText(this.props.text)].join('');
const disabledButton = disabled || isUploading || isChangingUpload || length(text) > MAX_POST_CHARACTER_COUNT || (length(text) !== 0 && length(text.trim()) === 0 && !anyMedia);
......@@ -325,9 +327,9 @@ class ComposeForm extends ImmutablePureComponent {
<div className={actionsContainerClasses}>
<div className={[_s.default, _s.flexRow, _s.mrAuto].join(' ')}>
{ /* <EmojiPickerButton small={shouldCondense} isMatch={isMatch} /> */ }
{ /* <EmojiPickerButton small={shouldCondense} isMatch={isMatch} /> */}
{ /* <UploadButton small={shouldCondense} /> */ }
{ /* <UploadButton small={shouldCondense} /> */}
<div className={commentPublishBtnClasses}>
<Button
......@@ -400,6 +402,7 @@ class ComposeForm extends ImmutablePureComponent {
placeholder={intl.formatMessage((shouldCondense || !!reduxReplyToId) && isMatch ? messages.commentPlaceholder : messages.placeholder)}
disabled={disabled}
value={this.props.text}
valueMarkdown={this.props.markdown}
onChange={this.handleChange}
suggestions={this.props.suggestions}
onKeyDown={this.handleKeyDown}
......
......@@ -39,6 +39,8 @@ const mapStateToProps = (state, props) => {
// console.log("isMatch:", isMatch, reduxReplyToId, replyToId, state.getIn(['compose', 'text']))
// console.log("reduxReplyToId:", reduxReplyToId, isModalOpen, isStandalone)
const edit = state.getIn(['compose', 'id'])
if (!isMatch) {
return {
isMatch,
......@@ -46,6 +48,7 @@ const mapStateToProps = (state, props) => {
reduxReplyToId,
edit: null,
text: '',
markdown: null,
suggestions: ImmutableList(),
spoiler: false,
spoilerText: '',
......@@ -65,13 +68,14 @@ const mapStateToProps = (state, props) => {
selectedGifSrc: null,
}
}
return {
isMatch,
isModalOpen,
reduxReplyToId,
edit: state.getIn(['compose', 'id']) !== null,
text: state.getIn(['compose', 'text']),
markdown: state.getIn(['compose', 'markdown']),
suggestions: state.getIn(['compose', 'suggestions']),
spoiler: state.getIn(['compose', 'spoiler']),
spoilerText: state.getIn(['compose', 'spoiler_text']),
......
This diff is collapsed.
......@@ -366,9 +366,11 @@ export default function compose(state = initialState, action) {
return item;
}));
case STATUS_EDIT:
const hasMarkdown = !!action.status.get('plain_markdown')
return state.withMutations(map => {
map.set('id', action.status.get('id'));
map.set('text', unescapeHTML(expandMentions(action.status)));
map.set('markdown', action.status.get('plain_markdown'));
map.set('in_reply_to', action.status.get('in_reply_to_id'));
map.set('quote_of_id', action.status.get('quote_of_id'));
map.set('privacy', action.status.get('visibility'));
......@@ -376,7 +378,7 @@ export default function compose(state = initialState, action) {
map.set('focusDate', new Date());
map.set('caretPosition', null);
map.set('idempotencyKey', uuid());
map.set('rte_controls_visible', false);
map.set('rte_controls_visible', hasMarkdown);
if (action.status.get('spoiler_text').length > 0) {
map.set('spoiler', true);
......
......@@ -13,6 +13,7 @@ class REST::StatusSerializer < ActiveModel::Serializer
attribute :content, unless: :source_requested?
attribute :rich_content, unless: :source_requested?
attribute :plain_markdown, unless: :source_requested?
attribute :text, if: :source_requested?
belongs_to :reblog, serializer: REST::StatusSerializer
......@@ -76,6 +77,10 @@ class REST::StatusSerializer < ActiveModel::Serializer
Formatter.instance.format(object, use_markdown: true).strip
end
def plain_markdown
object.markdown
end
def url
TagManager.instance.url_for(object)
end
......
......@@ -7,6 +7,7 @@ class EditStatusService < BaseService
# @param [Status] status Status being edited
# @param [Hash] options
# @option [String] :text Message
# @option [String] :markdown Optional message in markdown
# @option [Boolean] :sensitive
# @option [String] :visibility
# @option [String] :spoiler_text
......@@ -20,6 +21,7 @@ class EditStatusService < BaseService
@account = status.account
@options = options
@text = @options[:text] || ''
@markdown = @options[:markdown] if @account.is_pro
return idempotency_duplicate if idempotency_given? && idempotency_duplicate?
......@@ -119,6 +121,7 @@ class EditStatusService < BaseService
{
revised_at: Time.now,
text: @text,
markdown: @markdown,
media_attachments: @media || [],
sensitive: (@options[:sensitive].nil? ? @account.user&.setting_default_sensitive : @options[:sensitive]) || @options[:spoiler_text].present?,
spoiler_text: @options[:spoiler_text] || '',
......
......@@ -26,7 +26,7 @@ class PostStatusService < BaseService
@account = account
@options = options
@text = @options[:text] || ''
@markdown = @options[:markdown]
@markdown = @options[:markdown] if @account.is_pro
@in_reply_to = @options[:thread]
return idempotency_duplicate if idempotency_given? && idempotency_duplicate?
......
......@@ -1472,7 +1472,7 @@ are-we-there-yet@~1.1.2:
delegates "^1.0.0"
readable-stream "^2.0.6"
argparse@^1.0.7:
argparse@^1.0.10, argparse@^1.0.7:
version "1.0.10"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
......@@ -1640,6 +1640,13 @@ atob@^2.1.2:
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
autolinker@^3.11.0:
version "3.14.1"
resolved "https://registry.yarnpkg.com/autolinker/-/autolinker-3.14.1.tgz#6ae4b812b6eaf42d4d68138b9e67757cbf2bc1e4"
integrity sha512-yvsRHIaY51EYDml6MGlbqyJGfl4n7zezGYf+R7gvM8c5LNpRGc4SISkvgAswSS8SWxk/OrGCylKV9mJyVstz7w==
dependencies:
tslib "^1.9.3"
autoprefixer@^9.5.1:
version "9.7.6"
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.7.6.tgz#63ac5bbc0ce7934e6997207d5bb00d68fa8293a4"
......@@ -8211,6 +8218,14 @@ regjsparser@^0.6.4:
dependencies:
jsesc "~0.5.0"
remarkable@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/remarkable/-/remarkable-2.0.1.tgz#280ae6627384dfb13d98ee3995627ca550a12f31"
integrity sha512-YJyMcOH5lrR+kZdmB0aJJ4+93bEojRZ1HGDn9Eagu6ibg7aVZhc3OWbbShRid+Q5eAfsEqWxpe+g5W5nYNfNiA==
dependencies:
argparse "^1.0.10"
autolinker "^3.11.0"
remove-trailing-separator@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
......@@ -9425,6 +9440,11 @@ tslib@^1.9.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35"
integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==
tslib@^1.9.3:
version "1.13.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==
tty-browserify@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
......
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