diff --git a/bskyweb/templates/base.html b/bskyweb/templates/base.html
index ea048a9a..b5ed329c 100644
--- a/bskyweb/templates/base.html
+++ b/bskyweb/templates/base.html
@@ -102,6 +102,7 @@
.ProseMirror-focused {
outline: 0;
}
+ textarea:focus,
input:focus {
outline: 0;
}
diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx
index 1043db20..864dcc84 100644
--- a/src/view/com/modals/Modal.tsx
+++ b/src/view/com/modals/Modal.tsx
@@ -10,13 +10,13 @@ import {usePalette} from 'lib/hooks/usePalette'
import * as ConfirmModal from './Confirm'
import * as EditProfileModal from './EditProfile'
import * as ServerInputModal from './ServerInput'
-import * as ReportPostModal from './ReportPost'
+import * as ReportPostModal from './report/ReportPost'
import * as RepostModal from './Repost'
import * as CreateOrEditMuteListModal from './CreateOrEditMuteList'
import * as ListAddRemoveUserModal from './ListAddRemoveUser'
import * as AltImageModal from './AltImage'
import * as EditImageModal from './AltImage'
-import * as ReportAccountModal from './ReportAccount'
+import * as ReportAccountModal from './report/ReportAccount'
import * as DeleteAccountModal from './DeleteAccount'
import * as ChangeHandleModal from './ChangeHandle'
import * as WaitlistModal from './Waitlist'
diff --git a/src/view/com/modals/Modal.web.tsx b/src/view/com/modals/Modal.web.tsx
index f2cf72a0..27b2641b 100644
--- a/src/view/com/modals/Modal.web.tsx
+++ b/src/view/com/modals/Modal.web.tsx
@@ -9,8 +9,8 @@ import {isMobileWeb} from 'platform/detection'
import * as ConfirmModal from './Confirm'
import * as EditProfileModal from './EditProfile'
import * as ServerInputModal from './ServerInput'
-import * as ReportPostModal from './ReportPost'
-import * as ReportAccountModal from './ReportAccount'
+import * as ReportPostModal from './report/ReportPost'
+import * as ReportAccountModal from './report/ReportAccount'
import * as CreateOrEditMuteListModal from './CreateOrEditMuteList'
import * as ListAddRemoveUserModal from './ListAddRemoveUser'
import * as DeleteAccountModal from './DeleteAccount'
diff --git a/src/view/com/modals/report/InputIssueDetails.tsx b/src/view/com/modals/report/InputIssueDetails.tsx
new file mode 100644
index 00000000..a2e5069a
--- /dev/null
+++ b/src/view/com/modals/report/InputIssueDetails.tsx
@@ -0,0 +1,93 @@
+import React from 'react'
+import {View, TouchableOpacity, StyleSheet} from 'react-native'
+import {TextInput} from '../util'
+import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import {CharProgress} from '../../composer/char-progress/CharProgress'
+import {Text} from '../../util/text/Text'
+import {usePalette} from 'lib/hooks/usePalette'
+import {s} from 'lib/styles'
+import {SendReportButton} from './SendReportButton'
+import {isDesktopWeb} from 'platform/detection'
+
+export function InputIssueDetails({
+ details,
+ setDetails,
+ goBack,
+ submitReport,
+ isProcessing,
+}: {
+ details: string | undefined
+ setDetails: (v: string) => void
+ goBack: () => void
+ submitReport: () => void
+ isProcessing: boolean
+}) {
+ const pal = usePalette('default')
+
+ return (
+
+
+
+ Back
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+const styles = StyleSheet.create({
+ detailsContainer: {
+ marginTop: isDesktopWeb ? 0 : 12,
+ },
+ backBtn: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ detailsInputContainer: {
+ borderRadius: 8,
+ },
+ detailsInput: {
+ paddingHorizontal: 12,
+ paddingTop: 12,
+ paddingBottom: 12,
+ borderRadius: 8,
+ minHeight: 100,
+ fontSize: 16,
+ },
+ detailsInputBottomBar: {
+ alignSelf: 'flex-end',
+ },
+ charCounter: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ paddingRight: 10,
+ paddingBottom: 8,
+ },
+})
diff --git a/src/view/com/modals/ReportAccount.tsx b/src/view/com/modals/report/ReportAccount.tsx
similarity index 58%
rename from src/view/com/modals/ReportAccount.tsx
rename to src/view/com/modals/report/ReportAccount.tsx
index b59a1b69..3ea221a8 100644
--- a/src/view/com/modals/ReportAccount.tsx
+++ b/src/view/com/modals/report/ReportAccount.tsx
@@ -1,32 +1,97 @@
import React, {useState, useMemo} from 'react'
-import {
- ActivityIndicator,
- StyleSheet,
- TouchableOpacity,
- View,
-} from 'react-native'
+import {TouchableOpacity, StyleSheet, View} from 'react-native'
import {ComAtprotoModerationDefs} from '@atproto/api'
-import LinearGradient from 'react-native-linear-gradient'
import {useStores} from 'state/index'
-import {s, colors, gradients} 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 {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 = ['50%']
+export const snapPoints = [400]
export function Component({did}: {did: string}) {
const store = useStores()
const pal = usePalette('default')
- const [isProcessing, setIsProcessing] = useState(false)
- const [error, setError] = useState('')
- const [issue, setIssue] = useState('')
+ const [isProcessing, setIsProcessing] = useState(false)
+ const [error, setError] = useState()
+ const [issue, setIssue] = useState()
const onSelectIssue = (v: string) => setIssue(v)
+ const [details, setDetails] = useState()
+ 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 (
+
+ {showDetailsInput ? (
+
+ ) : (
+
+ )}
+
+ )
+}
+
+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(
() => [
{
@@ -58,31 +123,8 @@ export function Component({did}: {did: string}) {
],
[pal],
)
-
- 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,
- },
- })
- 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)
- }
- }
return (
-
+ <>
Report account
@@ -102,28 +144,17 @@ export function Component({did}: {did: string}) {
) : undefined}
- {isProcessing ? (
-
-
-
- ) : issue ? (
-
-
- Send Report
-
-
- ) : undefined}
-
+
+
+ Add details to report
+
+ >
)
}
@@ -142,13 +173,8 @@ const styles = StyleSheet.create({
paddingHorizontal: 22,
marginBottom: 10,
},
- btn: {
- flexDirection: 'row',
- alignItems: 'center',
- justifyContent: 'center',
- width: '100%',
- borderRadius: 32,
+ addDetailsBtn: {
padding: 14,
- backgroundColor: colors.gray1,
+ alignSelf: 'center',
},
})
diff --git a/src/view/com/modals/ReportPost.tsx b/src/view/com/modals/report/ReportPost.tsx
similarity index 57%
rename from src/view/com/modals/ReportPost.tsx
rename to src/view/com/modals/report/ReportPost.tsx
index ec75dc4c..fe2a5bca 100644
--- a/src/view/com/modals/ReportPost.tsx
+++ b/src/view/com/modals/report/ReportPost.tsx
@@ -1,25 +1,20 @@
import React, {useState, useMemo} from 'react'
-import {
- ActivityIndicator,
- Linking,
- StyleSheet,
- TouchableOpacity,
- View,
-} from 'react-native'
+import {Linking, StyleSheet, TouchableOpacity, View} from 'react-native'
import {ComAtprotoModerationDefs} from '@atproto/api'
-import LinearGradient from 'react-native-linear-gradient'
import {useStores} from 'state/index'
-import {s, colors, gradients} 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 {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 {SendReportButton} from './SendReportButton'
+import {InputIssueDetails} from './InputIssueDetails'
const DMCA_LINK = 'https://bsky.app/support/copyright'
-export const snapPoints = [550]
+export const snapPoints = [575]
export function Component({
postUri,
@@ -30,11 +25,86 @@ export function Component({
}) {
const store = useStores()
const pal = usePalette('default')
- const [isProcessing, setIsProcessing] = useState(false)
- const [error, setError] = useState('')
- const [issue, setIssue] = useState('')
- const onSelectIssue = (v: string) => setIssue(v)
+ const [isProcessing, setIsProcessing] = useState(false)
+ const [showTextInput, setShowTextInput] = useState(false)
+ const [error, setError] = useState()
+ const [issue, setIssue] = useState()
+ const [details, setDetails] = useState()
+ const submitReport = async () => {
+ setError('')
+ if (!issue) {
+ return
+ }
+ setIsProcessing(true)
+ try {
+ if (issue === '__copyright__') {
+ Linking.openURL(DMCA_LINK)
+ return
+ }
+ await store.agent.createModerationReport({
+ reasonType: issue,
+ subject: {
+ $type: 'com.atproto.repo.strongRef',
+ uri: postUri,
+ cid: postCid,
+ },
+ 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 = () => {
+ setShowTextInput(false)
+ }
+
+ return (
+
+ {showTextInput ? (
+
+ ) : (
+
+ )}
+
+ )
+}
+
+const SelectIssue = ({
+ error,
+ setShowTextInput,
+ issue,
+ setIssue,
+ submitReport,
+ isProcessing,
+}: {
+ error: string | undefined
+ setShowTextInput: (v: boolean) => void
+ issue: string | undefined
+ setIssue: (v: string) => void
+ submitReport: () => void
+ isProcessing: boolean
+}) => {
+ const pal = usePalette('default')
const ITEMS: RadioGroupItem[] = useMemo(
() => [
{
@@ -115,35 +185,17 @@ export function Component({
[pal],
)
- const onPress = async () => {
- setError('')
- if (!issue) {
+ const onSelectIssue = (v: string) => setIssue(v)
+ const goToDetails = () => {
+ if (issue === '__copyright__') {
+ Linking.openURL(DMCA_LINK)
return
}
- setIsProcessing(true)
- try {
- if (issue === '__copyright__') {
- Linking.openURL(DMCA_LINK)
- } else {
- await store.agent.createModerationReport({
- reasonType: issue,
- subject: {
- $type: 'com.atproto.repo.strongRef',
- uri: postUri,
- cid: postCid,
- },
- })
- 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)
- }
+ setShowTextInput(true)
}
+
return (
-
+ <>
Report post
What is the issue with this post?
@@ -158,28 +210,24 @@ export function Component({
) : undefined}
- {isProcessing ? (
-
-
-
- ) : issue ? (
-
-
- Send Report
-
-
+ {issue ? (
+ <>
+
+
+ Add details to report
+
+ >
) : undefined}
-
+ >
)
}
@@ -196,13 +244,8 @@ const styles = StyleSheet.create({
paddingHorizontal: 22,
marginBottom: 10,
},
- btn: {
- flexDirection: 'row',
- alignItems: 'center',
- justifyContent: 'center',
- width: '100%',
- borderRadius: 32,
+ addDetailsBtn: {
padding: 14,
- backgroundColor: colors.gray1,
+ alignSelf: 'center',
},
})
diff --git a/src/view/com/modals/report/SendReportButton.tsx b/src/view/com/modals/report/SendReportButton.tsx
new file mode 100644
index 00000000..82fb65f2
--- /dev/null
+++ b/src/view/com/modals/report/SendReportButton.tsx
@@ -0,0 +1,57 @@
+import React from 'react'
+import LinearGradient from 'react-native-linear-gradient'
+import {
+ ActivityIndicator,
+ StyleSheet,
+ TouchableOpacity,
+ View,
+} from 'react-native'
+import {Text} from '../../util/text/Text'
+import {s, gradients, colors} from 'lib/styles'
+
+export function SendReportButton({
+ onPress,
+ isProcessing,
+}: {
+ onPress: () => void
+ isProcessing: boolean
+}) {
+ // loading state
+ // =
+ if (isProcessing) {
+ return (
+
+
+
+ )
+ }
+ return (
+
+
+ Send Report
+
+
+ )
+}
+
+const styles = StyleSheet.create({
+ btn: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center',
+ width: '100%',
+ borderRadius: 32,
+ padding: 14,
+ backgroundColor: colors.gray1,
+ },
+})
diff --git a/web/index.html b/web/index.html
index f518665c..1bfa8f68 100644
--- a/web/index.html
+++ b/web/index.html
@@ -106,6 +106,7 @@
.ProseMirror-focused {
outline: 0;
}
+ textarea:focus,
input:focus {
outline: 0;
}