add feed hidden screen

This commit is contained in:
Hailey
2024-08-19 16:13:11 -07:00
parent e54298ec2c
commit 216d3cd9ae
4 changed files with 273 additions and 34 deletions
+4 -28
View File
@@ -2,21 +2,18 @@ import React from 'react'
import {View} from 'react-native'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useNavigation} from '@react-navigation/core'
import {StackActions} from '@react-navigation/native'
import {NavigationProp} from 'lib/routes/types'
import {useGoBack} from 'lib/hooks/useGoBack'
import {CenteredView} from 'view/com/util/Views'
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import {Text} from '#/components/Typography'
import {router} from '#/routes'
export function Error({
title,
message,
onRetry,
onGoBack: onGoBackProp,
onGoBack,
hideBackButton,
sideBorders = true,
}: {
@@ -27,31 +24,10 @@ export function Error({
hideBackButton?: boolean
sideBorders?: boolean
}) {
const navigation = useNavigation<NavigationProp>()
const {_} = useLingui()
const t = useTheme()
const {gtMobile} = useBreakpoints()
const canGoBack = navigation.canGoBack()
const onGoBack = React.useCallback(() => {
if (onGoBackProp) {
onGoBackProp()
return
}
if (canGoBack) {
navigation.goBack()
} else {
navigation.navigate('HomeTab')
// Checking the state for routes ensures that web doesn't encounter errors while going back
if (navigation.getState()?.routes) {
navigation.dispatch(StackActions.push(...router.matchPath('/')))
} else {
navigation.navigate('HomeTab')
navigation.dispatch(StackActions.popToTop())
}
}
}, [navigation, canGoBack, onGoBackProp])
const goBack = useGoBack(onGoBack)
return (
<CenteredView
@@ -96,7 +72,7 @@ export function Error({
variant="solid"
color={onRetry ? 'secondary' : 'primary'}
label={_(msg`Return to previous page`)}
onPress={onGoBack}
onPress={goBack}
size="large"
style={[a.rounded_sm, a.overflow_hidden, {paddingVertical: 10}]}>
<ButtonText>
+23
View File
@@ -0,0 +1,23 @@
import {StackActions, useNavigation} from '@react-navigation/native'
import {NavigationProp} from 'lib/routes/types'
import {router} from '#/routes'
export function useGoBack(onGoBack?: () => unknown) {
const navigation = useNavigation<NavigationProp>()
return () => {
onGoBack?.()
if (navigation.canGoBack()) {
navigation.goBack()
} else {
navigation.navigate('HomeTab')
// Checking the state for routes ensures that web doesn't encounter errors while going back
if (navigation.getState()?.routes) {
navigation.dispatch(StackActions.push(...router.matchPath('/')))
} else {
navigation.navigate('HomeTab')
navigation.dispatch(StackActions.popToTop())
}
}
}
}
+222
View File
@@ -0,0 +1,222 @@
import React from 'react'
import {View} from 'react-native'
import {AppBskyGraphDefs} from '@atproto/api'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {logger} from '#/logger'
import {useGoBack} from 'lib/hooks/useGoBack'
import {
useListBlockMutation,
useListDeleteMutation,
useListMuteMutation,
} from 'state/queries/list'
import {
UsePreferencesQueryResponse,
useRemoveFeedMutation,
} from 'state/queries/preferences'
import {useSession} from 'state/session'
import * as Toast from 'view/com/util/Toast'
import {CenteredView} from 'view/com/util/Views'
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import {EyeSlash_Stroke2_Corner0_Rounded} from '#/components/icons/EyeSlash'
import {Text} from '#/components/Typography'
export function ListHiddenScreen({
list,
preferences,
}: {
list: AppBskyGraphDefs.ListView
preferences: UsePreferencesQueryResponse
}) {
const {_} = useLingui()
const t = useTheme()
const {currentAccount} = useSession()
const {gtMobile} = useBreakpoints()
const isOwner = currentAccount?.did === list.creator.did
const goBack = useGoBack()
const isModList = list.purpose === 'app.bsky.graph.defs#modlist'
const [isProcessing, setIsProcessing] = React.useState(false)
const listBlockMutation = useListBlockMutation()
const listMuteMutation = useListMuteMutation()
const listDeleteMutation = useListDeleteMutation()
const {mutateAsync: removeSavedFeed} = useRemoveFeedMutation()
const savedFeedConfig = preferences.savedFeeds.find(f => f.value === list.uri)
const onUnsubscribe = async () => {
setIsProcessing(true)
if (list.viewer?.muted) {
try {
await listMuteMutation.mutateAsync({uri: list.uri, mute: false})
} catch (e) {
setIsProcessing(false)
logger.error('Failed to unmute list', {message: e})
Toast.show(
_(
msg`There was an issue. Please check your internet connection and try again.`,
),
)
return
}
}
if (list.viewer?.blocked) {
try {
await listBlockMutation.mutateAsync({uri: list.uri, block: false})
} catch (e) {
setIsProcessing(false)
logger.error('Failed to unblock list', {message: e})
Toast.show(
_(
msg`There was an issue. Please check your internet connection and try again.`,
),
)
return
}
}
Toast.show(_(msg`Unsubscribed from list`))
setIsProcessing(false)
}
const onDeleteList = async () => {
setIsProcessing(true)
try {
await listDeleteMutation.mutateAsync({uri: list.uri})
Toast.show(_(msg`List deleted`))
} catch (e) {
logger.error('Failed to delete list from saved feeds', {message: e})
Toast.show(
_(
msg`There was an issue. Please check your internet connection and try again.`,
),
)
} finally {
setIsProcessing(false)
goBack()
}
}
const onRemoveList = async () => {
if (!savedFeedConfig) return
try {
await removeSavedFeed(savedFeedConfig)
Toast.show(_(msg`Removed from saved feeds`))
} catch (e) {
logger.error('Failed to remove list from saved feeds', {message: e})
Toast.show(
_(
msg`There was an issue. Please check your internet connection and try again.`,
),
)
} finally {
setIsProcessing(false)
}
}
return (
<CenteredView
style={[
a.flex_1,
a.align_center,
a.gap_5xl,
!gtMobile && a.justify_between,
t.atoms.border_contrast_low,
{paddingTop: 175, paddingBottom: 110},
]}
sideBorders={true}>
<View style={[a.w_full, a.align_center, a.gap_lg]}>
<EyeSlash_Stroke2_Corner0_Rounded
style={{color: t.atoms.text_contrast_medium.color}}
height={42}
width={42}
/>
<View style={[a.gap_sm, a.align_center]}>
<Text style={[a.font_bold, a.text_3xl]}>
<Trans>List hidden</Trans>
</Text>
<Text
style={[
a.text_md,
a.text_center,
t.atoms.text_contrast_high,
{lineHeight: 1.4},
gtMobile ? {width: 450} : [a.w_full, a.px_lg],
]}>
{isOwner ? (
<Trans>
The list you are trying to view (
<Text style={[a.font_bold, a.text_md]}>{list.name}</Text>) has
been hidden.
</Trans>
) : (
<Trans>The list you are trying to view has been hidden.</Trans>
)}
</Text>
</View>
</View>
<View style={[a.gap_md, gtMobile ? {width: 350} : [a.w_full, a.px_lg]]}>
<View style={[a.gap_md]}>
{savedFeedConfig ? (
<Button
variant="solid"
color="secondary"
size="medium"
label={_(msg`Remove from saved feeds`)}
onPress={onRemoveList}
disabled={isProcessing}>
<ButtonText>
<Trans>Removed from saved feeds</Trans>
</ButtonText>
</Button>
) : null}
{isOwner ? (
<Button
variant="solid"
color="secondary"
size="medium"
label={_(msg`Delete List`)}
onPress={onDeleteList}
disabled={isProcessing}>
<ButtonText>
<Trans>Delete list</Trans>
</ButtonText>
</Button>
) : null}
{list.viewer?.muted || list.viewer?.blocked ? (
<Button
variant="solid"
color="secondary"
size="medium"
label={_(msg`Unsubscribe from list`)}
onPress={() => {
if (isModList) {
onUnsubscribe()
} else {
onRemoveList()
}
}}
disabled={isProcessing}>
<ButtonText>
<Trans>Unsubscribe from list</Trans>
</ButtonText>
</Button>
) : null}
</View>
<Button
variant="solid"
color="primary"
label={_(msg`Return to previous page`)}
onPress={goBack}
size="medium"
disabled={isProcessing}>
<ButtonText>
<Trans>Go Back</Trans>
</ButtonText>
</Button>
</View>
</CenteredView>
)
}
+24 -6
View File
@@ -32,6 +32,7 @@ import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed'
import {
useAddSavedFeedsMutation,
usePreferencesQuery,
UsePreferencesQueryResponse,
useRemoveFeedMutation,
useUpdateSavedFeedsMutation,
} from '#/state/queries/preferences'
@@ -67,6 +68,7 @@ import {LoadingScreen} from 'view/com/util/LoadingScreen'
import {Text} from 'view/com/util/text/Text'
import * as Toast from 'view/com/util/Toast'
import {CenteredView} from 'view/com/util/Views'
import {ListHiddenScreen} from '#/screens/List/ListHiddenScreen'
import {atoms as a, useTheme} from '#/alf'
import {useDialogControl} from '#/components/Dialog'
import {ScreenHider} from '#/components/moderation/ScreenHider'
@@ -88,6 +90,7 @@ export function ProfileListScreen(props: Props) {
const {data: resolvedUri, error: resolveError} = useResolveUriQuery(
AtUri.make(handleOrDid, 'app.bsky.graph.list', rkey).toString(),
)
const {data: preferences} = usePreferencesQuery()
const {data: list, error: listError} = useListQuery(resolvedUri?.uri)
const moderationOpts = useModerationOpts()
@@ -110,12 +113,13 @@ export function ProfileListScreen(props: Props) {
)
}
return resolvedUri && list && moderationOpts ? (
return resolvedUri && list && moderationOpts && preferences ? (
<ProfileListScreenLoaded
{...props}
uri={resolvedUri.uri}
list={list}
moderationOpts={moderationOpts}
preferences={preferences}
/>
) : (
<LoadingScreen />
@@ -127,10 +131,12 @@ function ProfileListScreenLoaded({
uri,
list,
moderationOpts,
preferences,
}: Props & {
uri: string
list: AppBskyGraphDefs.ListView
moderationOpts: ModerationOpts
preferences: UsePreferencesQueryResponse
}) {
const {_} = useLingui()
const queryClient = useQueryClient()
@@ -142,6 +148,7 @@ function ProfileListScreenLoaded({
const {openModal} = useModalControls()
const isCurateList = list.purpose === 'app.bsky.graph.defs#curatelist'
const isScreenFocused = useIsFocused()
const isHidden = list.labels?.findIndex(l => l.val === '!hide') !== -1
const moderation = React.useMemo(() => {
return moderateUserList(list, moderationOpts)
@@ -179,8 +186,12 @@ function ProfileListScreenLoaded({
)
const renderHeader = useCallback(() => {
return <Header rkey={rkey} list={list} />
}, [rkey, list])
return <Header rkey={rkey} list={list} preferences={preferences} />
}, [rkey, list, preferences])
if (isHidden) {
return <ListHiddenScreen list={list} preferences={preferences} />
}
if (isCurateList) {
return (
@@ -267,7 +278,15 @@ function ProfileListScreenLoaded({
)
}
function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
function Header({
rkey,
list,
preferences,
}: {
rkey: string
list: AppBskyGraphDefs.ListView
preferences: UsePreferencesQueryResponse
}) {
const pal = usePalette('default')
const palInverted = usePalette('inverted')
const {_} = useLingui()
@@ -283,7 +302,6 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
const isBlocking = !!list.viewer?.blocked
const isMuting = !!list.viewer?.muted
const isOwner = list.creator.did === currentAccount?.did
const {data: preferences} = usePreferencesQuery()
const {track} = useAnalytics()
const playHaptic = useHaptics()
@@ -644,7 +662,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
cid: list.cid,
}}
/>
{isCurateList || isPinned ? (
{isCurateList ? (
<Button
testID={isPinned ? 'unpinBtn' : 'pinBtn'}
type={isPinned ? 'default' : 'inverted'}