diff --git a/layouts/default.vue b/layouts/default.vue index bef3db66e..be1f04cb8 100644 --- a/layouts/default.vue +++ b/layouts/default.vue @@ -8,7 +8,7 @@ import useDark from '~/composables/useDark.ts'; import useDialogue from '~/composables/useDialogue.ts'; import { longtimeCookieSetting } from '~/src/cookieSettings.ts'; import { LoadScriptError } from '~/src/errors.ts'; -import { newDate } from '~/src/helpers.ts'; +import { executeUnlessPrerendering, newDate } from '~/src/helpers.ts'; import { useMainStore } from '~/store/index.ts'; // no need to be super secure, just a sign that the page is not public @@ -42,7 +42,7 @@ const checkTesterPassword = (): void => { testerPasswordCookie.value = testerPassword.value; }; -onMounted(() => { +onMounted(executeUnlessPrerendering(() => { sorter(); confirmAge(); @@ -67,7 +67,7 @@ onMounted(() => { window.location.reload(); } }; -}); +})); const dialogue = useDialogue(); const confirmAge = async (): Promise => { diff --git a/plugins/speculationrules.ts b/plugins/speculationrules.ts new file mode 100644 index 000000000..827fc7a48 --- /dev/null +++ b/plugins/speculationrules.ts @@ -0,0 +1,23 @@ +export default defineNuxtPlugin(() => { + useHead({ + script: [ + { + type: 'speculationrules', + innerHTML: JSON.stringify({ + prerender: [ + { + where: { + and: [ + { href_matches: '/*' }, + { not: { selector_matches: '.no-prerender' } }, + { not: { selector_matches: '[rel~=nofollow]' } }, + ], + }, + }, + ], + eagerness: 'moderate', + }), + }, + ], + }); +}); diff --git a/plugins/track.client.ts b/plugins/track.client.ts index d259a7574..cf1b44781 100644 --- a/plugins/track.client.ts +++ b/plugins/track.client.ts @@ -2,6 +2,8 @@ import * as Sentry from '@sentry/vue'; import { defineNuxtPlugin, useRouter } from 'nuxt/app'; import type { RouteLocationNormalized } from 'vue-router'; +import { executeUnlessPrerendering } from '~/src/helpers.ts'; + const USER_AT = /^\/@.+/; const USER_SUBPAGE = /^\/(u|card)\/.*/; @@ -21,17 +23,19 @@ export default defineNuxtPlugin((nuxtApp) => { const router = useRouter(); const trackPageview = (route: RouteLocationNormalized): void => { - try { - const toUrl = normalizeUrl(new URL(route.fullPath, window.location.href)); - console.debug('[analytics] tracking page view:', toUrl.toString()); - useTrackPageview({ - data: { - url: toUrl.toString(), - }, - }); - } catch (error) { - Sentry.captureException(error); - } + executeUnlessPrerendering(() => { + try { + const toUrl = normalizeUrl(new URL(route.fullPath, window.location.href)); + console.debug('[analytics] tracking page view:', toUrl.toString()); + useTrackPageview({ + data: { + url: toUrl.toString(), + }, + }); + } catch (error) { + Sentry.captureException(error); + } + })(); }; // Track the initial page load diff --git a/src/helpers.ts b/src/helpers.ts index 792598ac8..de04327f1 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -562,3 +562,13 @@ export const filterObjectKeys = , K extends keyof return filteredObj; }, {} as Pick); }; + +export const executeUnlessPrerendering = (fn: () => void): (() => void) => { + return () => { + if ((document as any).prerendering) { + document.addEventListener('prerenderingchange', fn, { once: true }); + } else { + fn(); + } + }; +};