Add list hidden screen (#4958)

Co-authored-by: Hailey <me@haileyok.com>
Co-authored-by: Eric Bailey <git@esb.lol>
This commit is contained in:
Hailey 2024-08-20 15:43:40 -07:00 committed by GitHub
parent e54298ec2c
commit 723896a45f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 494 additions and 339 deletions

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>

View file

@ -1,13 +1,20 @@
import React from 'react'
import {View} from 'react-native'
import {AppBskyActorDefs, AppBskyGraphDefs, AtUri} from '@atproto/api'
import {
AppBskyActorDefs,
AppBskyGraphDefs,
AtUri,
moderateUserList,
ModerationUI,
} from '@atproto/api'
import {Trans} from '@lingui/macro'
import {useQueryClient} from '@tanstack/react-query'
import {sanitizeHandle} from 'lib/strings/handles'
import {useModerationOpts} from 'state/preferences/moderation-opts'
import {precacheList} from 'state/queries/feed'
import {useTheme} from '#/alf'
import {atoms as a} from '#/alf'
import {useSession} from 'state/session'
import {atoms as a, useTheme} from '#/alf'
import {
Avatar,
Description,
@ -16,6 +23,7 @@ import {
SaveButton,
} from '#/components/FeedCard'
import {Link as InternalLink, LinkProps} from '#/components/Link'
import * as Hider from '#/components/moderation/Hider'
import {Text} from '#/components/Typography'
/*
@ -43,6 +51,11 @@ type Props = {
export function Default(props: Props) {
const {view, showPinButton} = props
const moderationOpts = useModerationOpts()
const moderation = moderationOpts
? moderateUserList(view, moderationOpts)
: undefined
return (
<Link {...props}>
<Outer>
@ -52,6 +65,7 @@ export function Default(props: Props) {
title={view.name}
creator={view.creator}
purpose={view.purpose}
modUi={moderation?.ui('contentView')}
/>
{showPinButton && view.purpose === CURATELIST && (
<SaveButton view={view} pin />
@ -89,18 +103,40 @@ export function TitleAndByline({
title,
creator,
purpose = CURATELIST,
modUi,
}: {
title: string
creator?: AppBskyActorDefs.ProfileViewBasic
purpose?: AppBskyGraphDefs.ListView['purpose']
modUi?: ModerationUI
}) {
const t = useTheme()
const {currentAccount} = useSession()
return (
<View style={[a.flex_1]}>
<Text style={[a.text_md, a.font_bold, a.leading_snug]} numberOfLines={1}>
{title}
</Text>
<Hider.Outer
modui={modUi}
isContentVisibleInitialState={
creator && currentAccount?.did === creator.did
}
allowOverride={creator && currentAccount?.did === creator.did}>
<Hider.Mask>
<Text
style={[a.text_md, a.font_bold, a.leading_snug, a.italic]}
numberOfLines={1}>
<Trans>Hidden list</Trans>
</Text>
</Hider.Mask>
<Hider.Content>
<Text
style={[a.text_md, a.font_bold, a.leading_snug]}
numberOfLines={1}>
{title}
</Text>
</Hider.Content>
</Hider.Outer>
{creator && (
<Text
style={[a.leading_snug, t.atoms.text_contrast_medium]}

View file

@ -0,0 +1,89 @@
import React from 'react'
import {ModerationUI} from '@atproto/api'
import {
ModerationCauseDescription,
useModerationCauseDescription,
} from '#/lib/moderation/useModerationCauseDescription'
import {
ModerationDetailsDialog,
useModerationDetailsDialogControl,
} from '#/components/moderation/ModerationDetailsDialog'
type Context = {
isContentVisible: boolean
setIsContentVisible: (show: boolean) => void
info: ModerationCauseDescription
showInfoDialog: () => void
meta: {
isNoPwi: boolean
allowOverride: boolean
}
}
const Context = React.createContext<Context>({} as Context)
export const useHider = () => React.useContext(Context)
export function Outer({
modui,
isContentVisibleInitialState,
allowOverride,
children,
}: React.PropsWithChildren<{
isContentVisibleInitialState?: boolean
allowOverride?: boolean
modui: ModerationUI | undefined
}>) {
const control = useModerationDetailsDialogControl()
const blur = modui?.blurs[0]
const [isContentVisible, setIsContentVisible] = React.useState(
isContentVisibleInitialState || !blur,
)
const info = useModerationCauseDescription(blur)
const meta = {
isNoPwi: Boolean(
modui?.blurs.find(
cause =>
cause.type === 'label' &&
cause.labelDef.identifier === '!no-unauthenticated',
),
),
allowOverride: allowOverride ?? !modui?.noOverride,
}
const showInfoDialog = () => {
control.open()
}
const onSetContentVisible = (show: boolean) => {
if (meta.allowOverride) return
setIsContentVisible(show)
}
const ctx = {
isContentVisible,
setIsContentVisible: onSetContentVisible,
showInfoDialog,
info,
meta,
}
return (
<Context.Provider value={ctx}>
{children}
<ModerationDetailsDialog control={control} modcause={blur} />
</Context.Provider>
)
}
export function Content({children}: {children: React.ReactNode}) {
const ctx = useHider()
return ctx.isContentVisible ? children : null
}
export function Mask({children}: {children: React.ReactNode}) {
const ctx = useHider()
return ctx.isContentVisible ? null : children
}

View file

@ -18,7 +18,7 @@ export {useDialogControl as useModerationDetailsDialogControl} from '#/component
export interface ModerationDetailsDialogProps {
control: Dialog.DialogOuterProps['control']
modcause: ModerationCause
modcause?: ModerationCause
}
export function ModerationDetailsDialog(props: ModerationDetailsDialogProps) {
@ -123,7 +123,7 @@ function ModerationDetailsDialogInner({
{description}
</Text>
{modcause.type === 'label' && (
{modcause?.type === 'label' && (
<>
<Divider />
<Text style={[t.atoms.text, a.text_md, a.leading_snug, a.mt_lg]}>