remove unused button, massively simplify

This commit is contained in:
Samuel Newman
2025-06-27 11:31:48 +03:00
parent 8fd2665046
commit f5f34979af
+76 -217
View File
@@ -1,4 +1,3 @@
import React from 'react'
import { import {
Linking, Linking,
Pressable, Pressable,
@@ -13,33 +12,23 @@ import {
type AppBskyGraphDefs, type AppBskyGraphDefs,
AtUri, AtUri,
} from '@atproto/api' } from '@atproto/api'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {Plural, Trans} from '@lingui/macro'
import {msg, Plural, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useNavigationDeduped} from '#/lib/hooks/useNavigationDeduped' import {useNavigationDeduped} from '#/lib/hooks/useNavigationDeduped'
import {usePalette} from '#/lib/hooks/usePalette' import {usePalette} from '#/lib/hooks/usePalette'
import {sanitizeHandle} from '#/lib/strings/handles' import {sanitizeHandle} from '#/lib/strings/handles'
import {s} from '#/lib/styles' import {s} from '#/lib/styles'
import {logger} from '#/logger'
import { import {
type FeedSourceInfo, type FeedSourceInfo,
hydrateFeedGenerator, hydrateFeedGenerator,
hydrateList, hydrateList,
useFeedSourceInfoQuery, useFeedSourceInfoQuery,
} from '#/state/queries/feed' } from '#/state/queries/feed'
import {
useAddSavedFeedsMutation,
usePreferencesQuery,
useRemoveFeedMutation,
} from '#/state/queries/preferences'
import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
import {Text} from '#/view/com/util/text/Text' import {Text} from '#/view/com/util/text/Text'
import * as Toast from '#/view/com/util/Toast'
import {UserAvatar} from '#/view/com/util/UserAvatar' import {UserAvatar} from '#/view/com/util/UserAvatar'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {shouldClickOpenNewTab} from '#/components/Link' import {shouldClickOpenNewTab} from '#/components/Link'
import * as Prompt from '#/components/Prompt'
import {RichText} from '#/components/RichText' import {RichText} from '#/components/RichText'
type FeedSourceCardProps = { type FeedSourceCardProps = {
@@ -68,7 +57,7 @@ export function FeedSourceCard({
} else { } else {
feed = hydrateList(feedData) feed = hydrateList(feedData)
} }
return <FeedSourceCardLoaded feedUri={feedUri} feed={feed} {...props} /> return <FeedSourceCardLoaded feed={feed} {...props} />
} else { } else {
return <FeedSourceCardWithoutData feedUri={feedUri} {...props} /> return <FeedSourceCardWithoutData feedUri={feedUri} {...props} />
} }
@@ -82,93 +71,36 @@ export function FeedSourceCardWithoutData({
uri: feedUri, uri: feedUri,
}) })
return <FeedSourceCardLoaded feedUri={feedUri} feed={feed} {...props} /> return <FeedSourceCardLoaded feed={feed} {...props} />
} }
export function FeedSourceCardLoaded({ export function FeedSourceCardLoaded({
feedUri,
feed, feed,
style, style,
showSaveBtn = false,
showDescription = false, showDescription = false,
showLikes = false, showLikes = false,
pinOnSave = false,
showMinimalPlaceholder, showMinimalPlaceholder,
hideTopBorder, hideTopBorder,
}: { }: {
feedUri: string
feed?: FeedSourceInfo feed?: FeedSourceInfo
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
showSaveBtn?: boolean
showDescription?: boolean showDescription?: boolean
showLikes?: boolean showLikes?: boolean
pinOnSave?: boolean
showMinimalPlaceholder?: boolean showMinimalPlaceholder?: boolean
hideTopBorder?: boolean hideTopBorder?: boolean
}) { }) {
const {data: preferences} = usePreferencesQuery()
const t = useTheme() const t = useTheme()
const pal = usePalette('default') const pal = usePalette('default')
const {_} = useLingui()
const removePromptControl = Prompt.usePromptControl()
const navigation = useNavigationDeduped() const navigation = useNavigationDeduped()
const {isPending: isAddSavedFeedPending, mutateAsync: addSavedFeeds} =
useAddSavedFeedsMutation()
const {isPending: isRemovePending, mutateAsync: removeFeed} =
useRemoveFeedMutation()
const savedFeedConfig = preferences?.savedFeeds?.find(
f => f.value === feedUri,
)
const isSaved = Boolean(savedFeedConfig)
const onSave = React.useCallback(async () => {
if (!feed || isSaved) return
try {
await addSavedFeeds([
{
type: 'feed',
value: feed.uri,
pinned: pinOnSave,
},
])
Toast.show(_(msg`Added to my feeds`))
} catch (e) {
Toast.show(_(msg`There was an issue contacting your server`), 'xmark')
logger.error('Failed to save feed', {message: e})
}
}, [_, feed, pinOnSave, addSavedFeeds, isSaved])
const onUnsave = React.useCallback(async () => {
if (!savedFeedConfig) return
try {
await removeFeed(savedFeedConfig)
// await item.unsave()
Toast.show(_(msg`Removed from my feeds`))
} catch (e) {
Toast.show(_(msg`There was an issue contacting your server`), 'xmark')
logger.error('Failed to unsave feed', {message: e})
}
}, [_, removeFeed, savedFeedConfig])
const onToggleSaved = React.useCallback(async () => {
if (isSaved) {
removePromptControl.open()
} else {
await onSave()
}
}, [isSaved, removePromptControl, onSave])
/* /*
* LOAD STATE * LOAD STATE
* *
* This state also captures the scenario where a feed can't load for whatever * This state also captures the scenario where a feed can't load for whatever
* reason. * reason.
*/ */
if (!feed || !preferences) if (!feed)
return ( return (
<View <View
style={[ style={[
@@ -186,162 +118,89 @@ export function FeedSourceCardLoaded({
]}> ]}>
{showMinimalPlaceholder ? ( {showMinimalPlaceholder ? (
<FeedLoadingPlaceholder <FeedLoadingPlaceholder
style={{flex: 1}} style={[a.flex_1]}
showTopBorder={false} showTopBorder={false}
showLowerPlaceholder={false} showLowerPlaceholder={false}
/> />
) : ( ) : (
<FeedLoadingPlaceholder style={{flex: 1}} showTopBorder={false} /> <FeedLoadingPlaceholder style={[a.flex_1]} showTopBorder={false} />
)}
{showSaveBtn && (
<Pressable
testID={`feed-${feedUri}-toggleSave`}
disabled={isRemovePending}
accessibilityRole="button"
accessibilityLabel={_(msg`Remove from my feeds`)}
accessibilityHint=""
onPress={onUnsave}
hitSlop={15}
style={styles.btn}>
<FontAwesomeIcon
icon={['far', 'trash-can']}
size={19}
color={pal.colors.icon}
/>
</Pressable>
)} )}
</View> </View>
) )
return ( return (
<> <Pressable
<Pressable testID={`feed-${feed.displayName}`}
testID={`feed-${feed.displayName}`} accessibilityRole="button"
accessibilityRole="button" style={[
style={[ a.flex_1,
a.flex_1, a.p_lg,
a.p_lg, a.gap_md,
a.gap_md, !hideTopBorder && !a.border_t,
!hideTopBorder && !a.border_t, t.atoms.border_contrast_low,
t.atoms.border_contrast_low, style,
style, ]}
]} onPress={e => {
onPress={e => { const shouldOpenInNewTab = shouldClickOpenNewTab(e)
const shouldOpenInNewTab = shouldClickOpenNewTab(e) if (feed.type === 'feed') {
if (feed.type === 'feed') { if (shouldOpenInNewTab) {
if (shouldOpenInNewTab) { Linking.openURL(
Linking.openURL( `/profile/${feed.creatorDid}/feed/${new AtUri(feed.uri).rkey}`,
`/profile/${feed.creatorDid}/feed/${new AtUri(feed.uri).rkey}`, )
) } else {
} else { navigation.push('ProfileFeed', {
navigation.push('ProfileFeed', { name: feed.creatorDid,
name: feed.creatorDid, rkey: new AtUri(feed.uri).rkey,
rkey: new AtUri(feed.uri).rkey, })
})
}
} else if (feed.type === 'list') {
if (shouldOpenInNewTab) {
Linking.openURL(
`/profile/${feed.creatorDid}/lists/${new AtUri(feed.uri).rkey}`,
)
} else {
navigation.push('ProfileList', {
name: feed.creatorDid,
rkey: new AtUri(feed.uri).rkey,
})
}
} }
}} } else if (feed.type === 'list') {
key={feed.uri}> if (shouldOpenInNewTab) {
<View style={[a.flex_row, a.align_center]}> Linking.openURL(
<View style={[s.mr10]}> `/profile/${feed.creatorDid}/lists/${new AtUri(feed.uri).rkey}`,
<UserAvatar type="algo" size={36} avatar={feed.avatar} /> )
</View> } else {
<View style={[a.flex_1, a.gap_2xs]}> navigation.push('ProfileList', {
<Text emoji style={[pal.text, s.bold]} numberOfLines={1}> name: feed.creatorDid,
{feed.displayName} rkey: new AtUri(feed.uri).rkey,
</Text> })
<Text type="sm" style={[pal.textLight]} numberOfLines={1}> }
{feed.type === 'feed' ? ( }
<Trans>Feed by {sanitizeHandle(feed.creatorHandle, '@')}</Trans> }}
) : ( key={feed.uri}>
<Trans>List by {sanitizeHandle(feed.creatorHandle, '@')}</Trans> <View style={[a.flex_row, a.align_center]}>
)} <View style={[s.mr10]}>
</Text> <UserAvatar type="algo" size={36} avatar={feed.avatar} />
</View>
{showSaveBtn && (
<View style={{alignSelf: 'center'}}>
<Pressable
testID={`feed-${feed.displayName}-toggleSave`}
disabled={isAddSavedFeedPending || isRemovePending}
accessibilityRole="button"
accessibilityLabel={
isSaved
? _(msg`Remove from my feeds`)
: _(msg`Add to my feeds`)
}
accessibilityHint=""
onPress={onToggleSaved}
hitSlop={15}
style={styles.btn}>
{isSaved ? (
<FontAwesomeIcon
icon={['far', 'trash-can']}
size={19}
color={pal.colors.icon}
/>
) : (
<FontAwesomeIcon
icon="plus"
size={18}
color={pal.colors.link}
/>
)}
</Pressable>
</View>
)}
</View> </View>
<View style={[a.flex_1, a.gap_2xs]}>
{showDescription && feed.description ? ( <Text emoji style={[pal.text, s.bold]} numberOfLines={1}>
<RichText {feed.displayName}
style={[t.atoms.text_contrast_high, a.flex_1, a.flex_wrap]}
value={feed.description}
numberOfLines={3}
/>
) : null}
{showLikes && feed.type === 'feed' ? (
<Text type="sm-medium" style={[pal.text, pal.textLight]}>
<Trans>
Liked by{' '}
<Plural
value={feed.likeCount || 0}
one="# user"
other="# users"
/>
</Trans>
</Text> </Text>
) : null} <Text type="sm" style={[pal.textLight]} numberOfLines={1}>
</Pressable> {feed.type === 'feed' ? (
<Trans>Feed by {sanitizeHandle(feed.creatorHandle, '@')}</Trans>
) : (
<Trans>List by {sanitizeHandle(feed.creatorHandle, '@')}</Trans>
)}
</Text>
</View>
</View>
<Prompt.Basic {showDescription && feed.description ? (
control={removePromptControl} <RichText
title={_(msg`Remove from your feeds?`)} style={[t.atoms.text_contrast_high, a.flex_1, a.flex_wrap]}
description={_( value={feed.description}
msg`Are you sure you want to remove ${feed.displayName} from your feeds?`, numberOfLines={3}
)} />
onConfirm={onUnsave} ) : null}
confirmButtonCta={_(msg`Remove`)}
confirmButtonColor="negative" {showLikes && feed.type === 'feed' ? (
/> <Text type="sm-medium" style={[pal.text, pal.textLight]}>
</> <Trans>
Liked by{' '}
<Plural value={feed.likeCount || 0} one="# user" other="# users" />
</Trans>
</Text>
) : null}
</Pressable>
) )
} }
const styles = StyleSheet.create({
btn: {
paddingVertical: 6,
},
})