Bluesky app fork with some witchin' additions 馃挮
at main 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}