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 70fa21401c..e0d6030c68 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/api/queries/thread/channel.js b/api/queries/thread/channel.js index 16d7013ce4..12fb461368 100644 --- a/api/queries/thread/channel.js +++ b/api/queries/thread/channel.js @@ -2,5 +2,16 @@ import type { GraphQLContext } from '../../'; import type { DBThread } from 'shared/types'; -export default ({ channelId }: DBThread, _: any, { loaders }: GraphQLContext) => - loaders.channel.load(channelId); +export default async (root: DBThread, _: any, ctx: GraphQLContext) => { + const { channelId, id } = root; + const { loaders } = ctx; + const channel = await loaders.channel.load(channelId); + if (!channel) { + console.error( + 'User queried thread of non-existent/deleted channel: ', + channelId + ); + console.error('Thread queried: ', id); + } + return channel; +}; diff --git a/api/queries/thread/community.js b/api/queries/thread/community.js index 03eda41434..758156671d 100644 --- a/api/queries/thread/community.js +++ b/api/queries/thread/community.js @@ -2,8 +2,16 @@ import type { GraphQLContext } from '../../'; import type { DBThread } from 'shared/types'; -export default ( - { communityId }: DBThread, - _: any, - { loaders }: GraphQLContext -) => loaders.community.load(communityId); +export default async (root: DBThread, _: any, ctx: GraphQLContext) => { + const { communityId, id } = root; + const { loaders } = ctx; + const community = await loaders.community.load(communityId); + if (!community) { + console.error( + 'User queried thread of non-existent/deleted community: ', + communityId + ); + console.error('Thread queried: ', id); + } + return community; +}; 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/package.json b/package.json index 32c54a14e4..a3efcd5df5 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/clients/draft-js/utils/isShort.js b/shared/clients/draft-js/utils/isShort.js index 588fc68017..25f822494d 100644 --- a/shared/clients/draft-js/utils/isShort.js +++ b/shared/clients/draft-js/utils/isShort.js @@ -1,14 +1,9 @@ // @flow -// Determine whether a DraftJS message is short -import { toPlainText, toState } from '../../../draft-utils'; - -import type { RawDraftContentState } from 'draft-js'; +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 && toPlainText(toState(jsonBody)).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/draft-utils/add-embeds-to-draft-js.js b/shared/draft-utils/add-embeds-to-draft-js.js index b711233ecf..3e675451a6 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++; @@ -64,19 +64,6 @@ export const addEmbedsToEditorState = ( type: 'embed', }; }); - // If this is the last block we need to add an empty block below the atomic block, - // otherwise users cannot remove the embed during editing - if (index === input.blocks.length - 1) { - newBlocks.push({ - type: 'unstyled', - data: {}, - text: ' ', - depth: 0, - entityRanges: [], - inlineStyleRanges: [], - key: genKey(), - }); - } }); return { 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/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 || ''; } }; 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..9136aa209e --- /dev/null +++ b/shared/draft-utils/test/__snapshots__/add-embeds-to-draft-js.test.js.snap @@ -0,0 +1,43 @@ +// 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", + }, + ], + "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('