Bluesky app fork with some witchin' additions 馃挮
fork

Configure Feed

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

at main 167 lines 4.2 kB view raw
1import React from 'react' 2import {View} from 'react-native' 3import { 4 type AppBskyGraphDefs, 5 AtUri, 6 moderateUserList, 7 type ModerationUI, 8} from '@atproto/api' 9import {msg, Trans} from '@lingui/macro' 10import {useLingui} from '@lingui/react' 11import {useQueryClient} from '@tanstack/react-query' 12 13import {sanitizeHandle} from '#/lib/strings/handles' 14import {useModerationOpts} from '#/state/preferences/moderation-opts' 15import {precacheList} from '#/state/queries/feed' 16import {useSession} from '#/state/session' 17import {atoms as a, useTheme} from '#/alf' 18import { 19 Avatar, 20 Description, 21 Header, 22 Outer, 23 SaveButton, 24} from '#/components/FeedCard' 25import {Link as InternalLink, type LinkProps} from '#/components/Link' 26import * as Hider from '#/components/moderation/Hider' 27import {Text} from '#/components/Typography' 28import type * as bsky from '#/types/bsky' 29 30/* 31 * This component is based on `FeedCard` and is tightly coupled with that 32 * component. Please refer to `FeedCard` for more context. 33 */ 34 35export { 36 Avatar, 37 AvatarPlaceholder, 38 Description, 39 Header, 40 Outer, 41 SaveButton, 42 TitleAndBylinePlaceholder, 43} from '#/components/FeedCard' 44 45const CURATELIST = 'app.bsky.graph.defs#curatelist' 46const MODLIST = 'app.bsky.graph.defs#modlist' 47 48type Props = { 49 view: AppBskyGraphDefs.ListView 50 showPinButton?: boolean 51} 52 53export function Default( 54 props: Props & Omit<LinkProps, 'to' | 'label' | 'children'>, 55) { 56 const {view, showPinButton} = props 57 const moderationOpts = useModerationOpts() 58 const moderation = moderationOpts 59 ? moderateUserList(view, moderationOpts) 60 : undefined 61 62 return ( 63 <Link {...props}> 64 <Outer> 65 <Header> 66 <Avatar src={view.avatar} /> 67 <TitleAndByline 68 title={view.name} 69 creator={view.creator} 70 purpose={view.purpose} 71 modUi={moderation?.ui('contentView')} 72 /> 73 {showPinButton && view.purpose === CURATELIST && ( 74 <SaveButton view={view} pin /> 75 )} 76 </Header> 77 <Description description={view.description} /> 78 </Outer> 79 </Link> 80 ) 81} 82 83export function Link({ 84 view, 85 children, 86 ...props 87}: Props & Omit<LinkProps, 'to' | 'label'>) { 88 const queryClient = useQueryClient() 89 90 const href = React.useMemo(() => { 91 return createProfileListHref({list: view}) 92 }, [view]) 93 94 React.useEffect(() => { 95 precacheList(queryClient, view) 96 }, [view, queryClient]) 97 98 return ( 99 <InternalLink label={view.name} to={href} {...props}> 100 {children} 101 </InternalLink> 102 ) 103} 104 105export function TitleAndByline({ 106 title, 107 creator, 108 purpose = CURATELIST, 109 modUi, 110}: { 111 title: string 112 creator?: bsky.profile.AnyProfileView 113 purpose?: AppBskyGraphDefs.ListView['purpose'] 114 modUi?: ModerationUI 115}) { 116 const t = useTheme() 117 const {_} = useLingui() 118 const {currentAccount} = useSession() 119 120 return ( 121 <View style={[a.flex_1]}> 122 <Hider.Outer 123 modui={modUi} 124 isContentVisibleInitialState={ 125 creator && currentAccount?.did === creator.did 126 } 127 allowOverride={creator && currentAccount?.did === creator.did}> 128 <Hider.Mask> 129 <Text 130 style={[a.text_md, a.font_semi_bold, a.leading_snug, a.italic]} 131 numberOfLines={1}> 132 <Trans>Hidden list</Trans> 133 </Text> 134 </Hider.Mask> 135 <Hider.Content> 136 <Text 137 emoji 138 style={[a.text_md, a.font_semi_bold, a.leading_snug]} 139 numberOfLines={1}> 140 {title} 141 </Text> 142 </Hider.Content> 143 </Hider.Outer> 144 145 {creator && ( 146 <Text 147 emoji 148 style={[a.leading_snug, t.atoms.text_contrast_medium]} 149 numberOfLines={1}> 150 {purpose === MODLIST 151 ? _(msg`Moderation list by ${sanitizeHandle(creator.handle, '@')}`) 152 : _(msg`List by ${sanitizeHandle(creator.handle, '@')}`)} 153 </Text> 154 )} 155 </View> 156 ) 157} 158 159export function createProfileListHref({ 160 list, 161}: { 162 list: AppBskyGraphDefs.ListView 163}) { 164 const urip = new AtUri(list.uri) 165 const handleOrDid = list.creator.handle || list.creator.did 166 return `/profile/${handleOrDid}/lists/${urip.rkey}` 167}