diff --git a/src/state/models/shell-ui.ts b/src/state/models/shell-ui.ts
index fa2e78d5..d1f45854 100644
--- a/src/state/models/shell-ui.ts
+++ b/src/state/models/shell-ui.ts
@@ -51,6 +51,14 @@ export class ServerInputModal {
}
}
+export class ReportPostModal {
+ name = 'report-post'
+
+ constructor(public postUrl: string) {
+ makeAutoObservable(this)
+ }
+}
+
interface LightboxModel {
canSwipeLeft: boolean
canSwipeRight: boolean
@@ -127,6 +135,7 @@ export class ShellUiModel {
| EditProfileModal
| CreateSceneModal
| ServerInputModal
+ | ReportPostModal
| undefined
isLightboxActive = false
activeLightbox:
@@ -154,7 +163,8 @@ export class ShellUiModel {
| ConfirmModal
| EditProfileModal
| CreateSceneModal
- | ServerInputModal,
+ | ServerInputModal
+ | ReportPostModal,
) {
this.isModalActive = true
this.activeModal = modal
diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx
index 610d30eb..e1d2eaca 100644
--- a/src/view/com/modals/Modal.tsx
+++ b/src/view/com/modals/Modal.tsx
@@ -12,6 +12,7 @@ import * as EditProfileModal from './EditProfile'
import * as CreateSceneModal from './CreateScene'
import * as InviteToSceneModal from './InviteToScene'
import * as ServerInputModal from './ServerInput'
+import * as ReportPostModal from './ReportPost'
const CLOSED_SNAPPOINTS = ['10%']
@@ -70,6 +71,13 @@ export const Modal = observer(function Modal() {
{...(store.shell.activeModal as models.ServerInputModal)}
/>
)
+ } else if (store.shell.activeModal?.name === 'report-post') {
+ snapPoints = ReportPostModal.snapPoints
+ element = (
+
+ )
} else {
element =
}
diff --git a/src/view/com/modals/ReportPost.tsx b/src/view/com/modals/ReportPost.tsx
new file mode 100644
index 00000000..5f5a4180
--- /dev/null
+++ b/src/view/com/modals/ReportPost.tsx
@@ -0,0 +1,94 @@
+import React, {useState} from 'react'
+import {
+ ActivityIndicator,
+ StyleSheet,
+ Text,
+ TouchableOpacity,
+ View,
+} from 'react-native'
+import LinearGradient from 'react-native-linear-gradient'
+import {useStores} from '../../../state'
+import {s, colors, gradients} from '../../lib/styles'
+import {RadioGroup, RadioGroupItem} from '../util/forms/RadioGroup'
+import {ErrorMessage} from '../util/ErrorMessage'
+
+const ITEMS: RadioGroupItem[] = [
+ {key: 'spam', label: 'Spam or excessive repeat posts'},
+ {key: 'abuse', label: 'Abusive, rude, or hateful'},
+ {key: 'copyright', label: 'Contains copyrighted material'},
+ {key: 'illegal', label: 'Contains illegal content'},
+]
+
+export const snapPoints = ['50%']
+
+export function Component({postUrl}: {postUrl: string}) {
+ const store = useStores()
+ const [isProcessing, setIsProcessing] = useState(false)
+ const [error, setError] = useState('')
+ const [issue, setIssue] = useState('')
+ const onSelectIssue = (v: string) => setIssue(v)
+ const onPress = async () => {
+ setError('')
+ setIsProcessing(true)
+ try {
+ // TODO
+ store.shell.closeModal()
+ return
+ } catch (e: any) {
+ setError(e.toString())
+ setIsProcessing(false)
+ }
+ }
+ return (
+
+ Report post
+ What is the issue with this post?
+
+ {error ? (
+
+
+
+ ) : undefined}
+ {isProcessing ? (
+
+
+
+ ) : issue ? (
+
+
+ Send Report
+
+
+ ) : undefined}
+
+ )
+}
+
+const styles = StyleSheet.create({
+ title: {
+ textAlign: 'center',
+ fontWeight: 'bold',
+ fontSize: 24,
+ marginBottom: 12,
+ },
+ description: {
+ textAlign: 'center',
+ fontSize: 17,
+ paddingHorizontal: 22,
+ color: colors.gray5,
+ marginBottom: 10,
+ },
+ btn: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center',
+ width: '100%',
+ borderRadius: 32,
+ padding: 14,
+ backgroundColor: colors.gray1,
+ },
+})
diff --git a/src/view/com/util/DropdownBtn.tsx b/src/view/com/util/DropdownBtn.tsx
index d2b82c91..0ca7e2cf 100644
--- a/src/view/com/util/DropdownBtn.tsx
+++ b/src/view/com/util/DropdownBtn.tsx
@@ -15,7 +15,7 @@ import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {colors} from '../../lib/styles'
import {toShareUrl} from '../../../lib/strings'
import {useStores} from '../../../state'
-import {ConfirmModal} from '../../../state/models/shell-ui'
+import {ReportPostModal, ConfirmModal} from '../../../state/models/shell-ui'
import {TABS_ENABLED} from '../../../build-flags'
const HITSLOP = {left: 10, top: 10, right: 10, bottom: 10}
@@ -116,6 +116,13 @@ export function PostDropdownBtn({
Share.share({url: toShareUrl(itemHref)})
},
},
+ {
+ icon: 'circle-exclamation',
+ label: 'Report post',
+ onPress() {
+ store.shell.openModal(new ReportPostModal(itemHref))
+ },
+ },
isAuthor
? {
icon: ['far', 'trash-can'],
diff --git a/src/view/com/util/forms/RadioButton.tsx b/src/view/com/util/forms/RadioButton.tsx
new file mode 100644
index 00000000..b311a426
--- /dev/null
+++ b/src/view/com/util/forms/RadioButton.tsx
@@ -0,0 +1,54 @@
+import React from 'react'
+import {StyleSheet, Text, TouchableOpacity, View} from 'react-native'
+import {colors} from '../../../lib/styles'
+
+export function RadioButton({
+ label,
+ isSelected,
+ onPress,
+}: {
+ label: string
+ isSelected: boolean
+ onPress: () => void
+}) {
+ return (
+
+
+ {isSelected ? : undefined}
+
+ {label}
+
+ )
+}
+
+const styles = StyleSheet.create({
+ outer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginBottom: 5,
+ borderRadius: 8,
+ borderWidth: 1,
+ borderColor: colors.gray2,
+ paddingHorizontal: 10,
+ paddingVertical: 8,
+ },
+ circle: {
+ width: 30,
+ height: 30,
+ borderRadius: 15,
+ padding: 4,
+ borderWidth: 1,
+ borderColor: colors.gray3,
+ marginRight: 10,
+ },
+ circleFill: {
+ width: 20,
+ height: 20,
+ borderRadius: 10,
+ backgroundColor: colors.blue3,
+ },
+ label: {
+ flex: 1,
+ fontSize: 17,
+ },
+})
diff --git a/src/view/com/util/forms/RadioGroup.tsx b/src/view/com/util/forms/RadioGroup.tsx
new file mode 100644
index 00000000..6684cde5
--- /dev/null
+++ b/src/view/com/util/forms/RadioGroup.tsx
@@ -0,0 +1,34 @@
+import React, {useState} from 'react'
+import {View} from 'react-native'
+import {RadioButton} from './RadioButton'
+
+export interface RadioGroupItem {
+ label: string
+ key: string
+}
+
+export function RadioGroup({
+ items,
+ onSelect,
+}: {
+ items: RadioGroupItem[]
+ onSelect: (key: string) => void
+}) {
+ const [selection, setSelection] = useState('')
+ const onSelectInner = (key: string) => {
+ setSelection(key)
+ onSelect(key)
+ }
+ return (
+
+ {items.map(item => (
+ onSelectInner(item.key)}
+ />
+ ))}
+
+ )
+}
diff --git a/src/view/index.ts b/src/view/index.ts
index 8f119cd1..26695e5c 100644
--- a/src/view/index.ts
+++ b/src/view/index.ts
@@ -21,6 +21,7 @@ import {faBookmark as farBookmark} from '@fortawesome/free-regular-svg-icons/faB
import {faCamera} from '@fortawesome/free-solid-svg-icons/faCamera'
import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck'
import {faCircleCheck} from '@fortawesome/free-regular-svg-icons/faCircleCheck'
+import {faCircleExclamation} from '@fortawesome/free-solid-svg-icons/faCircleExclamation'
import {faCircleUser} from '@fortawesome/free-regular-svg-icons/faCircleUser'
import {faClone} from '@fortawesome/free-solid-svg-icons/faClone'
import {faClone as farClone} from '@fortawesome/free-regular-svg-icons/faClone'
@@ -86,6 +87,7 @@ export function setup() {
faCamera,
faCheck,
faCircleCheck,
+ faCircleExclamation,
faCircleUser,
faClone,
farClone,