Increase perceived performance by scrolling to the right spot before Hydrogen loads (#128)

This commit is contained in:
Eric Eastwood 2022-11-09 18:57:33 -06:00 committed by GitHub
parent 3671da0405
commit fa4720af04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 64 additions and 13 deletions

View File

@ -16,9 +16,7 @@ const vm = require('vm');
const path = require('path');
const { readFile } = require('fs').promises;
const crypto = require('crypto');
const { parseHTML } = require('linkedom');
const safeJson = require('../lib/safe-json');
// Setup the DOM context with any necessary shims/polyfills and ensure the VM
// context global has everything that a normal document does so Hydrogen can
@ -67,24 +65,36 @@ async function _renderHydrogenToStringUnsafe(renderOptions) {
assert(renderOptions.vmRenderScriptFilePath);
assert(renderOptions.vmRenderContext);
assert(renderOptions.pageOptions);
assert(renderOptions.pageOptions.locationHref);
assert(renderOptions.pageOptions.cspNonce);
const { dom, vmContext } = createDomAndSetupVmContext();
// A small `window.location` stub
if (!dom.window.location) {
const locationUrl = new URL(renderOptions.pageOptions.locationHref);
dom.window.location = {};
[
'hash',
'host',
'hostname',
'href',
'origin',
'password',
'pathname',
'port',
'protocol',
'search',
'username',
].forEach((key) => {
dom.window.location[key] = locationUrl[key];
});
}
// Define this for the SSR context
dom.window.matrixPublicArchiveContext = {
...renderOptions.vmRenderContext,
};
// Serialize it for when we run this again client-side
const serializedContext = JSON.stringify(dom.window.matrixPublicArchiveContext);
dom.document.body.insertAdjacentHTML(
'beforeend',
`
<script type="text/javascript" nonce="${renderOptions.pageOptions.cspNonce}">
window.matrixPublicArchiveContext = ${safeJson(serializedContext)}
</script>
`
);
const vmRenderScriptFilePath = renderOptions.vmRenderScriptFilePath;
const hydrogenRenderScriptCode = await readFile(vmRenderScriptFilePath, 'utf8');

View File

@ -26,6 +26,12 @@ async function renderHydrogenVmRenderScriptToPageHtml(
pageOptions,
});
// Serialize the state for when we run the Hydrogen render again client-side to
// re-hydrate the DOM
const serializedMatrixPublicArchiveContext = JSON.stringify({
...vmRenderContext,
});
const serializableSpans = getSerializableSpans();
const serializedSpans = JSON.stringify(serializableSpans);
@ -51,6 +57,34 @@ async function renderHydrogenVmRenderScriptToPageHtml(
</head>
<body>
${hydrogenHtmlOutput}
${
/**
* This inline snippet is used in to scroll the Hydrogen timeline to the
* right place immediately when the page loads instead of waiting for
* Hydrogen to load, hydrate and finally scroll.
*/ ''
}
<script type="text/javascript" nonce="${pageOptions.cspNonce}">
const qs = new URLSearchParams(window?.location?.search);
const atEventId = qs.get('at');
if (atEventId) {
const el = document.querySelector(\`[data-event-id="\${atEventId}"]\`);
requestAnimationFrame(() => {
el && el.scrollIntoView({ block: 'center' });
});
} else {
const el = document.querySelector('.js-bottom-scroll-anchor');
requestAnimationFrame(() => {
el && el.scrollIntoView({ block: 'end' });
});
}
</script>
<script type="text/javascript" nonce="${pageOptions.cspNonce}">
window.matrixPublicArchiveContext = ${safeJson(serializedMatrixPublicArchiveContext)}
</script>
${pageOptions.scripts
.map(
(scriptUrl) =>

View File

@ -93,6 +93,7 @@ router.get(
title: `Matrix Public Archive`,
styles: [hydrogenStylesUrl, stylesUrl, roomDirectoryStylesUrl],
scripts: [jsBundleUrl],
locationHref: urlJoin(basePath, req.originalUrl),
shouldIndex,
cspNonce: res.locals.cspNonce,
}

View File

@ -420,6 +420,7 @@ router.get(
title: `${roomData.name} - Matrix Public Archive`,
styles: [hydrogenStylesUrl, stylesUrl],
scripts: [jsBundleUrl],
locationHref: urlJoin(basePath, req.originalUrl),
shouldIndex,
cspNonce: res.locals.cspNonce,
}

View File

@ -23,7 +23,12 @@ class JumpToNextActivitySummaryTileView extends TemplateView {
return t.div(
{
className: 'JumpToNextActivitySummaryTileView',
className: {
JumpToNextActivitySummaryTileView: true,
// Used by page loaded JavaScript to quickly jump the scroll viewport down
// while we wait for the rest of the JavaScript to load.
'js-bottom-scroll-anchor': true,
},
'data-event-id': vm.eventId,
},
[