Add modal state provider, replace usage except methods (#1833)
* Add modal state provider, replace usage except methods * Replace easy spots * Fix sticky spots * Replace final usages * Memorize context objects * Add more warnings
This commit is contained in:
parent
5eadadffbf
commit
f18b15241a
70 changed files with 634 additions and 498 deletions
|
|
@ -13,6 +13,7 @@ import {
|
|||
import Clipboard from '@react-native-clipboard/clipboard'
|
||||
import * as Toast from '../util/Toast'
|
||||
import {logger} from '#/logger'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['70%']
|
||||
|
||||
|
|
@ -54,6 +55,7 @@ const shadesOfBlue: string[] = [
|
|||
export function Component({}: {}) {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const [name, setName] = useState(
|
||||
shadesOfBlue[Math.floor(Math.random() * shadesOfBlue.length)],
|
||||
)
|
||||
|
|
@ -69,8 +71,8 @@ export function Component({}: {}) {
|
|||
}, [appPassword])
|
||||
|
||||
const onDone = React.useCallback(() => {
|
||||
store.shell.closeModal()
|
||||
}, [store])
|
||||
closeModal()
|
||||
}, [closeModal])
|
||||
|
||||
const createAppPassword = async () => {
|
||||
// if name is all whitespace, we don't allow it
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ import {MAX_ALT_TEXT} from 'lib/constants'
|
|||
import {useTheme} from 'lib/ThemeContext'
|
||||
import {Text} from '../util/text/Text'
|
||||
import LinearGradient from 'react-native-linear-gradient'
|
||||
import {useStores} from 'state/index'
|
||||
import {isAndroid, isWeb} from 'platform/detection'
|
||||
import {ImageModel} from 'state/models/media/image'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['fullscreen']
|
||||
|
||||
|
|
@ -29,10 +29,10 @@ interface Props {
|
|||
|
||||
export function Component({image}: Props) {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const theme = useTheme()
|
||||
const [altText, setAltText] = useState(image.altText)
|
||||
const windim = useWindowDimensions()
|
||||
const {closeModal} = useModalControls()
|
||||
|
||||
const imageStyles = useMemo<ImageStyle>(() => {
|
||||
const maxWidth = isWeb ? 450 : windim.width
|
||||
|
|
@ -53,11 +53,11 @@ export function Component({image}: Props) {
|
|||
|
||||
const onPressSave = useCallback(() => {
|
||||
image.setAltText(altText)
|
||||
store.shell.closeModal()
|
||||
}, [store, image, altText])
|
||||
closeModal()
|
||||
}, [closeModal, image, altText])
|
||||
|
||||
const onPressCancel = () => {
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -15,12 +15,14 @@ import {usePalette} from 'lib/hooks/usePalette'
|
|||
import {isWeb} from 'platform/detection'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {cleanError} from 'lib/strings/errors'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['50%']
|
||||
|
||||
export const Component = observer(function Component({}: {}) {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const [date, setDate] = useState<Date>(
|
||||
store.preferences.birthDate || new Date(),
|
||||
)
|
||||
|
|
@ -33,7 +35,7 @@ export const Component = observer(function Component({}: {}) {
|
|||
setIsProcessing(true)
|
||||
try {
|
||||
await store.preferences.setBirthDate(date)
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
} catch (e) {
|
||||
setError(cleanError(String(e)))
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import {usePalette} from 'lib/hooks/usePalette'
|
|||
import {isWeb} from 'platform/detection'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {cleanError} from 'lib/strings/errors'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
enum Stages {
|
||||
InputEmail,
|
||||
|
|
@ -32,6 +33,7 @@ export const Component = observer(function Component({}: {}) {
|
|||
const [isProcessing, setIsProcessing] = useState<boolean>(false)
|
||||
const [error, setError] = useState<string>('')
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const {openModal, closeModal} = useModalControls()
|
||||
|
||||
const onRequestChange = async () => {
|
||||
if (email === store.session.currentSession?.email) {
|
||||
|
|
@ -90,8 +92,8 @@ export const Component = observer(function Component({}: {}) {
|
|||
}
|
||||
|
||||
const onVerify = async () => {
|
||||
store.shell.closeModal()
|
||||
store.shell.openModal({name: 'verify-email'})
|
||||
closeModal()
|
||||
openModal({name: 'verify-email'})
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -207,7 +209,7 @@ export const Component = observer(function Component({}: {}) {
|
|||
<Button
|
||||
testID="cancelBtn"
|
||||
type="default"
|
||||
onPress={() => store.shell.closeModal()}
|
||||
onPress={() => closeModal()}
|
||||
accessibilityLabel="Cancel"
|
||||
accessibilityHint=""
|
||||
label="Cancel"
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import {useTheme} from 'lib/ThemeContext'
|
|||
import {useAnalytics} from 'lib/analytics/analytics'
|
||||
import {cleanError} from 'lib/strings/errors'
|
||||
import {logger} from '#/logger'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['100%']
|
||||
|
||||
|
|
@ -30,6 +31,7 @@ export function Component({onChanged}: {onChanged: () => void}) {
|
|||
const [error, setError] = useState<string>('')
|
||||
const pal = usePalette('default')
|
||||
const {track} = useAnalytics()
|
||||
const {closeModal} = useModalControls()
|
||||
|
||||
const [isProcessing, setProcessing] = useState<boolean>(false)
|
||||
const [retryDescribeTrigger, setRetryDescribeTrigger] = React.useState<any>(
|
||||
|
|
@ -85,8 +87,8 @@ export function Component({onChanged}: {onChanged: () => void}) {
|
|||
// events
|
||||
// =
|
||||
const onPressCancel = React.useCallback(() => {
|
||||
store.shell.closeModal()
|
||||
}, [store])
|
||||
closeModal()
|
||||
}, [closeModal])
|
||||
const onPressRetryConnect = React.useCallback(
|
||||
() => setRetryDescribeTrigger({}),
|
||||
[setRetryDescribeTrigger],
|
||||
|
|
@ -110,7 +112,7 @@ export function Component({onChanged}: {onChanged: () => void}) {
|
|||
await store.agent.updateHandle({
|
||||
handle: newHandle,
|
||||
})
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
onChanged()
|
||||
} catch (err: any) {
|
||||
setError(cleanError(err))
|
||||
|
|
@ -127,6 +129,7 @@ export function Component({onChanged}: {onChanged: () => void}) {
|
|||
isCustom,
|
||||
onChanged,
|
||||
track,
|
||||
closeModal,
|
||||
])
|
||||
|
||||
// rendering
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ import {
|
|||
View,
|
||||
} from 'react-native'
|
||||
import {Text} from '../util/text/Text'
|
||||
import {useStores} from 'state/index'
|
||||
import {s, colors} from 'lib/styles'
|
||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||
import {cleanError} from 'lib/strings/errors'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {isWeb} from 'platform/detection'
|
||||
import type {ConfirmModal} from 'state/models/ui/shell'
|
||||
import type {ConfirmModal} from '#/state/modals'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['50%']
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ export function Component({
|
|||
cancelBtnText,
|
||||
}: ConfirmModal) {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const [isProcessing, setIsProcessing] = useState<boolean>(false)
|
||||
const [error, setError] = useState<string>('')
|
||||
const onPress = async () => {
|
||||
|
|
@ -34,7 +34,7 @@ export function Component({
|
|||
setIsProcessing(true)
|
||||
try {
|
||||
await onPressConfirm()
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
return
|
||||
} catch (e: any) {
|
||||
setError(cleanError(e))
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import {isIOS} from 'platform/detection'
|
|||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import * as Toast from '../util/Toast'
|
||||
import {logger} from '#/logger'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['90%']
|
||||
|
||||
|
|
@ -24,14 +25,15 @@ export const Component = observer(
|
|||
const store = useStores()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const pal = usePalette('default')
|
||||
const {closeModal} = useModalControls()
|
||||
|
||||
React.useEffect(() => {
|
||||
store.preferences.sync()
|
||||
}, [store])
|
||||
|
||||
const onPressDone = React.useCallback(() => {
|
||||
store.shell.closeModal()
|
||||
}, [store])
|
||||
closeModal()
|
||||
}, [closeModal])
|
||||
|
||||
return (
|
||||
<View testID="contentFilteringModal" style={[pal.view, styles.container]}>
|
||||
|
|
@ -89,8 +91,9 @@ const AdultContentEnabledPref = observer(
|
|||
function AdultContentEnabledPrefImpl() {
|
||||
const store = useStores()
|
||||
const pal = usePalette('default')
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
const onSetAge = () => store.shell.openModal({name: 'birth-date-settings'})
|
||||
const onSetAge = () => openModal({name: 'birth-date-settings'})
|
||||
|
||||
const onToggleAdultContent = async () => {
|
||||
if (isIOS) {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import {useTheme} from 'lib/ThemeContext'
|
|||
import {useAnalytics} from 'lib/analytics/analytics'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {cleanError, isNetworkError} from 'lib/strings/errors'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
const MAX_NAME = 64 // todo
|
||||
const MAX_DESCRIPTION = 300 // todo
|
||||
|
|
@ -40,6 +41,7 @@ export function Component({
|
|||
list?: ListModel
|
||||
}) {
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const [error, setError] = useState<string>('')
|
||||
const pal = usePalette('default')
|
||||
|
|
@ -67,8 +69,8 @@ export function Component({
|
|||
const [newAvatar, setNewAvatar] = useState<RNImage | undefined | null>()
|
||||
|
||||
const onPressCancel = useCallback(() => {
|
||||
store.shell.closeModal()
|
||||
}, [store])
|
||||
closeModal()
|
||||
}, [closeModal])
|
||||
|
||||
const onSelectNewAvatar = useCallback(
|
||||
async (img: RNImage | null) => {
|
||||
|
|
@ -123,7 +125,7 @@ export function Component({
|
|||
Toast.show(`${purposeLabel} list created`)
|
||||
onSave?.(res.uri)
|
||||
}
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
} catch (e: any) {
|
||||
if (isNetworkError(e)) {
|
||||
setError(
|
||||
|
|
@ -141,6 +143,7 @@ export function Component({
|
|||
error,
|
||||
onSave,
|
||||
store,
|
||||
closeModal,
|
||||
activePurpose,
|
||||
isCurateList,
|
||||
purposeLabel,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
|||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||
import {cleanError} from 'lib/strings/errors'
|
||||
import {resetToTab} from '../../../Navigation'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['60%']
|
||||
|
||||
|
|
@ -24,6 +25,7 @@ export function Component({}: {}) {
|
|||
const pal = usePalette('default')
|
||||
const theme = useTheme()
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const [isEmailSent, setIsEmailSent] = React.useState<boolean>(false)
|
||||
const [confirmCode, setConfirmCode] = React.useState<string>('')
|
||||
|
|
@ -55,14 +57,14 @@ export function Component({}: {}) {
|
|||
Toast.show('Your account has been deleted')
|
||||
resetToTab('HomeTab')
|
||||
store.session.clear()
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
} catch (e: any) {
|
||||
setError(cleanError(e))
|
||||
}
|
||||
setIsProcessing(false)
|
||||
}
|
||||
const onCancel = () => {
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
}
|
||||
return (
|
||||
<View style={[styles.container, pal.view]}>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import {gradients, s} from 'lib/styles'
|
|||
import {useTheme} from 'lib/ThemeContext'
|
||||
import {Text} from '../util/text/Text'
|
||||
import LinearGradient from 'react-native-linear-gradient'
|
||||
import {useStores} from 'state/index'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import ImageEditor, {Position} from 'react-avatar-editor'
|
||||
import {TextInput} from './util'
|
||||
|
|
@ -19,6 +18,7 @@ import {Slider} from '@miblanchard/react-native-slider'
|
|||
import {MaterialIcons} from '@expo/vector-icons'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import {getKeys} from 'lib/type-assertions'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['80%']
|
||||
|
||||
|
|
@ -52,9 +52,9 @@ export const Component = observer(function EditImageImpl({
|
|||
}: Props) {
|
||||
const pal = usePalette('default')
|
||||
const theme = useTheme()
|
||||
const store = useStores()
|
||||
const windowDimensions = useWindowDimensions()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const {closeModal} = useModalControls()
|
||||
|
||||
const {
|
||||
aspectRatio,
|
||||
|
|
@ -128,8 +128,8 @@ export const Component = observer(function EditImageImpl({
|
|||
}, [image])
|
||||
|
||||
const onCloseModal = useCallback(() => {
|
||||
store.shell.closeModal()
|
||||
}, [store.shell])
|
||||
closeModal()
|
||||
}, [closeModal])
|
||||
|
||||
const onPressCancel = useCallback(async () => {
|
||||
await gallery.previous(image)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import LinearGradient from 'react-native-linear-gradient'
|
|||
import {Image as RNImage} from 'react-native-image-crop-picker'
|
||||
import {Text} from '../util/text/Text'
|
||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||
import {useStores} from 'state/index'
|
||||
import {ProfileModel} from 'state/models/content/profile'
|
||||
import {s, colors, gradients} from 'lib/styles'
|
||||
import {enforceLen} from 'lib/strings/helpers'
|
||||
|
|
@ -27,6 +26,7 @@ import {useAnalytics} from 'lib/analytics/analytics'
|
|||
import {cleanError, isNetworkError} from 'lib/strings/errors'
|
||||
import Animated, {FadeOut} from 'react-native-reanimated'
|
||||
import {isWeb} from 'platform/detection'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
const AnimatedTouchableOpacity =
|
||||
Animated.createAnimatedComponent(TouchableOpacity)
|
||||
|
|
@ -40,11 +40,11 @@ export function Component({
|
|||
profileView: ProfileModel
|
||||
onUpdate?: () => void
|
||||
}) {
|
||||
const store = useStores()
|
||||
const [error, setError] = useState<string>('')
|
||||
const pal = usePalette('default')
|
||||
const theme = useTheme()
|
||||
const {track} = useAnalytics()
|
||||
const {closeModal} = useModalControls()
|
||||
|
||||
const [isProcessing, setProcessing] = useState<boolean>(false)
|
||||
const [displayName, setDisplayName] = useState<string>(
|
||||
|
|
@ -66,7 +66,7 @@ export function Component({
|
|||
RNImage | undefined | null
|
||||
>()
|
||||
const onPressCancel = () => {
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
}
|
||||
const onSelectNewAvatar = useCallback(
|
||||
async (img: RNImage | null) => {
|
||||
|
|
@ -123,7 +123,7 @@ export function Component({
|
|||
)
|
||||
Toast.show('Profile updated')
|
||||
onUpdate?.()
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
} catch (e: any) {
|
||||
if (isNetworkError(e)) {
|
||||
setError(
|
||||
|
|
@ -141,7 +141,7 @@ export function Component({
|
|||
error,
|
||||
profileView,
|
||||
onUpdate,
|
||||
store,
|
||||
closeModal,
|
||||
displayName,
|
||||
description,
|
||||
newUserAvatar,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import {ScrollView} from './util'
|
|||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {isWeb} from 'platform/detection'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {useInvitesState, useInvitesAPI} from '#/state/invites'
|
||||
import {UserInfoText} from '../util/UserInfoText'
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
|
|
@ -25,11 +26,12 @@ export const snapPoints = ['70%']
|
|||
export function Component({}: {}) {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const {isTabletOrDesktop} = useWebMediaQueries()
|
||||
|
||||
const onClose = React.useCallback(() => {
|
||||
store.shell.closeModal()
|
||||
}, [store])
|
||||
closeModal()
|
||||
}, [closeModal])
|
||||
|
||||
if (store.me.invites.length === 0) {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ import {observer} from 'mobx-react-lite'
|
|||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {Text} from '../util/text/Text'
|
||||
import {Button} from '../util/forms/Button'
|
||||
import {useStores} from 'state/index'
|
||||
import {s, colors} from 'lib/styles'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {isWeb} from 'platform/detection'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {isPossiblyAUrl, splitApexDomain} from 'lib/strings/url-helpers'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['50%']
|
||||
|
||||
|
|
@ -22,12 +22,12 @@ export const Component = observer(function Component({
|
|||
href: string
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const potentiallyMisleading = isPossiblyAUrl(text)
|
||||
|
||||
const onPressVisit = () => {
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
Linking.openURL(href)
|
||||
}
|
||||
|
||||
|
|
@ -83,7 +83,7 @@ export const Component = observer(function Component({
|
|||
<Button
|
||||
testID="cancelBtn"
|
||||
type="default"
|
||||
onPress={() => store.shell.closeModal()}
|
||||
onPress={() => closeModal()}
|
||||
accessibilityLabel="Cancel"
|
||||
accessibilityHint=""
|
||||
label="Cancel"
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import {cleanError} from 'lib/strings/errors'
|
|||
import {sanitizeDisplayName} from 'lib/strings/display-names'
|
||||
import {sanitizeHandle} from 'lib/strings/handles'
|
||||
import {HITSLOP_20} from '#/lib/constants'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['90%']
|
||||
|
||||
|
|
@ -38,6 +39,7 @@ export const Component = observer(function Component({
|
|||
}) {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const [query, setQuery] = useState('')
|
||||
const autocompleteView = useMemo<UserAutocompleteModel>(
|
||||
|
|
@ -146,7 +148,7 @@ export const Component = observer(function Component({
|
|||
<Button
|
||||
testID="doneBtn"
|
||||
type="default"
|
||||
onPress={() => store.shell.closeModal()}
|
||||
onPress={() => closeModal()}
|
||||
accessibilityLabel="Done"
|
||||
accessibilityHint=""
|
||||
label="Done"
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ import {StyleSheet} from 'react-native'
|
|||
import {SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import BottomSheet from '@gorhom/bottom-sheet'
|
||||
import {useStores} from 'state/index'
|
||||
import {createCustomBackdrop} from '../util/BottomSheetCustomBackdrop'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {timeout} from 'lib/async/timeout'
|
||||
import {navigate} from '../../../Navigation'
|
||||
import once from 'lodash.once'
|
||||
|
||||
import {useModals, useModalControls} from '#/state/modals'
|
||||
import * as ConfirmModal from './Confirm'
|
||||
import * as EditProfileModal from './EditProfile'
|
||||
import * as ProfilePreviewModal from './ProfilePreview'
|
||||
|
|
@ -41,17 +41,17 @@ const DEFAULT_SNAPPOINTS = ['90%']
|
|||
const HANDLE_HEIGHT = 24
|
||||
|
||||
export const ModalsContainer = observer(function ModalsContainer() {
|
||||
const store = useStores()
|
||||
const {isModalActive, activeModals} = useModals()
|
||||
const {closeModal} = useModalControls()
|
||||
const bottomSheetRef = useRef<BottomSheet>(null)
|
||||
const pal = usePalette('default')
|
||||
const safeAreaInsets = useSafeAreaInsets()
|
||||
|
||||
const activeModal =
|
||||
store.shell.activeModals[store.shell.activeModals.length - 1]
|
||||
const activeModal = activeModals[activeModals.length - 1]
|
||||
|
||||
const navigateOnce = once(navigate)
|
||||
|
||||
const onBottomSheetAnimate = (fromIndex: number, toIndex: number) => {
|
||||
const onBottomSheetAnimate = (_fromIndex: number, toIndex: number) => {
|
||||
if (activeModal?.name === 'profile-preview' && toIndex === 1) {
|
||||
// begin loading the profile screen behind the scenes
|
||||
navigateOnce('Profile', {name: activeModal.did})
|
||||
|
|
@ -59,7 +59,7 @@ export const ModalsContainer = observer(function ModalsContainer() {
|
|||
}
|
||||
const onBottomSheetChange = async (snapPoint: number) => {
|
||||
if (snapPoint === -1) {
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
} else if (activeModal?.name === 'profile-preview' && snapPoint === 1) {
|
||||
await navigateOnce('Profile', {name: activeModal.did})
|
||||
// There is no particular callback for when the view has actually been presented.
|
||||
|
|
@ -67,21 +67,21 @@ export const ModalsContainer = observer(function ModalsContainer() {
|
|||
// It's acceptable because the data is already being fetched + it usually takes longer anyway.
|
||||
// TODO: Figure out why avatar/cover don't always show instantly from cache.
|
||||
await timeout(200)
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
}
|
||||
}
|
||||
const onClose = () => {
|
||||
bottomSheetRef.current?.close()
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (store.shell.isModalActive) {
|
||||
if (isModalActive) {
|
||||
bottomSheetRef.current?.expand()
|
||||
} else {
|
||||
bottomSheetRef.current?.close()
|
||||
}
|
||||
}, [store.shell.isModalActive, bottomSheetRef, activeModal?.name])
|
||||
}, [isModalActive, bottomSheetRef, activeModal?.name])
|
||||
|
||||
let needsSafeTopInset = false
|
||||
let snapPoints: (string | number)[] = DEFAULT_SNAPPOINTS
|
||||
|
|
@ -184,12 +184,12 @@ export const ModalsContainer = observer(function ModalsContainer() {
|
|||
snapPoints={snapPoints}
|
||||
topInset={topInset}
|
||||
handleHeight={HANDLE_HEIGHT}
|
||||
index={store.shell.isModalActive ? 0 : -1}
|
||||
index={isModalActive ? 0 : -1}
|
||||
enablePanDownToClose
|
||||
android_keyboardInputMode="adjustResize"
|
||||
keyboardBlurBehavior="restore"
|
||||
backdropComponent={
|
||||
store.shell.isModalActive ? createCustomBackdrop(onClose) : undefined
|
||||
isModalActive ? createCustomBackdrop(onClose) : undefined
|
||||
}
|
||||
handleIndicatorStyle={{backgroundColor: pal.text.color}}
|
||||
handleStyle={[styles.handle, pal.view]}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import React from 'react'
|
||||
import {TouchableWithoutFeedback, StyleSheet, View} from 'react-native'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import {useStores} from 'state/index'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import type {Modal as ModalIface} from 'state/models/ui/shell'
|
||||
import type {Modal as ModalIface} from '#/state/modals'
|
||||
|
||||
import {useModals, useModalControls} from '#/state/modals'
|
||||
import * as ConfirmModal from './Confirm'
|
||||
import * as EditProfileModal from './EditProfile'
|
||||
import * as ProfilePreviewModal from './ProfilePreview'
|
||||
|
|
@ -34,15 +34,15 @@ import * as ChangeEmailModal from './ChangeEmail'
|
|||
import * as LinkWarningModal from './LinkWarning'
|
||||
|
||||
export const ModalsContainer = observer(function ModalsContainer() {
|
||||
const store = useStores()
|
||||
const {isModalActive, activeModals} = useModals()
|
||||
|
||||
if (!store.shell.isModalActive) {
|
||||
if (!isModalActive) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{store.shell.activeModals.map((modal, i) => (
|
||||
{activeModals.map((modal, i) => (
|
||||
<Modal key={`modal-${i}`} modal={modal} />
|
||||
))}
|
||||
</>
|
||||
|
|
@ -50,11 +50,12 @@ export const ModalsContainer = observer(function ModalsContainer() {
|
|||
})
|
||||
|
||||
function Modal({modal}: {modal: ModalIface}) {
|
||||
const store = useStores()
|
||||
const {isModalActive} = useModals()
|
||||
const {closeModal} = useModalControls()
|
||||
const pal = usePalette('default')
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
|
||||
if (!store.shell.isModalActive) {
|
||||
if (!isModalActive) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
|
@ -62,7 +63,7 @@ function Modal({modal}: {modal: ModalIface}) {
|
|||
if (modal.name === 'crop-image' || modal.name === 'edit-image') {
|
||||
return // dont close on mask presses during crop
|
||||
}
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
}
|
||||
const onInnerPress = () => {
|
||||
// TODO: can we use prevent default?
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import React from 'react'
|
||||
import {StyleSheet, View} from 'react-native'
|
||||
import {ModerationUI} from '@atproto/api'
|
||||
import {useStores} from 'state/index'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {s} from 'lib/styles'
|
||||
import {Text} from '../util/text/Text'
|
||||
|
|
@ -10,6 +9,7 @@ import {usePalette} from 'lib/hooks/usePalette'
|
|||
import {isWeb} from 'platform/detection'
|
||||
import {listUriToHref} from 'lib/strings/url-helpers'
|
||||
import {Button} from '../util/forms/Button'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = [300]
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ export function Component({
|
|||
context: 'account' | 'content'
|
||||
moderation: ModerationUI
|
||||
}) {
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const pal = usePalette('default')
|
||||
|
||||
|
|
@ -99,10 +99,7 @@ export function Component({
|
|||
{description}
|
||||
</Text>
|
||||
<View style={s.flex1} />
|
||||
<Button
|
||||
type="primary"
|
||||
style={styles.btn}
|
||||
onPress={() => store.shell.closeModal()}>
|
||||
<Button type="primary" style={styles.btn} onPress={() => closeModal()}>
|
||||
<Text type="button-lg" style={[pal.textLight, s.textCenter, s.white]}>
|
||||
Okay
|
||||
</Text>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import React from 'react'
|
||||
import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
||||
import LinearGradient from 'react-native-linear-gradient'
|
||||
import {useStores} from 'state/index'
|
||||
import {s, colors, gradients} from 'lib/styles'
|
||||
import {Text} from '../util/text/Text'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {RepostIcon} from 'lib/icons'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = [250]
|
||||
|
||||
|
|
@ -20,10 +20,10 @@ export function Component({
|
|||
isReposted: boolean
|
||||
// TODO: Add author into component
|
||||
}) {
|
||||
const store = useStores()
|
||||
const pal = usePalette('default')
|
||||
const {closeModal} = useModalControls()
|
||||
const onPress = async () => {
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import React, {useState} from 'react'
|
|||
import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import {Text} from '../util/text/Text'
|
||||
import {useStores} from 'state/index'
|
||||
import {s, colors} from 'lib/styles'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
|
|
@ -10,6 +9,7 @@ import {isWeb} from 'platform/detection'
|
|||
import {Button} from '../util/forms/Button'
|
||||
import {SelectableBtn} from '../util/forms/SelectableBtn'
|
||||
import {ScrollView} from 'view/com/modals/util'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
const ADULT_CONTENT_LABELS = ['sexual', 'nudity', 'porn']
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ export const Component = observer(function Component({
|
|||
onChange: (labels: string[]) => void
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const [selected, setSelected] = useState(labels)
|
||||
|
||||
|
|
@ -143,7 +143,7 @@ export const Component = observer(function Component({
|
|||
<TouchableOpacity
|
||||
testID="confirmBtn"
|
||||
onPress={() => {
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
}}
|
||||
style={styles.btn}
|
||||
accessibilityRole="button"
|
||||
|
|
|
|||
|
|
@ -6,26 +6,26 @@ import {
|
|||
} from '@fortawesome/react-native-fontawesome'
|
||||
import {ScrollView, TextInput} from './util'
|
||||
import {Text} from '../util/text/Text'
|
||||
import {useStores} from 'state/index'
|
||||
import {s, colors} from 'lib/styles'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useTheme} from 'lib/ThemeContext'
|
||||
import {LOCAL_DEV_SERVICE, STAGING_SERVICE, PROD_SERVICE} from 'state/index'
|
||||
import {LOGIN_INCLUDE_DEV_SERVERS} from 'lib/build-flags'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['80%']
|
||||
|
||||
export function Component({onSelect}: {onSelect: (url: string) => void}) {
|
||||
const theme = useTheme()
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const [customUrl, setCustomUrl] = useState<string>('')
|
||||
const {closeModal} = useModalControls()
|
||||
|
||||
const doSelect = (url: string) => {
|
||||
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
||||
url = `https://${url}`
|
||||
}
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
onSelect(url)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import {usePalette} from 'lib/hooks/usePalette'
|
|||
import {isWeb, isAndroid} from 'platform/detection'
|
||||
import isEqual from 'lodash.isequal'
|
||||
import {logger} from '#/logger'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['fullscreen']
|
||||
|
||||
|
|
@ -36,6 +37,7 @@ export const Component = observer(function UserAddRemoveListsImpl({
|
|||
onRemove?: (listUri: string) => void
|
||||
}) {
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const pal = usePalette('default')
|
||||
const palPrimary = usePalette('primary')
|
||||
const palInverted = usePalette('inverted')
|
||||
|
|
@ -69,8 +71,8 @@ export const Component = observer(function UserAddRemoveListsImpl({
|
|||
}, [memberships, listsList, store, setSelected, setMembershipsLoaded])
|
||||
|
||||
const onPressCancel = useCallback(() => {
|
||||
store.shell.closeModal()
|
||||
}, [store])
|
||||
closeModal()
|
||||
}, [closeModal])
|
||||
|
||||
const onPressSave = useCallback(async () => {
|
||||
let changes
|
||||
|
|
@ -87,8 +89,8 @@ export const Component = observer(function UserAddRemoveListsImpl({
|
|||
for (const uri of changes.removed) {
|
||||
onRemove?.(uri)
|
||||
}
|
||||
store.shell.closeModal()
|
||||
}, [store, selected, memberships, onAdd, onRemove])
|
||||
closeModal()
|
||||
}, [closeModal, selected, memberships, onAdd, onRemove])
|
||||
|
||||
const onToggleSelected = useCallback(
|
||||
(uri: string) => {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import {usePalette} from 'lib/hooks/usePalette'
|
|||
import {isWeb} from 'platform/detection'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {cleanError} from 'lib/strings/errors'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['90%']
|
||||
|
||||
|
|
@ -43,6 +44,7 @@ export const Component = observer(function Component({
|
|||
const [isProcessing, setIsProcessing] = useState<boolean>(false)
|
||||
const [error, setError] = useState<string>('')
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const {openModal, closeModal} = useModalControls()
|
||||
|
||||
const onSendEmail = async () => {
|
||||
setError('')
|
||||
|
|
@ -67,7 +69,7 @@ export const Component = observer(function Component({
|
|||
})
|
||||
store.session.updateLocalAccountData({emailConfirmed: true})
|
||||
Toast.show('Email verified')
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
} catch (e) {
|
||||
setError(cleanError(String(e)))
|
||||
} finally {
|
||||
|
|
@ -76,8 +78,8 @@ export const Component = observer(function Component({
|
|||
}
|
||||
|
||||
const onEmailIncorrect = () => {
|
||||
store.shell.closeModal()
|
||||
store.shell.openModal({name: 'change-email'})
|
||||
closeModal()
|
||||
openModal({name: 'change-email'})
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -224,7 +226,7 @@ export const Component = observer(function Component({
|
|||
<Button
|
||||
testID="cancelBtn"
|
||||
type="default"
|
||||
onPress={() => store.shell.closeModal()}
|
||||
onPress={() => closeModal()}
|
||||
accessibilityLabel={
|
||||
stage === Stages.Reminder ? 'Not right now' : 'Cancel'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,19 +12,19 @@ import {
|
|||
} from '@fortawesome/react-native-fontawesome'
|
||||
import LinearGradient from 'react-native-linear-gradient'
|
||||
import {Text} from '../util/text/Text'
|
||||
import {useStores} from 'state/index'
|
||||
import {s, gradients} from 'lib/styles'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useTheme} from 'lib/ThemeContext'
|
||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||
import {cleanError} from 'lib/strings/errors'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['80%']
|
||||
|
||||
export function Component({}: {}) {
|
||||
const pal = usePalette('default')
|
||||
const theme = useTheme()
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const [email, setEmail] = React.useState<string>('')
|
||||
const [isEmailSent, setIsEmailSent] = React.useState<boolean>(false)
|
||||
const [isProcessing, setIsProcessing] = React.useState<boolean>(false)
|
||||
|
|
@ -54,7 +54,7 @@ export function Component({}: {}) {
|
|||
setIsProcessing(false)
|
||||
}
|
||||
const onCancel = () => {
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ import {Text} from 'view/com/util/text/Text'
|
|||
import {Dimensions} from 'lib/media/types'
|
||||
import {getDataUriSize} from 'lib/media/util'
|
||||
import {s, gradients} from 'lib/styles'
|
||||
import {useStores} from 'state/index'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {SquareIcon, RectWideIcon, RectTallIcon} from 'lib/icons'
|
||||
import {Image as RNImage} from 'react-native-image-crop-picker'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
enum AspectRatio {
|
||||
Square = 'square',
|
||||
|
|
@ -33,7 +33,7 @@ export function Component({
|
|||
uri: string
|
||||
onSelect: (img?: RNImage) => void
|
||||
}) {
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const pal = usePalette('default')
|
||||
const [as, setAs] = React.useState<AspectRatio>(AspectRatio.Square)
|
||||
const [scale, setScale] = React.useState<number>(1)
|
||||
|
|
@ -43,7 +43,7 @@ export function Component({
|
|||
|
||||
const onPressCancel = () => {
|
||||
onSelect(undefined)
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
}
|
||||
const onPressDone = () => {
|
||||
const canvas = editorRef.current?.getImageScaledToCanvas()
|
||||
|
|
@ -59,7 +59,7 @@ export function Component({
|
|||
} else {
|
||||
onSelect(undefined)
|
||||
}
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
}
|
||||
|
||||
let cropperStyle
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import React from 'react'
|
||||
import {StyleSheet, View} from 'react-native'
|
||||
import {ScrollView} from '../util'
|
||||
import {useStores} from 'state/index'
|
||||
import {Text} from '../../util/text/Text'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
|
|
@ -9,6 +8,7 @@ import {deviceLocales} from 'platform/detection'
|
|||
import {LANGUAGES, LANGUAGES_MAP_CODE2} from '../../../../locale/languages'
|
||||
import {LanguageToggle} from './LanguageToggle'
|
||||
import {ConfirmLanguagesButton} from './ConfirmLanguagesButton'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {
|
||||
useLanguagePrefs,
|
||||
useSetLanguagePrefs,
|
||||
|
|
@ -18,14 +18,14 @@ import {
|
|||
export const snapPoints = ['100%']
|
||||
|
||||
export function Component({}: {}) {
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const langPrefs = useLanguagePrefs()
|
||||
const setLangPrefs = useSetLanguagePrefs()
|
||||
const pal = usePalette('default')
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const onPressDone = React.useCallback(() => {
|
||||
store.shell.closeModal()
|
||||
}, [store])
|
||||
closeModal()
|
||||
}, [closeModal])
|
||||
|
||||
const languages = React.useMemo(() => {
|
||||
const langs = LANGUAGES.filter(
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import React from 'react'
|
|||
import {StyleSheet, View} from 'react-native'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import {ScrollView} from '../util'
|
||||
import {useStores} from 'state/index'
|
||||
import {Text} from '../../util/text/Text'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
|
|
@ -10,6 +9,7 @@ import {deviceLocales} from 'platform/detection'
|
|||
import {LANGUAGES, LANGUAGES_MAP_CODE2} from '../../../../locale/languages'
|
||||
import {ConfirmLanguagesButton} from './ConfirmLanguagesButton'
|
||||
import {ToggleButton} from 'view/com/util/forms/ToggleButton'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {
|
||||
useLanguagePrefs,
|
||||
useSetLanguagePrefs,
|
||||
|
|
@ -20,14 +20,14 @@ import {
|
|||
export const snapPoints = ['100%']
|
||||
|
||||
export const Component = observer(function PostLanguagesSettingsImpl() {
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const langPrefs = useLanguagePrefs()
|
||||
const setLangPrefs = useSetLanguagePrefs()
|
||||
const pal = usePalette('default')
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const onPressDone = React.useCallback(() => {
|
||||
store.shell.closeModal()
|
||||
}, [store])
|
||||
closeModal()
|
||||
}, [closeModal])
|
||||
|
||||
const languages = React.useMemo(() => {
|
||||
const langs = LANGUAGES.filter(
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {SendReportButton} from './SendReportButton'
|
|||
import {InputIssueDetails} from './InputIssueDetails'
|
||||
import {ReportReasonOptions} from './ReasonOptions'
|
||||
import {CollectionId} from './types'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
const DMCA_LINK = 'https://blueskyweb.xyz/support/copyright'
|
||||
|
||||
|
|
@ -37,6 +38,7 @@ type ReportComponentProps =
|
|||
|
||||
export function Component(content: ReportComponentProps) {
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const pal = usePalette('default')
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const [isProcessing, setIsProcessing] = useState(false)
|
||||
|
|
@ -60,7 +62,7 @@ export function Component(content: ReportComponentProps) {
|
|||
try {
|
||||
if (issue === '__copyright__') {
|
||||
Linking.openURL(DMCA_LINK)
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
return
|
||||
}
|
||||
const $type = !isAccountReport
|
||||
|
|
@ -76,7 +78,7 @@ export function Component(content: ReportComponentProps) {
|
|||
})
|
||||
Toast.show("Thank you for your report! We'll look into it promptly.")
|
||||
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
return
|
||||
} catch (e: any) {
|
||||
setError(cleanError(e))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue