chore: add media migration scripts, migrate media.
This commit is contained in:
@@ -2,9 +2,11 @@
|
||||
|
||||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
import CmsMediaRewriter from '@/spa/components/CmsMediaRewriter'
|
||||
import Layout from '@/spa/components/layout/Layout'
|
||||
import ScrollToTop from '@/spa/components/layout/ScrollToTop'
|
||||
import type { SpaFooterData, SpaHeaderData } from '@/spa/cmsNavigation'
|
||||
import type { SpaMediaMap } from '@/spa/cmsMedia'
|
||||
import About from '@/spa/pages/About'
|
||||
import BlogDetail from '@/spa/pages/BlogDetail'
|
||||
import Contact from '@/spa/pages/Contact'
|
||||
@@ -46,10 +48,12 @@ export default function NextApp({
|
||||
pathname,
|
||||
header,
|
||||
footer,
|
||||
media,
|
||||
}: {
|
||||
pathname: string
|
||||
header?: SpaHeaderData
|
||||
footer?: SpaFooterData
|
||||
media?: SpaMediaMap
|
||||
}) {
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
@@ -78,6 +82,7 @@ export default function NextApp({
|
||||
|
||||
return (
|
||||
<>
|
||||
<CmsMediaRewriter media={media} />
|
||||
<ScrollToTop />
|
||||
<Layout header={header} footer={footer}>
|
||||
<Page />
|
||||
|
||||
48
src/spa/cmsMedia.ts
Normal file
48
src/spa/cmsMedia.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import type { Media } from '@/payload-types'
|
||||
|
||||
export type SpaMediaEntry = {
|
||||
source: string
|
||||
media?: { url?: string } | string | number | Media
|
||||
}
|
||||
|
||||
export type SpaMediaMap = Record<string, string>
|
||||
|
||||
export const defaultSpaMediaSources = [
|
||||
'/bmp-logo.png',
|
||||
'/logo.png',
|
||||
'/gala-preistraeger.jpg',
|
||||
'/munich-skyline.jpg',
|
||||
'/bavarian-flag.jpg',
|
||||
'/rauten.webp',
|
||||
'/website-template-OG.webp',
|
||||
'/images/bmp-banner.jpg',
|
||||
'/images/buehne-gewinner.jpg',
|
||||
'/images/buehne-moderatoren.jpg',
|
||||
'/images/ewif-logo.png',
|
||||
'/images/gala-dinner.jpg',
|
||||
'/images/gala-saal-overview.jpg',
|
||||
'/images/gewinner-gruppenfoto.jpg',
|
||||
'/images/mitglied-hero.jpg',
|
||||
'/images/netzwerk-hero.jpg',
|
||||
'/images/networking-innenhof.jpg',
|
||||
'/images/partner-logos.png',
|
||||
'/images/preisuebergabe.jpg',
|
||||
'/images/preistraeger-award.jpg',
|
||||
'/images/preistraeger-gruppe.jpg',
|
||||
'/images/preistraeger-jubel.jpg',
|
||||
'/images/saal-gedeckt.jpg',
|
||||
'/images/saal-leer-atmosphaere.jpg',
|
||||
'/images/tisch-detail.jpg',
|
||||
]
|
||||
|
||||
const isMedia = (value: unknown): value is Media => Boolean(value && typeof value === 'object' && 'url' in value)
|
||||
|
||||
export function normalizeSpaMediaMap(global: any): SpaMediaMap {
|
||||
const entries: SpaMediaEntry[] = Array.isArray(global?.assets) ? global.assets : []
|
||||
|
||||
return entries.reduce((acc: SpaMediaMap, entry: SpaMediaEntry) => {
|
||||
if (!entry?.source) return acc
|
||||
if (isMedia(entry.media) && entry.media.url) acc[entry.source] = entry.media.url
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
79
src/spa/components/CmsMediaRewriter.tsx
Normal file
79
src/spa/components/CmsMediaRewriter.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect } from 'react'
|
||||
|
||||
import type { SpaMediaMap } from '@/spa/cmsMedia'
|
||||
|
||||
const CSS_URL_RE = /url\((['"]?)(.*?)\1\)/g
|
||||
|
||||
function canonical(value: string) {
|
||||
if (!value) return value
|
||||
try {
|
||||
const url = new URL(value, window.location.origin)
|
||||
return url.origin === window.location.origin ? `${url.pathname}${url.search}` : value
|
||||
} catch {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
function rewriteCssUrls(value: string, media: SpaMediaMap) {
|
||||
return value.replace(CSS_URL_RE, (_match, quote: string, url: string) => {
|
||||
const replacement = media[url] || media[canonical(url)]
|
||||
return replacement ? `url(${quote}${replacement}${quote})` : _match
|
||||
})
|
||||
}
|
||||
|
||||
function rewrite(root: ParentNode, media: SpaMediaMap) {
|
||||
root.querySelectorAll('img, source, video').forEach((node) => {
|
||||
const el = node as HTMLImageElement | HTMLSourceElement | HTMLVideoElement
|
||||
const current = el.getAttribute('src')
|
||||
if (current) {
|
||||
const replacement = media[current] || media[canonical(current)]
|
||||
if (replacement && current !== replacement) el.setAttribute('src', replacement)
|
||||
}
|
||||
|
||||
const poster = el.getAttribute('poster')
|
||||
if (poster) {
|
||||
const replacement = media[poster] || media[canonical(poster)]
|
||||
if (replacement && poster !== replacement) el.setAttribute('poster', replacement)
|
||||
}
|
||||
})
|
||||
|
||||
root.querySelectorAll<HTMLElement>('[style*="url("]').forEach((el) => {
|
||||
const backgroundImage = el.style.backgroundImage
|
||||
if (backgroundImage) {
|
||||
const rewritten = rewriteCssUrls(backgroundImage, media)
|
||||
if (rewritten !== backgroundImage) el.style.backgroundImage = rewritten
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export default function CmsMediaRewriter({ media }: { media?: SpaMediaMap }) {
|
||||
useEffect(() => {
|
||||
if (!media || Object.keys(media).length === 0) return
|
||||
|
||||
rewrite(document, media)
|
||||
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
for (const mutation of mutations) {
|
||||
mutation.addedNodes.forEach((node) => {
|
||||
if (node.nodeType === Node.ELEMENT_NODE) rewrite(node as Element, media)
|
||||
})
|
||||
if (mutation.type === 'attributes' && mutation.target.nodeType === Node.ELEMENT_NODE) {
|
||||
rewrite((mutation.target as Element).parentNode || document, media)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
observer.observe(document.body, {
|
||||
attributes: true,
|
||||
attributeFilter: ['src', 'poster', 'style'],
|
||||
childList: true,
|
||||
subtree: true,
|
||||
})
|
||||
|
||||
return () => observer.disconnect()
|
||||
}, [media])
|
||||
|
||||
return null
|
||||
}
|
||||
Reference in New Issue
Block a user