From 9f98c7956d394f955e5e00841bba1922bd533280 Mon Sep 17 00:00:00 2001 From: Max Stoiber Date: Fri, 1 Mar 2019 09:51:28 +0100 Subject: [PATCH 01/18] Switch message editing to markdown microservice --- src/components/message/editingBody.js | 51 +++++++++++++++++---------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/src/components/message/editingBody.js b/src/components/message/editingBody.js index 6e6ce73a4b..1040608b05 100644 --- a/src/components/message/editingBody.js +++ b/src/components/message/editingBody.js @@ -28,21 +28,30 @@ type State = { const EditingChatInput = (props: Props) => { const initialState = - props.message.messageType === 'text' - ? props.message.content.body - : stateToMarkdown( - convertFromRaw(JSON.parse(props.message.content.body)), - { - gfm: true, - } - // NOTE(@mxstbr): draft-js-export-markdown sometimes appends an empty line at the end, - // which we really never want - ).replace(/\n$/, ''); + props.message.messageType === 'text' ? props.message.content.body : null; // $FlowIssue const [text, setText] = React.useState(initialState); // $FlowIssue const [saving, setSaving] = React.useState(false); + React.useEffect( + () => { + if (props.message.messageType === 'text') return; + + setText(null); + fetch('https://convert.spectrum.chat/to', { + method: 'POST', + body: props.message.content.body, + }) + .then(res => res.text()) + .then(md => { + console.log(md); + setText(md); + }); + }, + [props.message.id] + ); + const onChange = e => { const text = e.target.value; setText(text); @@ -101,15 +110,19 @@ const EditingChatInput = (props: Props) => { return ( - + {text !== null ? ( + + ) : ( +

Loading...

+ )}
{!saving && ( From 5adf93d08498419788f33475aa76629119c130b5 Mon Sep 17 00:00:00 2001 From: Max Stoiber Date: Fri, 1 Mar 2019 10:37:31 +0100 Subject: [PATCH 02/18] Adjust UX, remove console.log --- src/components/message/editingBody.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/components/message/editingBody.js b/src/components/message/editingBody.js index 1040608b05..225d5fad8b 100644 --- a/src/components/message/editingBody.js +++ b/src/components/message/editingBody.js @@ -45,7 +45,6 @@ const EditingChatInput = (props: Props) => { }) .then(res => res.text()) .then(md => { - console.log(md); setText(md); }); }, @@ -109,8 +108,8 @@ const EditingChatInput = (props: Props) => { return ( - - {text !== null ? ( + {text !== null ? ( + { inputRef={props.editorRef} autoFocus /> - ) : ( -

Loading...

- )} -
+
+ ) : ( +

Loading...

+ )} {!saving && ( From f57ec842835f08365a171193000637ada0797818 Mon Sep 17 00:00:00 2001 From: Max Stoiber Date: Tue, 5 Mar 2019 11:50:24 +0100 Subject: [PATCH 03/18] Go via network --- .../graphql/mutations/message/sendMessage.js | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/shared/graphql/mutations/message/sendMessage.js b/shared/graphql/mutations/message/sendMessage.js index 809ad0a30d..916b74380f 100644 --- a/shared/graphql/mutations/message/sendMessage.js +++ b/shared/graphql/mutations/message/sendMessage.js @@ -27,14 +27,24 @@ export const sendMessageMutation = gql` const sendMessageOptions = { props: ({ ownProps, mutate }) => ({ - sendMessage: (message, author) => { + sendMessage: async (message, author) => { const fakeId = Math.round(Math.random() * -1000000); + let body = message.content.body; + if (message.messageType === messageTypeObj.text) { + body = await fetch('https://convert.spectrum.chat/from', { + method: 'POST', + body, + }) + .then(res => res.json()) + .then(json => JSON.stringify(json)); + } return mutate({ variables: { message: { ...message, + messageType: messageTypeObj.draftjs, content: { - body: message.messageType === 'media' ? '' : message.content.body, + body: message.messageType === messageTypeObj.media ? '' : body, }, }, }, @@ -70,15 +80,7 @@ const sendMessageOptions = { body: message.messageType === 'media' ? message.content.body - : JSON.stringify( - convertToRaw( - stateFromMarkdown(message.content.body, { - parserOptions: { - breaks: true, - }, - }) - ) - ), + : processMessageContent(messageTypeObj.draftjs, body), __typename: 'MessageContent', }, reactions: { From c62eb8dd996537303d95fefb10253cfcf0282a24 Mon Sep 17 00:00:00 2001 From: Max Stoiber Date: Fri, 8 Mar 2019 11:00:28 +0100 Subject: [PATCH 04/18] Render optimistic message responses with snarkdown --- package.json | 3 ++- .../graphql/mutations/message/sendMessage.js | 21 +++++++------------ src/components/message/view.js | 8 +++++++ yarn.lock | 5 +++++ 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index ebb84f8ef7..c2656a2512 100644 --- a/package.json +++ b/package.json @@ -194,6 +194,7 @@ "slate": "^0.20.1", "slate-markdown": "0.1.1", "slugg": "^1.1.0", + "snarkdown": "^1.2.2", "stopword": "^0.1.13", "string-replace-to-array": "^1.0.3", "string-similarity": "^2.0.0", @@ -293,4 +294,4 @@ ] }, "pre-commit": "lint:staged" -} \ No newline at end of file +} diff --git a/shared/graphql/mutations/message/sendMessage.js b/shared/graphql/mutations/message/sendMessage.js index dfcd7eb292..cf3d10fcc3 100644 --- a/shared/graphql/mutations/message/sendMessage.js +++ b/shared/graphql/mutations/message/sendMessage.js @@ -2,6 +2,7 @@ import gql from 'graphql-tag'; import { graphql } from 'react-apollo'; import { btoa } from 'b2a'; +import snarkdown from 'snarkdown'; import { stateFromMarkdown } from 'draft-js-import-markdown'; import messageInfoFragment from '../../fragments/message/messageInfo'; import type { MessageInfoType } from '../../fragments/message/messageInfo'; @@ -31,22 +32,16 @@ const sendMessageOptions = { props: ({ ownProps, mutate }) => ({ sendMessage: async (message, author) => { const fakeId = Math.round(Math.random() * -1000000); - let body = message.content.body; - if (message.messageType === messageTypeObj.text) { - body = await fetch('https://convert.spectrum.chat/from', { - method: 'POST', - body, - }) - .then(res => res.json()) - .then(json => JSON.stringify(json)); - } return mutate({ variables: { message: { ...message, - messageType: messageTypeObj.draftjs, + messageType: messageTypeObj.text, content: { - body: message.messageType === messageTypeObj.media ? '' : body, + body: + message.messageType === messageTypeObj.media + ? '' + : message.content.body, }, }, }, @@ -58,7 +53,7 @@ const sendMessageOptions = { messageType: message.messageType === messageTypeObj.media ? messageTypeObj.media - : messageTypeObj.draftjs, + : 'optimistic', modifiedAt: '', author: { user: { @@ -85,7 +80,7 @@ const sendMessageOptions = { body: message.messageType === messageTypeObj.media ? message.content.body - : processMessageContent(messageTypeObj.draftjs, body), + : snarkdown(message.content.body), __typename: 'MessageContent', }, reactions: { diff --git a/src/components/message/view.js b/src/components/message/view.js index 4b70d6a8fb..1f50d923a4 100644 --- a/src/components/message/view.js +++ b/src/components/message/view.js @@ -47,6 +47,14 @@ export const Body = (props: BodyProps) => { draftOnlyContainsEmoji(JSON.parse(message.content.body)); const WrapperComponent = bubble ? Text : QuotedParagraph; switch (message.messageType) { + case 'optimistic': + return ( +
+ +
+ +
+ ); case messageTypeObj.text: default: return ( diff --git a/yarn.lock b/yarn.lock index 8365e49add..4bd2435c18 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13273,6 +13273,11 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" +snarkdown@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/snarkdown/-/snarkdown-1.2.2.tgz#0cfe2f3012b804de120fc0c9f7791e869c59cc74" + integrity sha1-DP4vMBK4BN4SD8DJ93kehpxZzHQ= + sockjs-client@1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.5.tgz#1bb7c0f7222c40f42adf14f4442cbd1269771a83" From c547fd0ed9cd7823a22bf6ffa70d222b3afbf79d Mon Sep 17 00:00:00 2001 From: Max Stoiber Date: Fri, 8 Mar 2019 11:11:53 +0100 Subject: [PATCH 05/18] Move thread editing to network markdown conversion --- flow-typed/npm/snarkdown_vx.x.x.js | 53 +++++++++++++++++++++ src/components/composer/inputs.js | 5 +- src/components/message/editingBody.js | 2 +- src/views/thread/components/threadDetail.js | 39 +++++++++++---- 4 files changed, 86 insertions(+), 13 deletions(-) create mode 100644 flow-typed/npm/snarkdown_vx.x.x.js diff --git a/flow-typed/npm/snarkdown_vx.x.x.js b/flow-typed/npm/snarkdown_vx.x.x.js new file mode 100644 index 0000000000..96b404c0a3 --- /dev/null +++ b/flow-typed/npm/snarkdown_vx.x.x.js @@ -0,0 +1,53 @@ +// flow-typed signature: 8f063755e65f032485439ac4cefffded +// flow-typed version: <>/snarkdown_vx.x.x/flow_v0.66.0 + +/** + * This is an autogenerated libdef stub for: + * + * 'snarkdown' + * + * Fill this stub out by replacing all the `any` types. + * + * Once filled out, we encourage you to share your work with the + * community by sending a pull request to: + * https://github.com/flowtype/flow-typed + */ + +declare module 'snarkdown' { + declare module.exports: any; +} + +/** + * We include stubs for each file inside this npm package in case you need to + * require those files directly. Feel free to delete any files that aren't + * needed. + */ +declare module 'snarkdown/dist/snarkdown.es' { + declare module.exports: any; +} + +declare module 'snarkdown/dist/snarkdown' { + declare module.exports: any; +} + +declare module 'snarkdown/dist/snarkdown.umd' { + declare module.exports: any; +} + +declare module 'snarkdown/src/index' { + declare module.exports: any; +} + +// Filename aliases +declare module 'snarkdown/dist/snarkdown.es.js' { + declare module.exports: $Exports<'snarkdown/dist/snarkdown.es'>; +} +declare module 'snarkdown/dist/snarkdown.js' { + declare module.exports: $Exports<'snarkdown/dist/snarkdown'>; +} +declare module 'snarkdown/dist/snarkdown.umd.js' { + declare module.exports: $Exports<'snarkdown/dist/snarkdown.umd'>; +} +declare module 'snarkdown/src/index.js' { + declare module.exports: $Exports<'snarkdown/src/index'>; +} diff --git a/src/components/composer/inputs.js b/src/components/composer/inputs.js index f0e84c1ab1..aa23608896 100644 --- a/src/components/composer/inputs.js +++ b/src/components/composer/inputs.js @@ -19,7 +19,7 @@ import processThreadContent from 'shared/draft-utils/process-thread-content'; type Props = { title: string, - body: string, + body: ?string, changeBody: Function, changeTitle: Function, uploadFiles: Function, @@ -99,7 +99,8 @@ export default (props: Props) => { { // $FlowIssue const [saving, setSaving] = React.useState(false); + // $FlowIssue React.useEffect( () => { if (props.message.messageType === 'text') return; diff --git a/src/views/thread/components/threadDetail.js b/src/views/thread/components/threadDetail.js index eedfb7458c..d0037ca9cf 100644 --- a/src/views/thread/components/threadDetail.js +++ b/src/views/thread/components/threadDetail.js @@ -40,7 +40,7 @@ import { ErrorBoundary } from 'src/components/error'; type State = { isEditing?: boolean, - body: string, + body: ?string, title: string, receiveNotifications?: boolean, isSavingEdit?: boolean, @@ -96,9 +96,7 @@ class ThreadDetailPure extends React.Component { return this.setState({ isEditing: false, - body: stateToMarkdown(convertFromRaw(parsedBody), { - gfm: true, - }), + body: '', title: thread.content.title, // We store this in the state to avoid having to JSON.parse on every render parsedBody, @@ -195,13 +193,36 @@ class ThreadDetailPure extends React.Component { this.setState({ isEditing: !isEditing, - // Reset body and title state - body: stateToMarkdown(convertFromRaw(JSON.parse(thread.content.body)), { - gfm: true, - }), title: thread.content.title, + body: null, }); + fetch('https://convert.spectrum.chat/to', { + method: 'POST', + body: thread.content.body, + }) + .then(res => { + if (res.status >= 300 || res.status < 200) + throw new Error('Oops, something went wrong.'); + return res; + }) + .then(res => res.text()) + .then(md => { + this.setState({ + body: md, + }); + }) + .catch(err => { + this.props.dispatch(addToastWithTimeout('error', err.message)); + this.setState({ + isEditing, + body: '', + }); + this.props.toggleEdit(); + }); + + this.props.toggleEdit(); + if (!isEditing) { track(events.THREAD_EDITED_INITED, { thread: transformations.analyticsThread(thread), @@ -209,8 +230,6 @@ class ThreadDetailPure extends React.Component { community: transformations.analyticsCommunity(thread.community), }); } - - this.props.toggleEdit(); }; handleKeyPress = e => { From 23dc56d9a11f6778cdad90cbc53db28b8fee0f78 Mon Sep 17 00:00:00 2001 From: Max Stoiber Date: Fri, 8 Mar 2019 11:13:11 +0100 Subject: [PATCH 06/18] Fix flow --- shared/draft-utils/process-thread-content.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shared/draft-utils/process-thread-content.js b/shared/draft-utils/process-thread-content.js index 5e1679a680..1b21528ee5 100644 --- a/shared/draft-utils/process-thread-content.js +++ b/shared/draft-utils/process-thread-content.js @@ -3,12 +3,12 @@ import { stateFromMarkdown } from 'draft-js-import-markdown'; import { convertFromRaw, convertToRaw, EditorState } from 'draft-js'; import { addEmbedsToEditorState } from './add-embeds-to-draft-js'; -export default (type: 'TEXT' | 'DRAFTJS', body: string): string => { +export default (type: 'TEXT' | 'DRAFTJS', body: ?string): string => { let newBody = body; if (type === 'TEXT') { // workaround react-mentions bug by replacing @[username] with @username // @see withspectrum/spectrum#4587 - newBody = newBody.replace(/@\[([a-z0-9_-]+)\]/g, '@$1'); + newBody = newBody ? newBody.replace(/@\[([a-z0-9_-]+)\]/g, '@$1') : ''; newBody = JSON.stringify( convertToRaw( stateFromMarkdown(newBody, { @@ -43,10 +43,10 @@ export default (type: 'TEXT' | 'DRAFTJS', body: string): string => { // Add automatic embeds to body try { - return JSON.stringify(addEmbedsToEditorState(JSON.parse(newBody))); + return JSON.stringify(addEmbedsToEditorState(JSON.parse(newBody || ''))); // Ignore errors during automatic embed detection } catch (err) { console.error(err); - return newBody; + return newBody || ''; } }; From e051c1888bfde80a231e47a770f83c6096b7877e Mon Sep 17 00:00:00 2001 From: Max Stoiber Date: Fri, 8 Mar 2019 11:18:09 +0100 Subject: [PATCH 07/18] Do not import process-message-content in frontend --- .../directMessageThread/createDirectMessageThread.js | 4 ++-- api/mutations/message/addMessage.js | 2 +- api/mutations/message/editMessage.js | 2 +- shared/draft-utils/message-types.js | 8 ++++++++ shared/draft-utils/process-message-content.js | 8 ++------ shared/graphql/mutations/message/sendMessage.js | 5 +---- shared/notification-to-text.js | 2 +- shared/types.js | 2 +- src/components/message/view.js | 2 +- src/views/thread/components/threadDetail.js | 2 -- 10 files changed, 18 insertions(+), 19 deletions(-) create mode 100644 shared/draft-utils/message-types.js diff --git a/api/mutations/directMessageThread/createDirectMessageThread.js b/api/mutations/directMessageThread/createDirectMessageThread.js index 4b39981dcd..ab05c7710a 100644 --- a/api/mutations/directMessageThread/createDirectMessageThread.js +++ b/api/mutations/directMessageThread/createDirectMessageThread.js @@ -17,8 +17,8 @@ import type { FileUpload } from 'shared/types'; import { events } from 'shared/analytics'; import { trackQueue } from 'shared/bull/queues'; import { isAuthedResolver as requireAuth } from '../../utils/permissions'; -import { messageTypeObj } from 'shared/draft-utils/process-message-content'; -import type { MessageType } from 'shared/draft-utils/process-message-content'; +import { messageTypeObj } from 'shared/draft-utils/message-types'; +import type { MessageType } from 'shared/draft-utils/message-types'; export type CreateDirectMessageThreadInput = { input: { diff --git a/api/mutations/message/addMessage.js b/api/mutations/message/addMessage.js index 453ccb32af..3cb92da708 100644 --- a/api/mutations/message/addMessage.js +++ b/api/mutations/message/addMessage.js @@ -22,7 +22,7 @@ import { validateRawContentState } from '../../utils/validate-draft-js-input'; import processMessageContent, { messageTypeObj, } from 'shared/draft-utils/process-message-content'; -import type { MessageType } from 'shared/draft-utils/process-message-content'; +import type { MessageType } from 'shared/draft-utils/message-types'; type Input = { message: { diff --git a/api/mutations/message/editMessage.js b/api/mutations/message/editMessage.js index 9303a7d1a9..c7aa471493 100644 --- a/api/mutations/message/editMessage.js +++ b/api/mutations/message/editMessage.js @@ -19,7 +19,7 @@ import { validateRawContentState } from '../../utils/validate-draft-js-input'; import processMessageContent, { messageTypeObj, } from 'shared/draft-utils/process-message-content'; -import type { MessageType } from 'shared/draft-utils/process-message-content'; +import type { MessageType } from 'shared/draft-utils/message-types'; type Args = { input: { diff --git a/shared/draft-utils/message-types.js b/shared/draft-utils/message-types.js new file mode 100644 index 0000000000..9506be4bef --- /dev/null +++ b/shared/draft-utils/message-types.js @@ -0,0 +1,8 @@ +// @flow + +export const messageTypeObj = { + text: 'text', + media: 'media', + draftjs: 'draftjs', +}; +export type MessageType = $Keys; diff --git a/shared/draft-utils/process-message-content.js b/shared/draft-utils/process-message-content.js index eff34835fa..6cdeb23735 100644 --- a/shared/draft-utils/process-message-content.js +++ b/shared/draft-utils/process-message-content.js @@ -2,13 +2,9 @@ import { stateFromMarkdown } from 'draft-js-import-markdown'; import { convertFromRaw, convertToRaw, EditorState } from 'draft-js'; import { addEmbedsToEditorState } from './add-embeds-to-draft-js'; +import { messageTypeObj, type MessageType } from './message-types'; -export const messageTypeObj = { - text: 'text', - media: 'media', - draftjs: 'draftjs', -}; -export type MessageType = $Keys; +export { messageTypeObj }; export default (type: MessageType, body: string): string => { let newBody = body; diff --git a/shared/graphql/mutations/message/sendMessage.js b/shared/graphql/mutations/message/sendMessage.js index cf3d10fcc3..7e0d09ffc5 100644 --- a/shared/graphql/mutations/message/sendMessage.js +++ b/shared/graphql/mutations/message/sendMessage.js @@ -3,13 +3,10 @@ import gql from 'graphql-tag'; import { graphql } from 'react-apollo'; import { btoa } from 'b2a'; import snarkdown from 'snarkdown'; -import { stateFromMarkdown } from 'draft-js-import-markdown'; import messageInfoFragment from '../../fragments/message/messageInfo'; import type { MessageInfoType } from '../../fragments/message/messageInfo'; import { getThreadMessageConnectionQuery } from '../../queries/thread/getThreadMessageConnection'; -import processMessageContent, { - messageTypeObj, -} from 'shared/draft-utils/process-message-content'; +import { messageTypeObj } from 'shared/draft-utils/message-types'; export type SendMessageType = { data: { diff --git a/shared/notification-to-text.js b/shared/notification-to-text.js index 673cb4b763..bdff4ffa2f 100644 --- a/shared/notification-to-text.js +++ b/shared/notification-to-text.js @@ -1,7 +1,7 @@ import sentencify from 'shared/sentencify'; import sortByDate from 'shared/sort-by-date'; import { toState, toPlainText } from 'shared/draft-utils'; -import { messageTypeObj } from 'shared/draft-utils/process-message-content'; +import { messageTypeObj } from 'shared/draft-utils/message-types'; const sortThreads = (entities, currentUser) => { // filter out the current user's threads diff --git a/shared/types.js b/shared/types.js index dc4d82a67c..bbcbe7ff8c 100644 --- a/shared/types.js +++ b/shared/types.js @@ -6,7 +6,7 @@ attempt to use or update the types here */ -import type { MessageType } from 'shared/draft-utils/process-message-content'; +import type { MessageType } from 'shared/draft-utils/message-types'; export type DBChannel = { communityId: string, diff --git a/src/components/message/view.js b/src/components/message/view.js index 1f50d923a4..907b3478c0 100644 --- a/src/components/message/view.js +++ b/src/components/message/view.js @@ -18,7 +18,7 @@ import { draftOnlyContainsEmoji } from 'shared/only-contains-emoji'; import { Byline, Name, Username } from './style'; import { isShort } from 'shared/clients/draft-js/utils/isShort'; import type { MessageInfoType } from 'shared/graphql/fragments/message/messageInfo.js'; -import { messageTypeObj } from 'shared/draft-utils/process-message-content'; +import { messageTypeObj } from 'shared/draft-utils/message-types'; type BodyProps = { openGallery: Function, diff --git a/src/views/thread/components/threadDetail.js b/src/views/thread/components/threadDetail.js index d0037ca9cf..630322a1cf 100644 --- a/src/views/thread/components/threadDetail.js +++ b/src/views/thread/components/threadDetail.js @@ -4,8 +4,6 @@ import compose from 'recompose/compose'; import { Link } from 'react-router-dom'; import { connect } from 'react-redux'; import { withRouter } from 'react-router'; -import { convertFromRaw } from 'draft-js'; -import { stateToMarkdown } from 'draft-js-export-markdown'; import { timeDifference } from 'shared/time-difference'; import { convertTimestampToDate } from 'shared/time-formatting'; import { openModal } from 'src/actions/modals'; From ddf1d6fc423414e5913ec839dd5d8a91af8b8d56 Mon Sep 17 00:00:00 2001 From: Max Stoiber Date: Fri, 8 Mar 2019 12:20:55 +0100 Subject: [PATCH 08/18] Move thread preview markdown conversion to network --- src/components/composer/inputs.js | 35 ++++++++++++++++++++++----- src/components/message/editingBody.js | 1 - 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/components/composer/inputs.js b/src/components/composer/inputs.js index aa23608896..6c54c35309 100644 --- a/src/components/composer/inputs.js +++ b/src/components/composer/inputs.js @@ -15,7 +15,6 @@ import { ThreadHeading } from 'src/views/thread/style'; import { SegmentedControl, Segment } from 'src/components/segmentedControl'; import MentionsInput from 'src/components/mentionsInput'; import ThreadRenderer from '../threadRenderer'; -import processThreadContent from 'shared/draft-utils/process-thread-content'; type Props = { title: string, @@ -32,6 +31,8 @@ type Props = { export default (props: Props) => { // $FlowIssue const [showPreview, setShowPreview] = React.useState(false); + // $FlowIssue + const [previewBody, setPreviewBody] = React.useState(null); const { title, @@ -45,6 +46,26 @@ export default (props: Props) => { isEditing, } = props; + const onClick = (show: boolean) => { + setShowPreview(show); + + if (show) { + setPreviewBody(null); + fetch('https://convert.spectrum.chat/from', { + method: 'POST', + body, + }) + .then(res => { + if (res.status < 200 || res.status >= 300) + throw new Error('Oops, something went wrong'); + return res.json(); + }) + .then(json => { + setPreviewBody(json); + }); + } + }; + return ( { background: '#FFF', }} > - setShowPreview(false)}> + onClick(false)}> Write - setShowPreview(true)}> + onClick(true)}> Preview @@ -70,9 +91,11 @@ export default (props: Props) => { /* $FlowFixMe */ {title} - + {previewBody === null ? ( +

Loading...

+ ) : ( + + )}
) : ( Date: Fri, 8 Mar 2019 13:26:20 +0100 Subject: [PATCH 09/18] Remove more draft-js stuff from client --- src/components/message/view.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/message/view.js b/src/components/message/view.js index 907b3478c0..aa4891c924 100644 --- a/src/components/message/view.js +++ b/src/components/message/view.js @@ -13,7 +13,6 @@ import { } from './style'; import ThreadAttachment from './ThreadAttachment'; import { messageRenderer } from 'shared/clients/draft-js/message/renderer'; -import { toPlainText, toState } from 'shared/draft-utils'; import { draftOnlyContainsEmoji } from 'shared/only-contains-emoji'; import { Byline, Name, Username } from './style'; import { isShort } from 'shared/clients/draft-js/utils/isShort'; @@ -68,7 +67,9 @@ export const Body = (props: BodyProps) => { } case messageTypeObj.draftjs: { const parsed = JSON.parse(message.content.body); - const ids = getSpectrumThreadIds(toPlainText(toState(parsed))); + const ids = getSpectrumThreadIds( + parsed.blocks.map(block => block.text).join('\n') + ); const uniqueIds = ids.filter((x, i, a) => a.indexOf(x) === i); return ( From 302c0dc6f5503457a43f943ee79673e7f562acf3 Mon Sep 17 00:00:00 2001 From: Max Stoiber Date: Fri, 8 Mar 2019 13:32:58 +0100 Subject: [PATCH 10/18] =?UTF-8?q?Completely=20kick=20DraftJS=20and=20Immut?= =?UTF-8?q?ableJS=20out=20of=20the=20bundle!=20=F0=9F=8E=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/clients/draft-js/utils/isShort.js | 9 ++++----- shared/generate-meta-info.js | 5 +++-- shared/notification-to-text.js | 9 ++++++++- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/shared/clients/draft-js/utils/isShort.js b/shared/clients/draft-js/utils/isShort.js index 588fc68017..503d88e2b2 100644 --- a/shared/clients/draft-js/utils/isShort.js +++ b/shared/clients/draft-js/utils/isShort.js @@ -1,14 +1,13 @@ // @flow -// Determine whether a DraftJS message is short -import { toPlainText, toState } from '../../../draft-utils'; - -import type { RawDraftContentState } from 'draft-js'; import type { MessageInfoType } from '../../../graphql/fragments/message/messageInfo'; export const isShort = (message: MessageInfoType): boolean => { if (message.messageType === 'media') return false; const jsonBody = JSON.parse(message.content.body); return ( - jsonBody.blocks.length <= 1 && toPlainText(toState(jsonBody)).length <= 170 + jsonBody.blocks.length <= 1 && + jsonBody.blocks + .filter(block => block.type === 'unstyled') + .map(block => block.text).length <= 170 ); }; diff --git a/shared/generate-meta-info.js b/shared/generate-meta-info.js index 0c506a5427..43e4b077c4 100644 --- a/shared/generate-meta-info.js +++ b/shared/generate-meta-info.js @@ -7,7 +7,6 @@ * so it chokes on the Flow syntax. * More info: https://flow.org/en/docs/types/comments/ */ -var draft = require('./draft-utils'); var truncate = require('./truncate'); var striptags = require('striptags'); @@ -104,7 +103,9 @@ function generateMetaInfo(input /*: Input */) /*: Meta */ { data && data.body && (data.type === 'DRAFTJS' - ? draft.toPlainText(draft.toState(JSON.parse(data.body))) + ? JSON.parse(data.body) + .blocks.filter(block => block.type === 'unstyled') + .map(block => block.text) : data.body); return setDefault({ title: data && data.title + ' · ' + data.communityName, diff --git a/shared/notification-to-text.js b/shared/notification-to-text.js index bdff4ffa2f..2a40d70c5e 100644 --- a/shared/notification-to-text.js +++ b/shared/notification-to-text.js @@ -1,8 +1,15 @@ import sentencify from 'shared/sentencify'; import sortByDate from 'shared/sort-by-date'; -import { toState, toPlainText } from 'shared/draft-utils'; import { messageTypeObj } from 'shared/draft-utils/message-types'; +const toPlainText = raw => { + return raw.blocks + .filter(block => block.type === 'unstyled') + .map(block => block.text); +}; + +const toState = input => input; + const sortThreads = (entities, currentUser) => { // filter out the current user's threads let threads = entities.filter( From ee80b090cce51bb3abdf57610af596c81f460779 Mon Sep 17 00:00:00 2001 From: Max Stoiber Date: Fri, 8 Mar 2019 13:45:44 +0100 Subject: [PATCH 11/18] Use shared toPlainText util --- shared/clients/draft-js/utils/isShort.js | 8 ++------ shared/clients/draft-js/utils/plaintext.js | 9 +++++++++ shared/generate-meta-info.js | 5 ++--- shared/notification-to-text.js | 20 +++++--------------- 4 files changed, 18 insertions(+), 24 deletions(-) create mode 100644 shared/clients/draft-js/utils/plaintext.js diff --git a/shared/clients/draft-js/utils/isShort.js b/shared/clients/draft-js/utils/isShort.js index 503d88e2b2..25f822494d 100644 --- a/shared/clients/draft-js/utils/isShort.js +++ b/shared/clients/draft-js/utils/isShort.js @@ -1,13 +1,9 @@ // @flow +import { toPlainText } from './plaintext'; import type { MessageInfoType } from '../../../graphql/fragments/message/messageInfo'; export const isShort = (message: MessageInfoType): boolean => { if (message.messageType === 'media') return false; const jsonBody = JSON.parse(message.content.body); - return ( - jsonBody.blocks.length <= 1 && - jsonBody.blocks - .filter(block => block.type === 'unstyled') - .map(block => block.text).length <= 170 - ); + return jsonBody.blocks.length <= 1 && toPlainText(jsonBody).length <= 170; }; diff --git a/shared/clients/draft-js/utils/plaintext.js b/shared/clients/draft-js/utils/plaintext.js new file mode 100644 index 0000000000..a533f86fc4 --- /dev/null +++ b/shared/clients/draft-js/utils/plaintext.js @@ -0,0 +1,9 @@ +// @flow +import type { RawContentState } from 'draft-js'; + +export const toPlainText = (raw: RawContentState) => { + return raw.blocks + .filter(block => block.type === 'unstyled') + .map(block => block.text) + .join('\n'); +}; diff --git a/shared/generate-meta-info.js b/shared/generate-meta-info.js index 43e4b077c4..833c6e97be 100644 --- a/shared/generate-meta-info.js +++ b/shared/generate-meta-info.js @@ -9,6 +9,7 @@ */ var truncate = require('./truncate'); var striptags = require('striptags'); +var toPlainText = require('./clients/draft-js/utils/plaintext').toPlainText; var DEFAULT_META = { title: 'Spectrum', @@ -103,9 +104,7 @@ function generateMetaInfo(input /*: Input */) /*: Meta */ { data && data.body && (data.type === 'DRAFTJS' - ? JSON.parse(data.body) - .blocks.filter(block => block.type === 'unstyled') - .map(block => block.text) + ? toPlainText(JSON.parse(data.body)) : data.body); return setDefault({ title: data && data.title + ' · ' + data.communityName, diff --git a/shared/notification-to-text.js b/shared/notification-to-text.js index 2a40d70c5e..f2a8051683 100644 --- a/shared/notification-to-text.js +++ b/shared/notification-to-text.js @@ -1,14 +1,7 @@ import sentencify from 'shared/sentencify'; import sortByDate from 'shared/sort-by-date'; import { messageTypeObj } from 'shared/draft-utils/message-types'; - -const toPlainText = raw => { - return raw.blocks - .filter(block => block.type === 'unstyled') - .map(block => block.text); -}; - -const toState = input => input; +import { toPlainText } from 'shared/clients/draft-js/utils/plaintext'; const sortThreads = (entities, currentUser) => { // filter out the current user's threads @@ -121,10 +114,7 @@ const formatNotification = ( let body = payload.content.body; if (typeof body === 'string') body = JSON.parse(payload.content.body); - return `"${toPlainText(toState(body)).replace( - /[ \n\r\v]+/g, - ' ' - )}"`; + return `"${toPlainText(body).replace(/[ \n\r\v]+/g, ' ')}"`; } return `"${payload.content.body.replace(/[ \n\r\v]+/g, ' ')}"`; @@ -165,7 +155,7 @@ const formatNotification = ( body = JSON.parse(payload.content.body); return `${sender.payload.name} (@${ sender.payload.username - }): ${toPlainText(toState(body))}`; + }): ${toPlainText(body)}`; } return `${sender.payload.name}: ${ @@ -182,7 +172,7 @@ const formatNotification = ( href = `/thread/${message.threadId}`; body = message.messageType.toLowerCase() === messageTypeObj.draftjs - ? `${toPlainText(toState(JSON.parse(message.content.body)))}` + ? `${toPlainText(JSON.parse(message.content.body))}` : message.content.body; break; } @@ -191,7 +181,7 @@ const formatNotification = ( href = `/thread/${thread.id}`; body = thread.type.toLowerCase() === messageTypeObj.draftjs - ? `${toPlainText(toState(JSON.parse(thread.content.body)))}` + ? `${toPlainText(JSON.parse(thread.content.body))}` : thread.content.body; break; } From 6304c2b9682c2d47a2055900ed2180ed628ff3f3 Mon Sep 17 00:00:00 2001 From: Max Stoiber Date: Sat, 9 Mar 2019 13:41:13 +0100 Subject: [PATCH 12/18] Fix messages getting empty lines appended --- shared/draft-utils/add-embeds-to-draft-js.js | 2 +- .../add-embeds-to-draft-js.test.js.snap | 52 +++++++++++++++++++ .../test/add-embeds-to-draft-js.test.js | 46 +++++++++++++++- 3 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 shared/draft-utils/test/__snapshots__/add-embeds-to-draft-js.test.js.snap diff --git a/shared/draft-utils/add-embeds-to-draft-js.js b/shared/draft-utils/add-embeds-to-draft-js.js index b711233ecf..c5e48ad5a7 100644 --- a/shared/draft-utils/add-embeds-to-draft-js.js +++ b/shared/draft-utils/add-embeds-to-draft-js.js @@ -34,7 +34,7 @@ export const addEmbedsToEditorState = ( if (block.type !== 'unstyled') return; const embeds = getEmbedsFromText(block.text); - if (!embeds.length === 0) return; + if (embeds.length === 0) return; embeds.forEach(embed => { lastEntityKey++; diff --git a/shared/draft-utils/test/__snapshots__/add-embeds-to-draft-js.test.js.snap b/shared/draft-utils/test/__snapshots__/add-embeds-to-draft-js.test.js.snap new file mode 100644 index 0000000000..df433d57c4 --- /dev/null +++ b/shared/draft-utils/test/__snapshots__/add-embeds-to-draft-js.test.js.snap @@ -0,0 +1,52 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should add embeds 1`] = ` +Object { + "blocks": Array [ + Object { + "data": Object {}, + "depth": 0, + "entityRanges": Array [], + "inlineStyleRanges": Array [], + "key": "g0000", + "text": "https://simplecast.com/s/a1f11d11", + "type": "unstyled", + }, + Object { + "data": Object {}, + "depth": 0, + "entityRanges": Array [ + Object { + "key": -Infinity, + "length": 1, + "offset": 0, + }, + ], + "inlineStyleRanges": Array [], + "key": "0", + "text": " ", + "type": "atomic", + }, + Object { + "data": Object {}, + "depth": 0, + "entityRanges": Array [], + "inlineStyleRanges": Array [], + "key": "oldvrah", + "text": " ", + "type": "unstyled", + }, + ], + "entityMap": Object { + "-Infinity": Object { + "data": Object { + "height": 200, + "src": "https://embed.simplecast.com/a1f11d11", + "url": "https://embed.simplecast.com/a1f11d11", + }, + "mutability": "MUTABLE", + "type": "embed", + }, + }, +} +`; diff --git a/shared/draft-utils/test/add-embeds-to-draft-js.test.js b/shared/draft-utils/test/add-embeds-to-draft-js.test.js index 59dff64139..a148713256 100644 --- a/shared/draft-utils/test/add-embeds-to-draft-js.test.js +++ b/shared/draft-utils/test/add-embeds-to-draft-js.test.js @@ -1,5 +1,13 @@ // @flow -import { getEmbedsFromText } from '../add-embeds-to-draft-js'; +jest.mock('draft-js/lib/generateRandomKey', () => { + let last = 0; + const multiplier = Math.exp(24); + return () => Math.floor(last++ * multiplier).toString(32); +}); +import { + getEmbedsFromText, + addEmbedsToEditorState, +} from '../add-embeds-to-draft-js'; describe('sites', () => { describe('