From 6e4ae28a2b31b5942343757a5bc286aa7b0edcdf Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Thu, 15 Feb 2024 11:37:29 -0600 Subject: [PATCH 1/3] Better loading flow on Mod screen --- src/screens/Moderation/index.tsx | 106 +++++++++++++++++++++++-------- 1 file changed, 79 insertions(+), 27 deletions(-) diff --git a/src/screens/Moderation/index.tsx b/src/screens/Moderation/index.tsx index 32b7cfb174..d1d2030ef5 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' @@ -49,24 +50,78 @@ 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() + const {gtMobile} = useBreakpoints() + const {height} = useSafeAreaFrame() - return isPreferencesLoading ? ( - - - - ) : preferences ? ( - - ) : // TODO - null + return ( + + + + {isPreferencesLoading ? ( + + + + ) : preferencesError || !preferences ? ( + + ) : ( + + )} + + ) } function ModerationScreenIntermediate({ @@ -75,25 +130,32 @@ function ModerationScreenIntermediate({ preferences: UsePreferencesQueryResponse }) { const t = useTheme() + const {_} = useLingui() const { isLoading: isModServicesLoading, data: modservices, - // error: modservicesError, + error: modservicesError, } = useModServicesDetailedInfoQuery({ dids: preferences.moderationOpts.mods.map(m => m.did), }) return isModServicesLoading ? ( - + - ) : modservices ? ( + ) : modservicesError || !modservices ? ( + + ) : ( - ) : // TODO - null + ) } export function ModerationScreenInner({ @@ -104,10 +166,9 @@ export function ModerationScreenInner({ modservices: AppBskyModerationDefs.ModServiceViewDetailed[] }) { const t = useTheme() - const {_} = useLingui() const setMinimalShellMode = useSetMinimalShellMode() const {screen} = useAnalytics() - const {gtMobile, gtTablet} = useBreakpoints() + const {gtTablet} = useBreakpoints() const labelGroupStrings = useLabelGroupStrings() const modSettingsDialogControl = Dialog.useDialogControl() @@ -165,21 +226,12 @@ export function ModerationScreenInner({ ) return ( - + - - {!gtTablet && } @@ -280,7 +332,7 @@ export function ModerationScreenInner({ - + ) } From 90662d6cc3d630ff4765c22f0ff6b570e61c8d3b Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Thu, 15 Feb 2024 11:57:27 -0600 Subject: [PATCH 2/3] Clean up Mod screen --- src/screens/Moderation/index.tsx | 113 ++++++++----------------------- src/state/queries/modservice.ts | 2 + 2 files changed, 32 insertions(+), 83 deletions(-) diff --git a/src/screens/Moderation/index.tsx b/src/screens/Moderation/index.tsx index d1d2030ef5..620133f1ad 100644 --- a/src/screens/Moderation/index.tsx +++ b/src/screens/Moderation/index.tsx @@ -40,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 { @@ -91,9 +91,19 @@ export function ModerationScreen( error: preferencesError, data: preferences, } = usePreferencesQuery() + const { + isLoading: isModServicesLoading, + data: modservices, + error: modservicesError, + } = useModServicesDetailedInfoQuery({ + dids: preferences ? preferences.moderationOpts.mods.map(m => m.did) : [], + }) const {gtMobile} = useBreakpoints() const {height} = useSafeAreaFrame() + const isLoading = isPreferencesLoading || isModServicesLoading + const error = preferencesError || modservicesError + return ( - {isPreferencesLoading ? ( + {isLoading ? ( - ) : preferencesError || !preferences ? ( + ) : error || !(preferences && modservices) ? ( ) : ( - + )} ) } -function ModerationScreenIntermediate({ - preferences, -}: { - preferences: UsePreferencesQueryResponse -}) { - const t = useTheme() - const {_} = useLingui() - const { - isLoading: isModServicesLoading, - data: modservices, - error: modservicesError, - } = useModServicesDetailedInfoQuery({ - dids: preferences.moderationOpts.mods.map(m => m.did), - }) - - return isModServicesLoading ? ( - - - - ) : modservicesError || !modservices ? ( - - ) : ( - - ) -} - export function ModerationScreenInner({ preferences, modservices, @@ -169,15 +148,17 @@ export function ModerationScreenInner({ const setMinimalShellMode = useSetMinimalShellMode() const {screen} = useAnalytics() const {gtTablet} = useBreakpoints() - const labelGroupStrings = useLabelGroupStrings() 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(() => { @@ -186,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({ @@ -304,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 && } ) @@ -338,21 +287,18 @@ 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() @@ -364,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/state/queries/modservice.ts b/src/state/queries/modservice.ts index d79110a421..73ebedf6f5 100644 --- a/src/state/queries/modservice.ts +++ b/src/state/queries/modservice.ts @@ -17,6 +17,7 @@ export const modServicesDetailedInfoQueryKey = (dids: string[]) => [ export function useModServiceInfoQuery({did}: {did: string}) { return useQuery({ + enabled: !!did, queryKey: modServiceInfoQueryKey(did), queryFn: async () => { const res = await getAgent().app.bsky.moderation.getService({did}) @@ -38,6 +39,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[] = [] From 91bc7039645d7ce7dfbe9d3982cf571fea05afb5 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Thu, 15 Feb 2024 13:24:26 -0600 Subject: [PATCH 3/3] Clean up ProfileMod screen --- src/components/Loader.tsx | 8 +- .../ProfileModerationService/index.tsx | 76 ++++++++----------- src/state/queries/modservice.ts | 8 +- 3 files changed, 43 insertions(+), 49 deletions(-) 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/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 73ebedf6f5..4a4ebf674e 100644 --- a/src/state/queries/modservice.ts +++ b/src/state/queries/modservice.ts @@ -15,12 +15,14 @@ export const modServicesDetailedInfoQueryKey = (dids: string[]) => [ dids, ] -export function useModServiceInfoQuery({did}: {did: string}) { +export function useModServiceInfoQuery({did}: {did?: string}) { return useQuery({ enabled: !!did, - queryKey: modServiceInfoQueryKey(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 }, })