From e1532ed9d136d3747b85f14efcb1683e1f81f6ea Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Sun, 26 Apr 2020 18:30:49 -0700 Subject: [PATCH] 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). --- src/routes/_actions/createMakeProps.js | 25 ++++++++++++++++++++++- src/routes/_components/status/Status.html | 4 +--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/routes/_actions/createMakeProps.js b/src/routes/_actions/createMakeProps.js index 9c71d727..f3e2b003 100644 --- a/src/routes/_actions/createMakeProps.js +++ b/src/routes/_actions/createMakeProps.js @@ -2,6 +2,8 @@ import { database } from '../_database/database' import { decode as decodeBlurhash, init as initBlurhash } from '../_utils/blurhash' import { mark, stop } from '../_utils/marks' import { get } from '../_utils/lodash-lite' +import { statusHtmlToPlainText } from '../_utils/statusHtmlToPlainText' +import { scheduleIdleTask } from '../_utils/scheduleIdleTask' async function getNotification (instanceName, timelineType, timelineValue, itemId) { 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) { let promiseChain = Promise.resolve() @@ -64,7 +84,10 @@ export function createMakeProps (instanceName, timelineType, timelineValue) { async function getStatusOrNotification (itemId) { const statusOrNotification = await fetchFromIndexedDB(itemId) - await decodeAllBlurhashes(statusOrNotification) + await Promise.all([ + decodeAllBlurhashes(statusOrNotification), + calculatePlainTextContent(statusOrNotification) + ]) return statusOrNotification } diff --git a/src/routes/_components/status/Status.html b/src/routes/_components/status/Status.html index 438a638c..123389e1 100644 --- a/src/routes/_components/status/Status.html +++ b/src/routes/_components/status/Status.html @@ -129,7 +129,6 @@ import { absoluteDateFormatter } from '../../_utils/formatters' import { composeNewStatusMentioning } from '../../_actions/mention' import { createStatusOrNotificationUuid } from '../../_utils/createStatusOrNotificationUuid' - import { statusHtmlToPlainText } from '../../_utils/statusHtmlToPlainText' const INPUT_TAGS = new Set(['a', 'button', 'input', 'textarea', 'label']) const isUserInputElement = node => INPUT_TAGS.has(node.localName) @@ -224,8 +223,7 @@ originalAccountId: ({ originalAccount }) => originalAccount.id, accountForShortcut: ({ originalAccount, notification }) => notification ? notification.account : originalAccount, visibility: ({ originalStatus }) => originalStatus.visibility, - mentions: ({ originalStatus }) => originalStatus.mentions || [], - plainTextContent: ({ content, mentions }) => statusHtmlToPlainText(content, mentions), + plainTextContent: ({ originalStatus }) => originalStatus.plainTextContent || '', plainTextContentOverLength: ({ plainTextContent }) => ( // 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