about summary refs log tree commit diff
path: root/src/view/com
diff options
context:
space:
mode:
authorFoysal Ahamed <foysal@blueskyweb.xyz>2023-08-15 23:32:06 +0200
committerGitHub <noreply@github.com>2023-08-15 14:32:06 -0700
commitabbc6543f423efdcd26f44666d9847ecd70779dc (patch)
tree4324068713cc947f9e28b081bd45fec587ef33ef /src/view/com
parenta5762c2d7df89bcb89588e2ab8969ea1e8945514 (diff)
downloadvoidsky-abbc6543f423efdcd26f44666d9847ecd70779dc.tar.zst
:sparkles: Repurpose report post modal and re-use for list reporting (#1070)
* :sparkles: Repupose report post modal and re-use for list reporting

* :sparkles: Allow reporting a feed generator

* :sparkles: :recycle: Refactor report modal into one shared component for reporting different collections

* :white_check_mark: Adjust report option selector in tests

* :white_check_mark: Add test for list reporting

* :recycle: :sparkles: Refactor reason options and add options for list and feedgen

* :broom: Cleanup remaining todo

* Fix to mutelist react keys

* Fix regression from rebase

* Improve customfeed mobile header

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
Diffstat (limited to 'src/view/com')
-rw-r--r--src/view/com/lists/ListActions.tsx13
-rw-r--r--src/view/com/lists/ListItems.tsx7
-rw-r--r--src/view/com/modals/Modal.tsx12
-rw-r--r--src/view/com/modals/Modal.web.tsx9
-rw-r--r--src/view/com/modals/report/Modal.tsx (renamed from src/view/com/modals/report/ReportPost.tsx)165
-rw-r--r--src/view/com/modals/report/ReasonOptions.tsx123
-rw-r--r--src/view/com/modals/report/ReportAccount.tsx197
-rw-r--r--src/view/com/modals/report/types.ts8
-rw-r--r--src/view/com/profile/ProfileHeader.tsx2
-rw-r--r--src/view/com/util/forms/PostDropdownBtn.tsx6
10 files changed, 220 insertions, 322 deletions
diff --git a/src/view/com/lists/ListActions.tsx b/src/view/com/lists/ListActions.tsx
index ee5a2afcb..353338198 100644
--- a/src/view/com/lists/ListActions.tsx
+++ b/src/view/com/lists/ListActions.tsx
@@ -11,6 +11,7 @@ export const ListActions = ({
   isOwner,
   onPressDeleteList,
   onPressShareList,
+  onPressReportList,
   reversed = false, // Default value of reversed is false
 }: {
   isOwner: boolean
@@ -19,6 +20,7 @@ export const ListActions = ({
   onPressEditList?: () => void
   onPressDeleteList?: () => void
   onPressShareList?: () => void
+  onPressReportList?: () => void
   reversed?: boolean // New optional prop
 }) => {
   const pal = usePalette('default')
@@ -64,6 +66,17 @@ export const ListActions = ({
       onPress={onPressShareList}>
       <FontAwesomeIcon icon={'share'} style={[pal.text]} />
     </Button>,
+    !isOwner && (
+      <Button
+        key="reportListBtn"
+        testID="reportListBtn"
+        type="default"
+        accessibilityLabel="Report list"
+        accessibilityHint=""
+        onPress={onPressReportList}>
+        <FontAwesomeIcon icon={'circle-exclamation'} style={[pal.text]} />
+      </Button>
+    ),
   ]
 
   // If reversed is true, reverse the array to reverse the order of the buttons
diff --git a/src/view/com/lists/ListItems.tsx b/src/view/com/lists/ListItems.tsx
index 188518ea5..94e22f35e 100644
--- a/src/view/com/lists/ListItems.tsx
+++ b/src/view/com/lists/ListItems.tsx
@@ -45,6 +45,7 @@ export const ListItems = observer(
     onPressEditList,
     onPressDeleteList,
     onPressShareList,
+    onPressReportList,
     renderEmptyState,
     testID,
     headerOffset = 0,
@@ -57,6 +58,7 @@ export const ListItems = observer(
     onPressEditList: () => void
     onPressDeleteList: () => void
     onPressShareList: () => void
+    onPressReportList: () => void
     renderEmptyState?: () => JSX.Element
     testID?: string
     headerOffset?: number
@@ -169,6 +171,7 @@ export const ListItems = observer(
               onPressEditList={onPressEditList}
               onPressDeleteList={onPressDeleteList}
               onPressShareList={onPressShareList}
+              onPressReportList={onPressReportList}
             />
           ) : null
         } else if (item === ERROR_ITEM) {
@@ -208,6 +211,7 @@ export const ListItems = observer(
         onPressEditList,
         onPressDeleteList,
         onPressShareList,
+        onPressReportList,
         onPressTryAgain,
         onPressRetryLoadMore,
       ],
@@ -267,6 +271,7 @@ const ListHeader = observer(
     onPressEditList,
     onPressDeleteList,
     onPressShareList,
+    onPressReportList,
   }: {
     list: AppBskyGraphDefs.ListView
     isOwner: boolean
@@ -274,6 +279,7 @@ const ListHeader = observer(
     onPressEditList: () => void
     onPressDeleteList: () => void
     onPressShareList: () => void
+    onPressReportList: () => void
   }) => {
     const pal = usePalette('default')
     const store = useStores()
@@ -319,6 +325,7 @@ const ListHeader = observer(
                 onPressEditList={onPressEditList}
                 onToggleSubscribed={onToggleSubscribed}
                 onPressShareList={onPressShareList}
+                onPressReportList={onPressReportList}
               />
             )}
           </View>
diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx
index ce5dc40e0..efd06412d 100644
--- a/src/view/com/modals/Modal.tsx
+++ b/src/view/com/modals/Modal.tsx
@@ -13,14 +13,13 @@ import * as ConfirmModal from './Confirm'
 import * as EditProfileModal from './EditProfile'
 import * as ProfilePreviewModal from './ProfilePreview'
 import * as ServerInputModal from './ServerInput'
-import * as ReportPostModal from './report/ReportPost'
 import * as RepostModal from './Repost'
 import * as SelfLabelModal from './SelfLabel'
 import * as CreateOrEditMuteListModal from './CreateOrEditMuteList'
 import * as ListAddRemoveUserModal from './ListAddRemoveUser'
 import * as AltImageModal from './AltImage'
 import * as EditImageModal from './AltImage'
-import * as ReportAccountModal from './report/ReportAccount'
+import * as ReportModal from './report/Modal'
 import * as DeleteAccountModal from './DeleteAccount'
 import * as ChangeHandleModal from './ChangeHandle'
 import * as WaitlistModal from './Waitlist'
@@ -87,12 +86,9 @@ export const ModalsContainer = observer(function ModalsContainer() {
   } else if (activeModal?.name === 'server-input') {
     snapPoints = ServerInputModal.snapPoints
     element = <ServerInputModal.Component {...activeModal} />
-  } else if (activeModal?.name === 'report-post') {
-    snapPoints = ReportPostModal.snapPoints
-    element = <ReportPostModal.Component {...activeModal} />
-  } else if (activeModal?.name === 'report-account') {
-    snapPoints = ReportAccountModal.snapPoints
-    element = <ReportAccountModal.Component {...activeModal} />
+  } else if (activeModal?.name === 'report') {
+    snapPoints = ReportModal.snapPoints
+    element = <ReportModal.Component {...activeModal} />
   } else if (activeModal?.name === 'create-or-edit-mute-list') {
     snapPoints = CreateOrEditMuteListModal.snapPoints
     element = <CreateOrEditMuteListModal.Component {...activeModal} />
diff --git a/src/view/com/modals/Modal.web.tsx b/src/view/com/modals/Modal.web.tsx
index 0f6247822..0e28b1618 100644
--- a/src/view/com/modals/Modal.web.tsx
+++ b/src/view/com/modals/Modal.web.tsx
@@ -10,8 +10,7 @@ import * as ConfirmModal from './Confirm'
 import * as EditProfileModal from './EditProfile'
 import * as ProfilePreviewModal from './ProfilePreview'
 import * as ServerInputModal from './ServerInput'
-import * as ReportPostModal from './report/ReportPost'
-import * as ReportAccountModal from './report/ReportAccount'
+import * as ReportModal from './report/Modal'
 import * as CreateOrEditMuteListModal from './CreateOrEditMuteList'
 import * as ListAddRemoveUserModal from './ListAddRemoveUser'
 import * as DeleteAccountModal from './DeleteAccount'
@@ -76,10 +75,8 @@ function Modal({modal}: {modal: ModalIface}) {
     element = <ProfilePreviewModal.Component {...modal} />
   } else if (modal.name === 'server-input') {
     element = <ServerInputModal.Component {...modal} />
-  } else if (modal.name === 'report-post') {
-    element = <ReportPostModal.Component {...modal} />
-  } else if (modal.name === 'report-account') {
-    element = <ReportAccountModal.Component {...modal} />
+  } else if (modal.name === 'report') {
+    element = <ReportModal.Component {...modal} />
   } else if (modal.name === 'create-or-edit-mute-list') {
     element = <CreateOrEditMuteListModal.Component {...modal} />
   } else if (modal.name === 'list-add-remove-user') {
diff --git a/src/view/com/modals/report/ReportPost.tsx b/src/view/com/modals/report/Modal.tsx
index 34ec8c2f2..f386b110d 100644
--- a/src/view/com/modals/report/ReportPost.tsx
+++ b/src/view/com/modals/report/Modal.tsx
@@ -1,10 +1,9 @@
 import React, {useState, useMemo} from 'react'
 import {Linking, StyleSheet, TouchableOpacity, View} from 'react-native'
 import {ScrollView} from 'react-native-gesture-handler'
-import {ComAtprotoModerationDefs} from '@atproto/api'
+import {AtUri} from '@atproto/api'
 import {useStores} from 'state/index'
 import {s} from 'lib/styles'
-import {RadioGroup, RadioGroupItem} from '../../util/forms/RadioGroup'
 import {Text} from '../../util/text/Text'
 import * as Toast from '../../util/Toast'
 import {ErrorMessage} from '../../util/error/ErrorMessage'
@@ -12,25 +11,43 @@ import {cleanError} from 'lib/strings/errors'
 import {usePalette} from 'lib/hooks/usePalette'
 import {SendReportButton} from './SendReportButton'
 import {InputIssueDetails} from './InputIssueDetails'
+import {ReportReasonOptions} from './ReasonOptions'
+import {CollectionId} from './types'
 
 const DMCA_LINK = 'https://bsky.app/support/copyright'
 
 export const snapPoints = [575]
 
-export function Component({
-  postUri,
-  postCid,
-}: {
-  postUri: string
-  postCid: string
-}) {
+const CollectionNames = {
+  [CollectionId.FeedGenerator]: 'Feed',
+  [CollectionId.Profile]: 'Profile',
+  [CollectionId.List]: 'List',
+  [CollectionId.Post]: 'Post',
+}
+
+type ReportComponentProps =
+  | {
+      uri: string
+      cid: string
+    }
+  | {
+      did: string
+    }
+
+export function Component(content: ReportComponentProps) {
   const store = useStores()
   const pal = usePalette('default')
   const [isProcessing, setIsProcessing] = useState(false)
-  const [showTextInput, setShowTextInput] = useState(false)
+  const [showDetailsInput, setShowDetailsInput] = useState(false)
   const [error, setError] = useState<string>()
   const [issue, setIssue] = useState<string>()
   const [details, setDetails] = useState<string>()
+  const isAccountReport = 'did' in content
+  const subjectKey = isAccountReport ? content.did : content.uri
+  const atUri = useMemo(
+    () => (!isAccountReport ? new AtUri(subjectKey) : null),
+    [isAccountReport, subjectKey],
+  )
 
   const submitReport = async () => {
     setError('')
@@ -43,12 +60,14 @@ export function Component({
         Linking.openURL(DMCA_LINK)
         return
       }
+      const $type = !isAccountReport
+        ? 'com.atproto.repo.strongRef'
+        : 'com.atproto.admin.defs#repoRef'
       await store.agent.createModerationReport({
         reasonType: issue,
         subject: {
-          $type: 'com.atproto.repo.strongRef',
-          uri: postUri,
-          cid: postCid,
+          $type,
+          ...content,
         },
         reason: details,
       })
@@ -63,13 +82,13 @@ export function Component({
   }
 
   const goBack = () => {
-    setShowTextInput(false)
+    setShowDetailsInput(false)
   }
 
   return (
-    <ScrollView testID="reportPostModal" style={[s.flex1, pal.view]}>
+    <ScrollView testID="reportModal" style={[s.flex1, pal.view]}>
       <View style={styles.container}>
-        {showTextInput ? (
+        {showDetailsInput ? (
           <InputIssueDetails
             details={details}
             setDetails={setDetails}
@@ -79,12 +98,13 @@ export function Component({
           />
         ) : (
           <SelectIssue
-            setShowTextInput={setShowTextInput}
+            setShowDetailsInput={setShowDetailsInput}
             error={error}
             issue={issue}
             setIssue={setIssue}
             submitReport={submitReport}
             isProcessing={isProcessing}
+            atUri={atUri}
           />
         )}
       </View>
@@ -92,128 +112,59 @@ export function Component({
   )
 }
 
+// If no atUri is passed, that means the reporting collection is account
+const getCollectionNameForReport = (atUri: AtUri | null) => {
+  if (!atUri) return 'Account'
+  // Generic fallback for any collection being reported
+  return CollectionNames[atUri.collection as CollectionId] || 'Content'
+}
+
 const SelectIssue = ({
   error,
-  setShowTextInput,
+  setShowDetailsInput,
   issue,
   setIssue,
   submitReport,
   isProcessing,
+  atUri,
 }: {
   error: string | undefined
-  setShowTextInput: (v: boolean) => void
+  setShowDetailsInput: (v: boolean) => void
   issue: string | undefined
   setIssue: (v: string) => void
   submitReport: () => void
   isProcessing: boolean
+  atUri: AtUri | null
 }) => {
   const pal = usePalette('default')
-  const ITEMS: RadioGroupItem[] = useMemo(
-    () => [
-      {
-        key: ComAtprotoModerationDefs.REASONSPAM,
-        label: (
-          <View>
-            <Text style={pal.text} type="md-bold">
-              Spam
-            </Text>
-            <Text style={pal.textLight}>Excessive mentions or replies</Text>
-          </View>
-        ),
-      },
-      {
-        key: ComAtprotoModerationDefs.REASONSEXUAL,
-        label: (
-          <View>
-            <Text style={pal.text} type="md-bold">
-              Unwanted Sexual Content
-            </Text>
-            <Text style={pal.textLight}>
-              Nudity or pornography not labeled as such
-            </Text>
-          </View>
-        ),
-      },
-      {
-        key: '__copyright__',
-        label: (
-          <View>
-            <Text style={pal.text} type="md-bold">
-              Copyright Violation
-            </Text>
-            <Text style={pal.textLight}>Contains copyrighted material</Text>
-          </View>
-        ),
-      },
-      {
-        key: ComAtprotoModerationDefs.REASONRUDE,
-        label: (
-          <View>
-            <Text style={pal.text} type="md-bold">
-              Anti-Social Behavior
-            </Text>
-            <Text style={pal.textLight}>
-              Harassment, trolling, or intolerance
-            </Text>
-          </View>
-        ),
-      },
-      {
-        key: ComAtprotoModerationDefs.REASONVIOLATION,
-        label: (
-          <View>
-            <Text style={pal.text} type="md-bold">
-              Illegal and Urgent
-            </Text>
-            <Text style={pal.textLight}>
-              Glaring violations of law or terms of service
-            </Text>
-          </View>
-        ),
-      },
-      {
-        key: ComAtprotoModerationDefs.REASONOTHER,
-        label: (
-          <View>
-            <Text style={pal.text} type="md-bold">
-              Other
-            </Text>
-            <Text style={pal.textLight}>
-              An issue not included in these options
-            </Text>
-          </View>
-        ),
-      },
-    ],
-    [pal],
-  )
-
+  const collectionName = getCollectionNameForReport(atUri)
   const onSelectIssue = (v: string) => setIssue(v)
   const goToDetails = () => {
     if (issue === '__copyright__') {
       Linking.openURL(DMCA_LINK)
       return
     }
-    setShowTextInput(true)
+    setShowDetailsInput(true)
   }
 
   return (
     <>
-      <Text style={[pal.text, styles.title]}>Report post</Text>
+      <Text style={[pal.text, styles.title]}>Report {collectionName}</Text>
       <Text style={[pal.textLight, styles.description]}>
-        What is the issue with this post?
+        What is the issue with this {collectionName}?
       </Text>
-      <RadioGroup
-        testID="reportPostRadios"
-        items={ITEMS}
-        onSelect={onSelectIssue}
+      <ReportReasonOptions
+        atUri={atUri}
+        selectedIssue={issue}
+        onSelectIssue={onSelectIssue}
       />
       {error ? (
         <View style={s.mt10}>
           <ErrorMessage message={error} />
         </View>
       ) : undefined}
-      {issue ? (
+      {/* If no atUri is present, the report would be for account in which case, we allow sending without specifying a reason */}
+      {issue || !atUri ? (
         <>
           <SendReportButton
             onPress={submitReport}
diff --git a/src/view/com/modals/report/ReasonOptions.tsx b/src/view/com/modals/report/ReasonOptions.tsx
new file mode 100644
index 000000000..23b49b664
--- /dev/null
+++ b/src/view/com/modals/report/ReasonOptions.tsx
@@ -0,0 +1,123 @@
+import {View} from 'react-native'
+import React, {useMemo} from 'react'
+import {AtUri, ComAtprotoModerationDefs} from '@atproto/api'
+
+import {Text} from '../../util/text/Text'
+import {UsePaletteValue, usePalette} from 'lib/hooks/usePalette'
+import {RadioGroup, RadioGroupItem} from 'view/com/util/forms/RadioGroup'
+import {CollectionId} from './types'
+
+type ReasonMap = Record<string, {title: string; description: string}>
+const CommonReasons = {
+  [ComAtprotoModerationDefs.REASONRUDE]: {
+    title: 'Anti-Social Behavior',
+    description: 'Harassment, trolling, or intolerance',
+  },
+  [ComAtprotoModerationDefs.REASONVIOLATION]: {
+    title: 'Illegal and Urgent',
+    description: 'Glaring violations of law or terms of service',
+  },
+  [ComAtprotoModerationDefs.REASONOTHER]: {
+    title: 'Other',
+    description: 'An issue not included in these options',
+  },
+}
+const CollectionToReasonsMap: Record<string, ReasonMap> = {
+  [CollectionId.Post]: {
+    [ComAtprotoModerationDefs.REASONSPAM]: {
+      title: 'Spam',
+      description: 'Excessive mentions or replies',
+    },
+    [ComAtprotoModerationDefs.REASONSEXUAL]: {
+      title: 'Unwanted Sexual Content',
+      description: 'Nudity or pornography not labeled as such',
+    },
+    __copyright__: {
+      title: 'Copyright Violation',
+      description: 'Contains copyrighted material',
+    },
+    ...CommonReasons,
+  },
+  [CollectionId.List]: {
+    ...CommonReasons,
+    [ComAtprotoModerationDefs.REASONVIOLATION]: {
+      title: 'Name or Description Violates Community Standards',
+      description: 'Terms used violate community standards',
+    },
+  },
+}
+const AccountReportReasons = {
+  [ComAtprotoModerationDefs.REASONMISLEADING]: {
+    title: 'Misleading Account',
+    description: 'Impersonation or false claims about identity or affiliation',
+  },
+  [ComAtprotoModerationDefs.REASONSPAM]: {
+    title: 'Frequently Posts Unwanted Content',
+    description: 'Spam; excessive mentions or replies',
+  },
+  [ComAtprotoModerationDefs.REASONVIOLATION]: {
+    title: 'Name or Description Violates Community Standards',
+    description: 'Terms used violate community standards',
+  },
+}
+
+const Option = ({
+  pal,
+  title,
+  description,
+}: {
+  pal: UsePaletteValue
+  description: string
+  title: string
+}) => {
+  return (
+    <View>
+      <Text style={pal.text} type="md-bold">
+        {title}
+      </Text>
+      <Text style={pal.textLight}>{description}</Text>
+    </View>
+  )
+}
+
+// This is mostly just content copy without almost any logic
+// so this may grow over time and it makes sense to split it up into its own file
+// to keep it separate from the actual reporting modal logic
+const useReportRadioOptions = (pal: UsePaletteValue, atUri: AtUri | null) =>
+  useMemo(() => {
+    let items: ReasonMap = {...CommonReasons}
+    // If no atUri is passed, that means the reporting collection is account
+    if (!atUri) {
+      items = {...AccountReportReasons}
+    }
+
+    if (atUri?.collection && CollectionToReasonsMap[atUri.collection]) {
+      items = {...CollectionToReasonsMap[atUri.collection]}
+    }
+
+    return Object.entries(items).map(([key, {title, description}]) => ({
+      key,
+      label: <Option pal={pal} title={title} description={description} />,
+    }))
+  }, [pal, atUri])
+
+export const ReportReasonOptions = ({
+  atUri,
+  selectedIssue,
+  onSelectIssue,
+}: {
+  atUri: AtUri | null
+  selectedIssue?: string
+  onSelectIssue: (key: string) => void
+}) => {
+  const pal = usePalette('default')
+  const ITEMS: RadioGroupItem[] = useReportRadioOptions(pal, atUri)
+  return (
+    <RadioGroup
+      items={ITEMS}
+      onSelect={onSelectIssue}
+      testID="reportReasonRadios"
+      initialSelection={selectedIssue}
+    />
+  )
+}
diff --git a/src/view/com/modals/report/ReportAccount.tsx b/src/view/com/modals/report/ReportAccount.tsx
deleted file mode 100644
index b53c54caa..000000000
--- a/src/view/com/modals/report/ReportAccount.tsx
+++ /dev/null
@@ -1,197 +0,0 @@
-import React, {useState, useMemo} from 'react'
-import {TouchableOpacity, StyleSheet, View} from 'react-native'
-import {ScrollView} from 'react-native-gesture-handler'
-import {ComAtprotoModerationDefs} from '@atproto/api'
-import {useStores} from 'state/index'
-import {s} from 'lib/styles'
-import {RadioGroup, RadioGroupItem} from '../../util/forms/RadioGroup'
-import {Text} from '../../util/text/Text'
-import * as Toast from '../../util/Toast'
-import {ErrorMessage} from '../../util/error/ErrorMessage'
-import {cleanError} from 'lib/strings/errors'
-import {usePalette} from 'lib/hooks/usePalette'
-import {isDesktopWeb} from 'platform/detection'
-import {SendReportButton} from './SendReportButton'
-import {InputIssueDetails} from './InputIssueDetails'
-
-export const snapPoints = [500]
-
-export function Component({did}: {did: string}) {
-  const store = useStores()
-  const pal = usePalette('default')
-  const [isProcessing, setIsProcessing] = useState(false)
-  const [error, setError] = useState<string>()
-  const [issue, setIssue] = useState<string>()
-  const onSelectIssue = (v: string) => setIssue(v)
-  const [details, setDetails] = useState<string>()
-  const [showDetailsInput, setShowDetailsInput] = useState(false)
-
-  const onPress = async () => {
-    setError('')
-    if (!issue) {
-      return
-    }
-    setIsProcessing(true)
-    try {
-      await store.agent.com.atproto.moderation.createReport({
-        reasonType: issue,
-        subject: {
-          $type: 'com.atproto.admin.defs#repoRef',
-          did,
-        },
-        reason: details,
-      })
-      Toast.show("Thank you for your report! We'll look into it promptly.")
-      store.shell.closeModal()
-      return
-    } catch (e: any) {
-      setError(cleanError(e))
-      setIsProcessing(false)
-    }
-  }
-  const goBack = () => {
-    setShowDetailsInput(false)
-  }
-  const goToDetails = () => {
-    setShowDetailsInput(true)
-  }
-
-  return (
-    <ScrollView
-      testID="reportAccountModal"
-      style={[styles.container, pal.view]}>
-      {showDetailsInput ? (
-        <InputIssueDetails
-          submitReport={onPress}
-          setDetails={setDetails}
-          details={details}
-          isProcessing={isProcessing}
-          goBack={goBack}
-        />
-      ) : (
-        <SelectIssue
-          onPress={onPress}
-          onSelectIssue={onSelectIssue}
-          error={error}
-          isProcessing={isProcessing}
-          goToDetails={goToDetails}
-        />
-      )}
-    </ScrollView>
-  )
-}
-
-const SelectIssue = ({
-  onPress,
-  onSelectIssue,
-  error,
-  isProcessing,
-  goToDetails,
-}: {
-  onPress: () => void
-  onSelectIssue: (v: string) => void
-  error: string | undefined
-  isProcessing: boolean
-  goToDetails: () => void
-}) => {
-  const pal = usePalette('default')
-  const ITEMS: RadioGroupItem[] = useMemo(
-    () => [
-      {
-        key: ComAtprotoModerationDefs.REASONMISLEADING,
-        label: (
-          <View>
-            <Text style={pal.text} type="md-bold">
-              Misleading Account
-            </Text>
-            <Text style={pal.textLight}>
-              Impersonation or false claims about identity or affiliation
-            </Text>
-          </View>
-        ),
-      },
-      {
-        key: ComAtprotoModerationDefs.REASONSPAM,
-        label: (
-          <View>
-            <Text style={pal.text} type="md-bold">
-              Frequently Posts Unwanted Content
-            </Text>
-            <Text style={pal.textLight}>
-              Spam; excessive mentions or replies
-            </Text>
-          </View>
-        ),
-      },
-      {
-        key: ComAtprotoModerationDefs.REASONVIOLATION,
-        label: (
-          <View>
-            <Text style={pal.text} type="md-bold">
-              Name or Description Violates Community Standards
-            </Text>
-            <Text style={pal.textLight}>
-              Terms used violate community standards
-            </Text>
-          </View>
-        ),
-      },
-    ],
-    [pal],
-  )
-  return (
-    <>
-      <Text type="title-xl" style={[pal.text, styles.title]}>
-        Report Account
-      </Text>
-      <Text type="xl" style={[pal.text, styles.description]}>
-        What is the issue with this account?
-      </Text>
-      <RadioGroup
-        testID="reportAccountRadios"
-        items={ITEMS}
-        onSelect={onSelectIssue}
-      />
-      <Text type="sm" style={[pal.text, styles.description, s.pt10]}>
-        For other issues, please report specific posts.
-      </Text>
-      {error ? (
-        <View style={s.mt10}>
-          <ErrorMessage message={error} />
-        </View>
-      ) : undefined}
-      <SendReportButton onPress={onPress} isProcessing={isProcessing} />
-      <TouchableOpacity
-        testID="addDetailsBtn"
-        style={styles.addDetailsBtn}
-        onPress={goToDetails}
-        accessibilityRole="button"
-        accessibilityLabel="Add details"
-        accessibilityHint="Add more details to your report">
-        <Text style={[s.f18, pal.link]}>Add details to report</Text>
-      </TouchableOpacity>
-    </>
-  )
-}
-
-const styles = StyleSheet.create({
-  container: {
-    flex: 1,
-    paddingHorizontal: isDesktopWeb ? 0 : 10,
-  },
-  title: {
-    textAlign: 'center',
-    fontWeight: 'bold',
-    marginBottom: 12,
-  },
-  description: {
-    textAlign: 'center',
-    paddingHorizontal: 22,
-    marginBottom: 10,
-  },
-  addDetailsBtn: {
-    padding: 14,
-    alignSelf: 'center',
-    marginBottom: 40,
-  },
-})
diff --git a/src/view/com/modals/report/types.ts b/src/view/com/modals/report/types.ts
new file mode 100644
index 000000000..ca947ecbd
--- /dev/null
+++ b/src/view/com/modals/report/types.ts
@@ -0,0 +1,8 @@
+// TODO: ATM, @atproto/api does not export ids but it does have these listed at @atproto/api/client/lexicons
+// once we start exporting the ids from the @atproto/ap package, replace these hardcoded ones
+export enum CollectionId {
+  FeedGenerator = 'app.bsky.feed.generator',
+  Profile = 'app.bsky.actor.profile',
+  List = 'app.bsky.graph.list',
+  Post = 'app.bsky.feed.post',
+}
diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx
index f8531d76c..dd3fb530e 100644
--- a/src/view/com/profile/ProfileHeader.tsx
+++ b/src/view/com/profile/ProfileHeader.tsx
@@ -245,7 +245,7 @@ const ProfileHeaderLoaded = observer(
     const onPressReportAccount = React.useCallback(() => {
       track('ProfileHeader:ReportAccountButtonClicked')
       store.shell.openModal({
-        name: 'report-account',
+        name: 'report',
         did: view.did,
       })
     }, [track, store, view])
diff --git a/src/view/com/util/forms/PostDropdownBtn.tsx b/src/view/com/util/forms/PostDropdownBtn.tsx
index 27a1f20d0..65050d8c0 100644
--- a/src/view/com/util/forms/PostDropdownBtn.tsx
+++ b/src/view/com/util/forms/PostDropdownBtn.tsx
@@ -102,9 +102,9 @@ export function PostDropdownBtn({
       label: 'Report post',
       onPress() {
         store.shell.openModal({
-          name: 'report-post',
-          postUri: itemUri,
-          postCid: itemCid,
+          name: 'report',
+          uri: itemUri,
+          cid: itemCid,
         })
       },
       testID: 'postDropdownReportBtn',