Bump API SDK, add validation to MutedWords (#3055)

* Bump API SDK, add validation to MutedWords

* Tweaks to error state

* Comment

* Early return
zio/stable
Eric Bailey 2024-02-29 19:30:30 -06:00 committed by GitHub
parent 21bdddcfbe
commit cecb6e4e69
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 89 additions and 24 deletions

View File

@ -44,7 +44,7 @@
"update-extensions": "scripts/updateExtensions.sh" "update-extensions": "scripts/updateExtensions.sh"
}, },
"dependencies": { "dependencies": {
"@atproto/api": "^0.10.0", "@atproto/api": "^0.10.3",
"@bam.tech/react-native-image-resizer": "^3.0.4", "@bam.tech/react-native-image-resizer": "^3.0.4",
"@braintree/sanitize-url": "^6.0.2", "@braintree/sanitize-url": "^6.0.2",
"@emoji-mart/react": "^1.1.1", "@emoji-mart/react": "^1.1.1",

View File

@ -215,14 +215,12 @@ export function TagMenu({
if (isMuted) { if (isMuted) {
resetUpsert() resetUpsert()
removeMutedWord({ removeMutedWord({
value: sanitizedTag, value: tag,
targets: ['tag'], targets: ['tag'],
}) })
} else { } else {
resetRemove() resetRemove()
upsertMutedWord([ upsertMutedWord([{value: tag, targets: ['tag']}])
{value: sanitizedTag, targets: ['tag']},
])
} }
}) })
}}> }}>

View File

@ -104,9 +104,9 @@ export function TagMenu({
: _(msg`Mute ${truncatedTag}`), : _(msg`Mute ${truncatedTag}`),
onPress() { onPress() {
if (isMuted) { if (isMuted) {
removeMutedWord({value: sanitizedTag, targets: ['tag']}) removeMutedWord({value: tag, targets: ['tag']})
} else { } else {
upsertMutedWord([{value: sanitizedTag, targets: ['tag']}]) upsertMutedWord([{value: tag, targets: ['tag']}])
} }
}, },
testID: 'tagMenuMute', testID: 'tagMenuMute',
@ -127,7 +127,6 @@ export function TagMenu({
preferences, preferences,
tag, tag,
truncatedTag, truncatedTag,
sanitizedTag,
upsertMutedWord, upsertMutedWord,
removeMutedWord, removeMutedWord,
]) ])

View File

@ -2,7 +2,7 @@ import React from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {msg, Trans} from '@lingui/macro' import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {AppBskyActorDefs} from '@atproto/api' import {AppBskyActorDefs, sanitizeMutedWordValue} from '@atproto/api'
import { import {
usePreferencesQuery, usePreferencesQuery,
@ -10,7 +10,14 @@ import {
useRemoveMutedWordMutation, useRemoveMutedWordMutation,
} from '#/state/queries/preferences' } from '#/state/queries/preferences'
import {isNative} from '#/platform/detection' import {isNative} from '#/platform/detection'
import {atoms as a, useTheme, useBreakpoints, ViewStyleProp, web} from '#/alf' import {
atoms as a,
useTheme,
useBreakpoints,
ViewStyleProp,
web,
native,
} from '#/alf'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {Button, ButtonIcon, ButtonText} from '#/components/Button' import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
@ -48,24 +55,29 @@ function MutedWordsInner({}: {control: Dialog.DialogOuterProps['control']}) {
const {isPending, mutateAsync: addMutedWord} = useUpsertMutedWordsMutation() const {isPending, mutateAsync: addMutedWord} = useUpsertMutedWordsMutation()
const [field, setField] = React.useState('') const [field, setField] = React.useState('')
const [options, setOptions] = React.useState(['content']) const [options, setOptions] = React.useState(['content'])
const [_error, setError] = React.useState('') const [error, setError] = React.useState('')
const submit = React.useCallback(async () => { const submit = React.useCallback(async () => {
const value = field.trim() const sanitizedValue = sanitizeMutedWordValue(field)
const targets = ['tag', options.includes('content') && 'content'].filter( const targets = ['tag', options.includes('content') && 'content'].filter(
Boolean, Boolean,
) as AppBskyActorDefs.MutedWord['targets'] ) as AppBskyActorDefs.MutedWord['targets']
if (!value || !targets.length) return if (!sanitizedValue || !targets.length) {
setField('')
setError(_(msg`Please enter a valid word, tag, or phrase to mute`))
return
}
try { try {
await addMutedWord([{value, targets}]) // send raw value and rely on SDK as sanitization source of truth
await addMutedWord([{value: field, targets}])
setField('') setField('')
} catch (e: any) { } catch (e: any) {
logger.error(`Failed to save muted word`, {message: e.message}) logger.error(`Failed to save muted word`, {message: e.message})
setError(e.message) setError(e.message)
} }
}, [field, options, addMutedWord, setField]) }, [_, field, options, addMutedWord, setField])
return ( return (
<Dialog.ScrollableInner label={_(msg`Manage your muted words and tags`)}> <Dialog.ScrollableInner label={_(msg`Manage your muted words and tags`)}>
@ -87,7 +99,12 @@ function MutedWordsInner({}: {control: Dialog.DialogOuterProps['control']}) {
label={_(msg`Enter a word or tag`)} label={_(msg`Enter a word or tag`)}
placeholder={_(msg`Enter a word or tag`)} placeholder={_(msg`Enter a word or tag`)}
value={field} value={field}
onChangeText={setField} onChangeText={value => {
if (error) {
setError('')
}
setField(value)
}}
onSubmitEditing={submit} onSubmitEditing={submit}
/> />
@ -99,7 +116,7 @@ function MutedWordsInner({}: {control: Dialog.DialogOuterProps['control']}) {
<View <View
style={[ style={[
a.pt_sm, a.pt_sm,
a.pb_md, a.py_sm,
a.flex_row, a.flex_row,
a.align_center, a.align_center,
a.gap_sm, a.gap_sm,
@ -151,8 +168,33 @@ function MutedWordsInner({}: {control: Dialog.DialogOuterProps['control']}) {
</View> </View>
</Toggle.Group> </Toggle.Group>
{error && (
<View
style={[
a.mb_lg,
a.flex_row,
a.rounded_sm,
a.p_md,
a.mb_xs,
t.atoms.bg_contrast_25,
{
backgroundColor: t.palette.negative_400,
},
]}>
<Text
style={[
a.italic,
{color: t.palette.white},
native({marginTop: 2}),
]}>
{error}
</Text>
</View>
)}
<Text <Text
style={[ style={[
a.pt_xs,
a.text_sm, a.text_sm,
a.italic, a.italic,
a.leading_snug, a.leading_snug,

View File

@ -34,15 +34,15 @@
jsonpointer "^5.0.0" jsonpointer "^5.0.0"
leven "^3.1.0" leven "^3.1.0"
"@atproto/api@^0.10.0": "@atproto/api@^0.10.3":
version "0.10.0" version "0.10.3"
resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.10.0.tgz#ca34dfa8f9b1e6ba021094c40cb0ff3c4c254044" resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.10.3.tgz#cdef37a23b53e2e84840e527992482b0a0ab9d47"
integrity sha512-TSVCHh3UUZLtNzh141JwLicfYTc7TvVFvQJSWeOZLHr3Sk+9hqEY+9Itaqp1DAW92r4i25ChaMc/50sg4etAWQ== integrity sha512-eNP/6YLor48SUD38Jn5C7xocQL9XzW+BbYat2whWerKsFMn6Kfkk5O3fW1pvcc6NKtKVTo7/4ixMAS+dG2o/Yg==
dependencies: dependencies:
"@atproto/common-web" "^0.2.3" "@atproto/common-web" "^0.2.3"
"@atproto/lexicon" "^0.3.1" "@atproto/lexicon" "^0.3.2"
"@atproto/syntax" "^0.1.5" "@atproto/syntax" "^0.2.0"
"@atproto/xrpc" "^0.4.1" "@atproto/xrpc" "^0.4.2"
multiformats "^9.9.0" multiformats "^9.9.0"
tlds "^1.234.0" tlds "^1.234.0"
typed-emitter "^2.1.0" typed-emitter "^2.1.0"
@ -245,6 +245,17 @@
multiformats "^9.9.0" multiformats "^9.9.0"
zod "^3.21.4" zod "^3.21.4"
"@atproto/lexicon@^0.3.2":
version "0.3.2"
resolved "https://registry.yarnpkg.com/@atproto/lexicon/-/lexicon-0.3.2.tgz#0085a3acd3a77867b8efe188297a1bbacc55ce5c"
integrity sha512-kmGCkrRwpWIqmn/KO4BZwUf8Nmfndk3XvFC06V0ygCWc42g6+t4QP/6ywNW4PgqfZY0Q5aW4EuDfD7KjAFkFtQ==
dependencies:
"@atproto/common-web" "^0.2.3"
"@atproto/syntax" "^0.2.0"
iso-datestring-validator "^2.2.2"
multiformats "^9.9.0"
zod "^3.21.4"
"@atproto/ozone@^0.0.7": "@atproto/ozone@^0.0.7":
version "0.0.7" version "0.0.7"
resolved "https://registry.yarnpkg.com/@atproto/ozone/-/ozone-0.0.7.tgz#bfad82bc1d0900e79401a82f13581f707415505a" resolved "https://registry.yarnpkg.com/@atproto/ozone/-/ozone-0.0.7.tgz#bfad82bc1d0900e79401a82f13581f707415505a"
@ -340,6 +351,13 @@
dependencies: dependencies:
"@atproto/common-web" "^0.2.3" "@atproto/common-web" "^0.2.3"
"@atproto/syntax@^0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@atproto/syntax/-/syntax-0.2.0.tgz#4bab724c02e11f8943b8ec101251082cf55067e9"
integrity sha512-K+9jl6mtxC9ytlR7msSiP9jVNqtdxEBSt0kOfsC924lqGwuD8nlUAMi1GSMgAZJGg/Rd+0MKXh789heTdeL3HQ==
dependencies:
"@atproto/common-web" "^0.2.3"
"@atproto/xrpc-server@^0.4.2": "@atproto/xrpc-server@^0.4.2":
version "0.4.2" version "0.4.2"
resolved "https://registry.yarnpkg.com/@atproto/xrpc-server/-/xrpc-server-0.4.2.tgz#23efd89086b85933f1b0cc00c86e895adcaac315" resolved "https://registry.yarnpkg.com/@atproto/xrpc-server/-/xrpc-server-0.4.2.tgz#23efd89086b85933f1b0cc00c86e895adcaac315"
@ -365,6 +383,14 @@
"@atproto/lexicon" "^0.3.1" "@atproto/lexicon" "^0.3.1"
zod "^3.21.4" zod "^3.21.4"
"@atproto/xrpc@^0.4.2":
version "0.4.2"
resolved "https://registry.yarnpkg.com/@atproto/xrpc/-/xrpc-0.4.2.tgz#57812e0624be597b85f21471acf336513f35ccda"
integrity sha512-x4x2QB4nWmLjIpz2Ue9n/QVbVyJkk6tQMhvmDQaVFF89E3FcVI4rxF4uhzSxaLpbNtyVQBNEEmNHOr5EJLeHVA==
dependencies:
"@atproto/lexicon" "^0.3.2"
zod "^3.21.4"
"@aws-crypto/crc32@3.0.0": "@aws-crypto/crc32@3.0.0":
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/@aws-crypto/crc32/-/crc32-3.0.0.tgz#07300eca214409c33e3ff769cd5697b57fdd38fa" resolved "https://registry.yarnpkg.com/@aws-crypto/crc32/-/crc32-3.0.0.tgz#07300eca214409c33e3ff769cd5697b57fdd38fa"