import {AppBskyActorDefs, sanitizeMutedWordValue} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import React from 'react' import {Keyboard, View} from 'react-native' import { atoms as a, native, useBreakpoints, useTheme, ViewStyleProp, web, } from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' import {useGlobalDialogsControlContext} from '#/components/dialogs/Context' import {Divider} from '#/components/Divider' import * as Toggle from '#/components/forms/Toggle' import {Hashtag_Stroke2_Corner0_Rounded as Hashtag} from '#/components/icons/Hashtag' import {PageText_Stroke2_Corner0_Rounded as PageText} from '#/components/icons/PageText' import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' import {Loader} from '#/components/Loader' import * as Prompt from '#/components/Prompt' import {Text} from '#/components/Typography' import {logger} from '#/logger' import {isNative} from '#/platform/detection' import { usePreferencesQuery, useRemoveMutedWordMutation, useUpsertMutedWordsMutation, } from '#/state/queries/preferences' export function MutedWordsDialog() { const {mutedWordsDialogControl: control} = useGlobalDialogsControlContext() return ( ) } function MutedWordsInner({}: {control: Dialog.DialogOuterProps['control']}) { const t = useTheme() const {_} = useLingui() const {gtMobile} = useBreakpoints() const { isLoading: isPreferencesLoading, data: preferences, error: preferencesError, } = usePreferencesQuery() const {isPending, mutateAsync: addMutedWord} = useUpsertMutedWordsMutation() const [field, setField] = React.useState('') const [options, setOptions] = React.useState(['content']) const [error, setError] = React.useState('') const submit = React.useCallback(async () => { const sanitizedValue = sanitizeMutedWordValue(field) const targets = ['tag', options.includes('content') && 'content'].filter( Boolean, ) as AppBskyActorDefs.MutedWord['targets'] if (!sanitizedValue || !targets.length) { setField('') setError(_(msg`Please enter a valid word, tag, or phrase to mute`)) return } try { // send raw value and rely on SDK as sanitization source of truth await addMutedWord([{value: field, targets}]) setField('') } catch (e: any) { logger.error(`Failed to save muted word`, {message: e.message}) setError(e.message) } }, [_, field, options, addMutedWord, setField]) return ( Add muted words and tags Posts can be muted based on their text, their tags, or both. { if (error) { setError('') } setField(value) }} onSubmitEditing={submit} /> Mute in text & tags Mute in tags only {error && ( {error} )} We recommend avoiding common words that appear in many posts, since it can result in no posts being shown. Your muted words {isPreferencesLoading ? ( ) : preferencesError || !preferences ? ( We're sorry, but we weren't able to load your muted words at this time. Please try again. ) : preferences.mutedWords.length ? ( [...preferences.mutedWords] .reverse() .map((word, i) => ( )) ) : ( You haven't muted any words or tags yet )} {isNative && } ) } function MutedWordRow({ style, word, }: ViewStyleProp & {word: AppBskyActorDefs.MutedWord}) { const t = useTheme() const {_} = useLingui() const {isPending, mutateAsync: removeMutedWord} = useRemoveMutedWordMutation() const control = Prompt.usePromptControl() const remove = React.useCallback(async () => { control.close() removeMutedWord(word) }, [removeMutedWord, word, control]) return ( <> Are you sure? This will delete {word.value} from your muted words. You can always add it back later. Nevermind Remove {word.value} {word.targets.map(target => ( {target === 'content' ? _(msg`text`) : _(msg`tag`)} ))} ) } function TargetToggle({children}: React.PropsWithChildren<{}>) { const t = useTheme() const ctx = Toggle.useItemContext() const {gtMobile} = useBreakpoints() return ( {children} ) }