From 46d95350fb20f710e21dc13720833867f4d11f59 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Wed, 14 Feb 2024 20:35:26 -0600 Subject: [PATCH] Hook up data to ReportDialog --- src/components/dialogs/ReportDialog/index.tsx | 175 ++++++++++++++---- src/components/forms/TextField.tsx | 1 + src/screens/Moderation/index.tsx | 4 +- src/state/queries/modservice.ts | 17 +- src/view/screens/Storybook/Forms.tsx | 1 + 5 files changed, 156 insertions(+), 42 deletions(-) diff --git a/src/components/dialogs/ReportDialog/index.tsx b/src/components/dialogs/ReportDialog/index.tsx index ab7eb8eaff..4868d552d6 100644 --- a/src/components/dialogs/ReportDialog/index.tsx +++ b/src/components/dialogs/ReportDialog/index.tsx @@ -2,7 +2,7 @@ import React from 'react' import {View, Dimensions} from 'react-native' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {LABEL_GROUPS, LabelGroupDefinition} from '@atproto/api' +import {AppBskyModerationDefs, LabelGroupDefinition} from '@atproto/api' import {useSafeAreaInsets} from 'react-native-safe-area-context' import {atoms as a, useTheme, tokens, native} from '#/alf' @@ -25,10 +25,16 @@ import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' import * as Toggle from '#/components/forms/Toggle' import {GradientFill} from '#/components/GradientFill' -import * as TextField from '#/components/forms/TextField' import {CharProgress} from '#/view/com/composer/char-progress/CharProgress' import {Loader} from '#/components/Loader' import * as Toast from '#/view/com/util/Toast' +import {usePreferencesQuery} from '#/state/queries/preferences' +import {useModServicesDetailedInfoQuery} from '#/state/queries/modservice' +import { + getLabelGroupsFromLabels, + getModerationServiceTitle, + useConfigurableLabelGroups, +} from '#/lib/moderation' export type ReportDialogProps = | { @@ -41,7 +47,11 @@ export type ReportDialogProps = did: string } -function LabelGroupButton({labelGroup}: {labelGroup: string}) { +function LabelGroupButton({ + labelGroup, +}: { + labelGroup: LabelGroupDefinition['id'] +}) { const t = useTheme() const {hovered, focused, pressed} = useButtonContext() const labelGroupStrings = useLabelGroupStrings() @@ -143,22 +153,89 @@ function ModServiceToggle({title}: {title: string}) { ) } +function SubmitViewLoader({ + children, +}: { + children: (props: { + labelers: AppBskyModerationDefs.ModServiceViewDetailed[] + labelGroupToModServiceMap: Record + }) => React.ReactNode +}) { + const { + isLoading: isPreferencesLoading, + error: preferencesError, + data: preferences, + } = usePreferencesQuery() + const { + isLoading: isModServicesLoading, + data: modservices, + error: modservicesError, + } = useModServicesDetailedInfoQuery({ + dids: preferences ? preferences.moderationOpts.mods.map(m => m.did) : [], + }) + const labelGroupToModServiceMap = React.useMemo(() => { + if (!modservices) return {} + + const groups: Partial< + Record< + LabelGroupDefinition['id'], + AppBskyModerationDefs.ModServiceViewDetailed[] + > + > = {} + + for (const modservice of modservices) { + const labelGroups = getLabelGroupsFromLabels( + modservice.policies.labelValues, + ) + for (const group of labelGroups) { + const g = (groups[group.id] = groups[group.id] || []) + g.push(modservice) + } + } + + return groups + }, [modservices]) + + const isLoading = isPreferencesLoading || isModServicesLoading + const error = preferencesError || modservicesError + + return isLoading ? ( + + + + ) : error || !(preferences && modservices) ? null : ( // TODO + children({ + labelers: modservices, + // TODO mismatched types + // @ts-ignore + labelGroupToModServiceMap, + }) + ) +} + function SubmitView({ selectedLabelGroup, goBack, onSubmitComplete, + labelGroupToModServiceMap, }: { - selectedLabelGroup: string + selectedLabelGroup: LabelGroupDefinition['id'] goBack: () => void onSubmitComplete: () => void + labelers: AppBskyModerationDefs.ModServiceViewDetailed[] + labelGroupToModServiceMap: Record< + LabelGroupDefinition['id'], + AppBskyModerationDefs.ModServiceViewDetailed[] + > }) { const t = useTheme() const {_} = useLingui() const labelGroupStrings = useLabelGroupStrings() const groupInfoStrings = labelGroupStrings[selectedLabelGroup] - const [selectedServices, setSelectedServices] = React.useState([]) const [details, setDetails] = React.useState('') const [submitting, setSubmitting] = React.useState(false) + const [selectedServices, setSelectedServices] = React.useState([]) + const supportedLabelers = labelGroupToModServiceMap[selectedLabelGroup] const submit = React.useCallback(async () => { setSubmitting(true) @@ -208,22 +285,35 @@ function SubmitView({ Select the moderation service(s) to report to - - - - - - - - - - - + {supportedLabelers ? ( + + + {supportedLabelers.map(labeler => { + const title = getModerationServiceTitle({ + displayName: labeler.creator.displayName, + handle: labeler.creator.handle, + }) + return ( + + + + ) + })} + + + ) : ( + + + None of your subscribed labelers support this content type. + - + )} @@ -231,13 +321,14 @@ function SubmitView({ - @@ -284,7 +376,9 @@ export function ReportDialog({ const {_} = useLingui() const insets = useSafeAreaInsets() const control = Dialog.useDialogControl() - const [selectedLabelGroup, setSelectedLabelGroup] = React.useState('') + const [selectedLabelGroup, setSelectedLabelGroup] = React.useState< + LabelGroupDefinition['id'] | undefined + >() const labelGroupStrings = useLabelGroupStrings() // REQUIRED CLEANUP @@ -305,11 +399,7 @@ export function ReportDialog({ } }, [_, params.type]) - const groups = React.useMemo< - [keyof typeof LABEL_GROUPS, LabelGroupDefinition][] - >(() => { - return Object.entries(LABEL_GROUPS).filter(([, def]) => def.configurable) - }, []) + const groups = useConfigurableLabelGroups() return ( {selectedLabelGroup ? ( - setSelectedLabelGroup('')} - onSubmitComplete={control.close} - /> + + {props => ( + // TODO same types mismatch + // @ts-ignore + setSelectedLabelGroup(undefined)} + onSubmitComplete={control.close} + /> + )} + ) : ( @@ -342,14 +439,14 @@ export function ReportDialog({ - {groups.map(([name, def]) => { - const groupStrings = labelGroupStrings[name] + {groups.map(def => { + const groupStrings = labelGroupStrings[def.id] return ( ) })} diff --git a/src/components/forms/TextField.tsx b/src/components/forms/TextField.tsx index 3b6a8e879c..93f40ba5a4 100644 --- a/src/components/forms/TextField.tsx +++ b/src/components/forms/TextField.tsx @@ -149,6 +149,7 @@ export type InputProps = Omit & { value: string onChangeText: (value: string) => void isInvalid?: boolean + disabled?: boolean } export function createInput(Component: typeof TextInput) { diff --git a/src/screens/Moderation/index.tsx b/src/screens/Moderation/index.tsx index 13770b0be1..32b7cfb174 100644 --- a/src/screens/Moderation/index.tsx +++ b/src/screens/Moderation/index.tsx @@ -23,7 +23,7 @@ import { usePreferencesQuery, useSetContentLabelMutation, } from '#/state/queries/preferences' -import {useModServicesInfoQuery} from '#/state/queries/modservice' +import {useModServicesDetailedInfoQuery} from '#/state/queries/modservice' import {useTheme, atoms as a, useBreakpoints} from '#/alf' import {Divider} from '#/components/Divider' @@ -79,7 +79,7 @@ function ModerationScreenIntermediate({ isLoading: isModServicesLoading, data: modservices, // error: modservicesError, - } = useModServicesInfoQuery({ + } = useModServicesDetailedInfoQuery({ dids: preferences.moderationOpts.mods.map(m => m.did), }) diff --git a/src/state/queries/modservice.ts b/src/state/queries/modservice.ts index 6acfc48da9..d79110a421 100644 --- a/src/state/queries/modservice.ts +++ b/src/state/queries/modservice.ts @@ -7,7 +7,11 @@ import {preferencesQueryKey} from '#/state/queries/preferences' export const modServiceInfoQueryKey = (did: string) => ['mod-service-info', did] export const modServicesInfoQueryKey = (dids: string[]) => [ - 'mod-service-info', + 'mod-services-info', + dids, +] +export const modServicesDetailedInfoQueryKey = (dids: string[]) => [ + 'mod-services-detailed-info', dids, ] @@ -23,7 +27,18 @@ export function useModServiceInfoQuery({did}: {did: string}) { export function useModServicesInfoQuery({dids}: {dids: string[]}) { return useQuery({ + enabled: !!dids.length, queryKey: modServicesInfoQueryKey(dids), + queryFn: async () => { + const res = await getAgent().app.bsky.moderation.getServices({dids}) + return res.data.views + }, + }) +} + +export function useModServicesDetailedInfoQuery({dids}: {dids: string[]}) { + return useQuery({ + queryKey: modServicesDetailedInfoQueryKey(dids), queryFn: async () => { const views: AppBskyModerationDefs.ModServiceViewDetailed[] = [] diff --git a/src/view/screens/Storybook/Forms.tsx b/src/view/screens/Storybook/Forms.tsx index 2d5495d706..0bfad1aab2 100644 --- a/src/view/screens/Storybook/Forms.tsx +++ b/src/view/screens/Storybook/Forms.tsx @@ -62,6 +62,7 @@ export function Forms() { value={value} onChangeText={setValue} label="Text field" + disabled />