Bluesky app fork with some witchin' additions 💫

[APP-1809] add cleanup button for unavailable moderation services (#9901)

Co-authored-by: Samuel Newman <mozzius@protonmail.com>

authored by

Spence Pope
Samuel Newman
and committed by
GitHub
5e0ffc09 3fae42c0

+72 -5
+56 -5
src/screens/Moderation/index.tsx
··· 5 import {useLingui} from '@lingui/react' 6 import {useFocusEffect} from '@react-navigation/native' 7 8 - import {getLabelingServiceTitle} from '#/lib/moderation' 9 import { 10 type CommonNavigatorParams, 11 type NativeStackScreenProps, 12 } from '#/lib/routes/types' 13 import {logger} from '#/logger' 14 import {useIsBirthdateUpdateAllowed} from '#/state/birthdate' 15 import { 16 useMyLabelersQuery, 17 usePreferencesQuery, ··· 21 import {isNonConfigurableModerationAuthority} from '#/state/session/additional-moderation-authorities' 22 import {useSetMinimalShellMode} from '#/state/shell' 23 import {atoms as a, useBreakpoints, useTheme, type ViewStyleProp} from '#/alf' 24 - import {Admonition} from '#/components/Admonition' 25 import {AgeAssuranceAdmonition} from '#/components/ageAssurance/AgeAssuranceAdmonition' 26 import {useAgeAssuranceCopy} from '#/components/ageAssurance/useAgeAssuranceCopy' 27 - import {Button} from '#/components/Button' 28 import {useGlobalDialogsControlContext} from '#/components/dialogs/Context' 29 import {Divider} from '#/components/Divider' 30 import * as Toggle from '#/components/forms/Toggle' ··· 42 import {ListMaybePlaceholder} from '#/components/Lists' 43 import {Loader} from '#/components/Loader' 44 import {GlobalLabelPreference} from '#/components/moderation/LabelPreference' 45 import {Text} from '#/components/Typography' 46 import {useAgeAssurance} from '#/ageAssurance' 47 import {IS_IOS} from '#/env' ··· 166 data: labelers, 167 error: labelersError, 168 } = useMyLabelersQuery() 169 const aa = useAgeAssurance() 170 const isBirthdateUpdateAllowed = useIsBirthdateUpdateAllowed() 171 const aaCopy = useAgeAssuranceCopy() 172 173 useFocusEffect( 174 useCallback(() => { 175 setMinimalShellMode(false) ··· 209 <View style={[a.pt_2xl, a.px_lg, gtMobile && a.px_2xl]}> 210 {aa.flags.adultContentDisabled && isBirthdateUpdateAllowed && ( 211 <View style={[a.pb_2xl]}> 212 - <Admonition type="tip" style={[a.pb_md]}> 213 <Trans> 214 Your declared age is under 18. Some settings below may be 215 disabled. If this was a mistake, you may edit your birthdate in ··· 221 </InlineLinkText> 222 . 223 </Trans> 224 - </Admonition> 225 </View> 226 )} 227 ··· 438 ]}> 439 <Trans>Advanced</Trans> 440 </Text> 441 442 {isLabelersLoading ? ( 443 <View style={[a.w_full, a.align_center, a.p_lg]}>
··· 5 import {useLingui} from '@lingui/react' 6 import {useFocusEffect} from '@react-navigation/native' 7 8 + import {getLabelingServiceTitle, isAppLabeler} from '#/lib/moderation' 9 import { 10 type CommonNavigatorParams, 11 type NativeStackScreenProps, 12 } from '#/lib/routes/types' 13 import {logger} from '#/logger' 14 import {useIsBirthdateUpdateAllowed} from '#/state/birthdate' 15 + import {useRemoveLabelersMutation} from '#/state/queries/labeler' 16 import { 17 useMyLabelersQuery, 18 usePreferencesQuery, ··· 22 import {isNonConfigurableModerationAuthority} from '#/state/session/additional-moderation-authorities' 23 import {useSetMinimalShellMode} from '#/state/shell' 24 import {atoms as a, useBreakpoints, useTheme, type ViewStyleProp} from '#/alf' 25 + import * as Admonition from '#/components/Admonition' 26 import {AgeAssuranceAdmonition} from '#/components/ageAssurance/AgeAssuranceAdmonition' 27 import {useAgeAssuranceCopy} from '#/components/ageAssurance/useAgeAssuranceCopy' 28 + import {Button, ButtonIcon, ButtonText} from '#/components/Button' 29 import {useGlobalDialogsControlContext} from '#/components/dialogs/Context' 30 import {Divider} from '#/components/Divider' 31 import * as Toggle from '#/components/forms/Toggle' ··· 43 import {ListMaybePlaceholder} from '#/components/Lists' 44 import {Loader} from '#/components/Loader' 45 import {GlobalLabelPreference} from '#/components/moderation/LabelPreference' 46 + import * as Toast from '#/components/Toast' 47 import {Text} from '#/components/Typography' 48 import {useAgeAssurance} from '#/ageAssurance' 49 import {IS_IOS} from '#/env' ··· 168 data: labelers, 169 error: labelersError, 170 } = useMyLabelersQuery() 171 + const {mutateAsync: removeLabelers, isPending: isRemovingLabelers} = 172 + useRemoveLabelersMutation() 173 const aa = useAgeAssurance() 174 const isBirthdateUpdateAllowed = useIsBirthdateUpdateAllowed() 175 const aaCopy = useAgeAssuranceCopy() 176 177 + const subscribedDids = preferences.moderationPrefs.labelers.map(l => l.did) 178 + const returnedDids = new Set(labelers?.map(l => l.creator.did)) 179 + const unavailableDids = subscribedDids.filter( 180 + did => 181 + !returnedDids.has(did) && 182 + !isAppLabeler(did) && 183 + !isNonConfigurableModerationAuthority(did), 184 + ) 185 + 186 + const handleCleanup = async () => { 187 + try { 188 + await removeLabelers({dids: unavailableDids}) 189 + Toast.show(_(msg`Removed unavailable services`), { 190 + type: 'success', 191 + }) 192 + } catch (e: any) { 193 + logger.error('Failed to remove unavailable labelers', { 194 + safeMessage: e.message, 195 + }) 196 + } 197 + } 198 + 199 useFocusEffect( 200 useCallback(() => { 201 setMinimalShellMode(false) ··· 235 <View style={[a.pt_2xl, a.px_lg, gtMobile && a.px_2xl]}> 236 {aa.flags.adultContentDisabled && isBirthdateUpdateAllowed && ( 237 <View style={[a.pb_2xl]}> 238 + <Admonition.Admonition type="tip" style={[a.pb_md]}> 239 <Trans> 240 Your declared age is under 18. Some settings below may be 241 disabled. If this was a mistake, you may edit your birthdate in ··· 247 </InlineLinkText> 248 . 249 </Trans> 250 + </Admonition.Admonition> 251 </View> 252 )} 253 ··· 464 ]}> 465 <Trans>Advanced</Trans> 466 </Text> 467 + 468 + {unavailableDids.length > 0 && ( 469 + <Admonition.Outer type="tip" style={[a.mb_md]}> 470 + <Admonition.Row> 471 + <Admonition.Icon /> 472 + <Admonition.Content> 473 + <Admonition.Text> 474 + <Trans> 475 + Some moderation services in your list are no longer available. 476 + </Trans> 477 + </Admonition.Text> 478 + </Admonition.Content> 479 + <Admonition.Button 480 + color="primary_subtle" 481 + label={_(msg`Remove unavailable moderation services`)} 482 + onPress={handleCleanup} 483 + disabled={isRemovingLabelers}> 484 + <ButtonText> 485 + <Trans>Remove</Trans> 486 + </ButtonText> 487 + {isRemovingLabelers && <ButtonIcon icon={Loader} />} 488 + </Admonition.Button> 489 + </Admonition.Row> 490 + </Admonition.Outer> 491 + )} 492 493 {isLabelersLoading ? ( 494 <View style={[a.w_full, a.align_center, a.p_lg]}>
+16
src/state/queries/labeler.ts
··· 82 }) 83 } 84 85 export function useLabelerSubscriptionMutation() { 86 const queryClient = useQueryClient() 87 const agent = useAgent()
··· 82 }) 83 } 84 85 + export function useRemoveLabelersMutation() { 86 + const queryClient = useQueryClient() 87 + const agent = useAgent() 88 + 89 + return useMutation({ 90 + async mutationFn({dids}: {dids: string[]}) { 91 + await Promise.all(dids.map(did => agent.removeLabeler(did))) 92 + }, 93 + async onSuccess() { 94 + await queryClient.invalidateQueries({ 95 + queryKey: preferencesQueryKey, 96 + }) 97 + }, 98 + }) 99 + } 100 + 101 export function useLabelerSubscriptionMutation() { 102 const queryClient = useQueryClient() 103 const agent = useAgent()