✨ Post attachments, title, createdAt
This commit is contained in:
parent
02ccbdac71
commit
cc2b34b2ab
35
src/components/attachments/AttachmentItem.tsx
Normal file
35
src/components/attachments/AttachmentItem.tsx
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { SnAttachment } from '@/services/attachment'
|
||||||
|
import { getAttachmentUrl } from '@/services/network'
|
||||||
|
import { QuestionMark } from '@mui/icons-material'
|
||||||
|
import { Link, Paper, Typography } from '@mui/material'
|
||||||
|
|
||||||
|
export function AttachmentItem({ item }: { item: SnAttachment }) {
|
||||||
|
switch (item.mimetype.split('/')[0]) {
|
||||||
|
case 'image':
|
||||||
|
return (
|
||||||
|
<Paper>
|
||||||
|
<img src={getAttachmentUrl(item.rid)} alt={item.alt} style={{ objectFit: 'cover', borderRadius: '8px' }} />
|
||||||
|
</Paper>
|
||||||
|
)
|
||||||
|
case 'video':
|
||||||
|
return (
|
||||||
|
<Paper>
|
||||||
|
<video src={getAttachmentUrl(item.rid)} controls style={{ borderRadius: '8px' }} />
|
||||||
|
</Paper>
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<Paper sx={{ width: '100%', height: '100%', p: 5, textAlign: 'center' }}>
|
||||||
|
<QuestionMark sx={{ mb: 2 }} />
|
||||||
|
<Typography>Unknown</Typography>
|
||||||
|
<Typography gutterBottom>{item.name}</Typography>
|
||||||
|
<Typography fontFamily="monospace" gutterBottom>
|
||||||
|
{item.mimetype}
|
||||||
|
</Typography>
|
||||||
|
<Link href={getAttachmentUrl(item.rid)} target="_blank" rel="noreferrer" fontSize={13}>
|
||||||
|
Open in browser
|
||||||
|
</Link>
|
||||||
|
</Paper>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,19 @@
|
|||||||
import { getAttachmentUrl, sni } from '@/services/network'
|
import { getAttachmentUrl, sni } from '@/services/network'
|
||||||
import { SnPost } from '@/services/post'
|
import { SnPost } from '@/services/post'
|
||||||
import { Alert, AlertTitle, Avatar, Box, Collapse, Container, IconButton, Link, Typography } from '@mui/material'
|
import { listAttachment, SnAttachment } from '@/services/attachment'
|
||||||
|
import {
|
||||||
|
Grid2 as Grid,
|
||||||
|
Alert,
|
||||||
|
AlertTitle,
|
||||||
|
Avatar,
|
||||||
|
Box,
|
||||||
|
Collapse,
|
||||||
|
Container,
|
||||||
|
IconButton,
|
||||||
|
Link,
|
||||||
|
Typography,
|
||||||
|
Divider,
|
||||||
|
} from '@mui/material'
|
||||||
import { GetServerSideProps, InferGetServerSidePropsType } from 'next'
|
import { GetServerSideProps, InferGetServerSidePropsType } from 'next'
|
||||||
import { useEffect, useMemo, useState } from 'react'
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
import { unified } from 'unified'
|
import { unified } from 'unified'
|
||||||
@ -11,6 +24,7 @@ import remarkParse from 'remark-parse'
|
|||||||
import remarkRehype from 'remark-rehype'
|
import remarkRehype from 'remark-rehype'
|
||||||
|
|
||||||
import CloseIcon from '@mui/icons-material/Close'
|
import CloseIcon from '@mui/icons-material/Close'
|
||||||
|
import { AttachmentItem } from '@/components/attachments/AttachmentItem'
|
||||||
|
|
||||||
export const getServerSideProps = (async (context) => {
|
export const getServerSideProps = (async (context) => {
|
||||||
const id = context.params!.id as string[]
|
const id = context.params!.id as string[]
|
||||||
@ -25,15 +39,19 @@ export const getServerSideProps = (async (context) => {
|
|||||||
.process(post.body.content)
|
.process(post.body.content)
|
||||||
post.body.content = String(out)
|
post.body.content = String(out)
|
||||||
}
|
}
|
||||||
return { props: { post } }
|
let attachments: SnAttachment[] = []
|
||||||
|
if (post.body.attachments) {
|
||||||
|
attachments = await listAttachment(post.body.attachments)
|
||||||
|
}
|
||||||
|
return { props: { post, attachments } }
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return {
|
return {
|
||||||
notFound: true,
|
notFound: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}) satisfies GetServerSideProps<{ post: SnPost }>
|
}) satisfies GetServerSideProps<{ post: SnPost; attachments: SnAttachment[] }>
|
||||||
|
|
||||||
export default function Post({ post }: InferGetServerSidePropsType<typeof getServerSideProps>) {
|
export default function Post({ post, attachments }: InferGetServerSidePropsType<typeof getServerSideProps>) {
|
||||||
const link = useMemo(() => `https://sn.solsynth.dev/posts/${post.id}`, [post])
|
const link = useMemo(() => `https://sn.solsynth.dev/posts/${post.id}`, [post])
|
||||||
|
|
||||||
const [openAppHint, setOpenAppHint] = useState<boolean>()
|
const [openAppHint, setOpenAppHint] = useState<boolean>()
|
||||||
@ -84,7 +102,7 @@ export default function Post({ post }: InferGetServerSidePropsType<typeof getSer
|
|||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Container sx={{ mt: 3, pb: 5 }} maxWidth="md">
|
<Container sx={{ mt: 3, pb: 5 }} maxWidth="md" component="article">
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
<Box sx={{ display: 'flex', gap: 2 }}>
|
<Box sx={{ display: 'flex', gap: 2 }}>
|
||||||
<Avatar src={getAttachmentUrl(post.publisher.avatar)} />
|
<Avatar src={getAttachmentUrl(post.publisher.avatar)} />
|
||||||
@ -97,9 +115,33 @@ export default function Post({ post }: InferGetServerSidePropsType<typeof getSer
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box sx={{ mt: 2.5, maxWidth: 'unset' }} component="article" className="prose prose-lg">
|
<Box sx={{ my: 2.5 }} display="flex" flexDirection="column" gap={1}>
|
||||||
|
{(post.body.title || post.body.content) && (
|
||||||
|
<Box>
|
||||||
|
{post.body.title && <Typography variant="h6">{post.body.title}</Typography>}
|
||||||
|
{post.body.description && <Typography variant="subtitle1">{post.body.description}</Typography>}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
<Box display="flex" gap={2} sx={{ opacity: 0.8 }}>
|
||||||
|
<Typography variant="body2">
|
||||||
|
Published at {new Date(post.publishedAt ?? post.createdAt).toLocaleString()}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<Box sx={{ mt: 2.5, maxWidth: 'unset' }} className="prose prose-lg">
|
||||||
{post.body.content && <div dangerouslySetInnerHTML={{ __html: post.body.content }} />}
|
{post.body.content && <div dangerouslySetInnerHTML={{ __html: post.body.content }} />}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{attachments && (
|
||||||
|
<Grid container spacing={2} sx={{ mt: 3 }} columns={{ md: Math.min(2, attachments.length) }}>
|
||||||
|
{attachments.map((a) => (
|
||||||
|
<AttachmentItem item={a} />
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
@ -32,12 +32,12 @@ export interface SnAttachment {
|
|||||||
metadata: Record<string, any>
|
metadata: Record<string, any>
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getAttachment(id: string | number): Promise<SnAttachment> {
|
export async function getAttachment(id: string | number): Promise<SnAttachment> {
|
||||||
const resp = await sni.get<SnAttachment>('/cgi/uc/attachments/' + id + '/meta')
|
const resp = await sni.get<SnAttachment>('/cgi/uc/attachments/' + id + '/meta')
|
||||||
return resp.data
|
return resp.data
|
||||||
}
|
}
|
||||||
|
|
||||||
async function listAttachment(id: string[]): Promise<SnAttachment[]> {
|
export async function listAttachment(id: string[]): Promise<SnAttachment[]> {
|
||||||
const resp = await sni.get<{ data: SnAttachment[] }>('/cgi/uc/attachments', {
|
const resp = await sni.get<{ data: SnAttachment[] }>('/cgi/uc/attachments', {
|
||||||
params: {
|
params: {
|
||||||
id: id.join(','),
|
id: id.join(','),
|
||||||
|
Loading…
Reference in New Issue
Block a user