Bump API SDK, add validation to MutedWords (#3055)
* Bump API SDK, add validation to MutedWords * Tweaks to error state * Comment * Early returnzio/stable
parent
21bdddcfbe
commit
cecb6e4e69
|
@ -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",
|
||||||
|
|
|
@ -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']},
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}}>
|
}}>
|
||||||
|
|
|
@ -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,
|
||||||
])
|
])
|
||||||
|
|
|
@ -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
|
<Text
|
||||||
style={[
|
style={[
|
||||||
|
a.italic,
|
||||||
|
{color: t.palette.white},
|
||||||
|
native({marginTop: 2}),
|
||||||
|
]}>
|
||||||
|
{error}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
a.pt_xs,
|
||||||
a.text_sm,
|
a.text_sm,
|
||||||
a.italic,
|
a.italic,
|
||||||
a.leading_snug,
|
a.leading_snug,
|
||||||
|
|
40
yarn.lock
40
yarn.lock
|
@ -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"
|
||||||
|
|
Loading…
Reference in New Issue