diff options
-rw-r--r-- | jest/jestSetup.js | 2 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | src/components/ReportDialog/SelectLabelerView.tsx | 115 | ||||
-rw-r--r-- | src/components/ReportDialog/SelectReportOptionView.tsx | 18 | ||||
-rw-r--r-- | src/components/ReportDialog/SubmitView.tsx | 8 | ||||
-rw-r--r-- | src/components/ReportDialog/index.tsx | 54 | ||||
-rw-r--r-- | src/state/queries/labeler.ts | 8 | ||||
-rw-r--r-- | yarn.lock | 60 |
8 files changed, 210 insertions, 57 deletions
diff --git a/jest/jestSetup.js b/jest/jestSetup.js index d8cee9bfd..e690e813a 100644 --- a/jest/jestSetup.js +++ b/jest/jestSetup.js @@ -88,3 +88,5 @@ jest.mock('sentry-expo', () => ({ ReactNavigationInstrumentation: jest.fn(), }, })) + +jest.mock('crypto', () => ({})) diff --git a/package.json b/package.json index 30df66950..b526825fb 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "update-extensions": "scripts/updateExtensions.sh" }, "dependencies": { - "@atproto/api": "^0.11.2", + "@atproto/api": "^0.12.0", "@bam.tech/react-native-image-resizer": "^3.0.4", "@braintree/sanitize-url": "^6.0.2", "@emoji-mart/react": "^1.1.1", diff --git a/src/components/ReportDialog/SelectLabelerView.tsx b/src/components/ReportDialog/SelectLabelerView.tsx new file mode 100644 index 000000000..300114fc4 --- /dev/null +++ b/src/components/ReportDialog/SelectLabelerView.tsx @@ -0,0 +1,115 @@ +import React from 'react' +import {View} from 'react-native' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {AppBskyLabelerDefs} from '@atproto/api' + +export {useDialogControl as useReportDialogControl} from '#/components/Dialog' + +import {atoms as a, useTheme} from '#/alf' +import {Text} from '#/components/Typography' +import {Button, useButtonContext} from '#/components/Button' +import {Divider} from '#/components/Divider' +import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron' + +import {ReportDialogProps} from './types' + +export function SelectLabelerView({ + ...props +}: ReportDialogProps & { + labelers: AppBskyLabelerDefs.LabelerViewDetailed[] + onSelectLabeler: (v: string) => void +}) { + const t = useTheme() + const {_} = useLingui() + + return ( + <View style={[a.gap_lg]}> + <View style={[a.justify_center, a.gap_sm]}> + <Text style={[a.text_2xl, a.font_bold]}> + <Trans>Select moderation service</Trans> + </Text> + <Text style={[a.text_md, t.atoms.text_contrast_medium]}> + <Trans>Who do you want to send this report to?</Trans> + </Text> + </View> + + <Divider /> + + <View style={[a.gap_sm, {marginHorizontal: a.p_md.padding * -1}]}> + {props.labelers.map(labeler => { + return ( + <Button + key={labeler.creator.did} + label={_(msg`Send report to ${labeler.creator.displayName}`)} + onPress={() => props.onSelectLabeler(labeler.creator.did)}> + <LabelerButton + title={labeler.creator.displayName || labeler.creator.handle} + description={labeler.creator.description || ''} + /> + </Button> + ) + })} + </View> + </View> + ) +} + +function LabelerButton({ + title, + description, +}: { + title: string + description: string +}) { + const t = useTheme() + const {hovered, pressed} = useButtonContext() + const interacted = hovered || pressed + + const styles = React.useMemo(() => { + return { + interacted: { + backgroundColor: t.palette.contrast_50, + }, + } + }, [t]) + + return ( + <View + style={[ + a.w_full, + a.flex_row, + a.align_center, + a.justify_between, + a.p_md, + a.rounded_md, + {paddingRight: 70}, + interacted && styles.interacted, + ]}> + <View style={[a.flex_1, a.gap_xs]}> + <Text style={[a.text_md, a.font_bold, t.atoms.text_contrast_medium]}> + {title} + </Text> + <Text style={[a.leading_tight, {maxWidth: 400}]} numberOfLines={3}> + {description} + </Text> + </View> + + <View + style={[ + a.absolute, + a.inset_0, + a.justify_center, + a.pr_md, + {left: 'auto'}, + ]}> + <ChevronRight + size="md" + fill={ + hovered ? t.palette.primary_500 : t.atoms.text_contrast_low.color + } + /> + </View> + </View> + ) +} diff --git a/src/components/ReportDialog/SelectReportOptionView.tsx b/src/components/ReportDialog/SelectReportOptionView.tsx index 8ae0b52ec..037a62fce 100644 --- a/src/components/ReportDialog/SelectReportOptionView.tsx +++ b/src/components/ReportDialog/SelectReportOptionView.tsx @@ -18,7 +18,10 @@ import { useButtonContext, } from '#/components/Button' import {Divider} from '#/components/Divider' -import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron' +import { + ChevronRight_Stroke2_Corner0_Rounded as ChevronRight, + ChevronLeft_Stroke2_Corner0_Rounded as ChevronLeft, +} from '#/components/icons/Chevron' import {SquareArrowTopRight_Stroke2_Corner0_Rounded as SquareArrowTopRight} from '#/components/icons/SquareArrowTopRight' import {ReportDialogProps} from './types' @@ -28,6 +31,7 @@ export function SelectReportOptionView({ }: ReportDialogProps & { labelers: AppBskyLabelerDefs.LabelerViewDetailed[] onSelectReportOption: (reportOption: ReportOption) => void + goBack: () => void }) { const t = useTheme() const {_} = useLingui() @@ -60,6 +64,18 @@ export function SelectReportOptionView({ return ( <View style={[a.gap_lg]}> + {props.labelers?.length > 1 ? ( + <Button + size="small" + variant="solid" + color="secondary" + shape="round" + label={_(msg`Go back to previous step`)} + onPress={props.goBack}> + <ButtonIcon icon={ChevronLeft} /> + </Button> + ) : null} + <View style={[a.justify_center, a.gap_sm]}> <Text style={[a.text_2xl, a.font_bold]}>{i18n.title}</Text> <Text style={[a.text_md, t.atoms.text_contrast_medium]}> diff --git a/src/components/ReportDialog/SubmitView.tsx b/src/components/ReportDialog/SubmitView.tsx index 99af64a2a..d47211c81 100644 --- a/src/components/ReportDialog/SubmitView.tsx +++ b/src/components/ReportDialog/SubmitView.tsx @@ -24,11 +24,13 @@ import {getAgent} from '#/state/session' export function SubmitView({ params, labelers, + selectedLabeler, selectedReportOption, goBack, onSubmitComplete, }: ReportDialogProps & { labelers: AppBskyLabelerDefs.LabelerViewDetailed[] + selectedLabeler: string selectedReportOption: ReportOption goBack: () => void onSubmitComplete: () => void @@ -37,9 +39,9 @@ export function SubmitView({ const {_} = useLingui() const [details, setDetails] = React.useState<string>('') const [submitting, setSubmitting] = React.useState<boolean>(false) - const [selectedServices, setSelectedServices] = React.useState<string[]>( - labelers?.map(labeler => labeler.creator.did) || [], - ) + const [selectedServices, setSelectedServices] = React.useState<string[]>([ + selectedLabeler, + ]) const [error, setError] = React.useState('') const submit = React.useCallback(async () => { diff --git a/src/components/ReportDialog/index.tsx b/src/components/ReportDialog/index.tsx index b35727c7d..f01ff3f3b 100644 --- a/src/components/ReportDialog/index.tsx +++ b/src/components/ReportDialog/index.tsx @@ -12,9 +12,11 @@ import * as Dialog from '#/components/Dialog' import {Text} from '#/components/Typography' import {ReportDialogProps} from './types' +import {SelectLabelerView} from './SelectLabelerView' import {SelectReportOptionView} from './SelectReportOptionView' import {SubmitView} from './SubmitView' import {useDelayedLoading} from '#/components/hooks/useDelayedLoading' +import {AppBskyLabelerDefs} from '@atproto/api' export function ReportDialog(props: ReportDialogProps) { return ( @@ -33,9 +35,6 @@ function ReportDialogInner(props: ReportDialogProps) { error, } = useMyLabelersQuery() const isLoading = useDelayedLoading(500, isLabelerLoading) - const [selectedReportOption, setSelectedReportOption] = React.useState< - ReportOption | undefined - >() return ( <Dialog.ScrollableInner label="Report Dialog"> @@ -51,23 +50,46 @@ function ReportDialogInner(props: ReportDialogProps) { <Trans>Something went wrong, please try again.</Trans> </Text> </View> - ) : selectedReportOption ? ( - <SubmitView - {...props} - labelers={labelers} - selectedReportOption={selectedReportOption} - goBack={() => setSelectedReportOption(undefined)} - onSubmitComplete={() => props.control.close()} - /> ) : ( - <SelectReportOptionView - {...props} - labelers={labelers} - onSelectReportOption={setSelectedReportOption} - /> + <ReportDialogLoaded labelers={labelers} {...props} /> )} <Dialog.Close /> </Dialog.ScrollableInner> ) } + +function ReportDialogLoaded( + props: ReportDialogProps & { + labelers: AppBskyLabelerDefs.LabelerViewDetailed[] + }, +) { + const [selectedLabeler, setSelectedLabeler] = React.useState< + string | undefined + >(props.labelers.length === 1 ? props.labelers[0].creator.did : undefined) + const [selectedReportOption, setSelectedReportOption] = React.useState< + ReportOption | undefined + >() + + if (selectedReportOption && selectedLabeler) { + return ( + <SubmitView + {...props} + selectedLabeler={selectedLabeler} + selectedReportOption={selectedReportOption} + goBack={() => setSelectedReportOption(undefined)} + onSubmitComplete={() => props.control.close()} + /> + ) + } + if (selectedLabeler) { + return ( + <SelectReportOptionView + {...props} + goBack={() => setSelectedLabeler(undefined)} + onSelectReportOption={setSelectedReportOption} + /> + ) + } + return <SelectLabelerView {...props} onSelectLabeler={setSelectedLabeler} /> +} diff --git a/src/state/queries/labeler.ts b/src/state/queries/labeler.ts index c405a6b57..b2f93c4a4 100644 --- a/src/state/queries/labeler.ts +++ b/src/state/queries/labeler.ts @@ -4,7 +4,7 @@ import {AppBskyLabelerDefs} from '@atproto/api' import {getAgent} from '#/state/session' import {preferencesQueryKey} from '#/state/queries/preferences' -import {STALE, PUBLIC_BSKY_AGENT} from '#/state/queries' +import {STALE} from '#/state/queries' export const labelerInfoQueryKey = (did: string) => ['labeler-info', did] export const labelersInfoQueryKey = (dids: string[]) => [ @@ -27,7 +27,7 @@ export function useLabelerInfoQuery({ enabled: !!did && enabled !== false, queryKey: labelerInfoQueryKey(did as string), queryFn: async () => { - const res = await PUBLIC_BSKY_AGENT.app.bsky.labeler.getServices({ + const res = await getAgent().app.bsky.labeler.getServices({ dids: [did as string], detailed: true, }) @@ -41,7 +41,7 @@ export function useLabelersInfoQuery({dids}: {dids: string[]}) { enabled: !!dids.length, queryKey: labelersInfoQueryKey(dids), queryFn: async () => { - const res = await PUBLIC_BSKY_AGENT.app.bsky.labeler.getServices({dids}) + const res = await getAgent().app.bsky.labeler.getServices({dids}) return res.data.views as AppBskyLabelerDefs.LabelerView[] }, }) @@ -54,7 +54,7 @@ export function useLabelersDetailedInfoQuery({dids}: {dids: string[]}) { gcTime: 1000 * 60 * 60 * 6, // 6 hours staleTime: STALE.MINUTES.ONE, queryFn: async () => { - const res = await PUBLIC_BSKY_AGENT.app.bsky.labeler.getServices({ + const res = await getAgent().app.bsky.labeler.getServices({ dids, detailed: true, }) diff --git a/yarn.lock b/yarn.lock index 83dee81cd..266734cdc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -34,19 +34,17 @@ jsonpointer "^5.0.0" leven "^3.1.0" -"@atproto/api@^0.11.2": - version "0.11.2" - resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.11.2.tgz#33432cdaeff674d8b21a28601542884bbe1473b2" - integrity sha512-VYpavKaEfuXhce9mP/+boZ7BNglnKSReFCRNXhFpa1KnAvWFHGsf+2jP69skEEnZ1XY4/lQhXdDzZdUIqXn2aA== - dependencies: - "@atproto/common-web" "^0.2.4" - "@atproto/lexicon" "^0.3.3" - "@atproto/syntax" "^0.2.1" - "@atproto/xrpc" "^0.4.3" +"@atproto/api@^0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.12.0.tgz#69e52f8761dc7d76c675fa7284bd49240bb0df64" + integrity sha512-nSWiad1Z6IC/oVFSVxD5gZLhkD+J4EW2CFqAqIhklJNc0cjFKdmf8D56Pac6Ktm1sJoM6TVZ8GEeuEG6bJS/aQ== + dependencies: + "@atproto/common-web" "^0.3.0" + "@atproto/lexicon" "^0.4.0" + "@atproto/syntax" "^0.3.0" + "@atproto/xrpc" "^0.5.0" multiformats "^9.9.0" tlds "^1.234.0" - typed-emitter "^2.1.0" - zod "^3.21.4" "@atproto/api@^0.9.5": version "0.9.5" @@ -144,10 +142,10 @@ uint8arrays "3.0.0" zod "^3.21.4" -"@atproto/common-web@^0.2.4": - version "0.2.4" - resolved "https://registry.yarnpkg.com/@atproto/common-web/-/common-web-0.2.4.tgz#a77e0a7f8f025115b3db4f7c530b934b10ab3d82" - integrity sha512-6+DOhQcTklFmeiSkZRx6iFeqi4OFtGl4yEDGATk00q4tEcPoPvyOBtYHN6+G9lrfJIfx5RfmggamvXlJv1PxxA== +"@atproto/common-web@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@atproto/common-web/-/common-web-0.3.0.tgz#36da8c2c31d8cf8a140c3c8f03223319bf4430bb" + integrity sha512-67VnV6JJyX+ZWyjV7xFQMypAgDmjVaR9ZCuU/QW+mqlqI7fex2uL4Fv+7/jHadgzhuJHVd6OHOvNn0wR5WZYtA== dependencies: graphemer "^1.4.0" multiformats "^9.9.0" @@ -255,13 +253,13 @@ multiformats "^9.9.0" zod "^3.21.4" -"@atproto/lexicon@^0.3.3": - version "0.3.3" - resolved "https://registry.yarnpkg.com/@atproto/lexicon/-/lexicon-0.3.3.tgz#e242bf8b024661b3312a8da5b84fe2cd627a6ab1" - integrity sha512-6FOjdc3V05JKrtkhjfhHMS7f/4hMJOeHNtoE3Na7iFMpzBz0Lw5sw8kIFKY8pc8IG79qGcFgELyHLsljZYX+5A== +"@atproto/lexicon@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@atproto/lexicon/-/lexicon-0.4.0.tgz#63e8829945d80c25524882caa8ed27b1151cc576" + integrity sha512-RvCBKdSI4M8qWm5uTNz1z3R2yIvIhmOsMuleOj8YR6BwRD+QbtUBy3l+xQ7iXf4M5fdfJFxaUNa6Ty0iRwdKqQ== dependencies: - "@atproto/common-web" "^0.2.4" - "@atproto/syntax" "^0.2.1" + "@atproto/common-web" "^0.3.0" + "@atproto/syntax" "^0.3.0" iso-datestring-validator "^2.2.2" multiformats "^9.9.0" zod "^3.21.4" @@ -361,12 +359,10 @@ dependencies: "@atproto/common-web" "^0.2.3" -"@atproto/syntax@^0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@atproto/syntax/-/syntax-0.2.1.tgz#3abcb050c122e7ce80fc25b49cd7d86c63dc6c2e" - integrity sha512-ImOuiICtB5h78j90hAYOfTYzr5q5Wut0irNdELiogA3i74a8EXThe+j6Tj8snanYggrShbu5c6BDc1tVj477Yw== - dependencies: - "@atproto/common-web" "^0.2.4" +"@atproto/syntax@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@atproto/syntax/-/syntax-0.3.0.tgz#fafa2dbea9add37253005cb663e7373e05e618b3" + integrity sha512-Weq0ZBxffGHDXHl9U7BQc2BFJi/e23AL+k+i5+D9hUq/bzT4yjGsrCejkjq0xt82xXDjmhhvQSZ0LqxyZ5woxA== "@atproto/xrpc-server@^0.4.2": version "0.4.2" @@ -393,12 +389,12 @@ "@atproto/lexicon" "^0.3.1" zod "^3.21.4" -"@atproto/xrpc@^0.4.3": - version "0.4.3" - resolved "https://registry.yarnpkg.com/@atproto/xrpc/-/xrpc-0.4.3.tgz#19d85cb7f343369363e664917945dec1f89fde97" - integrity sha512-0rn3abHORG0T93mci8WW97Cpg2ClU2aCtTq5rxdCPRsl9P4tyP+8F4snbkrIaMbVO05Rd9D9gFwtWs5Z473pCQ== +"@atproto/xrpc@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@atproto/xrpc/-/xrpc-0.5.0.tgz#dacbfd8f7b13f0ab5bd56f8fdd4b460e132a6032" + integrity sha512-swu+wyOLvYW4l3n+VAuJbHcPcES+tin2Lsrp8Bw5aIXIICiuFn1YMFlwK9JwVUzTH21Py1s1nHEjr4CJeElJog== dependencies: - "@atproto/lexicon" "^0.3.3" + "@atproto/lexicon" "^0.4.0" zod "^3.21.4" "@aws-crypto/crc32@3.0.0": |