Finish migrating ProfileFeed, FeedSourceCard

This commit is contained in:
Eric Bailey
2024-04-22 18:09:06 -05:00
parent dfbc6c32d7
commit fe9ad6b820
2 changed files with 89 additions and 190 deletions
+10 -12
View File
@@ -8,11 +8,10 @@ import {useLingui} from '@lingui/react'
import {logger} from '#/logger' import {logger} from '#/logger'
import {FeedSourceInfo, useFeedSourceInfoQuery} from '#/state/queries/feed' import {FeedSourceInfo, useFeedSourceInfoQuery} from '#/state/queries/feed'
import { import {
usePinFeedMutation, useAddSavedFeedMutation,
usePreferencesQuery, usePreferencesQuery,
UsePreferencesQueryResponse, UsePreferencesQueryResponse,
useRemoveFeedMutation, useRemoveFeedMutation,
useSaveFeedMutation,
} from '#/state/queries/preferences' } from '#/state/queries/preferences'
import {useNavigationDeduped} from 'lib/hooks/useNavigationDeduped' import {useNavigationDeduped} from 'lib/hooks/useNavigationDeduped'
import {usePalette} from 'lib/hooks/usePalette' import {usePalette} from 'lib/hooks/usePalette'
@@ -90,11 +89,10 @@ export function FeedSourceCardLoaded({
const removePromptControl = Prompt.usePromptControl() const removePromptControl = Prompt.usePromptControl()
const navigation = useNavigationDeduped() const navigation = useNavigationDeduped()
const {isPending: isSavePending, mutateAsync: saveFeed} = const {isPending: isAddSavedFeedPending, mutateAsync: addSavedFeed} =
useSaveFeedMutation() useAddSavedFeedMutation()
const {isPending: isRemovePending, mutateAsync: removeFeed} = const {isPending: isRemovePending, mutateAsync: removeFeed} =
useRemoveFeedMutation() useRemoveFeedMutation()
const {isPending: isPinPending, mutateAsync: pinFeed} = usePinFeedMutation()
const savedFeedConfig = preferences?.savedFeeds?.find( const savedFeedConfig = preferences?.savedFeeds?.find(
f => f.value === feed?.uri, f => f.value === feed?.uri,
@@ -105,17 +103,17 @@ export function FeedSourceCardLoaded({
if (!feed) return if (!feed) return
try { try {
if (pinOnSave) { await addSavedFeed({
await pinFeed({uri: feed.uri}) type: 'feed',
} else { value: feed.uri,
await saveFeed({uri: feed.uri}) pinned: pinOnSave,
} })
Toast.show(_(msg`Added to my feeds`)) Toast.show(_(msg`Added to my feeds`))
} catch (e) { } catch (e) {
Toast.show(_(msg`There was an issue contacting your server`)) Toast.show(_(msg`There was an issue contacting your server`))
logger.error('Failed to save feed', {message: e}) logger.error('Failed to save feed', {message: e})
} }
}, [_, feed, pinFeed, pinOnSave, saveFeed]) }, [_, feed, pinOnSave, addSavedFeed])
const onUnsave = React.useCallback(async () => { const onUnsave = React.useCallback(async () => {
if (!savedFeedConfig) return if (!savedFeedConfig) return
@@ -231,7 +229,7 @@ export function FeedSourceCardLoaded({
<View style={[s.justifyCenter]}> <View style={[s.justifyCenter]}>
<Pressable <Pressable
testID={`feed-${feed.displayName}-toggleSave`} testID={`feed-${feed.displayName}-toggleSave`}
disabled={isSavePending || isPinPending || isRemovePending} disabled={isAddSavedFeedPending || isRemovePending}
accessibilityRole="button" accessibilityRole="button"
accessibilityLabel={ accessibilityLabel={
isSaved isSaved
+79 -178
View File
@@ -7,7 +7,6 @@ import {NativeStackScreenProps} from '@react-navigation/native-stack'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
import {HITSLOP_20} from '#/lib/constants' import {HITSLOP_20} from '#/lib/constants'
import {useGate} from '#/lib/statsig/statsig'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isNative} from '#/platform/detection' import {isNative} from '#/platform/detection'
import {listenSoftReset} from '#/state/events' import {listenSoftReset} from '#/state/events'
@@ -16,13 +15,11 @@ import {useLikeMutation, useUnlikeMutation} from '#/state/queries/like'
import {FeedDescriptor} from '#/state/queries/post-feed' import {FeedDescriptor} from '#/state/queries/post-feed'
import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed' import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed'
import { import {
usePinFeedMutation, useAddSavedFeedMutation,
usePreferencesQuery, usePreferencesQuery,
UsePreferencesQueryResponse, UsePreferencesQueryResponse,
useRemoveFeedMutation, useRemoveFeedMutation,
useSaveFeedMutation, useUpdateSavedFeedMutation,
useSetPrimaryAlgorithmMutation,
useUnpinFeedMutation,
} from '#/state/queries/preferences' } from '#/state/queries/preferences'
import {useResolveUriQuery} from '#/state/queries/resolve-uri' import {useResolveUriQuery} from '#/state/queries/resolve-uri'
import {truncateAndInvalidate} from '#/state/queries/util' import {truncateAndInvalidate} from '#/state/queries/util'
@@ -54,22 +51,18 @@ import {Text} from 'view/com/util/text/Text'
import * as Toast from 'view/com/util/Toast' import * as Toast from 'view/com/util/Toast'
import {CenteredView} from 'view/com/util/Views' import {CenteredView} from 'view/com/util/Views'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {Button as NewButton, ButtonIcon, ButtonText} from '#/components/Button' import {Button as NewButton, ButtonText} from '#/components/Button'
import {useDialogControl} from '#/components/Dialog'
import {ArrowOutOfBox_Stroke2_Corner0_Rounded as Share} from '#/components/icons/ArrowOutOfBox' import {ArrowOutOfBox_Stroke2_Corner0_Rounded as Share} from '#/components/icons/ArrowOutOfBox'
import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
import {DotGrid_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid' import {DotGrid_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid'
import { import {
Heart2_Filled_Stroke2_Corner0_Rounded as HeartFilled, Heart2_Filled_Stroke2_Corner0_Rounded as HeartFilled,
Heart2_Stroke2_Corner0_Rounded as HeartOutline, Heart2_Stroke2_Corner0_Rounded as HeartOutline,
} from '#/components/icons/Heart2' } from '#/components/icons/Heart2'
import {Home_Stroke2_Corner0_Rounded as Home} from '#/components/icons/Home'
import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash' import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash'
import {InlineLinkText} from '#/components/Link' import {InlineLinkText} from '#/components/Link'
import * as Menu from '#/components/Menu' import * as Menu from '#/components/Menu'
import {PrimaryAlgoNoticeDialog} from '#/components/PrimaryAlgoNoticeDialog'
import {ReportDialog, useReportDialogControl} from '#/components/ReportDialog' import {ReportDialog, useReportDialogControl} from '#/components/ReportDialog'
import {RichText} from '#/components/RichText' import {RichText} from '#/components/RichText'
@@ -168,51 +161,21 @@ export function ProfileFeedScreenInner({
const playHaptic = useHaptics() const playHaptic = useHaptics()
const feedSectionRef = React.useRef<SectionRef>(null) const feedSectionRef = React.useRef<SectionRef>(null)
const isScreenFocused = useIsFocused() const isScreenFocused = useIsFocused()
const gate = useGate()
const primaryAlgoDialogControl = useDialogControl()
const primaryAlgo = preferences.primaryAlgorithm
const { const {mutateAsync: addSavedFeed, isPending: isAddSavedFeedPending} =
mutateAsync: saveFeed, useAddSavedFeedMutation()
variables: savedFeed, const {mutateAsync: removeFeed, isPending: isRemovePending} =
reset: resetSaveFeed, useRemoveFeedMutation()
isPending: isSavePending, const {mutateAsync: updateFeed, isPending: isUpdateFeedPending} =
} = useSaveFeedMutation() useUpdateSavedFeedMutation()
const {
mutateAsync: removeFeed,
variables: removedFeed,
reset: resetRemoveFeed,
isPending: isRemovePending,
} = useRemoveFeedMutation()
const {
mutateAsync: pinFeed,
variables: pinnedFeed,
reset: resetPinFeed,
isPending: isPinPending,
} = usePinFeedMutation()
const {
mutateAsync: unpinFeed,
variables: unpinnedFeed,
reset: resetUnpinFeed,
isPending: isUnpinPending,
} = useUnpinFeedMutation()
const {
mutateAsync: setPrimaryAlgo,
variables: primaryAlgoVariables,
isPending: isSetPrimaryAlgoPending,
} = useSetPrimaryAlgorithmMutation()
const isSaved = const isPending =
!removedFeed && isAddSavedFeedPending || isRemovePending || isUpdateFeedPending
(!!savedFeed || preferences.feeds.saved.includes(feedInfo.uri)) const savedFeedConfig = preferences.savedFeeds.find(
const isPinned = f => f.value === feedInfo.uri,
!unpinnedFeed && )
(!!pinnedFeed || preferences.feeds.pinned.includes(feedInfo.uri)) const isSaved = Boolean(savedFeedConfig)
const isPrimaryAlgo = const isPinned = Boolean(savedFeedConfig?.pinned)
(primaryAlgo?.enabled &&
primaryAlgo?.uri &&
primaryAlgo.uri === feedInfo.uri) ||
(primaryAlgoVariables?.enabled && primaryAlgoVariables.uri === feedInfo.uri)
useSetTitle(feedInfo?.displayName) useSetTitle(feedInfo?.displayName)
@@ -223,13 +186,18 @@ export function ProfileFeedScreenInner({
try { try {
playHaptic() playHaptic()
if (isSaved) { if (savedFeedConfig) {
await removeFeed({uri: feedInfo.uri}) // only allow removal of non-pinned feeds from this view
resetRemoveFeed() if (!savedFeedConfig.pinned) {
Toast.show(_(msg`Removed from your feeds`)) await removeFeed(savedFeedConfig)
Toast.show(_(msg`Removed from your feeds`))
}
} else { } else {
await saveFeed({uri: feedInfo.uri}) await addSavedFeed({
resetSaveFeed() type: 'feed',
value: feedInfo.uri,
pinned: false,
})
Toast.show(_(msg`Saved to your feeds`)) Toast.show(_(msg`Saved to your feeds`))
} }
} catch (err) { } catch (err) {
@@ -240,54 +208,36 @@ export function ProfileFeedScreenInner({
) )
logger.error('Failed up update feeds', {message: err}) logger.error('Failed up update feeds', {message: err})
} }
}, [ }, [_, playHaptic, feedInfo, removeFeed, addSavedFeed, savedFeedConfig])
playHaptic,
isSaved,
removeFeed,
feedInfo,
resetRemoveFeed,
_,
saveFeed,
resetSaveFeed,
])
const onTogglePinned = React.useCallback(async () => { const onTogglePinned = React.useCallback(async () => {
try { try {
playHaptic() playHaptic()
if (isPinned) { if (savedFeedConfig) {
await unpinFeed({uri: feedInfo.uri}) if (savedFeedConfig.pinned) {
resetUnpinFeed() await updateFeed({
...savedFeedConfig,
pinned: false,
})
} else {
await updateFeed({
...savedFeedConfig,
pinned: true,
})
}
} else { } else {
await pinFeed({uri: feedInfo.uri}) await addSavedFeed({
resetPinFeed() type: 'feed',
value: feedInfo.uri,
pinned: true,
})
} }
} catch (e) { } catch (e) {
Toast.show(_(msg`There was an issue contacting the server`)) Toast.show(_(msg`There was an issue contacting the server`))
logger.error('Failed to toggle pinned feed', {message: e}) logger.error('Failed to toggle pinned feed', {message: e})
} }
}, [ }, [playHaptic, feedInfo, _, savedFeedConfig, updateFeed, addSavedFeed])
playHaptic,
isPinned,
unpinFeed,
feedInfo,
resetUnpinFeed,
pinFeed,
resetPinFeed,
_,
])
const onSetPrimaryAlgo = React.useCallback(async () => {
try {
playHaptic()
await setPrimaryAlgo({enabled: true, uri: feedInfo.uri})
} catch (e: any) {
Toast.show(_(msg`There was an issue contacting the server`))
logger.error('ProfileFeed: failed to set primary algo', {
message: e.message,
})
}
}, [setPrimaryAlgo, feedInfo, _, playHaptic])
const onPressShare = React.useCallback(() => { const onPressShare = React.useCallback(() => {
const url = toShareUrl(feedInfo.route.href) const url = toShareUrl(feedInfo.route.href)
@@ -325,40 +275,18 @@ export function ProfileFeedScreenInner({
avatarType="algo"> avatarType="algo">
<View style={[a.flex_row, a.align_center, a.gap_sm]}> <View style={[a.flex_row, a.align_center, a.gap_sm]}>
{feedInfo && hasSession && ( {feedInfo && hasSession && (
<> <NewButton
{gate('reduced_onboarding_and_home_algo') && isPrimaryAlgo ? ( testID={isPinned ? 'unpinBtn' : 'pinBtn'}
<NewButton disabled={isPending}
variant="solid" size="small"
color="secondary" variant="solid"
size="small" color={isPinned ? 'secondary' : 'primary'}
label={_( label={isPinned ? _(msg`Unpin from home`) : _(msg`Pin to home`)}
msg`This feed is already set as your primary algorithm.`, onPress={onTogglePinned}>
)} <ButtonText>
onPress={() => { {isPinned ? _(msg`Unpin`) : _(msg`Pin to Home`)}
primaryAlgoDialogControl.open() </ButtonText>
}}> </NewButton>
<ButtonIcon icon={Check} position="left" />
<ButtonText>
<Trans>Primary Algorithm</Trans>
</ButtonText>
</NewButton>
) : (
<NewButton
testID={isPinned ? 'unpinBtn' : 'pinBtn'}
disabled={isPinPending || isUnpinPending}
size="small"
variant="solid"
color={isPinned ? 'secondary' : 'primary'}
label={
isPinned ? _(msg`Unpin from home`) : _(msg`Pin to home`)
}
onPress={onTogglePinned}>
<ButtonText>
{isPinned ? _(msg`Unpin`) : _(msg`Pin to Home`)}
</ButtonText>
</NewButton>
)}
</>
)} )}
<Menu.Root> <Menu.Root>
<Menu.Trigger label={_(msg`Open feed options menu`)}> <Menu.Trigger label={_(msg`Open feed options menu`)}>
@@ -389,44 +317,27 @@ export function ProfileFeedScreenInner({
<Menu.Outer> <Menu.Outer>
<Menu.Group> <Menu.Group>
{hasSession && ( {hasSession && !isPinned && (
<> <>
{!isPrimaryAlgo && ( <Menu.Item
<> disabled={isPending}
<Menu.Item testID="feedHeaderDropdownToggleSavedBtn"
disabled={isSavePending || isRemovePending} label={
testID="feedHeaderDropdownToggleSavedBtn" isSaved
label={ ? _(msg`Remove from my feeds`)
isSaved : _(msg`Save to my feeds`)
? _(msg`Remove from my feeds`) }
: _(msg`Save to my feeds`) onPress={onToggleSaved}>
} <Menu.ItemText>
onPress={onToggleSaved}> {isSaved
<Menu.ItemText> ? _(msg`Remove from my feeds`)
{isSaved : _(msg`Save to my feeds`)}
? _(msg`Remove from my feeds`) </Menu.ItemText>
: _(msg`Save to my feeds`)} <Menu.ItemIcon
</Menu.ItemText> icon={isSaved ? Trash : Plus}
<Menu.ItemIcon position="right"
icon={isSaved ? Trash : Plus} />
position="right" </Menu.Item>
/>
</Menu.Item>
{gate('reduced_onboarding_and_home_algo') && (
<Menu.Item
disabled={isSetPrimaryAlgoPending}
testID="feedHeaderDropdownSetPrimaryAlgoBtn"
label={_(msg`Set as primary algorithm`)}
onPress={onSetPrimaryAlgo}>
<Menu.ItemText>
{_(msg`Set as primary algorithm`)}
</Menu.ItemText>
<Menu.ItemIcon icon={Home} position="right" />
</Menu.Item>
)}
</>
)}
<Menu.Item <Menu.Item
testID="feedHeaderDropdownReportBtn" testID="feedHeaderDropdownReportBtn"
@@ -455,8 +366,6 @@ export function ProfileFeedScreenInner({
feedRkey={feedInfo.route.params.rkey} feedRkey={feedInfo.route.params.rkey}
feedInfo={feedInfo} feedInfo={feedInfo}
/> />
<PrimaryAlgoNoticeDialog control={primaryAlgoDialogControl} />
</> </>
) )
}, [ }, [
@@ -467,19 +376,11 @@ export function ProfileFeedScreenInner({
onTogglePinned, onTogglePinned,
onToggleSaved, onToggleSaved,
currentAccount?.did, currentAccount?.did,
isPinPending,
isRemovePending,
isSavePending,
isSaved, isSaved,
isUnpinPending,
onPressReport, onPressReport,
onPressShare, onPressShare,
t, t,
gate, isPending,
isPrimaryAlgo,
primaryAlgoDialogControl,
onSetPrimaryAlgo,
isSetPrimaryAlgoPending,
]) ])
return ( return (