This repository has no description
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}