Hook up data to ReportDialog
This commit is contained in:
@@ -2,7 +2,7 @@ import React from 'react'
|
|||||||
import {View, Dimensions} from 'react-native'
|
import {View, Dimensions} from 'react-native'
|
||||||
import {msg} from '@lingui/macro'
|
import {msg} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
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 {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||||
|
|
||||||
import {atoms as a, useTheme, tokens, native} from '#/alf'
|
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 {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
|
||||||
import * as Toggle from '#/components/forms/Toggle'
|
import * as Toggle from '#/components/forms/Toggle'
|
||||||
import {GradientFill} from '#/components/GradientFill'
|
import {GradientFill} from '#/components/GradientFill'
|
||||||
import * as TextField from '#/components/forms/TextField'
|
|
||||||
import {CharProgress} from '#/view/com/composer/char-progress/CharProgress'
|
import {CharProgress} from '#/view/com/composer/char-progress/CharProgress'
|
||||||
import {Loader} from '#/components/Loader'
|
import {Loader} from '#/components/Loader'
|
||||||
import * as Toast from '#/view/com/util/Toast'
|
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 =
|
export type ReportDialogProps =
|
||||||
| {
|
| {
|
||||||
@@ -41,7 +47,11 @@ export type ReportDialogProps =
|
|||||||
did: string
|
did: string
|
||||||
}
|
}
|
||||||
|
|
||||||
function LabelGroupButton({labelGroup}: {labelGroup: string}) {
|
function LabelGroupButton({
|
||||||
|
labelGroup,
|
||||||
|
}: {
|
||||||
|
labelGroup: LabelGroupDefinition['id']
|
||||||
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {hovered, focused, pressed} = useButtonContext()
|
const {hovered, focused, pressed} = useButtonContext()
|
||||||
const labelGroupStrings = useLabelGroupStrings()
|
const labelGroupStrings = useLabelGroupStrings()
|
||||||
@@ -143,22 +153,89 @@ function ModServiceToggle({title}: {title: string}) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function SubmitViewLoader({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: (props: {
|
||||||
|
labelers: AppBskyModerationDefs.ModServiceViewDetailed[]
|
||||||
|
labelGroupToModServiceMap: Record<LabelGroupDefinition['id'], string[]>
|
||||||
|
}) => 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 ? (
|
||||||
|
<View style={[{height: 500}]}>
|
||||||
|
<Loader />
|
||||||
|
</View>
|
||||||
|
) : error || !(preferences && modservices) ? null : ( // TODO
|
||||||
|
children({
|
||||||
|
labelers: modservices,
|
||||||
|
// TODO mismatched types
|
||||||
|
// @ts-ignore
|
||||||
|
labelGroupToModServiceMap,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function SubmitView({
|
function SubmitView({
|
||||||
selectedLabelGroup,
|
selectedLabelGroup,
|
||||||
goBack,
|
goBack,
|
||||||
onSubmitComplete,
|
onSubmitComplete,
|
||||||
|
labelGroupToModServiceMap,
|
||||||
}: {
|
}: {
|
||||||
selectedLabelGroup: string
|
selectedLabelGroup: LabelGroupDefinition['id']
|
||||||
goBack: () => void
|
goBack: () => void
|
||||||
onSubmitComplete: () => void
|
onSubmitComplete: () => void
|
||||||
|
labelers: AppBskyModerationDefs.ModServiceViewDetailed[]
|
||||||
|
labelGroupToModServiceMap: Record<
|
||||||
|
LabelGroupDefinition['id'],
|
||||||
|
AppBskyModerationDefs.ModServiceViewDetailed[]
|
||||||
|
>
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const labelGroupStrings = useLabelGroupStrings()
|
const labelGroupStrings = useLabelGroupStrings()
|
||||||
const groupInfoStrings = labelGroupStrings[selectedLabelGroup]
|
const groupInfoStrings = labelGroupStrings[selectedLabelGroup]
|
||||||
const [selectedServices, setSelectedServices] = React.useState<string[]>([])
|
|
||||||
const [details, setDetails] = React.useState<string>('')
|
const [details, setDetails] = React.useState<string>('')
|
||||||
const [submitting, setSubmitting] = React.useState<boolean>(false)
|
const [submitting, setSubmitting] = React.useState<boolean>(false)
|
||||||
|
const [selectedServices, setSelectedServices] = React.useState<string[]>([])
|
||||||
|
const supportedLabelers = labelGroupToModServiceMap[selectedLabelGroup]
|
||||||
|
|
||||||
const submit = React.useCallback(async () => {
|
const submit = React.useCallback(async () => {
|
||||||
setSubmitting(true)
|
setSubmitting(true)
|
||||||
@@ -208,22 +285,35 @@ function SubmitView({
|
|||||||
Select the moderation service(s) to report to
|
Select the moderation service(s) to report to
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Toggle.Group
|
{supportedLabelers ? (
|
||||||
label="Select mod services"
|
<Toggle.Group
|
||||||
values={selectedServices}
|
label="Select mod services"
|
||||||
onChange={setSelectedServices}>
|
values={selectedServices}
|
||||||
<View style={[a.flex_row, a.gap_sm, a.flex_wrap]}>
|
onChange={setSelectedServices}>
|
||||||
<Toggle.Item name="bluesky" label="Bluesky">
|
<View style={[a.flex_row, a.gap_sm, a.flex_wrap]}>
|
||||||
<ModServiceToggle title="Bluesky" />
|
{supportedLabelers.map(labeler => {
|
||||||
</Toggle.Item>
|
const title = getModerationServiceTitle({
|
||||||
<Toggle.Item name="contraption" label="The Contraption">
|
displayName: labeler.creator.displayName,
|
||||||
<ModServiceToggle title="The Contraption" />
|
handle: labeler.creator.handle,
|
||||||
</Toggle.Item>
|
})
|
||||||
<Toggle.Item name="safety" label="Safety Corp">
|
return (
|
||||||
<ModServiceToggle title="Safety Corp" />
|
<Toggle.Item
|
||||||
</Toggle.Item>
|
key={labeler.creator.did}
|
||||||
|
name={labeler.creator.did}
|
||||||
|
label={title}>
|
||||||
|
<ModServiceToggle title={title} />
|
||||||
|
</Toggle.Item>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
</Toggle.Group>
|
||||||
|
) : (
|
||||||
|
<View style={[a.p_lg, a.rounded_sm, t.atoms.bg_contrast_25]}>
|
||||||
|
<Text style={[a.italic, t.atoms.text_contrast_medium]}>
|
||||||
|
None of your subscribed labelers support this content type.
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</Toggle.Group>
|
)}
|
||||||
</View>
|
</View>
|
||||||
<View style={[a.gap_md]}>
|
<View style={[a.gap_md]}>
|
||||||
<Text style={[t.atoms.text_contrast_medium]}>
|
<Text style={[t.atoms.text_contrast_medium]}>
|
||||||
@@ -231,13 +321,14 @@ function SubmitView({
|
|||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<View style={[a.relative, a.w_full]}>
|
<View style={[a.relative, a.w_full]}>
|
||||||
<TextField.Input
|
<Dialog.Input
|
||||||
multiline
|
multiline
|
||||||
value={details}
|
value={details}
|
||||||
onChangeText={setDetails}
|
onChangeText={setDetails}
|
||||||
label="Text field"
|
label="Text field"
|
||||||
style={{paddingRight: 60}}
|
style={{paddingRight: 60}}
|
||||||
numberOfLines={6}
|
numberOfLines={6}
|
||||||
|
disabled={!supportedLabelers}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
@@ -260,10 +351,11 @@ function SubmitView({
|
|||||||
<View style={[a.align_end]}>
|
<View style={[a.align_end]}>
|
||||||
<Button
|
<Button
|
||||||
size="large"
|
size="large"
|
||||||
variant="gradient"
|
variant="solid"
|
||||||
color="gradient_midnight"
|
color="primary"
|
||||||
label={_(msg`Submit`)}
|
label={_(msg`Submit`)}
|
||||||
onPress={submit}>
|
onPress={submit}
|
||||||
|
disabled={!supportedLabelers}>
|
||||||
<ButtonText>Submit</ButtonText>
|
<ButtonText>Submit</ButtonText>
|
||||||
{submitting && <ButtonIcon icon={Loader} />}
|
{submitting && <ButtonIcon icon={Loader} />}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -284,7 +376,9 @@ export function ReportDialog({
|
|||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const insets = useSafeAreaInsets()
|
const insets = useSafeAreaInsets()
|
||||||
const control = Dialog.useDialogControl()
|
const control = Dialog.useDialogControl()
|
||||||
const [selectedLabelGroup, setSelectedLabelGroup] = React.useState<string>('')
|
const [selectedLabelGroup, setSelectedLabelGroup] = React.useState<
|
||||||
|
LabelGroupDefinition['id'] | undefined
|
||||||
|
>()
|
||||||
const labelGroupStrings = useLabelGroupStrings()
|
const labelGroupStrings = useLabelGroupStrings()
|
||||||
|
|
||||||
// REQUIRED CLEANUP
|
// REQUIRED CLEANUP
|
||||||
@@ -305,11 +399,7 @@ export function ReportDialog({
|
|||||||
}
|
}
|
||||||
}, [_, params.type])
|
}, [_, params.type])
|
||||||
|
|
||||||
const groups = React.useMemo<
|
const groups = useConfigurableLabelGroups()
|
||||||
[keyof typeof LABEL_GROUPS, LabelGroupDefinition][]
|
|
||||||
>(() => {
|
|
||||||
return Object.entries(LABEL_GROUPS).filter(([, def]) => def.configurable)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.Outer
|
<Dialog.Outer
|
||||||
@@ -325,11 +415,18 @@ export function ReportDialog({
|
|||||||
|
|
||||||
<Dialog.ScrollableInner label="Report Dialog">
|
<Dialog.ScrollableInner label="Report Dialog">
|
||||||
{selectedLabelGroup ? (
|
{selectedLabelGroup ? (
|
||||||
<SubmitView
|
<SubmitViewLoader>
|
||||||
selectedLabelGroup={selectedLabelGroup}
|
{props => (
|
||||||
goBack={() => setSelectedLabelGroup('')}
|
// TODO same types mismatch
|
||||||
onSubmitComplete={control.close}
|
// @ts-ignore
|
||||||
/>
|
<SubmitView
|
||||||
|
{...props}
|
||||||
|
selectedLabelGroup={selectedLabelGroup}
|
||||||
|
goBack={() => setSelectedLabelGroup(undefined)}
|
||||||
|
onSubmitComplete={control.close}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</SubmitViewLoader>
|
||||||
) : (
|
) : (
|
||||||
<View style={[a.gap_lg]}>
|
<View style={[a.gap_lg]}>
|
||||||
<View style={[a.justify_center, a.gap_sm]}>
|
<View style={[a.justify_center, a.gap_sm]}>
|
||||||
@@ -342,14 +439,14 @@ export function ReportDialog({
|
|||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<View style={[a.gap_sm, {marginHorizontal: a.p_md.padding * -1}]}>
|
<View style={[a.gap_sm, {marginHorizontal: a.p_md.padding * -1}]}>
|
||||||
{groups.map(([name, def]) => {
|
{groups.map(def => {
|
||||||
const groupStrings = labelGroupStrings[name]
|
const groupStrings = labelGroupStrings[def.id]
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
key={def.id}
|
key={def.id}
|
||||||
label={_(msg`Create report for ${groupStrings.name}`)}
|
label={_(msg`Create report for ${groupStrings.name}`)}
|
||||||
onPress={() => setSelectedLabelGroup(name)}>
|
onPress={() => setSelectedLabelGroup(def.id)}>
|
||||||
<LabelGroupButton labelGroup={name} />
|
<LabelGroupButton labelGroup={def.id} />
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ export type InputProps = Omit<TextInputProps, 'value' | 'onChangeText'> & {
|
|||||||
value: string
|
value: string
|
||||||
onChangeText: (value: string) => void
|
onChangeText: (value: string) => void
|
||||||
isInvalid?: boolean
|
isInvalid?: boolean
|
||||||
|
disabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createInput(Component: typeof TextInput) {
|
export function createInput(Component: typeof TextInput) {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import {
|
|||||||
usePreferencesQuery,
|
usePreferencesQuery,
|
||||||
useSetContentLabelMutation,
|
useSetContentLabelMutation,
|
||||||
} from '#/state/queries/preferences'
|
} 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 {useTheme, atoms as a, useBreakpoints} from '#/alf'
|
||||||
import {Divider} from '#/components/Divider'
|
import {Divider} from '#/components/Divider'
|
||||||
@@ -79,7 +79,7 @@ function ModerationScreenIntermediate({
|
|||||||
isLoading: isModServicesLoading,
|
isLoading: isModServicesLoading,
|
||||||
data: modservices,
|
data: modservices,
|
||||||
// error: modservicesError,
|
// error: modservicesError,
|
||||||
} = useModServicesInfoQuery({
|
} = useModServicesDetailedInfoQuery({
|
||||||
dids: preferences.moderationOpts.mods.map(m => m.did),
|
dids: preferences.moderationOpts.mods.map(m => m.did),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,11 @@ import {preferencesQueryKey} from '#/state/queries/preferences'
|
|||||||
|
|
||||||
export const modServiceInfoQueryKey = (did: string) => ['mod-service-info', did]
|
export const modServiceInfoQueryKey = (did: string) => ['mod-service-info', did]
|
||||||
export const modServicesInfoQueryKey = (dids: string[]) => [
|
export const modServicesInfoQueryKey = (dids: string[]) => [
|
||||||
'mod-service-info',
|
'mod-services-info',
|
||||||
|
dids,
|
||||||
|
]
|
||||||
|
export const modServicesDetailedInfoQueryKey = (dids: string[]) => [
|
||||||
|
'mod-services-detailed-info',
|
||||||
dids,
|
dids,
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -23,7 +27,18 @@ export function useModServiceInfoQuery({did}: {did: string}) {
|
|||||||
|
|
||||||
export function useModServicesInfoQuery({dids}: {dids: string[]}) {
|
export function useModServicesInfoQuery({dids}: {dids: string[]}) {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
|
enabled: !!dids.length,
|
||||||
queryKey: modServicesInfoQueryKey(dids),
|
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 () => {
|
queryFn: async () => {
|
||||||
const views: AppBskyModerationDefs.ModServiceViewDetailed[] = []
|
const views: AppBskyModerationDefs.ModServiceViewDetailed[] = []
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ export function Forms() {
|
|||||||
value={value}
|
value={value}
|
||||||
onChangeText={setValue}
|
onChangeText={setValue}
|
||||||
label="Text field"
|
label="Text field"
|
||||||
|
disabled
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user