feat: use swiper for post cover
This commit is contained in:
parent
f7fdc1e3fc
commit
88c8c8d586
|
|
@ -144,6 +144,7 @@
|
|||
"serialize-javascript": "6.0.1",
|
||||
"sharp": "0.32.3",
|
||||
"sortablejs": "1.15.0",
|
||||
"swiper": "^10.0.4",
|
||||
"tailwind-merge": "1.14.0",
|
||||
"tailwind-scrollbar-hide": "1.1.7",
|
||||
"tailwindcss-animate": "1.0.6",
|
||||
|
|
|
|||
|
|
@ -347,6 +347,9 @@ dependencies:
|
|||
sortablejs:
|
||||
specifier: 1.15.0
|
||||
version: 1.15.0
|
||||
swiper:
|
||||
specifier: ^10.0.4
|
||||
version: 10.0.4
|
||||
tailwind-merge:
|
||||
specifier: 1.14.0
|
||||
version: 1.14.0
|
||||
|
|
@ -12427,6 +12430,11 @@ packages:
|
|||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
/swiper@10.0.4:
|
||||
resolution: {integrity: sha512-DlNR3KiaVkPep6RraZ2tNr7lvyuzELuR+4NZr4wO42t0UworIO3DxDlz8b5Q3uUioh7cJad99EdRJLkPF+r8Ag==}
|
||||
engines: {node: '>= 4.7.0'}
|
||||
dev: false
|
||||
|
||||
/swr@2.2.0(react@18.2.0):
|
||||
resolution: {integrity: sha512-AjqHOv2lAhkuUdIiBu9xbuettzAzWXmCEcLONNKJRba87WAefz8Ca9d6ds/SzrPc235n1IxWYdhJ2zF3MNUaoQ==}
|
||||
peerDependencies:
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export default async function HomeLayout({
|
|||
}) {
|
||||
return (
|
||||
<>
|
||||
<header className="py-5 fixed w-full top-0 bg-white z-[1]">
|
||||
<header className="py-5 fixed w-full top-0 bg-white z-[2]">
|
||||
<div className="max-w-screen-xl px-5 mx-auto flex justify-between items-center">
|
||||
<div className="space-x-14 flex">
|
||||
<UniLink
|
||||
|
|
|
|||
|
|
@ -48,7 +48,10 @@ const Card = ({
|
|||
)}
|
||||
>
|
||||
<PostCover
|
||||
cover={post.metadata?.content.cover}
|
||||
uniqueKey={`${post.characterId}-${post.noteId}`}
|
||||
images={
|
||||
post.metadata?.content.images || [post.metadata?.content.cover as any]
|
||||
}
|
||||
title={post.metadata?.content?.title}
|
||||
/>
|
||||
<div className="px-3 py-2 sm:px-5 sm:py-4 w-full min-w-0 h-[148px] sm:h-[166px] flex flex-col space-y-2 text-sm">
|
||||
|
|
|
|||
|
|
@ -1,17 +1,23 @@
|
|||
import "swiper/css"
|
||||
import "swiper/css/effect-fade"
|
||||
import "swiper/css/navigation"
|
||||
import "swiper/css/pagination"
|
||||
import { Autoplay, EffectFade, Navigation, Pagination } from "swiper/modules"
|
||||
import { Swiper, SwiperSlide } from "swiper/react"
|
||||
import uniqolor from "uniqolor"
|
||||
|
||||
import { Image } from "~/components/ui/Image"
|
||||
|
||||
export default function PostCover({
|
||||
cover,
|
||||
priority,
|
||||
images,
|
||||
title,
|
||||
uniqueKey,
|
||||
}: {
|
||||
cover?: string
|
||||
priority?: boolean
|
||||
images?: string[]
|
||||
title?: string
|
||||
uniqueKey?: string
|
||||
}) {
|
||||
if (!cover) {
|
||||
if (!images) {
|
||||
if (title) {
|
||||
const bgAccent = uniqolor(title, {
|
||||
saturation: [30, 35],
|
||||
|
|
@ -48,14 +54,62 @@ export default function PostCover({
|
|||
return (
|
||||
<>
|
||||
<div className="xlog-post-cover rounded-t-2xl overflow-hidden flex items-center relative w-full aspect-video border-b">
|
||||
<Image
|
||||
className="object-cover w-full sm:group-hover:scale-105 sm:transition-transform sm:duration-400 sm:ease-in-out"
|
||||
alt="cover"
|
||||
src={cover}
|
||||
width={624}
|
||||
height={351}
|
||||
priority={priority}
|
||||
></Image>
|
||||
{images.length > 1 ? (
|
||||
<>
|
||||
<Swiper
|
||||
pagination={{
|
||||
type: "progressbar",
|
||||
}}
|
||||
loop={true}
|
||||
navigation={{
|
||||
prevEl: `#swiper-button-prev-${uniqueKey}`,
|
||||
nextEl: `#swiper-button-next-${uniqueKey}`,
|
||||
}}
|
||||
autoplay={{
|
||||
delay: 5000,
|
||||
disableOnInteraction: false,
|
||||
pauseOnMouseEnter: true,
|
||||
waitForTransition: false,
|
||||
}}
|
||||
effect={"fade"}
|
||||
speed={1000}
|
||||
modules={[EffectFade, Autoplay, Pagination, Navigation]}
|
||||
className="w-full h-full"
|
||||
>
|
||||
{images.map((image) => (
|
||||
<SwiperSlide key={image}>
|
||||
<Image
|
||||
className="object-cover w-full sm:group-hover:scale-105 sm:transition-transform sm:duration-400 sm:ease-in-out"
|
||||
alt="cover"
|
||||
src={image}
|
||||
width={624}
|
||||
height={351}
|
||||
></Image>
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
<div
|
||||
id={`swiper-button-prev-${uniqueKey}`}
|
||||
className="swiper-button left-2"
|
||||
>
|
||||
<i className="icon-[mingcute--left-fill]" />
|
||||
</div>
|
||||
<div
|
||||
id={`swiper-button-next-${uniqueKey}`}
|
||||
className="swiper-button right-2"
|
||||
>
|
||||
<i className="icon-[mingcute--right-fill]" />
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<Image
|
||||
className="object-cover w-full sm:group-hover:scale-105 sm:transition-transform sm:duration-400 sm:ease-in-out"
|
||||
alt="cover"
|
||||
src={images[0]}
|
||||
width={624}
|
||||
height={351}
|
||||
></Image>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
import { Image } from "~/components/ui/Image"
|
||||
|
||||
export default function PostCover({
|
||||
cover,
|
||||
priority,
|
||||
}: {
|
||||
cover?: string
|
||||
priority?: boolean
|
||||
}) {
|
||||
if (!cover) return null
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="xlog-post-cover rounded-2xl overflow-hidden flex items-center relative w-full sm:w-36 sm:h-36 aspect-video mb-4 sm:mb-0 sm:mr-8">
|
||||
<Image
|
||||
className="object-cover w-full sm:w-36 sm:group-hover:scale-105 sm:transition-transform sm:duration-400 esm:ase-in-out"
|
||||
alt="cover"
|
||||
src={cover}
|
||||
width={256}
|
||||
height={256}
|
||||
priority={priority}
|
||||
></Image>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
@ -529,3 +529,30 @@ textarea.input {
|
|||
.right {
|
||||
@apply flex justify-end;
|
||||
}
|
||||
|
||||
.swiper-button {
|
||||
@apply absolute top-1/2 -translate-y-1/2 bg-white rounded-full z-[1] w-6 h-6 flex items-center justify-center opacity-80;
|
||||
}
|
||||
|
||||
.swiper .swiper-pagination-bullet {
|
||||
@apply bg-zinc-100 opacity-50 w-[7px] h-[7px];
|
||||
}
|
||||
|
||||
.swiper .swiper-pagination-bullet-active {
|
||||
@apply opacity-100;
|
||||
}
|
||||
|
||||
.swiper .swiper-pagination-bullets {
|
||||
@apply leading-3;
|
||||
}
|
||||
|
||||
.swiper .swiper-pagination-progressbar .swiper-pagination-progressbar-fill {
|
||||
@apply bg-accent;
|
||||
transition-duration: 5000ms !important;
|
||||
transition-timing-function: linear;
|
||||
transform: translate3d(0px, 0px, 0px) scaleX(0) scaleY(1);
|
||||
}
|
||||
|
||||
.swiper .swiper-pagination-progressbar.swiper-pagination-horizontal {
|
||||
@apply h-[2px] bg-transparent;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,25 @@ export const expandCrossbellNote = async ({
|
|||
)?.address ||
|
||||
rendered.cover ||
|
||||
`${SITE_URL}/api/og?noteId=${expandedNote.noteId}&characterId=${expandedNote.characterId}`
|
||||
|
||||
expandedNote.metadata.content.images = []
|
||||
const cover = expandedNote.metadata?.content?.attachments?.find(
|
||||
(attachment) => attachment.name === "cover",
|
||||
)?.address
|
||||
if (cover) {
|
||||
expandedNote.metadata.content.images.push(cover)
|
||||
}
|
||||
expandedNote.metadata.content.images =
|
||||
expandedNote.metadata.content.images.concat(rendered.images)
|
||||
if (!expandedNote.metadata.content.images.length) {
|
||||
expandedNote.metadata.content.images.push(
|
||||
`${SITE_URL}/api/og?noteId=${expandedNote.noteId}&characterId=${expandedNote.characterId}`,
|
||||
)
|
||||
}
|
||||
expandedNote.metadata.content.images = [
|
||||
...new Set(expandedNote.metadata.content.images),
|
||||
]
|
||||
|
||||
expandedNote.metadata.content.audio = rendered.audio
|
||||
expandedNote.metadata.content.frontMatter = rendered.frontMatter
|
||||
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ export type ExpandedNote = NoteEntity & {
|
|||
content: {
|
||||
summary?: string
|
||||
cover?: string
|
||||
images?: string[]
|
||||
frontMatter?: Record<string, any>
|
||||
slug?: string
|
||||
audio?: string
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ export type MarkdownEnv = {
|
|||
frontMatter: Record<string, any>
|
||||
__internal: Record<string, any>
|
||||
cover: string
|
||||
images: string[]
|
||||
audio: string
|
||||
toc: TocResult | null
|
||||
tree: Root | null
|
||||
|
|
@ -91,6 +92,7 @@ export const renderPageContent = (
|
|||
__internal: {},
|
||||
frontMatter: {},
|
||||
cover: "",
|
||||
images: [],
|
||||
audio: "",
|
||||
toc: null,
|
||||
tree: null,
|
||||
|
|
@ -251,6 +253,7 @@ export const renderPageContent = (
|
|||
excerpt: result?.data.meta.description,
|
||||
frontMatter: env.frontMatter,
|
||||
cover: env.cover,
|
||||
images: env.images,
|
||||
audio: env.audio,
|
||||
toc: env.toc,
|
||||
tree: env.tree,
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ export const rehypeImage: Plugin<Array<{ env: MarkdownEnv }>, Root> = ({
|
|||
env.cover = url
|
||||
first = false
|
||||
}
|
||||
env.images.push(url)
|
||||
|
||||
if (isExternLink(url)) {
|
||||
if (!url.startsWith("https:") && IS_PROD) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue