Merge branch 'v1.1' into v1.1-lazylaoding

This commit is contained in:
Hossein Mehrabi 2024-01-22 17:29:45 +03:30 committed by GitHub
commit 3e5ed14dc6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 165 additions and 57 deletions

View File

@ -12,7 +12,7 @@ type Props = {
export const ArticleImageBlockWrapper = ({ image, order }: Props) => {
return (
<Container id={`i-${order}`}>
<LightBox caption={image.caption || ''}>
<LightBox caption={image.captionHTML || ''}>
<ResponsiveImage data={image} />
</LightBox>
</Container>

View File

@ -10,10 +10,7 @@ const ArticleFooter = ({ data: post }: { data: LPE.Article.Document }) => {
const { data, relatedArticles, articlesFromSameAuthors } = post
const footnotes = useMemo(() => {
return data.content.flatMap((b) =>
// @ts-ignore
b.type === 'text' ? b.footnotes : [],
)
return data.content.flatMap((b) => b.footnotes || [])
}, [data])
return (

View File

@ -16,8 +16,7 @@ const ArticleFootnotes = ({
component="a"
variant="body3"
href={`#${footnote.refId}`}
target="_blank"
id={footnote.id.replace('#', '')}
id={'fnt-' + footnote.id}
>
{footnote.refValue}
</Typography>

View File

@ -24,11 +24,15 @@ const ImageBlock = (props: Props) => {
type={BlockType.IMAGE}
date={document?.modifiedAt ? new Date(document?.modifiedAt) : null}
/>
{data.alt.length > 0 && (
{(data.caption || '').length > 0 && (
<Caption>
<Typography variant={'body2'} component={'p'}>
{data.alt}
</Typography>
<Typography
variant={'body2'}
component={'p'}
dangerouslySetInnerHTML={{
__html: data.captionHTML || '',
}}
/>
</Caption>
)}
<ContentBlockFooter type="image" data={document} order={order} />

View File

@ -240,9 +240,8 @@ export const LightBox = ({ children, caption }: LightBoxProps) => {
}
isActive={isActive}
ref={captionRef}
>
{caption}
</LightBoxCaption>
dangerouslySetInnerHTML={{ __html: caption }}
></LightBoxCaption>
</>
)
}

View File

@ -85,6 +85,16 @@ export default function App({ Component, pageProps }: AppLayoutProps) {
z-index: -1;
}
.footnote {
position: relative;
display: inline-block;
.anchor {
position: absolute;
top: 0;
}
}
[data-theme='light'] {
.light-mode-hidden {
display: none !important;

View File

@ -104,7 +104,7 @@ export const getStaticProps = async ({ params }: GetStaticPropsContext) => {
})
.then((data) => ({
...data,
data: data.data.map((post) => ({
data: (data.data || []).map((post) => ({
...post,
show: shows.find(
(show) => show.id === (post as LPE.Podcast.Document).showId,

View File

@ -1,5 +1,7 @@
import { CustomNextPage, GetServerSideProps } from 'next'
import SEO from '../components/SEO/SEO'
import { strapiApi } from '../services/strapi'
import { LPE } from '../types/lpe.types'
import { getPostLink } from '../utils/route.utils'
type PageProps = {}
@ -13,37 +15,80 @@ const Page: CustomNextPage<PageProps> = (props) => {
}
export const getServerSideProps: GetServerSideProps = async (ctx) => {
const type = Array.isArray(ctx.query.type)
? ctx.query.type[0]
: ctx.query.type
if (!type || !['page', 'post'].includes(type))
return {
notFound: true,
}
const id = Array.isArray(ctx.query.id) ? ctx.query.id[0] : ctx.query.id
if (!id)
return {
notFound: true,
}
// const { data, errors } = await unbodyApi.getDocById({
// id,
// includeDrafts: true,
// })
if (type === 'post') {
const { data: shows } = await strapiApi.getPodcastShows({
populateEpisodes: false,
})
const data = undefined as any
const errors = '' as string
const {
data: { data = [] },
} = await strapiApi.getPosts({
id: id,
published: false,
parseContent: false,
})
if (!data || errors) {
return {
notFound: typeof errors === 'string' && errors.includes('Not found'),
props: {},
const post = data?.[0]
if (post) {
return {
props: {},
redirect: {
destination: getPostLink(post.type, {
id,
postSlug: post.slug,
showSlug:
(post.type === 'podcast' &&
shows.find(
(show) => show.id === (post as LPE.Podcast.Document).showId,
)?.slug) ||
'',
}),
permanent: false,
},
notFound: false,
}
}
} else if (type === 'page') {
const { data: pages } = await strapiApi.getStaticPages({
id: id,
published: false,
parseContent: false,
})
const page = pages?.[0]
if (page) {
return {
props: {},
redirect: {
destination: getPostLink(page.type, {
id,
postSlug: page.slug,
}),
permanent: false,
},
notFound: false,
}
}
}
return {
props: {},
redirect: {
destination: getPostLink(data.type, {
id,
postSlug: data.slug,
showSlug: (data.type === 'podcast' && data.show?.slug) || null,
}),
permanent: false,
},
notFound: true,
}
}

View File

@ -75,6 +75,11 @@ export const transformStrapiHtmlContent = async ({
let root = htmlParser.parse(html, { parseNoneClosedTags: true })
// remove footnotes container as the content is already stored in sup tags
root.querySelectorAll('section.footnotes-container').forEach((node) => {
node.remove()
})
let blockIndex = -1
for (const child of root.childNodes) {
@ -82,24 +87,34 @@ export const transformStrapiHtmlContent = async ({
continue
}
const tagName = child.tagName.toLowerCase()
const { node, footnotes } = parseFootnotes(child)
// remove footnotes before parsing the text content
const clone = node.clone() as htmlParser.HTMLElement
clone.querySelectorAll('a.footnote').forEach((a) => {
a.remove()
})
const text = clone.textContent || ''
const tagName = node.tagName.toLowerCase()
const isFigure = tagName === 'figure'
const isMedia = isFigure && !!child.querySelector('oembed')
const isImage = isFigure && !isMedia && !!child.querySelector('img')
const empty = child.text.length === 0
const isMedia = isFigure && !!node.querySelector('oembed')
const isImage = isFigure && !isMedia && !!node.querySelector('img')
const empty = node.text.length === 0
if (!isFigure && empty) continue
blockIndex++
if (isImage) {
const image = child.querySelector('img')
const image = node.querySelector('img')
if (!image) {
blockIndex--
continue
}
const caption = child.textContent || ''
const caption = text
const alt = image.getAttribute('alt') || ''
const url = image.getAttribute('src') || ''
const width = parseInt(image.getAttribute('width') || '0', 10)
@ -108,6 +123,7 @@ export const transformStrapiHtmlContent = async ({
blocks.push({
id: '',
caption,
captionHTML: node.querySelector('figcaption')?.innerHTML || '',
type: 'image',
width,
height,
@ -115,6 +131,7 @@ export const transformStrapiHtmlContent = async ({
labels: [],
order: blockIndex,
url: url.startsWith('/') ? transformStrapiImageUrl(url) : url,
footnotes,
})
continue
@ -123,7 +140,7 @@ export const transformStrapiHtmlContent = async ({
if (isMedia) {
const labels: LPE.Post.ContentBlockLabel[] = ['embed']
const oembed = child.querySelector('oembed')
const oembed = node.querySelector('oembed')
const url = oembed?.getAttribute('url') || ''
if (!url) {
blockIndex--
@ -155,7 +172,7 @@ export const transformStrapiHtmlContent = async ({
type: 'text',
classNames: [],
footnotes: [],
html: child.outerHTML,
html: node.outerHTML,
labels,
tagName: 'p',
text: url,
@ -166,13 +183,12 @@ export const transformStrapiHtmlContent = async ({
})
}
const text = child.text || ''
const isHeading = tagName.startsWith('h')
const id =
child.id ||
node.id ||
(isHeading && slugify(text, { lower: true, trim: true })) ||
`p-${blockIndex}`
child.setAttribute('id', id)
node.setAttribute('id', id)
if (isHeading) {
toc.push({
@ -186,13 +202,13 @@ export const transformStrapiHtmlContent = async ({
blocks.push({
id: '',
footnotes: [],
html: child.outerHTML,
text,
footnotes,
html: node.outerHTML,
labels: [],
tagName,
text,
order: blockIndex,
classNames: Array.from(child.classList.values()),
classNames: Array.from(node.classList.values()),
type: 'text',
} as LPE.Post.TextBlock)
}
@ -204,3 +220,47 @@ export const transformStrapiHtmlContent = async ({
html: root.innerHTML,
}
}
const parseFootnotes = (
node: htmlParser.HTMLElement,
): {
node: htmlParser.HTMLElement
footnotes: LPE.Post.Footnotes
} => {
const footnotes = [] as LPE.Post.Footnotes
const clone = node.clone() as htmlParser.HTMLElement
clone.querySelectorAll('sup.footnote').forEach((sup, idx) => {
const id = sup.getAttribute('data-id') || ''
const index = parseInt(sup.getAttribute('data-index') || '', 10)
const footnote = htmlParser.parse(sup.getAttribute('data-content') || '')
const refId = `fntref-${id}`
const footnoteId = `fnt-${id}`
const anchor = htmlParser.parse(
`<a class="footnote" href="#${footnoteId}"><sup><span class="anchor" id="${refId}"></span><span>[${index}]</span></sup></a>`,
)
sup.replaceWith(anchor)
footnote.querySelectorAll('a').forEach((a) => {
a.setAttribute('target', '_blank')
})
footnotes.push({
id,
index,
refId,
refValue: anchor.textContent,
valueHTML: footnote.outerHTML,
valueText: footnote.textContent,
})
})
return {
footnotes,
node: clone,
}
}

View File

@ -23,6 +23,7 @@ export namespace LPE {
width: number
height: number
caption?: string
captionHTML?: string
placeholder?: string
}
}
@ -35,13 +36,6 @@ export namespace LPE {
}
}
export namespace Tag {
export type Document = {
value: string
count: number
}
}
export namespace Post {
export type Footnote = {
index: number
@ -92,6 +86,7 @@ export namespace LPE {
order: number
labels: ContentBlockLabel[]
document?: D
footnotes?: Post.Footnotes
}
export type TextBlockEmbed = {
@ -105,7 +100,6 @@ export namespace LPE {
tagName: string
classNames: string[]
type: Extract<ContentBlockType, 'text'>
footnotes: Post.Footnotes
embed?: TextBlockEmbed
}