forked from
jollywhoppers.com/witchsky.app
fork
Configure Feed
Select the types of activity you want to include in your feed.
Bluesky app fork with some witchin' additions 馃挮
fork
Configure Feed
Select the types of activity you want to include in your feed.
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}