perf: calculate plaintext from HTML content in advance in rIC (#1748)

On the Nexus 5 especially, this ensures we no longer have nearly so many
"long tasks" (i.e. responsiveness is better). It moves html-to-txt
calculation to the same step as blurhash decoding, where it can be done
in requestIdleCallback (heck, maybe someday it could just be done in a
worker thread as well).
This commit is contained in:
Nolan Lawson 2020-04-26 18:30:49 -07:00 committed by GitHub
parent ae3bd2bda2
commit e1532ed9d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 25 additions and 4 deletions

View File

@ -2,6 +2,8 @@ import { database } from '../_database/database'
import { decode as decodeBlurhash, init as initBlurhash } from '../_utils/blurhash' import { decode as decodeBlurhash, init as initBlurhash } from '../_utils/blurhash'
import { mark, stop } from '../_utils/marks' import { mark, stop } from '../_utils/marks'
import { get } from '../_utils/lodash-lite' import { get } from '../_utils/lodash-lite'
import { statusHtmlToPlainText } from '../_utils/statusHtmlToPlainText'
import { scheduleIdleTask } from '../_utils/scheduleIdleTask'
async function getNotification (instanceName, timelineType, timelineValue, itemId) { async function getNotification (instanceName, timelineType, timelineValue, itemId) {
return { return {
@ -45,6 +47,24 @@ async function decodeAllBlurhashes (statusOrNotification) {
} }
} }
async function calculatePlainTextContent (statusOrNotification) {
const status = statusOrNotification.status || statusOrNotification.notification.status
if (!status) {
return
}
const originalStatus = status.reblog ? status.reblog : status
const content = originalStatus.content || ''
const mentions = originalStatus.mentions || []
// Calculating the plaintext from the HTML is a non-trivial operation, so we might
// as well do it in advance, while blurhash is being decoded on the worker thread.
await new Promise(resolve => {
scheduleIdleTask(() => {
originalStatus.plainTextContent = statusHtmlToPlainText(content, mentions)
resolve()
})
})
}
export function createMakeProps (instanceName, timelineType, timelineValue) { export function createMakeProps (instanceName, timelineType, timelineValue) {
let promiseChain = Promise.resolve() let promiseChain = Promise.resolve()
@ -64,7 +84,10 @@ export function createMakeProps (instanceName, timelineType, timelineValue) {
async function getStatusOrNotification (itemId) { async function getStatusOrNotification (itemId) {
const statusOrNotification = await fetchFromIndexedDB(itemId) const statusOrNotification = await fetchFromIndexedDB(itemId)
await decodeAllBlurhashes(statusOrNotification) await Promise.all([
decodeAllBlurhashes(statusOrNotification),
calculatePlainTextContent(statusOrNotification)
])
return statusOrNotification return statusOrNotification
} }

View File

@ -129,7 +129,6 @@
import { absoluteDateFormatter } from '../../_utils/formatters' import { absoluteDateFormatter } from '../../_utils/formatters'
import { composeNewStatusMentioning } from '../../_actions/mention' import { composeNewStatusMentioning } from '../../_actions/mention'
import { createStatusOrNotificationUuid } from '../../_utils/createStatusOrNotificationUuid' import { createStatusOrNotificationUuid } from '../../_utils/createStatusOrNotificationUuid'
import { statusHtmlToPlainText } from '../../_utils/statusHtmlToPlainText'
const INPUT_TAGS = new Set(['a', 'button', 'input', 'textarea', 'label']) const INPUT_TAGS = new Set(['a', 'button', 'input', 'textarea', 'label'])
const isUserInputElement = node => INPUT_TAGS.has(node.localName) const isUserInputElement = node => INPUT_TAGS.has(node.localName)
@ -224,8 +223,7 @@
originalAccountId: ({ originalAccount }) => originalAccount.id, originalAccountId: ({ originalAccount }) => originalAccount.id,
accountForShortcut: ({ originalAccount, notification }) => notification ? notification.account : originalAccount, accountForShortcut: ({ originalAccount, notification }) => notification ? notification.account : originalAccount,
visibility: ({ originalStatus }) => originalStatus.visibility, visibility: ({ originalStatus }) => originalStatus.visibility,
mentions: ({ originalStatus }) => originalStatus.mentions || [], plainTextContent: ({ originalStatus }) => originalStatus.plainTextContent || '',
plainTextContent: ({ content, mentions }) => statusHtmlToPlainText(content, mentions),
plainTextContentOverLength: ({ plainTextContent }) => ( plainTextContentOverLength: ({ plainTextContent }) => (
// measureText() is expensive, so avoid doing it when possible. // measureText() is expensive, so avoid doing it when possible.
// Also measureText() typically only makes text shorter, not longer, so we can measure the raw length // Also measureText() typically only makes text shorter, not longer, so we can measure the raw length