fix: preserve newlines correctly in delete-and-redraft (#845)

fixes #830
This commit is contained in:
Nolan Lawson 2018-12-19 00:57:56 -08:00 committed by GitHub
parent d5eac4e119
commit 27da387a01
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 68 additions and 27 deletions

View file

@ -0,0 +1,24 @@
import { statusHtmlToPlainText } from '../_utils/statusHtmlToPlainText'
import { importShowComposeDialog } from '../_components/dialog/asyncDialogs'
import { doDeleteStatus } from './delete'
import { store } from '../_store/store'
export async function deleteAndRedraft (status) {
let deleteStatusPromise = doDeleteStatus(status.id)
let dialogPromise = importShowComposeDialog()
await deleteStatusPromise
store.setComposeData('dialog', {
text: statusHtmlToPlainText(status.content, status.mentions),
contentWarningShown: !!status.spoiler_text,
contentWarning: status.spoiler_text || '',
postPrivacy: status.visibility,
media: status.media_attachments && status.media_attachments.map(_ => ({
description: _.description || '',
data: _
})),
inReplyToId: status.in_reply_to_id
})
let showComposeDialog = await dialogPromise
showComposeDialog()
}

View file

@ -20,8 +20,7 @@ import { setAccountMuted } from '../../../_actions/mute'
import { setStatusPinnedOrUnpinned } from '../../../_actions/pin' import { setStatusPinnedOrUnpinned } from '../../../_actions/pin'
import { setConversationMuted } from '../../../_actions/muteConversation' import { setConversationMuted } from '../../../_actions/muteConversation'
import { copyText } from '../../../_actions/copyText' import { copyText } from '../../../_actions/copyText'
import { statusHtmlToPlainText } from '../../../_utils/statusHtmlToPlainText' import { deleteAndRedraft } from '../../../_actions/deleteAndRedraft'
import { importShowComposeDialog } from '../asyncDialogs'
export default { export default {
oncreate, oncreate,
@ -189,24 +188,8 @@ export default {
}, },
async onRedraft () { async onRedraft () {
let { status } = this.get() let { status } = this.get()
let deleteStatusPromise = doDeleteStatus(status.id) await deleteAndRedraft(status)
let dialogPromise = importShowComposeDialog()
await deleteStatusPromise
this.store.setComposeData('dialog', {
text: statusHtmlToPlainText(status.content, status.mentions),
contentWarningShown: !!status.spoiler_text,
contentWarning: status.spoiler_text || '',
postPrivacy: status.visibility,
media: status.media_attachments && status.media_attachments.map(_ => ({
description: _.description || '',
data: _
})),
inReplyToId: status.in_reply_to_id
})
this.close() this.close()
let showComposeDialog = await dialogPromise
showComposeDialog()
} }
} }
} }

View file

@ -2,13 +2,8 @@ import { mark, stop } from './marks'
let domParser = process.browser && new DOMParser() let domParser = process.browser && new DOMParser()
export function statusHtmlToPlainText (html, mentions) { // mentions like "@foo" have to be expanded to "@foo@example.com"
if (!html) { function massageMentions (doc, mentions) {
return ''
}
mark('statusHtmlToPlainText')
let doc = domParser.parseFromString(html, 'text/html')
// mentions like "@foo" have to be expanded to "@foo@example.com"
let anchors = doc.querySelectorAll('a.mention') let anchors = doc.querySelectorAll('a.mention')
for (let i = 0; i < anchors.length; i++) { for (let i = 0; i < anchors.length; i++) {
let anchor = anchors[i] let anchor = anchors[i]
@ -18,7 +13,29 @@ export function statusHtmlToPlainText (html, mentions) {
anchor.innerText = `@${mention.acct}` anchor.innerText = `@${mention.acct}`
} }
} }
let res = doc.documentElement.textContent }
// paragraphs should be separated by double newlines
// single <br/>s should become single newlines
function innerTextRetainingNewlines (doc) {
let paragraphs = doc.querySelectorAll('p')
return Array.from(paragraphs).map(paragraph => {
let brs = paragraph.querySelectorAll('br')
Array.from(brs).forEach(br => {
br.parentNode.replaceChild(doc.createTextNode('\n'), br)
})
return paragraph.textContent
}).join('\n\n')
}
export function statusHtmlToPlainText (html, mentions) {
if (!html) {
return ''
}
mark('statusHtmlToPlainText')
let doc = domParser.parseFromString(html, 'text/html')
massageMentions(doc, mentions)
let res = innerTextRetainingNewlines(doc)
stop('statusHtmlToPlainText') stop('statusHtmlToPlainText')
return res return res
} }

View file

@ -145,3 +145,20 @@ test('delete and redraft reply within thread', async t => {
}) })
.expect(getNthStatus(2).exists).notOk() .expect(getNthStatus(2).exists).notOk()
}) })
test('multiple paragraphs', async t => {
let text = 'hey ho\n\ndouble newline!\njust one newline\njust another newline\n\nanother double newline!'
await postAs('foobar', text)
await loginAsFoobar(t)
await t
.hover(getNthStatus(0))
.expect(getNthStatusContent(0).innerText).contains(text)
.click(getNthStatusOptionsButton(0))
.click(dialogOptionsOption.withText('Delete and redraft'))
.expect(modalDialog.hasAttribute('aria-hidden')).notOk()
.expect(composeModalInput.value).eql(text)
.typeText(composeModalInput, '\n\nwoot', { paste: true })
.click(composeModalComposeButton)
.expect(modalDialog.exists).notOk()
.expect(getNthStatusContent(0).innerText).contains(text + '\n\nwoot')
})