diff --git a/src/components/Loader.tsx b/src/components/Loader.tsx index bbe4e2f75c..031af03775 100644 --- a/src/components/Loader.tsx +++ b/src/components/Loader.tsx @@ -7,11 +7,12 @@ import Animated, { withTiming, } from 'react-native-reanimated' -import {atoms as a} from '#/alf' +import {atoms as a, useTheme} from '#/alf' import {Props, useCommonSVGProps} from '#/components/icons/common' import {Loader_Stroke2_Corner0_Rounded as Icon} from '#/components/icons/Loader' export function Loader(props: Props) { + const t = useTheme() const common = useCommonSVGProps(props) const rotation = useSharedValue(0) @@ -35,7 +36,10 @@ export function Loader(props: Props) { {width: common.size, height: common.size}, animatedStyles, ]}> - + ) } diff --git a/src/screens/Moderation/index.tsx b/src/screens/Moderation/index.tsx index 32b7cfb174..620133f1ad 100644 --- a/src/screens/Moderation/index.tsx +++ b/src/screens/Moderation/index.tsx @@ -5,6 +5,7 @@ import {ComAtprotoLabelDefs, LabelPreference} from '@atproto/api' import {Trans, msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {LabelGroupDefinition, AppBskyModerationDefs} from '@atproto/api' +import {useSafeAreaFrame} from 'react-native-safe-area-context' import {NativeStackScreenProps, CommonNavigatorParams} from '#/lib/routes/types' import {CenteredView} from '#/view/com/util/Views' @@ -39,9 +40,9 @@ import {useLabelGroupStrings} from '#/lib/moderation/useLabelGroupStrings' import * as Dialog from '#/components/Dialog' import {Button} from '#/components/Button' import { - getLabelGroupsFromLabels, getModerationServiceTitle, useConfigurableLabelGroups, + getLabelGroupToLabelerMap, } from '#/lib/moderation' import { @@ -49,51 +50,91 @@ import { SettingsDialogProps, } from '#/screens/Moderation/SettingsDialog' +function ErrorState({error}: {error: string}) { + const t = useTheme() + return ( + + + + Hmmmm, it seems we're having trouble loading this data. See below for + more details. If this issue persists, please contact us. + + + + {error} + + + ) +} + export function ModerationScreen( _props: NativeStackScreenProps, ) { const t = useTheme() + const {_} = useLingui() const { isLoading: isPreferencesLoading, - // error: preferencesError, + error: preferencesError, data: preferences, } = usePreferencesQuery() - - return isPreferencesLoading ? ( - - - - ) : preferences ? ( - - ) : // TODO - null -} - -function ModerationScreenIntermediate({ - preferences, -}: { - preferences: UsePreferencesQueryResponse -}) { - const t = useTheme() const { isLoading: isModServicesLoading, data: modservices, - // error: modservicesError, + error: modservicesError, } = useModServicesDetailedInfoQuery({ - dids: preferences.moderationOpts.mods.map(m => m.did), + dids: preferences ? preferences.moderationOpts.mods.map(m => m.did) : [], }) + const {gtMobile} = useBreakpoints() + const {height} = useSafeAreaFrame() - return isModServicesLoading ? ( - - - - ) : modservices ? ( - - ) : // TODO - null + const isLoading = isPreferencesLoading || isModServicesLoading + const error = preferencesError || modservicesError + + return ( + + + + {isLoading ? ( + + + + ) : error || !(preferences && modservices) ? ( + + ) : ( + + )} + + ) } export function ModerationScreenInner({ @@ -104,19 +145,20 @@ export function ModerationScreenInner({ modservices: AppBskyModerationDefs.ModServiceViewDetailed[] }) { const t = useTheme() - const {_} = useLingui() const setMinimalShellMode = useSetMinimalShellMode() const {screen} = useAnalytics() - const {gtMobile, gtTablet} = useBreakpoints() - const labelGroupStrings = useLabelGroupStrings() + const {gtTablet} = useBreakpoints() const modSettingsDialogControl = Dialog.useDialogControl() - const [settingsDialogProps, setSettingsDialogProps] = React.useState({ - // @ts-ignore - labelGroup: '', + // prefill with valid value to appease TS + labelGroup: 'intolerance', modservices: [], }) + const groups = useConfigurableLabelGroups() + const labelGroupToLabelerMap = React.useMemo(() => { + return getLabelGroupToLabelerMap(modservices) + }, [modservices]) useFocusEffect( React.useCallback(() => { @@ -125,34 +167,6 @@ export function ModerationScreenInner({ }, [screen, setMinimalShellMode]), ) - const groups = useConfigurableLabelGroups() - - const didToModServiceMap = React.useMemo< - Record - >(() => { - return modservices.reduce((acc, modservice) => { - return { - ...acc, - [modservice.creator.did]: modservice, - } - }, {}) - }, [modservices]) - const labelGroupToModServiceMap = React.useMemo(() => { - const groups: Partial> = {} - - 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.creator.did) - } - } - - return groups - }, [modservices]) - const openModSettingsDialog = React.useCallback( ({labelGroup, modservices}: Omit) => { setSettingsDialogProps({ @@ -165,21 +179,12 @@ export function ModerationScreenInner({ ) return ( - + - - {!gtTablet && } @@ -252,19 +257,15 @@ export function ModerationScreenInner({ {groups.map((def, i) => { - const groupStrings = labelGroupStrings[def.id] - const modDids = labelGroupToModServiceMap[def.id] || [] - const mods = modDids.map(did => didToModServiceMap[did]) + const labelers = labelGroupToLabelerMap[def.id] || [] return ( {i !== 0 && } ) @@ -280,27 +281,24 @@ export function ModerationScreenInner({ - + ) } function LabelGroup({ labelGroup, - name, - description, labelers: mods, preferences, openModSettingsDialog, }: { labelGroup: LabelGroupDefinition['id'] - name: string - description: string labelers: AppBskyModerationDefs.ModServiceViewDetailed[] preferences: UsePreferencesQueryResponse openModSettingsDialog: (props: SettingsDialogProps) => void }) { const t = useTheme() const {_} = useLingui() + const labelGroupStrings = useLabelGroupStrings() const {mutateAsync: setContentLabelPref, variables: optimisticContentLabel} = useSetContentLabelMutation() @@ -312,16 +310,17 @@ function LabelGroup({ visibility: values[0] as LabelPreference, }) } catch (e) { + // TODO console.error(e) } }, [labelGroup, setContentLabelPref], ) + const {name, description} = labelGroupStrings[labelGroup] const value = optimisticContentLabel?.visibility ?? preferences.moderationOpts.labelGroups[labelGroup] - const labelOptions = { hide: _(msg`Hide`), warn: _(msg`Warn`), diff --git a/src/screens/ProfileModerationService/index.tsx b/src/screens/ProfileModerationService/index.tsx index 854990b33f..938fe9c476 100644 --- a/src/screens/ProfileModerationService/index.tsx +++ b/src/screens/ProfileModerationService/index.tsx @@ -1,9 +1,10 @@ import React from 'react' -import {Dimensions, View} from 'react-native' +import {View} from 'react-native' import {NativeStackScreenProps} from '@react-navigation/native-stack' import {AppBskyModerationDefs} from '@atproto/api' import {Trans, msg} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {useSafeAreaFrame} from 'react-native-safe-area-context' import {usePalette} from '#/lib/hooks/usePalette' import {CommonNavigatorParams} from '#/lib/routes/types' @@ -45,17 +46,32 @@ import {ErrorState} from '#/screens/ProfileModerationService/ErrorState' import {Header} from '#/screens/ProfileModerationService/Header' import {PreferenceRow} from '#/screens/ProfileModerationService/PreferenceRow' -// TODO -const MIN_HEIGHT = Dimensions.get('window').height * 1.5 -// TODO loader default color - export function ProfileModserviceScreen( props: NativeStackScreenProps, ) { const t = useTheme() const {_} = useLingui() + const {height: minHeight} = useSafeAreaFrame() const {name: handleOrDid} = props.route.params - const {isLoading, error, data: resolvedDid} = useResolveDidQuery(handleOrDid) + const { + isLoading: isDidResolutionLoading, + error: didResolutionError, + data: did, + } = useResolveDidQuery(handleOrDid) + const { + isLoading: isPreferencesLoading, + error: preferencesError, + data: preferences, + } = usePreferencesQuery() + const { + isLoading: isModServiceLoading, + error: modServiceError, + data: modservice, + } = useModServiceInfoQuery({did}) + + const isLoading = + isDidResolutionLoading || isPreferencesLoading || isModServiceLoading + const error = didResolutionError || preferencesError || modServiceError return ( @@ -65,60 +81,31 @@ export function ProfileModserviceScreen( a.border_r, t.atoms.border_contrast_low, { - minHeight: MIN_HEIGHT, + minHeight, }, ]}> {isLoading ? ( - + - ) : resolvedDid ? ( - - ) : ( + ) : error || !(did && preferences && modservice) ? ( + ) : ( + )} ) } -function ProfileModservicecreenIntermediate({modDid}: {modDid: string}) { - const t = useTheme() - const {_} = useLingui() - const { - isLoading: isPreferencesLoading, - error: preferencesError, - data: preferences, - } = usePreferencesQuery() - const { - isLoading: isModServiceLoading, - error: modServiceError, - data: info, - } = useModServiceInfoQuery({did: modDid}) - - const isLoading = isPreferencesLoading || isModServiceLoading - const error = preferencesError || modServiceError - - return isLoading ? ( - - - - ) : preferences && info ? ( - - ) : ( - - ) -} - export function ProfileModserviceScreenInner({ preferences, modservice, @@ -128,6 +115,7 @@ export function ProfileModserviceScreenInner({ }) { const t = useTheme() const {_} = useLingui() + const {height: minHeight} = useSafeAreaFrame() const pal = usePalette('default') const {hasSession} = useSession() const {track} = useAnalytics() @@ -178,7 +166,7 @@ export function ProfileModserviceScreenInner({ diff --git a/src/state/queries/modservice.ts b/src/state/queries/modservice.ts index d79110a421..4a4ebf674e 100644 --- a/src/state/queries/modservice.ts +++ b/src/state/queries/modservice.ts @@ -15,11 +15,14 @@ export const modServicesDetailedInfoQueryKey = (dids: string[]) => [ dids, ] -export function useModServiceInfoQuery({did}: {did: string}) { +export function useModServiceInfoQuery({did}: {did?: string}) { return useQuery({ - queryKey: modServiceInfoQueryKey(did), + enabled: !!did, + queryKey: modServiceInfoQueryKey(did as string), queryFn: async () => { - const res = await getAgent().app.bsky.moderation.getService({did}) + const res = await getAgent().app.bsky.moderation.getService({ + did: did as string, + }) return res.data }, }) @@ -38,6 +41,7 @@ export function useModServicesInfoQuery({dids}: {dids: string[]}) { export function useModServicesDetailedInfoQuery({dids}: {dids: string[]}) { return useQuery({ + enabled: !!dids.length, queryKey: modServicesDetailedInfoQueryKey(dids), queryFn: async () => { const views: AppBskyModerationDefs.ModServiceViewDetailed[] = []