This repository has no description
0

Configure Feed

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

semble / src / webapp / features / feeds / components / feedActivityStatus / FeedActivityStatus.tsx
4.0 kB 129 lines
1'use client'; 2 3import { 4 Anchor, 5 Avatar, 6 Card, 7 Group, 8 Menu, 9 ScrollArea, 10 Stack, 11 Text, 12} from '@mantine/core'; 13import { FeedItem, Collection } from '@/api-client'; 14import { Fragment } from 'react'; 15import Link from 'next/link'; 16import { getRelativeTime } from '@/lib/utils/time'; 17import { getRecordKey } from '@/lib/utils/atproto'; 18import { sanitizeText } from '@/lib/utils/text'; 19import { useColorScheme } from '@mantine/hooks'; 20import { BiCollection } from 'react-icons/bi'; 21 22interface Props { 23 user: FeedItem['user']; 24 collections?: FeedItem['collections']; 25 createdAt: Date; 26} 27 28export default function FeedActivityStatus(props: Props) { 29 const colorScheme = useColorScheme(); 30 const MAX_DISPLAYED = 2; 31 const time = getRelativeTime(props.createdAt.toString()); 32 const relativeCreatedDate = time === 'just now' ? `Now` : `${time} ago`; 33 34 const renderActivityText = () => { 35 const collections = props.collections ?? []; 36 const displayedCollections = collections.slice(0, MAX_DISPLAYED); 37 const remainingCollections = collections.slice( 38 MAX_DISPLAYED, 39 collections.length, 40 ); 41 const remainingCount = collections.length - MAX_DISPLAYED; 42 43 return ( 44 <Text fw={500} c={'gray'}> 45 <Anchor 46 component={Link} 47 href={`/profile/${props.user.handle}`} 48 c="dark" 49 fw={600} 50 > 51 {sanitizeText(props.user.name)} 52 </Anchor>{' '} 53 {collections.length === 0 ? ( 54 'added to library' 55 ) : ( 56 <Fragment> 57 added to{' '} 58 {displayedCollections.map( 59 (collection: Collection, index: number) => ( 60 <span key={collection.id}> 61 <Anchor 62 component={Link} 63 href={`/profile/${collection.author.handle}/collections/${getRecordKey(collection.uri!)}`} 64 c="grape" 65 fw={500} 66 > 67 {collection.name} 68 </Anchor> 69 {index < displayedCollections.length - 1 ? ', ' : ''} 70 </span> 71 ), 72 )} 73 {remainingCount > 0 && ' and '} 74 {remainingCount > 0 && ( 75 <Menu shadow="sm"> 76 <Menu.Target> 77 <Text 78 fw={600} 79 c={'blue'} 80 style={{ cursor: 'pointer', userSelect: 'none' }} 81 span 82 > 83 {remainingCount} other collection 84 {remainingCount > 1 ? 's' : ''} 85 </Text> 86 </Menu.Target> 87 <Menu.Dropdown maw={380}> 88 <ScrollArea.Autosize mah={150} type="auto"> 89 {remainingCollections.map((c) => ( 90 <Menu.Item 91 key={c.id} 92 component={Link} 93 href={`/profile/${c.author.handle}/collections/${getRecordKey(c.uri!)}`} 94 target="_blank" 95 c="blue" 96 fw={600} 97 > 98 {c.name} 99 </Menu.Item> 100 ))} 101 </ScrollArea.Autosize> 102 </Menu.Dropdown> 103 </Menu> 104 )} 105 </Fragment> 106 )} 107 <Text fz={'sm'} fw={600} c={'gray'} span display={'block'}> 108 {relativeCreatedDate} 109 </Text> 110 </Text> 111 ); 112 }; 113 114 return ( 115 <Card p={0} bg={colorScheme === 'dark' ? 'dark.4' : 'gray.1'} radius={'lg'}> 116 <Stack gap={'xs'}> 117 <Group gap={'xs'} wrap="nowrap" align="center" p={'xs'}> 118 <Avatar 119 component={Link} 120 href={`/profile/${props.user.handle}`} 121 src={props.user.avatarUrl} 122 alt={`${props.user.name}'s' avatar`} 123 /> 124 {renderActivityText()} 125 </Group> 126 </Stack> 127 </Card> 128 ); 129}