From 0be14a1b46fc55c482b5e2171e94615a73114fd2 Mon Sep 17 00:00:00 2001 From: Ansh Date: Wed, 7 Jun 2023 09:11:04 -0700 Subject: [PATCH] [APP-680] Allow users to add details when reporting (#854) * allow user to add text when reporting post * add DMCA override * increase modal size * fix dark mode text color * re-organize components * add details option when reporting account * hard-code modal size so it works on smaller devices * fix modal on web * Remove outline from textarea focus * Tweak some styles * Fix lint --------- Co-authored-by: Paul Frazee --- bskyweb/templates/base.html | 1 + src/view/com/modals/Modal.tsx | 4 +- src/view/com/modals/Modal.web.tsx | 4 +- .../com/modals/report/InputIssueDetails.tsx | 93 +++++++++ .../com/modals/{ => report}/ReportAccount.tsx | 164 +++++++++------- .../com/modals/{ => report}/ReportPost.tsx | 185 +++++++++++------- .../com/modals/report/SendReportButton.tsx | 57 ++++++ web/index.html | 1 + 8 files changed, 365 insertions(+), 144 deletions(-) create mode 100644 src/view/com/modals/report/InputIssueDetails.tsx rename src/view/com/modals/{ => report}/ReportAccount.tsx (58%) rename src/view/com/modals/{ => report}/ReportPost.tsx (57%) create mode 100644 src/view/com/modals/report/SendReportButton.tsx 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; }