Bluesky app fork with some witchin' additions 💫 witchsky.app
bluesky fork client

[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 5 import {useLingui} from '@lingui/react' 6 6 import {useFocusEffect} from '@react-navigation/native' 7 7 8 - import {getLabelingServiceTitle} from '#/lib/moderation' 8 + import {getLabelingServiceTitle, isAppLabeler} from '#/lib/moderation' 9 9 import { 10 10 type CommonNavigatorParams, 11 11 type NativeStackScreenProps, 12 12 } from '#/lib/routes/types' 13 13 import {logger} from '#/logger' 14 14 import {useIsBirthdateUpdateAllowed} from '#/state/birthdate' 15 + import {useRemoveLabelersMutation} from '#/state/queries/labeler' 15 16 import { 16 17 useMyLabelersQuery, 17 18 usePreferencesQuery, ··· 21 22 import {isNonConfigurableModerationAuthority} from '#/state/session/additional-moderation-authorities' 22 23 import {useSetMinimalShellMode} from '#/state/shell' 23 24 import {atoms as a, useBreakpoints, useTheme, type ViewStyleProp} from '#/alf' 24 - import {Admonition} from '#/components/Admonition' 25 + import * as Admonition from '#/components/Admonition' 25 26 import {AgeAssuranceAdmonition} from '#/components/ageAssurance/AgeAssuranceAdmonition' 26 27 import {useAgeAssuranceCopy} from '#/components/ageAssurance/useAgeAssuranceCopy' 27 - import {Button} from '#/components/Button' 28 + import {Button, ButtonIcon, ButtonText} from '#/components/Button' 28 29 import {useGlobalDialogsControlContext} from '#/components/dialogs/Context' 29 30 import {Divider} from '#/components/Divider' 30 31 import * as Toggle from '#/components/forms/Toggle' ··· 42 43 import {ListMaybePlaceholder} from '#/components/Lists' 43 44 import {Loader} from '#/components/Loader' 44 45 import {GlobalLabelPreference} from '#/components/moderation/LabelPreference' 46 + import * as Toast from '#/components/Toast' 45 47 import {Text} from '#/components/Typography' 46 48 import {useAgeAssurance} from '#/ageAssurance' 47 49 import {IS_IOS} from '#/env' ··· 166 168 data: labelers, 167 169 error: labelersError, 168 170 } = useMyLabelersQuery() 171 + const {mutateAsync: removeLabelers, isPending: isRemovingLabelers} = 172 + useRemoveLabelersMutation() 169 173 const aa = useAgeAssurance() 170 174 const isBirthdateUpdateAllowed = useIsBirthdateUpdateAllowed() 171 175 const aaCopy = useAgeAssuranceCopy() 172 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 + 173 199 useFocusEffect( 174 200 useCallback(() => { 175 201 setMinimalShellMode(false) ··· 209 235 <View style={[a.pt_2xl, a.px_lg, gtMobile && a.px_2xl]}> 210 236 {aa.flags.adultContentDisabled && isBirthdateUpdateAllowed && ( 211 237 <View style={[a.pb_2xl]}> 212 - <Admonition type="tip" style={[a.pb_md]}> 238 + <Admonition.Admonition type="tip" style={[a.pb_md]}> 213 239 <Trans> 214 240 Your declared age is under 18. Some settings below may be 215 241 disabled. If this was a mistake, you may edit your birthdate in ··· 221 247 </InlineLinkText> 222 248 . 223 249 </Trans> 224 - </Admonition> 250 + </Admonition.Admonition> 225 251 </View> 226 252 )} 227 253 ··· 438 464 ]}> 439 465 <Trans>Advanced</Trans> 440 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 + )} 441 492 442 493 {isLabelersLoading ? ( 443 494 <View style={[a.w_full, a.align_center, a.p_lg]}>
+16
src/state/queries/labeler.ts
··· 82 82 }) 83 83 } 84 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 + 85 101 export function useLabelerSubscriptionMutation() { 86 102 const queryClient = useQueryClient() 87 103 const agent = useAgent()