From ee2558173afe06723aabd8c6362786c8d6c72462 Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Sun, 11 Dec 2022 08:33:38 -0800 Subject: [PATCH] fix: fix pinned status aria-label/blurhash --- src/routes/_actions/createMakeProps.js | 64 +----------------- src/routes/_actions/pinnedStatuses.js | 14 +++- .../_actions/rehydrateStatusOrNotification.js | 67 +++++++++++++++++++ 3 files changed, 82 insertions(+), 63 deletions(-) create mode 100644 src/routes/_actions/rehydrateStatusOrNotification.js diff --git a/src/routes/_actions/createMakeProps.js b/src/routes/_actions/createMakeProps.js index e53ae926..d88853d0 100644 --- a/src/routes/_actions/createMakeProps.js +++ b/src/routes/_actions/createMakeProps.js @@ -1,9 +1,6 @@ import { database } from '../_database/database.js' -import { decode as decodeBlurhash, init as initBlurhash } from '../_utils/blurhash.js' import { mark, stop } from '../_utils/marks.js' -import { get } from '../_utils/lodash-lite.js' -import { statusHtmlToPlainText } from '../_utils/statusHtmlToPlainText.js' -import { scheduleIdleTask } from '../_utils/scheduleIdleTask.js' +import { prepareToRehydrate, rehydrateStatusOrNotification } from './rehydrateStatusOrNotification.js' async function getNotification (instanceName, timelineType, timelineValue, itemId) { return { @@ -21,62 +18,10 @@ async function getStatus (instanceName, timelineType, timelineValue, itemId) { } } -function tryInitBlurhash () { - try { - initBlurhash() - } catch (err) { - console.error('could not start blurhash worker', err) - } -} - -function getActualStatus (statusOrNotification) { - return get(statusOrNotification, ['status']) || - get(statusOrNotification, ['notification', 'status']) -} - -async function decodeAllBlurhashes (statusOrNotification) { - const status = getActualStatus(statusOrNotification) - if (!status) { - return - } - const mediaWithBlurhashes = get(status, ['media_attachments'], []) - .concat(get(status, ['reblog', 'media_attachments'], [])) - .filter(_ => _.blurhash) - if (mediaWithBlurhashes.length) { - mark(`decodeBlurhash-${status.id}`) - await Promise.all(mediaWithBlurhashes.map(async media => { - try { - media.decodedBlurhash = await decodeBlurhash(media.blurhash) - } catch (err) { - console.warn('Could not decode blurhash, ignoring', err) - } - })) - stop(`decodeBlurhash-${status.id}`) - } -} - -async function calculatePlainTextContent (statusOrNotification) { - const status = getActualStatus(statusOrNotification) - 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() - tryInitBlurhash() // start the blurhash worker a bit early to save time + prepareToRehydrate() // start blurhash early to save time async function fetchFromIndexedDB (itemId) { mark(`fetchFromIndexedDB-${itemId}`) @@ -92,10 +37,7 @@ export function createMakeProps (instanceName, timelineType, timelineValue) { async function getStatusOrNotification (itemId) { const statusOrNotification = await fetchFromIndexedDB(itemId) - await Promise.all([ - decodeAllBlurhashes(statusOrNotification), - calculatePlainTextContent(statusOrNotification) - ]) + await rehydrateStatusOrNotification(statusOrNotification) return statusOrNotification } diff --git a/src/routes/_actions/pinnedStatuses.js b/src/routes/_actions/pinnedStatuses.js index 6b8e4096..ba630ab7 100644 --- a/src/routes/_actions/pinnedStatuses.js +++ b/src/routes/_actions/pinnedStatuses.js @@ -4,18 +4,28 @@ import { database } from '../_database/database.js' import { getPinnedStatuses } from '../_api/pinnedStatuses.js' +import { prepareToRehydrate, rehydrateStatusOrNotification } from './rehydrateStatusOrNotification.js' + +// Pinned statuses aren't a "normal" timeline, so their blurhashes/plaintext need to be calculated specially +async function rehydratePinnedStatuses (statuses) { + await Promise.all(statuses.map(status => rehydrateStatusOrNotification({ status }))) + return statuses +} export async function updatePinnedStatusesForAccount (accountId) { const { currentInstance, accessToken } = store.get() await cacheFirstUpdateAfter( - () => getPinnedStatuses(currentInstance, accessToken, accountId), async () => { + return rehydratePinnedStatuses(await getPinnedStatuses(currentInstance, accessToken, accountId)) + }, + async () => { + prepareToRehydrate() // start blurhash early to save time const pinnedStatuses = await database.getPinnedStatuses(currentInstance, accountId) if (!pinnedStatuses || !pinnedStatuses.every(Boolean)) { throw new Error('missing pinned statuses in idb') } - return pinnedStatuses + return rehydratePinnedStatuses(pinnedStatuses) }, statuses => database.insertPinnedStatuses(currentInstance, accountId, statuses), statuses => { diff --git a/src/routes/_actions/rehydrateStatusOrNotification.js b/src/routes/_actions/rehydrateStatusOrNotification.js new file mode 100644 index 00000000..113b27e7 --- /dev/null +++ b/src/routes/_actions/rehydrateStatusOrNotification.js @@ -0,0 +1,67 @@ +import { get } from '../_utils/lodash-lite.js' +import { mark, stop } from '../_utils/marks.js' +import { decode as decodeBlurhash, init as initBlurhash } from '../_utils/blurhash.js' +import { scheduleIdleTask } from '../_utils/scheduleIdleTask.js' +import { statusHtmlToPlainText } from '../_utils/statusHtmlToPlainText.js' + +function getActualStatus (statusOrNotification) { + return get(statusOrNotification, ['status']) || + get(statusOrNotification, ['notification', 'status']) +} + +export function prepareToRehydrate () { + // start the blurhash worker a bit early to save time + try { + initBlurhash() + } catch (err) { + console.error('could not start blurhash worker', err) + } +} + +async function decodeAllBlurhashes (statusOrNotification) { + const status = getActualStatus(statusOrNotification) + if (!status) { + return + } + const mediaWithBlurhashes = get(status, ['media_attachments'], []) + .concat(get(status, ['reblog', 'media_attachments'], [])) + .filter(_ => _.blurhash) + if (mediaWithBlurhashes.length) { + mark(`decodeBlurhash-${status.id}`) + await Promise.all(mediaWithBlurhashes.map(async media => { + try { + media.decodedBlurhash = await decodeBlurhash(media.blurhash) + } catch (err) { + console.warn('Could not decode blurhash, ignoring', err) + } + })) + stop(`decodeBlurhash-${status.id}`) + } +} + +async function calculatePlainTextContent (statusOrNotification) { + const status = getActualStatus(statusOrNotification) + 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() + }) + }) +} + +// Do stuff that we need to do when the status or notification is fetched from the database, +// like calculating the blurhash or calculating the plain text content +export async function rehydrateStatusOrNotification (statusOrNotification) { + await Promise.all([ + decodeAllBlurhashes(statusOrNotification), + calculatePlainTextContent(statusOrNotification) + ]) +}