Improve Android haptic, offer toggle for haptics in the app (#3482)
* improve android haptics, offer toggle for haptics * update haptics.ts * default to false * simplify to `playHaptic` * just leave them as `feedInfo` * use a hook for `playHaptic` * missed one of them
This commit is contained in:
parent
9007810cdb
commit
740cd029d7
14 changed files with 235 additions and 204 deletions
|
@ -27,7 +27,7 @@ import {truncateAndInvalidate} from '#/state/queries/util'
|
|||
import {useSession} from '#/state/session'
|
||||
import {useComposerControls} from '#/state/shell/composer'
|
||||
import {useAnalytics} from 'lib/analytics/analytics'
|
||||
import {Haptics} from 'lib/haptics'
|
||||
import {useHaptics} from 'lib/haptics'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useSetTitle} from 'lib/hooks/useSetTitle'
|
||||
import {ComposeIcon2} from 'lib/icons'
|
||||
|
@ -159,6 +159,7 @@ export function ProfileFeedScreenInner({
|
|||
const reportDialogControl = useReportDialogControl()
|
||||
const {openComposer} = useComposerControls()
|
||||
const {track} = useAnalytics()
|
||||
const playHaptic = useHaptics()
|
||||
const feedSectionRef = React.useRef<SectionRef>(null)
|
||||
const isScreenFocused = useIsFocused()
|
||||
|
||||
|
@ -201,7 +202,7 @@ export function ProfileFeedScreenInner({
|
|||
|
||||
const onToggleSaved = React.useCallback(async () => {
|
||||
try {
|
||||
Haptics.default()
|
||||
playHaptic()
|
||||
|
||||
if (isSaved) {
|
||||
await removeFeed({uri: feedInfo.uri})
|
||||
|
@ -221,18 +222,19 @@ export function ProfileFeedScreenInner({
|
|||
logger.error('Failed up update feeds', {message: err})
|
||||
}
|
||||
}, [
|
||||
feedInfo,
|
||||
playHaptic,
|
||||
isSaved,
|
||||
saveFeed,
|
||||
removeFeed,
|
||||
resetSaveFeed,
|
||||
feedInfo,
|
||||
resetRemoveFeed,
|
||||
_,
|
||||
saveFeed,
|
||||
resetSaveFeed,
|
||||
])
|
||||
|
||||
const onTogglePinned = React.useCallback(async () => {
|
||||
try {
|
||||
Haptics.default()
|
||||
playHaptic()
|
||||
|
||||
if (isPinned) {
|
||||
await unpinFeed({uri: feedInfo.uri})
|
||||
|
@ -245,7 +247,16 @@ export function ProfileFeedScreenInner({
|
|||
Toast.show(_(msg`There was an issue contacting the server`))
|
||||
logger.error('Failed to toggle pinned feed', {message: e})
|
||||
}
|
||||
}, [isPinned, feedInfo, pinFeed, unpinFeed, resetPinFeed, resetUnpinFeed, _])
|
||||
}, [
|
||||
playHaptic,
|
||||
isPinned,
|
||||
unpinFeed,
|
||||
feedInfo,
|
||||
resetUnpinFeed,
|
||||
pinFeed,
|
||||
resetPinFeed,
|
||||
_,
|
||||
])
|
||||
|
||||
const onPressShare = React.useCallback(() => {
|
||||
const url = toShareUrl(feedInfo.route.href)
|
||||
|
@ -517,6 +528,7 @@ function AboutSection({
|
|||
const [likeUri, setLikeUri] = React.useState(feedInfo.likeUri)
|
||||
const {hasSession} = useSession()
|
||||
const {track} = useAnalytics()
|
||||
const playHaptic = useHaptics()
|
||||
const {mutateAsync: likeFeed, isPending: isLikePending} = useLikeMutation()
|
||||
const {mutateAsync: unlikeFeed, isPending: isUnlikePending} =
|
||||
useUnlikeMutation()
|
||||
|
@ -527,7 +539,7 @@ function AboutSection({
|
|||
|
||||
const onToggleLiked = React.useCallback(async () => {
|
||||
try {
|
||||
Haptics.default()
|
||||
playHaptic()
|
||||
|
||||
if (isLiked && likeUri) {
|
||||
await unlikeFeed({uri: likeUri})
|
||||
|
@ -546,7 +558,7 @@ function AboutSection({
|
|||
)
|
||||
logger.error('Failed up toggle like', {message: err})
|
||||
}
|
||||
}, [likeUri, isLiked, feedInfo, likeFeed, unlikeFeed, track, _])
|
||||
}, [playHaptic, isLiked, likeUri, unlikeFeed, track, likeFeed, feedInfo, _])
|
||||
|
||||
return (
|
||||
<View style={[styles.aboutSectionContainer]}>
|
||||
|
|
|
@ -1,69 +1,70 @@
|
|||
import React, {useCallback, useMemo} from 'react'
|
||||
import {Pressable, StyleSheet, View} from 'react-native'
|
||||
import {useFocusEffect, useIsFocused} from '@react-navigation/native'
|
||||
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {AppBskyGraphDefs, AtUri, RichText as RichTextAPI} from '@atproto/api'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useFocusEffect, useIsFocused} from '@react-navigation/native'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
import {PagerWithHeader} from 'view/com/pager/PagerWithHeader'
|
||||
import {ProfileSubpageHeader} from 'view/com/profile/ProfileSubpageHeader'
|
||||
import {Feed} from 'view/com/posts/Feed'
|
||||
import {Text} from 'view/com/util/text/Text'
|
||||
import {NativeDropdown, DropdownItem} from 'view/com/util/forms/NativeDropdown'
|
||||
import {CenteredView} from 'view/com/util/Views'
|
||||
import {EmptyState} from 'view/com/util/EmptyState'
|
||||
import {LoadingScreen} from 'view/com/util/LoadingScreen'
|
||||
import {RichText} from '#/components/RichText'
|
||||
import {Button} from 'view/com/util/forms/Button'
|
||||
import {TextLink} from 'view/com/util/Link'
|
||||
import {ListRef} from 'view/com/util/List'
|
||||
import * as Toast from 'view/com/util/Toast'
|
||||
import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn'
|
||||
import {FAB} from 'view/com/util/fab/FAB'
|
||||
import {Haptics} from 'lib/haptics'
|
||||
|
||||
import {useAnalytics} from '#/lib/analytics/analytics'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {logger} from '#/logger'
|
||||
import {isNative, isWeb} from '#/platform/detection'
|
||||
import {listenSoftReset} from '#/state/events'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {
|
||||
useListBlockMutation,
|
||||
useListDeleteMutation,
|
||||
useListMuteMutation,
|
||||
useListQuery,
|
||||
} from '#/state/queries/list'
|
||||
import {FeedDescriptor} from '#/state/queries/post-feed'
|
||||
import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed'
|
||||
import {
|
||||
usePinFeedMutation,
|
||||
usePreferencesQuery,
|
||||
useSetSaveFeedsMutation,
|
||||
useUnpinFeedMutation,
|
||||
} from '#/state/queries/preferences'
|
||||
import {useResolveUriQuery} from '#/state/queries/resolve-uri'
|
||||
import {truncateAndInvalidate} from '#/state/queries/util'
|
||||
import {useSession} from '#/state/session'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {useComposerControls} from '#/state/shell/composer'
|
||||
import {useHaptics} from 'lib/haptics'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useSetTitle} from 'lib/hooks/useSetTitle'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed'
|
||||
import {NavigationProp} from 'lib/routes/types'
|
||||
import {toShareUrl} from 'lib/strings/url-helpers'
|
||||
import {shareUrl} from 'lib/sharing'
|
||||
import {s} from 'lib/styles'
|
||||
import {sanitizeHandle} from 'lib/strings/handles'
|
||||
import {makeProfileLink, makeListLink} from 'lib/routes/links'
|
||||
import {ComposeIcon2} from 'lib/icons'
|
||||
import {makeListLink, makeProfileLink} from 'lib/routes/links'
|
||||
import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types'
|
||||
import {NavigationProp} from 'lib/routes/types'
|
||||
import {shareUrl} from 'lib/sharing'
|
||||
import {sanitizeHandle} from 'lib/strings/handles'
|
||||
import {toShareUrl} from 'lib/strings/url-helpers'
|
||||
import {s} from 'lib/styles'
|
||||
import {ListMembers} from '#/view/com/lists/ListMembers'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {ReportDialog, useReportDialogControl} from '#/components/ReportDialog'
|
||||
import {useResolveUriQuery} from '#/state/queries/resolve-uri'
|
||||
import {
|
||||
useListQuery,
|
||||
useListMuteMutation,
|
||||
useListBlockMutation,
|
||||
useListDeleteMutation,
|
||||
} from '#/state/queries/list'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {useSession} from '#/state/session'
|
||||
import {useComposerControls} from '#/state/shell/composer'
|
||||
import {isNative, isWeb} from '#/platform/detection'
|
||||
import {truncateAndInvalidate} from '#/state/queries/util'
|
||||
import {
|
||||
usePreferencesQuery,
|
||||
usePinFeedMutation,
|
||||
useUnpinFeedMutation,
|
||||
useSetSaveFeedsMutation,
|
||||
} from '#/state/queries/preferences'
|
||||
import {logger} from '#/logger'
|
||||
import {useAnalytics} from '#/lib/analytics/analytics'
|
||||
import {listenSoftReset} from '#/state/events'
|
||||
import {PagerWithHeader} from 'view/com/pager/PagerWithHeader'
|
||||
import {Feed} from 'view/com/posts/Feed'
|
||||
import {ProfileSubpageHeader} from 'view/com/profile/ProfileSubpageHeader'
|
||||
import {EmptyState} from 'view/com/util/EmptyState'
|
||||
import {FAB} from 'view/com/util/fab/FAB'
|
||||
import {Button} from 'view/com/util/forms/Button'
|
||||
import {DropdownItem, NativeDropdown} from 'view/com/util/forms/NativeDropdown'
|
||||
import {TextLink} from 'view/com/util/Link'
|
||||
import {ListRef} from 'view/com/util/List'
|
||||
import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn'
|
||||
import {LoadingScreen} from 'view/com/util/LoadingScreen'
|
||||
import {Text} from 'view/com/util/text/Text'
|
||||
import * as Toast from 'view/com/util/Toast'
|
||||
import {CenteredView} from 'view/com/util/Views'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import * as Prompt from '#/components/Prompt'
|
||||
import {useDialogControl} from '#/components/Dialog'
|
||||
import * as Prompt from '#/components/Prompt'
|
||||
import {ReportDialog, useReportDialogControl} from '#/components/ReportDialog'
|
||||
import {RichText} from '#/components/RichText'
|
||||
|
||||
const SECTION_TITLES_CURATE = ['Posts', 'About']
|
||||
const SECTION_TITLES_MOD = ['About']
|
||||
|
@ -254,6 +255,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
|
|||
const {data: preferences} = usePreferencesQuery()
|
||||
const {mutate: setSavedFeeds} = useSetSaveFeedsMutation()
|
||||
const {track} = useAnalytics()
|
||||
const playHaptic = useHaptics()
|
||||
|
||||
const deleteListPromptControl = useDialogControl()
|
||||
const subscribeMutePromptControl = useDialogControl()
|
||||
|
@ -263,7 +265,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
|
|||
const isSaved = preferences?.feeds?.saved?.includes(list.uri)
|
||||
|
||||
const onTogglePinned = React.useCallback(async () => {
|
||||
Haptics.default()
|
||||
playHaptic()
|
||||
|
||||
try {
|
||||
if (isPinned) {
|
||||
|
@ -275,7 +277,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
|
|||
Toast.show(_(msg`There was an issue contacting the server`))
|
||||
logger.error('Failed to toggle pinned feed', {message: e})
|
||||
}
|
||||
}, [list.uri, isPinned, pinFeed, unpinFeed, _])
|
||||
}, [playHaptic, isPinned, unpinFeed, list.uri, pinFeed, _])
|
||||
|
||||
const onSubscribeMute = useCallback(async () => {
|
||||
try {
|
||||
|
|
|
@ -1,31 +1,32 @@
|
|||
import React from 'react'
|
||||
import {StyleSheet, View, ActivityIndicator, Pressable} from 'react-native'
|
||||
import {ActivityIndicator, Pressable, StyleSheet, View} from 'react-native'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useFocusEffect} from '@react-navigation/native'
|
||||
import {NativeStackScreenProps} from '@react-navigation/native-stack'
|
||||
|
||||
import {track} from '#/lib/analytics/analytics'
|
||||
import {useAnalytics} from 'lib/analytics/analytics'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {CommonNavigatorParams} from 'lib/routes/types'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {ViewHeader} from 'view/com/util/ViewHeader'
|
||||
import {ScrollView, CenteredView} from 'view/com/util/Views'
|
||||
import {Text} from 'view/com/util/text/Text'
|
||||
import {s, colors} from 'lib/styles'
|
||||
import {FeedSourceCard} from 'view/com/feeds/FeedSourceCard'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import * as Toast from 'view/com/util/Toast'
|
||||
import {Haptics} from 'lib/haptics'
|
||||
import {TextLink} from 'view/com/util/Link'
|
||||
import {logger} from '#/logger'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {
|
||||
usePreferencesQuery,
|
||||
usePinFeedMutation,
|
||||
useUnpinFeedMutation,
|
||||
usePreferencesQuery,
|
||||
useSetSaveFeedsMutation,
|
||||
useUnpinFeedMutation,
|
||||
} from '#/state/queries/preferences'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {useAnalytics} from 'lib/analytics/analytics'
|
||||
import {useHaptics} from 'lib/haptics'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {CommonNavigatorParams} from 'lib/routes/types'
|
||||
import {colors, s} from 'lib/styles'
|
||||
import {FeedSourceCard} from 'view/com/feeds/FeedSourceCard'
|
||||
import {TextLink} from 'view/com/util/Link'
|
||||
import {Text} from 'view/com/util/text/Text'
|
||||
import * as Toast from 'view/com/util/Toast'
|
||||
import {ViewHeader} from 'view/com/util/ViewHeader'
|
||||
import {CenteredView, ScrollView} from 'view/com/util/Views'
|
||||
|
||||
const HITSLOP_TOP = {
|
||||
top: 20,
|
||||
|
@ -189,13 +190,14 @@ function ListItem({
|
|||
}) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const playHaptic = useHaptics()
|
||||
const {isPending: isPinPending, mutateAsync: pinFeed} = usePinFeedMutation()
|
||||
const {isPending: isUnpinPending, mutateAsync: unpinFeed} =
|
||||
useUnpinFeedMutation()
|
||||
const isPending = isPinPending || isUnpinPending
|
||||
|
||||
const onTogglePinned = React.useCallback(async () => {
|
||||
Haptics.default()
|
||||
playHaptic()
|
||||
|
||||
try {
|
||||
resetSaveFeedsMutationState()
|
||||
|
@ -209,7 +211,15 @@ function ListItem({
|
|||
Toast.show(_(msg`There was an issue contacting the server`))
|
||||
logger.error('Failed to toggle pinned feed', {message: e})
|
||||
}
|
||||
}, [feedUri, isPinned, pinFeed, unpinFeed, resetSaveFeedsMutationState, _])
|
||||
}, [
|
||||
playHaptic,
|
||||
resetSaveFeedsMutationState,
|
||||
isPinned,
|
||||
unpinFeed,
|
||||
feedUri,
|
||||
pinFeed,
|
||||
_,
|
||||
])
|
||||
|
||||
const onPressUp = React.useCallback(async () => {
|
||||
if (!isPinned) return
|
||||
|
|
|
@ -20,10 +20,9 @@ import {useLingui} from '@lingui/react'
|
|||
import {useFocusEffect, useNavigation} from '@react-navigation/native'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {isNative} from '#/platform/detection'
|
||||
import {isIOS, isNative} from '#/platform/detection'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {clearLegacyStorage} from '#/state/persisted/legacy'
|
||||
// TODO import {useInviteCodesQuery} from '#/state/queries/invites'
|
||||
import {clear as clearStorage} from '#/state/persisted/store'
|
||||
import {
|
||||
useRequireAltTextEnabled,
|
||||
|
@ -57,6 +56,10 @@ import {makeProfileLink} from 'lib/routes/links'
|
|||
import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types'
|
||||
import {NavigationProp} from 'lib/routes/types'
|
||||
import {colors, s} from 'lib/styles'
|
||||
import {
|
||||
useHapticsDisabled,
|
||||
useSetHapticsDisabled,
|
||||
} from 'state/preferences/disable-haptics'
|
||||
import {AccountDropdownBtn} from 'view/com/util/AccountDropdownBtn'
|
||||
import {SelectableBtn} from 'view/com/util/forms/SelectableBtn'
|
||||
import {ToggleButton} from 'view/com/util/forms/ToggleButton'
|
||||
|
@ -155,6 +158,8 @@ export function SettingsScreen({}: Props) {
|
|||
const setRequireAltTextEnabled = useSetRequireAltTextEnabled()
|
||||
const inAppBrowserPref = useInAppBrowser()
|
||||
const setUseInAppBrowser = useSetInAppBrowser()
|
||||
const isHapticsDisabled = useHapticsDisabled()
|
||||
const setHapticsDisabled = useSetHapticsDisabled()
|
||||
const onboardingDispatch = useOnboardingDispatch()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
|
@ -162,9 +167,6 @@ export function SettingsScreen({}: Props) {
|
|||
const {openModal} = useModalControls()
|
||||
const {isSwitchingAccounts, accounts, currentAccount} = useSession()
|
||||
const {mutate: clearPreferences} = useClearPreferencesMutation()
|
||||
// TODO
|
||||
// const {data: invites} = useInviteCodesQuery()
|
||||
// const invitesAvailable = invites?.available?.length ?? 0
|
||||
const {setShowLoggedOut} = useLoggedOutViewControls()
|
||||
const closeAllActiveElements = useCloseAllActiveElements()
|
||||
const exportCarControl = useDialogControl()
|
||||
|
@ -220,13 +222,6 @@ export function SettingsScreen({}: Props) {
|
|||
exportCarControl.open()
|
||||
}, [exportCarControl])
|
||||
|
||||
/* TODO
|
||||
const onPressInviteCodes = React.useCallback(() => {
|
||||
track('Settings:InvitecodesButtonClicked')
|
||||
openModal({name: 'invite-codes'})
|
||||
}, [track, openModal])
|
||||
*/
|
||||
|
||||
const onPressLanguageSettings = React.useCallback(() => {
|
||||
navigation.navigate('LanguageSettings')
|
||||
}, [navigation])
|
||||
|
@ -414,58 +409,6 @@ export function SettingsScreen({}: Props) {
|
|||
|
||||
<View style={styles.spacer20} />
|
||||
|
||||
{/* TODO (
|
||||
<>
|
||||
<Text type="xl-bold" style={[pal.text, styles.heading]}>
|
||||
<Trans>Invite a Friend</Trans>
|
||||
</Text>
|
||||
|
||||
<TouchableOpacity
|
||||
testID="inviteFriendBtn"
|
||||
style={[
|
||||
styles.linkCard,
|
||||
pal.view,
|
||||
isSwitchingAccounts && styles.dimmed,
|
||||
]}
|
||||
onPress={isSwitchingAccounts ? undefined : onPressInviteCodes}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Invite`)}
|
||||
accessibilityHint={_(msg`Opens invite code list`)}
|
||||
disabled={invites?.disabled}>
|
||||
<View
|
||||
style={[
|
||||
styles.iconContainer,
|
||||
invitesAvailable > 0 ? primaryBg : pal.btn,
|
||||
]}>
|
||||
<FontAwesomeIcon
|
||||
icon="ticket"
|
||||
style={
|
||||
(invitesAvailable > 0
|
||||
? primaryText
|
||||
: pal.text) as FontAwesomeIconStyle
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
<Text
|
||||
type="lg"
|
||||
style={invitesAvailable > 0 ? pal.link : pal.text}>
|
||||
{invites?.disabled ? (
|
||||
<Trans>
|
||||
Your invite codes are hidden when logged in using an App
|
||||
Password
|
||||
</Trans>
|
||||
) : invitesAvailable === 1 ? (
|
||||
<Trans>{invitesAvailable} invite code available</Trans>
|
||||
) : (
|
||||
<Trans>{invitesAvailable} invite codes available</Trans>
|
||||
)}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<View style={styles.spacer20} />
|
||||
</>
|
||||
)*/}
|
||||
|
||||
<Text type="xl-bold" style={[pal.text, styles.heading]}>
|
||||
<Trans>Accessibility</Trans>
|
||||
</Text>
|
||||
|
@ -738,6 +681,19 @@ export function SettingsScreen({}: Props) {
|
|||
/>
|
||||
</View>
|
||||
)}
|
||||
{isNative && (
|
||||
<View style={[pal.view, styles.toggleCard]}>
|
||||
<ToggleButton
|
||||
type="default-light"
|
||||
label={
|
||||
isIOS ? _(msg`Disable haptics`) : _(msg`Disable vibrations`)
|
||||
}
|
||||
labelType="lg"
|
||||
isSelected={isHapticsDisabled}
|
||||
onPress={() => setHapticsDisabled(!isHapticsDisabled)}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
<View style={styles.spacer20} />
|
||||
<Text type="xl-bold" style={[pal.text, styles.heading]}>
|
||||
<Trans>Account</Trans>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue