about summary refs log tree commit diff
path: root/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/components')
-rw-r--r--src/components/Error.tsx32
-rw-r--r--src/components/ListCard.tsx48
-rw-r--r--src/components/moderation/Hider.tsx89
-rw-r--r--src/components/moderation/ModerationDetailsDialog.tsx4
4 files changed, 137 insertions, 36 deletions
diff --git a/src/components/Error.tsx b/src/components/Error.tsx
index 481532434..59d219831 100644
--- a/src/components/Error.tsx
+++ b/src/components/Error.tsx
@@ -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>
diff --git a/src/components/ListCard.tsx b/src/components/ListCard.tsx
index 0ed27cf50..829f36d47 100644
--- a/src/components/ListCard.tsx
+++ b/src/components/ListCard.tsx
@@ -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]}
diff --git a/src/components/moderation/Hider.tsx b/src/components/moderation/Hider.tsx
new file mode 100644
index 000000000..fcb88ddd9
--- /dev/null
+++ b/src/components/moderation/Hider.tsx
@@ -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
+}
diff --git a/src/components/moderation/ModerationDetailsDialog.tsx b/src/components/moderation/ModerationDetailsDialog.tsx
index ebfe45232..b8f02582c 100644
--- a/src/components/moderation/ModerationDetailsDialog.tsx
+++ b/src/components/moderation/ModerationDetailsDialog.tsx
@@ -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]}>