✨ Post attachments, title, createdAt
This commit is contained in:
		
							
								
								
									
										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(','), | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user