This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

semble / src / webapp / features / platforms / bluesky / container / blueskySembleHeader / BlueskySembleHeader.tsx
3.7 kB 116 lines
1import GuestSembleActions from '@/features/semble/components/sembleActions/GusetSembleActions'; 2import SembleActions from '@/features/semble/components/sembleActions/SembleActions'; 3import UrlAddedBySummary from '@/features/semble/components/urlAddedBySummary/UrlAddedBySummary'; 4import { getDomain } from '@/lib/utils/link'; 5import { 6 Stack, 7 Tooltip, 8 Anchor, 9 Text, 10 Avatar, 11 Group, 12 Box, 13} from '@mantine/core'; 14import Link from 'next/link'; 15import { getBlueskyPost } from '../../lib/dal'; 16import { getPostUriFromUrl } from '@/lib/utils/atproto'; 17import { verifySessionOnServer } from '@/lib/auth/dal.server'; 18import { AppBskyFeedDefs, AppBskyFeedPost } from '@atproto/api'; 19import RichTextRenderer from '@/components/contentDisplay/richTextRenderer/RichTextRenderer'; 20import PostEmbed from '../../components/postEmbed/PostEmbed'; 21import { FaBluesky } from 'react-icons/fa6'; 22import SembleHeader from '@/features/semble/components/SembleHeader/SembleHeader'; 23import { getFormattedDate } from '@/lib/utils/time'; 24 25interface Props { 26 url: string; 27} 28 29export default async function BlueskySembleHeader(props: Props) { 30 const postUri = getPostUriFromUrl(props.url); 31 const data = await getBlueskyPost(postUri); 32 const session = await verifySessionOnServer(); 33 34 if ( 35 !data.thread || 36 !AppBskyFeedDefs.isThreadViewPost(data.thread) || 37 AppBskyFeedDefs.isNotFoundPost(data.thread.post) || 38 AppBskyFeedDefs.isBlockedPost(data.thread.post) 39 ) { 40 // fallback 41 return <SembleHeader url={props.url} />; 42 } 43 44 const post = data.thread.post; 45 const record = post.record as AppBskyFeedPost.Record; 46 47 return ( 48 <Stack gap={'sm'} mx={'auto'} w={'100%'}> 49 <Stack gap={'xs'}> 50 <Text> 51 <Text fw={700} c="tangerine" span> 52 Semble 53 </Text> 54 <Text fw={700} c={'gray'} span> 55 {` · `} 56 </Text> 57 <Tooltip label={props.url}> 58 <Anchor 59 component={Link} 60 target="_blank" 61 fw={700} 62 c={'blue'} 63 href={props.url} 64 > 65 {getDomain(props.url)} 66 </Anchor> 67 </Tooltip> 68 </Text> 69 70 {/* Post */} 71 <Stack gap={'xs'}> 72 <Group gap="xs" justify="space-between" wrap="nowrap"> 73 <Group gap={'xs'} wrap="nowrap"> 74 <Avatar 75 src={post.author.avatar} 76 alt={`${post.author.handle} social preview image`} 77 radius="xl" 78 /> 79 <Stack gap={0} flex={1}> 80 <Text c="bright" lineClamp={1} fw={500} w="fit-content"> 81 {post.author.displayName || post.author.handle} 82 </Text> 83 <Text c="gray" lineClamp={1} w="fit-content"> 84 @{post.author.handle} 85 </Text> 86 </Stack> 87 </Group> 88 <FaBluesky fill="#0085ff" size={18} /> 89 </Group> 90 <Stack gap={'xs'} w={'100%'}> 91 <Box> 92 <RichTextRenderer 93 text={record.text} 94 textProps={{ lineClamp: 3, c: 'bright' }} 95 /> 96 </Box> 97 {post.embed && <PostEmbed embed={post.embed} />} 98 </Stack> 99 <Text c={'gray'} fz={'sm'} fw={500}> 100 {getFormattedDate(post.indexedAt)} 101 </Text> 102 </Stack> 103 </Stack> 104 105 <Stack gap={'sm'} align="center"> 106 {session ? ( 107 <SembleActions url={props.url} /> 108 ) : ( 109 <GuestSembleActions url={props.url} /> 110 )} 111 </Stack> 112 113 <UrlAddedBySummary url={props.url} /> 114 </Stack> 115 ); 116}