diff --git a/app/javascript/gabsocial/actions/compose.js b/app/javascript/gabsocial/actions/compose.js
index f476da49..0aecfcaa 100644
--- a/app/javascript/gabsocial/actions/compose.js
+++ b/app/javascript/gabsocial/actions/compose.js
@@ -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
diff --git a/app/javascript/gabsocial/actions/importer/normalizer.js b/app/javascript/gabsocial/actions/importer/normalizer.js
index eaa4ca17..ac17d6b6 100644
--- a/app/javascript/gabsocial/actions/importer/normalizer.js
+++ b/app/javascript/gabsocial/actions/importer/normalizer.js
@@ -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(/
/g, '\n').replace(/<\/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;
}
+
+
+//
attention!
+// #test @bob #nice https://bob.com http://techcrunch.com strike it
+// https://twitter.com
+// @bobitalic
+// jonincode
+
+// # attention!
+// #test @bob #nice https://bob.com http://techcrunch.com ~~strike it~~
+
+// ~~https://twitter.com~~
+
+// _@bobitalic_
+
+// ```
+// jonincode
+// ```
\ No newline at end of file
diff --git a/app/javascript/gabsocial/actions/statuses.js b/app/javascript/gabsocial/actions/statuses.js
index d22b6df1..60af0990 100644
--- a/app/javascript/gabsocial/actions/statuses.js
+++ b/app/javascript/gabsocial/actions/statuses.js
@@ -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 => {
diff --git a/app/javascript/gabsocial/components/autosuggest_textbox.js b/app/javascript/gabsocial/components/autosuggest_textbox.js
index 80a92e99..40e945a6 100644
--- a/app/javascript/gabsocial/components/autosuggest_textbox.js
+++ b/app/javascript/gabsocial/components/autosuggest_textbox.js
@@ -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 (
-
+ />*/}
- {/**/}
+ />
{children}
diff --git a/app/javascript/gabsocial/components/composer.js b/app/javascript/gabsocial/components/composer.js
index 88e1d1d9..1a0d9700 100644
--- a/app/javascript/gabsocial/components/composer.js
+++ b/app/javascript/gabsocial/components/composer.js
@@ -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 (
- { /** : todo : */
+ {
!small &&
diff --git a/app/javascript/gabsocial/features/compose/components/compose_form.js b/app/javascript/gabsocial/features/compose/components/compose_form.js
index c1410af8..7d495095 100644
--- a/app/javascript/gabsocial/features/compose/components/compose_form.js
+++ b/app/javascript/gabsocial/features/compose/components/compose_form.js
@@ -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 {
- { /* !shouldCondense && */}
+
diff --git a/app/javascript/styles/global.css b/app/javascript/styles/global.css
index be9d2f42..a7e7d6d4 100644
--- a/app/javascript/styles/global.css
+++ b/app/javascript/styles/global.css
@@ -120,6 +120,11 @@ body {
overscroll-behavior-y: none;
}
+code,
+pre {
+ font-family: monospace !important;
+}
+
.overflowYScroll {
overflow: hidden;
overflow-y: scroll;
@@ -166,9 +171,16 @@ body {
.statusContent ul,
.statusContent ol {
- list-style-type: disc;
padding-left: 40px;
- margin: 0;
+ margin: 0.5rem 0;
+}
+
+.statusContent ul {
+ /* list-style-type: disc; */
+}
+
+.statusContent ol {
+ /* list-style-type: disc; */
}
.dangerousContent,
@@ -1002,6 +1014,7 @@ body {
font-size: var(--fs_l);
} */
+.statusContent blockquote,
:global(.RichEditor-blockquote) {
border-left: 5px solid var(--border_color_secondary);
color: var(--text_color_secondary);
@@ -1010,11 +1023,14 @@ body {
padding: 10px 20px;
}
+.statusContent pre,
:global(.public-DraftStyleDefault-pre) {
background-color: rgba(0,0,0,.05);
font-family: 'Inconsolata', 'Menlo', 'Consolas', monospace;
font-size: var(--fs_l);
padding: 10px 20px;
+ margin-top: 0.5rem;
+ margin-bottom: 0.5rem;
}
/* */
diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb
index 7164af06..049924e0 100644
--- a/app/lib/formatter.rb
+++ b/app/lib/formatter.rb
@@ -42,19 +42,6 @@ class Formatter
html = encode_and_link_urls(html, linkable_accounts)
- # : todo :
- if options[:use_markdown]
- html = convert_headers(html)
- html = convert_strong(html)
- html = convert_italic(html)
- html = convert_strikethrough(html)
- html = convert_code(html)
- html = convert_codeblock(html)
- html = convert_links(html)
- html = convert_lists(html)
- html = convert_ordered_lists(html)
- end
-
html = encode_custom_emojis(html, status.emojis, options[:autoplay]) if options[:custom_emojify]
html = simple_format(html, {}, sanitize: false)
@@ -321,79 +308,4 @@ class Formatter
"@#{encode(account.acct)}"
end
- def convert_headers(html)
- html.gsub(/^\#{1,6}.*$/) do |header|
- weight = 0
- header.split('').each do |char|
- break unless char == '#'
- weight += 1
- end
- content = header.sub(/^\#{1,6}/, '')
- "#{content}"
- end
- end
-
- def convert_strong(html)
- html.gsub(/\*{2}.*\*{2}|_{2}.*_{2}/) do |strong|
- content = strong.gsub(/\*{2}|_{2}/, '')
- "#{content}"
- end
- end
-
- def convert_italic(html)
- html.gsub(/\*{1}(\w|\s)+\*{1}|_{1}(\w|\s)+_{1}/) do |italic|
- content = italic.gsub(/\*{1}|_{1}/, '')
- "#{content}"
- end
- end
-
- def convert_strikethrough(html)
- html.gsub(/~~(\w|\s)+~~/) do |strike|
- content = strike.gsub(/~~/, '')
- "#{content}"
- end
- end
-
- def convert_code(html)
- html.gsub(/`(\w|\s)+`/) do |code|
- content = code.gsub(/`/, '')
- "#{content}
"
- end
- end
-
- def convert_codeblock(html)
- html.gsub(/```\w*(.*(\r\n|\r|\n))+```/) do |code|
- lang = code.match(/```\w+/)[0].gsub(/`/, '')
- content = code.gsub(/```\w+/, '```').gsub(/`/, '')
- "#{content}
"
- end
- end
-
- def convert_links(html)
- html.gsub(/\[(\w|\s)+\]\((\w|\W)+\)/) do |anchor|
- link_text = anchor.match(/\[(\w|\s)+\]/)[0].gsub(/[\[\]]/, '')
- href = anchor.match(/\((\w|\W)+\)/)[0].gsub(/\(|\)/, '')
- "#{link_text}"
- end
- end
-
- def convert_lists(html)
- html.gsub(/(\-.+(\r|\n|\r\n))+/) do |list|
- items = "\n"
- list.gsub(/\-.+/) do |li|
- items << "- #{li.sub(/^\-/, '').strip}
\n"
- end
- items << "
\n"
- end
- end
-
- def convert_ordered_lists(html)
- html.gsub(/(\d\..+(\r|\n|\r\n))+/) do |list|
- items = "\n"
- list.gsub(/\d.+/) do |li|
- items << "- #{li.sub(/^\d\./, '').strip}
\n"
- end
- items << "
\n"
- end
- end
end
diff --git a/app/serializers/rest/status_serializer.rb b/app/serializers/rest/status_serializer.rb
index d2cba228..0aab40a7 100644
--- a/app/serializers/rest/status_serializer.rb
+++ b/app/serializers/rest/status_serializer.rb
@@ -73,8 +73,10 @@ class REST::StatusSerializer < ActiveModel::Serializer
end
def rich_content
- Formatter.instance.format(object).strip
- # Formatter.instance.format(object, use_markdown: true).strip
+ Formatter.instance.format(object, use_markdown: true).strip
+ # raw_content = object.markdown
+ # return '' if raw_content.blank?
+ # raw_content.strip
end
def url
diff --git a/package.json b/package.json
index a622b1c8..fa609023 100644
--- a/package.json
+++ b/package.json
@@ -92,6 +92,7 @@
"draft-js": "^0.11.4",
"draftjs-to-markdown": "^0.6.0",
"emoji-mart": "Gargron/emoji-mart#build",
+ "entities": "^2.0.3",
"es6-symbol": "^3.1.1",
"escape-html": "^1.0.3",
"exif-js": "^2.3.0",
@@ -153,6 +154,7 @@
"redux": "^4.0.1",
"redux-immutable": "^4.0.0",
"redux-thunk": "^2.2.0",
+ "remarkable": "^2.0.1",
"requestidlecallback": "^0.3.0",
"reselect": "^4.0.0",
"rimraf": "^2.6.3",
diff --git a/yarn.lock b/yarn.lock
index 087a3eb2..72b820df 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3394,6 +3394,11 @@ entities@^2.0.0:
resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4"
integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==
+entities@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f"
+ integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==
+
enzyme-adapter-react-16@^1.7.1:
version "1.15.2"
resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.2.tgz#b16db2f0ea424d58a808f9df86ab6212895a4501"
@@ -8233,6 +8238,14 @@ remarkable@2.0.0:
argparse "^1.0.10"
autolinker "^3.11.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"