From 3b2273ed2553308a16fb5e6d09d7ed5e27ecb84f Mon Sep 17 00:00:00 2001 From: Lightling Date: Tue, 19 Mar 2024 22:50:55 -0400 Subject: [PATCH] content warning prompt for routes --- src/components/shared/warning-prompt.vue | 67 +++++++++++++++++++++++ src/content-env.d.ts | 27 ++++++++++ src/main.vue | 68 ++++++++++++++++++++---- src/routes.ts | 25 +++++++-- 4 files changed, 175 insertions(+), 12 deletions(-) create mode 100644 src/components/shared/warning-prompt.vue diff --git a/src/components/shared/warning-prompt.vue b/src/components/shared/warning-prompt.vue new file mode 100644 index 0000000..9cb8727 --- /dev/null +++ b/src/components/shared/warning-prompt.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/src/content-env.d.ts b/src/content-env.d.ts index 8ee46de..4b17ff6 100644 --- a/src/content-env.d.ts +++ b/src/content-env.d.ts @@ -1,4 +1,17 @@ declare module 'content/routes.js' { + /** + * Defines the structure of the warning modal that shows when a route has a content warning of some sort. + */ + export type WarningModal = { + prompt: string + leave: { + url: string, + text: string, + }, + acknowledge: string, + remember: string, + } + /** * Defines the available `views` that can be used to set up routes. * Each `Template` has different configuration options @@ -16,6 +29,7 @@ declare module 'content/routes.js' { stylesheetUrls: string[] template: Template title: string + warning: boolean | WarningModal } /** @@ -88,6 +102,19 @@ declare module 'content/routes.js' { } | { children: HeaderEntry[] }) + + /** + * Defines global values for the site. + */ + type SiteGlobals = { + id: string + warning: WarningModal + } + + /** + * Global values the site uses. + */ + const siteGlobals: SiteGlobals /** * The header the app uses. diff --git a/src/main.vue b/src/main.vue index a446671..5118442 100644 --- a/src/main.vue +++ b/src/main.vue @@ -3,26 +3,51 @@ import { onMounted, ref } from 'vue' import { useRouter } from 'vue-router' import { getCurrentRoute } from 'src/utilities/vuetils' +import { storage } from './utilities/fetch' import { useRouteStore } from 'src/routes' -import HeaderLink from 'src/components/shared/header-link.vue' +import type { WarningModal } from 'content/routes.js' -const ready = ref(false) +import HeaderLink from 'src/components/shared/header-link.vue' +import WarningPrompt from 'src/components/shared/warning-prompt.vue' const router = useRouter() const routeStore = useRouteStore() const headerConfig = routeStore._header +const globalConfig = routeStore._globals -const refresh = async () => { - const currentRoute = getCurrentRoute() - const routeConfig = routeStore._routes[currentRoute.path] +let currentRoute = getCurrentRoute() +let routeConfig = routeStore._routes[currentRoute.path] +let rememberWarning = false +const ready = ref(false) +const acknowledged = ref(false) +const storageId = ref('') +const warning = ref({} as WarningModal) + +const determineWarning = () => { + if (!routeConfig.warning || routeStore.doesRouteRememberWarning(currentRoute.path)) { + acknowledged.value = true + return + } + rememberWarning = storage.read(`${storageId.value}::rememberWarning`) || false + if (rememberWarning) { + acknowledged.value = true + routeStore.rememberRouteWarning(currentRoute.path) + return + } + warning.value = routeConfig.warning === true + ? globalConfig.warning + : routeConfig.warning +} + +const determineStylesheets = (stylesheetUrls?: string[]) => { const staleStylesheets = document.head.querySelectorAll('link[rel="stylesheet"]') staleStylesheets.forEach(stylesheet => { document.head.removeChild(stylesheet) }) - routeConfig?.stylesheetUrls?.forEach(stylesheet => { + stylesheetUrls?.forEach(stylesheet => { const newElement = document.createElement('link') newElement.setAttribute('rel', 'stylesheet') newElement.setAttribute('href', stylesheet) @@ -30,12 +55,29 @@ const refresh = async () => { }) } +const refresh = async () => { + ready.value = false + acknowledged.value = false + currentRoute = getCurrentRoute() + routeConfig = routeStore._routes[currentRoute.path] + storageId.value = `${globalConfig.id}::${currentRoute.path}` + + determineWarning() + determineStylesheets(routeConfig.stylesheetUrls) + + ready.value = true +} + +const onAcknowledgedWarning = () => { + acknowledged.value = true + routeStore.rememberRouteWarning(currentRoute.path) +} + onMounted(async () => { await refresh() router.afterEach(async (to, from) => { await refresh() }) - ready.value = true }) @@ -49,11 +91,19 @@ onMounted(async () => { v-for='entry in headerConfig' :entry='entry' ) - #main-entry( + main( v-if='ready' ) - main + #main-entry( + v-if='acknowledged' + ) router-view + WarningPrompt( + v-else + :storageId='storageId' + :warning='warning' + @acknowledged='onAcknowledgedWarning()' + )