diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0d38c005f21..54c18b24f91 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@
- changed: Update various scenes with UI4 components
- changed: Light account re-enabled at 50% distribution
- changed: New dynamic menu tabs that responds to scene scroll
+- changed: New dynamic wallet list search drawer above tabs
- changed: Scene layout to support transparent and blurred header and tab-bar
- fixed: USP vs legacy landing experiment distribution
- fixed: Paybis sell from Tron USDT
diff --git a/src/__tests__/components/WalletListHeader.test.tsx b/src/__tests__/components/WalletListHeader.test.tsx
index 5f7a341ad27..4e7d1c35287 100644
--- a/src/__tests__/components/WalletListHeader.test.tsx
+++ b/src/__tests__/components/WalletListHeader.test.tsx
@@ -15,7 +15,6 @@ describe('WalletListHeader', () => {
navigation={fakeNavigation}
sorting
searching
- searchText="string"
openSortModal={() => undefined}
onChangeSearchText={() => undefined}
onChangeSearchingState={searching => undefined}
diff --git a/src/__tests__/components/__snapshots__/MenuTabs.test.tsx.snap b/src/__tests__/components/__snapshots__/MenuTabs.test.tsx.snap
index 09d8e14b61d..bbf8d527af9 100644
--- a/src/__tests__/components/__snapshots__/MenuTabs.test.tsx.snap
+++ b/src/__tests__/components/__snapshots__/MenuTabs.test.tsx.snap
@@ -2,7 +2,6 @@
exports[`MenuTabs should render with loading props 1`] = `
-
-
-
-
-
-
- Done
-
-
-
-
-`;
+exports[`WalletListHeader should render with loading props 1`] = ``;
diff --git a/src/__tests__/scenes/__snapshots__/CreateWalletAccountSetupScene.test.tsx.snap b/src/__tests__/scenes/__snapshots__/CreateWalletAccountSetupScene.test.tsx.snap
index e486363d008..5a1d31b84e5 100644
--- a/src/__tests__/scenes/__snapshots__/CreateWalletAccountSetupScene.test.tsx.snap
+++ b/src/__tests__/scenes/__snapshots__/CreateWalletAccountSetupScene.test.tsx.snap
@@ -515,6 +515,32 @@ exports[`CreateWalletAccountSelect renders 1`] = `
+
,
]
diff --git a/src/__tests__/scenes/__snapshots__/CreateWalletImportScene.test.tsx.snap b/src/__tests__/scenes/__snapshots__/CreateWalletImportScene.test.tsx.snap
index 49ae7ee9911..812ca507df5 100644
--- a/src/__tests__/scenes/__snapshots__/CreateWalletImportScene.test.tsx.snap
+++ b/src/__tests__/scenes/__snapshots__/CreateWalletImportScene.test.tsx.snap
@@ -530,6 +530,32 @@ exports[`CreateWalletImportScene should render with loading props 1`] = `
+
,
]
`;
diff --git a/src/__tests__/scenes/__snapshots__/CreateWalletSelectCryptoScene.test.tsx.snap b/src/__tests__/scenes/__snapshots__/CreateWalletSelectCryptoScene.test.tsx.snap
index 0d8df31d3ef..bc8085ecd29 100644
--- a/src/__tests__/scenes/__snapshots__/CreateWalletSelectCryptoScene.test.tsx.snap
+++ b/src/__tests__/scenes/__snapshots__/CreateWalletSelectCryptoScene.test.tsx.snap
@@ -898,6 +898,32 @@ exports[`CreateWalletSelectCrypto should render with loading props 1`] = `
+
,
]
`;
diff --git a/src/__tests__/scenes/__snapshots__/CreateWalletSelectFiatScene.test.tsx.snap b/src/__tests__/scenes/__snapshots__/CreateWalletSelectFiatScene.test.tsx.snap
index 613ca3ca859..4661758e993 100644
--- a/src/__tests__/scenes/__snapshots__/CreateWalletSelectFiatScene.test.tsx.snap
+++ b/src/__tests__/scenes/__snapshots__/CreateWalletSelectFiatScene.test.tsx.snap
@@ -828,6 +828,32 @@ exports[`CreateWalletSelectFiatComponent should render with loading props 1`] =
+
,
]
`;
diff --git a/src/__tests__/scenes/__snapshots__/CryptoExchangeQuoteScene.test.tsx.snap b/src/__tests__/scenes/__snapshots__/CryptoExchangeQuoteScene.test.tsx.snap
index 474310f809f..54dab81710a 100644
--- a/src/__tests__/scenes/__snapshots__/CryptoExchangeQuoteScene.test.tsx.snap
+++ b/src/__tests__/scenes/__snapshots__/CryptoExchangeQuoteScene.test.tsx.snap
@@ -1458,6 +1458,32 @@ exports[`CryptoExchangeQuoteScreenComponent should render with loading props 1`]
}
}
/>
+
,
]
`;
diff --git a/src/__tests__/scenes/__snapshots__/CurrencyNotificationScene.test.tsx.snap b/src/__tests__/scenes/__snapshots__/CurrencyNotificationScene.test.tsx.snap
index 0aea2fea9ec..87ea3fa8a08 100644
--- a/src/__tests__/scenes/__snapshots__/CurrencyNotificationScene.test.tsx.snap
+++ b/src/__tests__/scenes/__snapshots__/CurrencyNotificationScene.test.tsx.snap
@@ -283,6 +283,32 @@ exports[`CurrencyNotificationComponent should render with loading props 1`] = `
+
,
]
`;
diff --git a/src/__tests__/scenes/__snapshots__/CurrencySettings.ui.test.tsx.snap b/src/__tests__/scenes/__snapshots__/CurrencySettings.ui.test.tsx.snap
index 4eab60a2f29..0498496f7e3 100644
--- a/src/__tests__/scenes/__snapshots__/CurrencySettings.ui.test.tsx.snap
+++ b/src/__tests__/scenes/__snapshots__/CurrencySettings.ui.test.tsx.snap
@@ -489,6 +489,32 @@ exports[`CurrencySettings should render 1`] = `
+
,
]
`;
diff --git a/src/__tests__/scenes/__snapshots__/EdgeLoginScene.test.tsx.snap b/src/__tests__/scenes/__snapshots__/EdgeLoginScene.test.tsx.snap
index 9ac99244e6d..b2959d2ae88 100644
--- a/src/__tests__/scenes/__snapshots__/EdgeLoginScene.test.tsx.snap
+++ b/src/__tests__/scenes/__snapshots__/EdgeLoginScene.test.tsx.snap
@@ -455,6 +455,32 @@ exports[`EdgeLoginScene should render with loading props 1`] = `
+
,
]
`;
diff --git a/src/__tests__/scenes/__snapshots__/SendScene2.ui.test.tsx.snap b/src/__tests__/scenes/__snapshots__/SendScene2.ui.test.tsx.snap
index 14229108f88..1077b7c581a 100644
--- a/src/__tests__/scenes/__snapshots__/SendScene2.ui.test.tsx.snap
+++ b/src/__tests__/scenes/__snapshots__/SendScene2.ui.test.tsx.snap
@@ -798,6 +798,32 @@ exports[`SendScene2 1 spendTarget 1`] = `
}
}
/>
+
,
]
`;
@@ -1810,6 +1836,32 @@ exports[`SendScene2 1 spendTarget with info tiles 1`] = `
}
}
/>
+
,
]
`;
@@ -2744,6 +2796,32 @@ exports[`SendScene2 2 spendTargets 1`] = `
}
}
/>
+
,
]
`;
@@ -3548,6 +3626,32 @@ exports[`SendScene2 2 spendTargets hide tiles 1`] = `
}
}
/>
+
,
]
`;
@@ -4330,6 +4434,32 @@ exports[`SendScene2 2 spendTargets hide tiles 2`] = `
}
}
/>
+
,
]
`;
@@ -4982,6 +5112,32 @@ exports[`SendScene2 2 spendTargets hide tiles 3`] = `
}
}
/>
+
,
]
`;
@@ -5889,6 +6045,32 @@ exports[`SendScene2 2 spendTargets lock tiles 1`] = `
}
}
/>
+
,
]
`;
@@ -6769,6 +6951,32 @@ exports[`SendScene2 2 spendTargets lock tiles 2`] = `
}
}
/>
+
,
]
`;
@@ -7622,6 +7830,32 @@ exports[`SendScene2 2 spendTargets lock tiles 3`] = `
}
}
/>
+
,
]
`;
@@ -8534,6 +8768,32 @@ exports[`SendScene2 Render SendScene 1`] = `
}
}
/>
+
,
]
`;
diff --git a/src/__tests__/scenes/__snapshots__/SettingsScene.test.tsx.snap b/src/__tests__/scenes/__snapshots__/SettingsScene.test.tsx.snap
index cc539f1f90a..eb91f3843c0 100644
--- a/src/__tests__/scenes/__snapshots__/SettingsScene.test.tsx.snap
+++ b/src/__tests__/scenes/__snapshots__/SettingsScene.test.tsx.snap
@@ -2827,6 +2827,32 @@ exports[`MyComponent should render Locked SettingsOverview 1`] = `
+
,
]
`;
@@ -5658,6 +5684,32 @@ exports[`MyComponent should render UnLocked SettingsOverview 1`] = `
+
,
]
`;
diff --git a/src/__tests__/scenes/__snapshots__/TransactionDetailsScene.test.tsx.snap b/src/__tests__/scenes/__snapshots__/TransactionDetailsScene.test.tsx.snap
index 637a6ed335e..40047263e67 100644
--- a/src/__tests__/scenes/__snapshots__/TransactionDetailsScene.test.tsx.snap
+++ b/src/__tests__/scenes/__snapshots__/TransactionDetailsScene.test.tsx.snap
@@ -1539,6 +1539,32 @@ exports[`TransactionDetailsScene should render 1`] = `
}
}
/>
+
,
]
@@ -3083,6 +3109,32 @@ exports[`TransactionDetailsScene should render with negative nativeAmount and fi
}
}
/>
+
,
]
diff --git a/src/components/common/SceneWrapper.tsx b/src/components/common/SceneWrapper.tsx
index a6e3cab0ad7..5d2a8d43001 100644
--- a/src/components/common/SceneWrapper.tsx
+++ b/src/components/common/SceneWrapper.tsx
@@ -4,13 +4,14 @@ import { Animated, Dimensions, ScrollView, StyleSheet, View } from 'react-native
import LinearGradient from 'react-native-linear-gradient'
import { EdgeInsets, useSafeAreaFrame, useSafeAreaInsets } from 'react-native-safe-area-context'
-import { useDrawerOpenRatio } from '../../state/SceneDrawerState'
+import { useSceneDrawerState } from '../../state/SceneDrawerState'
import { useSelector } from '../../types/reactRedux'
import { maybeComponent } from '../hoc/maybeComponent'
import { styled } from '../hoc/styled'
import { NotificationView } from '../notification/NotificationView'
import { useTheme } from '../services/ThemeContext'
import { MAX_TAB_BAR_HEIGHT } from '../themed/MenuTabs'
+import { SceneDrawer } from '../themed/SceneDrawer'
import { KeyboardTracker } from './KeyboardTracker'
const windowDimensions = Dimensions.get('window')
@@ -52,6 +53,9 @@ interface SceneWrapperProps {
// Padding to add inside the scene border:
padding?: number
+ // Render function to render component for the tab drawer
+ renderDrawer?: () => React.ReactNode
+
// True to make the scene scrolling (if avoidKeyboard is false):
scroll?: boolean
}
@@ -67,6 +71,7 @@ export function SceneWrapper(props: SceneWrapperProps): JSX.Element {
avoidKeyboard = false,
background = 'theme',
children,
+ renderDrawer,
hasHeader = true,
hasNotifications = false,
hasTabs = false,
@@ -79,7 +84,7 @@ export function SceneWrapper(props: SceneWrapperProps): JSX.Element {
const activeUsername = useSelector(state => state.core.account.username)
const isLightAccount = accountId != null && activeUsername == null
- const { drawerHeight } = useDrawerOpenRatio()
+ const { tabDrawerHeight = 0 } = useSceneDrawerState()
const theme = useTheme()
@@ -95,6 +100,12 @@ export function SceneWrapper(props: SceneWrapperProps): JSX.Element {
const hasKeyboardAnimation = keyboardAnimation != null
const isFuncChildren = typeof children === 'function'
+ // Derive the keyboard height by getting the difference between screen height
+ // and trackerValue. This value should be from zero to keyboard height
+ // depending on the open state of the keyboard
+ const keyboardHeight = frame.height - trackerValue
+ const isKeyboardOpen = keyboardHeight !== 0
+
// These are the safeAreaInsets including the app's header and tab-bar
// heights.
const insets: EdgeInsets = {
@@ -109,7 +120,7 @@ export function SceneWrapper(props: SceneWrapperProps): JSX.Element {
// used for the ScrollView component internal to the SceneWrapper.
const insetStyles: InsetStyles = {
paddingTop: insets.top,
- paddingBottom: insets.bottom + drawerHeight.value
+ paddingBottom: insets.bottom + tabDrawerHeight
}
const maybeInsetStyles = isFuncChildren ? {} : insetStyles
@@ -134,6 +145,7 @@ export function SceneWrapper(props: SceneWrapperProps): JSX.Element {
{isFuncChildren ? children({ safeAreaInsets, insets, insetStyles: insetStyles }) : children}
{hasNotifications ? : null}
+ {renderDrawer == null ? null : renderDrawer()}
diff --git a/src/components/scenes/WalletListScene.tsx b/src/components/scenes/WalletListScene.tsx
index bc882286c8e..50b9d3333fc 100644
--- a/src/components/scenes/WalletListScene.tsx
+++ b/src/components/scenes/WalletListScene.tsx
@@ -17,6 +17,7 @@ import { cacheStyles, Theme, useTheme } from '../services/ThemeContext'
import { EdgeText } from '../themed/EdgeText'
import { WalletListFooter } from '../themed/WalletListFooter'
import { WalletListHeader } from '../themed/WalletListHeader'
+import { WalletListSearch } from '../themed/WalletListSearch'
import { WalletListSortable } from '../themed/WalletListSortable'
import { WalletListSwipeable } from '../themed/WalletListSwipeable'
import { WiredProgressBar } from '../themed/WiredProgressBar'
@@ -30,7 +31,7 @@ export function WalletListScene(props: Props) {
const dispatch = useDispatch()
const [sorting, setSorting] = React.useState(false)
- const [searching, setSearching] = React.useState(false)
+ const [isSearching, setIsSearching] = React.useState(false)
const [searchText, setSearchText] = React.useState('')
const needsPasswordCheck = useSelector(state => state.ui.passwordReminder.needsPasswordCheck)
@@ -47,13 +48,25 @@ export function WalletListScene(props: Props) {
})
const handleRefresh = useHandler(() => {
- setSearching(true)
+ setIsSearching(true)
})
- // Turn off searching mode when a wallet is selected
- const handlReset = useHandler(() => {
+ const handleReset = useHandler(() => {
setSearchText('')
- setSearching(false)
+ setIsSearching(false)
+ })
+
+ const handleStartSearching = useHandler(() => {
+ setIsSearching(true)
+ })
+
+ const handleDoneSearching = useHandler(() => {
+ setSearchText('')
+ setIsSearching(false)
+ })
+
+ const handleChangeText = useHandler((value: string) => {
+ setSearchText(value)
})
// Show the password reminder on mount if required:
@@ -78,19 +91,30 @@ export function WalletListScene(props: Props) {
{}}
+ onChangeSearchingState={() => {}}
/>
)
- }, [handleSort, navigation, searchText, searching, sorting])
+ }, [handleSort, navigation, isSearching, sorting])
const handlePressDone = useHandler(() => setSorting(false))
+ const renderDrawer = () => {
+ return (
+
+ )
+ }
+
return (
-
+
{({ insetStyles }) => (
<>
@@ -107,13 +131,13 @@ export function WalletListScene(props: Props) {
diff --git a/src/components/themed/MenuTabs.tsx b/src/components/themed/MenuTabs.tsx
index 73d7776e2dd..d8582235591 100644
--- a/src/components/themed/MenuTabs.tsx
+++ b/src/components/themed/MenuTabs.tsx
@@ -13,7 +13,7 @@ import { Fontello } from '../../assets/vector/index'
import { useHandler } from '../../hooks/useHandler'
import { LocaleStringKey } from '../../locales/en_US'
import { lstrings } from '../../locales/strings'
-import { useDrawerOpenRatio } from '../../state/SceneDrawerState'
+import { useDrawerOpenRatio, useLayoutHeightInTabBar } from '../../state/SceneDrawerState'
import { config } from '../../theme/appConfig'
import { styled } from '../hoc/styled'
import { useTheme } from '../services/ThemeContext'
@@ -22,6 +22,7 @@ import { VectorIcon } from './VectorIcon'
const extraTabString: LocaleStringKey = config.extraTab?.tabTitleKey ?? 'title_map'
export const MAX_TAB_BAR_HEIGHT = 92
+export const MIN_TAB_BAR_HEIGHT = 74
const title: { readonly [key: string]: string } = {
homeTab: lstrings.title_home,
@@ -53,15 +54,15 @@ export const MenuTabs = (props: BottomTabBarProps) => {
[state.routes]
)
- const insets = useSafeAreaInsets()
-
const activeTabRoute = state.routes[activeTabFullIndex]
const activeTabIndex = routes.findIndex(route => route.name === activeTabRoute.name)
- const { drawerOpenRatio, handleDrawerLayout, isRatioDisabled = false, resetDrawerRatio } = useDrawerOpenRatio()
+ const { drawerOpenRatio, resetDrawerRatio } = useDrawerOpenRatio()
+
+ const handleLayout = useLayoutHeightInTabBar()
return (
-
+
@@ -74,7 +75,6 @@ export const MenuTabs = (props: BottomTabBarProps) => {
isActive={activeTabIndex === index}
drawerOpenRatio={drawerOpenRatio}
resetDrawerRatio={resetDrawerRatio}
- isRatioDisabled={isRatioDisabled}
/>
))}
@@ -83,7 +83,7 @@ export const MenuTabs = (props: BottomTabBarProps) => {
)
}
-const Container = styled(View)<{ bottom: number; height?: number }>({
+const Container = styled(View)({
position: 'absolute',
left: 0,
right: 0,
@@ -104,15 +104,13 @@ const Tab = ({
drawerOpenRatio,
resetDrawerRatio,
currentName,
- isRatioDisabled,
navigation
}: {
isActive: boolean
currentName: string
route: BottomTabBarProps['state']['routes'][number]
- drawerOpenRatio: SharedValue | undefined
+ drawerOpenRatio: SharedValue
resetDrawerRatio: () => void
- isRatioDisabled: boolean
navigation: NavigationHelpers
}) => {
const theme = useTheme()
@@ -150,34 +148,25 @@ const Tab = ({
return (
{icon[route.name]}
-
)
}
-const TabContainer = styled(TouchableOpacity)<{ insetBottom: number }>(theme => props => ({
+const TabContainer = styled(TouchableOpacity)<{ insetBottom: number }>(theme => ({ insetBottom }) => ({
flex: 1,
paddingTop: theme.rem(0.75),
- paddingBottom: Math.max(theme.rem(0.75), props.insetBottom),
+ paddingBottom: Math.max(theme.rem(0.75), insetBottom),
justifyContent: 'center',
alignItems: 'center'
}))
const Label = styled(Animated.Text)<{
isActive: boolean
- isRatioDisabled: boolean
- openRatio: SharedValue | undefined
-}>(theme => ({ isActive, isRatioDisabled, openRatio }) => {
+ openRatio: SharedValue
+}>(theme => ({ isActive, openRatio }) => {
const rem = theme.rem(1)
return [
{
@@ -191,9 +180,10 @@ const Label = styled(Animated.Text)<{
},
useAnimatedStyle(() => {
'worklet'
+ if (openRatio == null) return {}
return {
- height: isRatioDisabled ? undefined : openRatio == null ? undefined : rem * openRatio.value,
- opacity: isRatioDisabled ? undefined : openRatio == null ? undefined : openRatio.value
+ height: rem * openRatio.value,
+ opacity: openRatio.value
}
})
]
diff --git a/src/components/themed/SceneDrawer.tsx b/src/components/themed/SceneDrawer.tsx
new file mode 100644
index 00000000000..6aeb4e38325
--- /dev/null
+++ b/src/components/themed/SceneDrawer.tsx
@@ -0,0 +1,48 @@
+import React from 'react'
+import Animated, { interpolate, SharedValue, useAnimatedStyle } from 'react-native-reanimated'
+
+import { useDrawerOpenRatio, useLayoutHeightInTabBar } from '../../state/SceneDrawerState'
+import { styled } from '../hoc/styled'
+import { MAX_TAB_BAR_HEIGHT, MIN_TAB_BAR_HEIGHT } from './MenuTabs'
+
+export interface SceneDrawerProps {
+ children: React.ReactNode
+ isKeyboardOpen: boolean
+}
+
+export const SceneDrawer = (props: SceneDrawerProps) => {
+ const { children, isKeyboardOpen } = props
+ const { drawerOpenRatio } = useDrawerOpenRatio()
+ const handleLayout = useLayoutHeightInTabBar()
+
+ return (
+ <>
+
+ {children}
+
+ >
+ )
+}
+
+const Drawer = styled(Animated.View)<{
+ drawerOpenRatio: SharedValue
+ isKeyboardOpen: boolean
+}>(theme => ({ drawerOpenRatio, isKeyboardOpen }) => {
+ return [
+ {
+ position: 'absolute',
+ bottom: 0,
+ left: 0,
+ right: 0,
+ flexDirection: 'column',
+ justifyContent: 'flex-start',
+ alignItems: 'stretch',
+ overflow: 'hidden'
+ },
+ useAnimatedStyle(() => {
+ return {
+ bottom: isKeyboardOpen ? 0 : interpolate(drawerOpenRatio.value, [0, 1], [MIN_TAB_BAR_HEIGHT, MAX_TAB_BAR_HEIGHT])
+ }
+ })
+ ]
+})
diff --git a/src/components/themed/WalletListHeader.tsx b/src/components/themed/WalletListHeader.tsx
index 392e2df7cf6..d8d4c266ae2 100644
--- a/src/components/themed/WalletListHeader.tsx
+++ b/src/components/themed/WalletListHeader.tsx
@@ -7,16 +7,14 @@ import { lstrings } from '../../locales/strings'
import { NavigationBase } from '../../types/routerTypes'
import { PromoCard } from '../cards/PromoCard'
import { cacheStyles, Theme, ThemeProps, withTheme } from '../services/ThemeContext'
-import { EdgeText } from '../themed/EdgeText'
import { BalanceCardUi4 } from '../ui4/BalanceCardUi4'
import { SectionHeaderUi4 } from '../ui4/SectionHeaderUi4'
-import { OutlinedTextInput, OutlinedTextInputRef } from './OutlinedTextInput'
+import { OutlinedTextInputRef } from './OutlinedTextInput'
interface OwnProps {
navigation: NavigationBase
sorting: boolean
searching: boolean
- searchText: string
openSortModal: () => void
onChangeSearchText: (search: string) => void
onChangeSearchingState: (searching: boolean) => void
@@ -47,7 +45,7 @@ export class WalletListHeaderComponent extends React.PureComponent {
}
render() {
- const { navigation, sorting, searching, searchText, theme } = this.props
+ const { navigation, sorting, searching, theme } = this.props
const styles = getStyles(theme)
const addSortButtons = (
@@ -63,25 +61,6 @@ export class WalletListHeaderComponent extends React.PureComponent {
return (
<>
-
-
-
-
- {searching && (
-
- {lstrings.string_done_cap}
-
- )}
-
{searching ? null : }
{sorting || searching ? null : }
@@ -99,18 +78,6 @@ const getStyles = cacheStyles((theme: Theme) => ({
},
addButton: {
marginRight: theme.rem(0.5)
- },
-
- searchContainer: {
- flexDirection: 'row',
- alignItems: 'center',
- marginTop: theme.rem(0.5),
- marginHorizontal: theme.rem(0.5)
- },
- searchDoneButton: {
- justifyContent: 'center',
- paddingLeft: theme.rem(0.75),
- paddingBottom: theme.rem(1)
}
}))
diff --git a/src/components/themed/WalletListSearch.tsx b/src/components/themed/WalletListSearch.tsx
new file mode 100644
index 00000000000..0f0f0c2ca60
--- /dev/null
+++ b/src/components/themed/WalletListSearch.tsx
@@ -0,0 +1,106 @@
+import React, { useEffect, useState } from 'react'
+import { LayoutChangeEvent, StyleSheet } from 'react-native'
+import Animated, { SharedValue, useAnimatedStyle, useDerivedValue } from 'react-native-reanimated'
+import { BlurView } from 'rn-id-blurview'
+
+import { useHandler } from '../../hooks/useHandler'
+import { lstrings } from '../../locales/strings'
+import { useDrawerOpenRatio } from '../../state/SceneDrawerState'
+import { styled } from '../hoc/styled'
+import { SearchIconAnimated } from '../icons/ThemedIcons'
+import { Space } from '../layout/Space'
+import { useTheme } from '../services/ThemeContext'
+import { SimpleTextInput, SimpleTextInputRef } from './SimpleTextInput'
+
+interface WalletListSearchProps {
+ isSearching: boolean
+ searchText: string
+
+ onChangeText: (value: string) => void
+ onDoneSearching: () => void
+ onStartSearching: () => void
+}
+
+export const WalletListSearch = (props: WalletListSearchProps) => {
+ const { isSearching, searchText, onChangeText, onDoneSearching, onStartSearching } = props
+ const theme = useTheme()
+
+ const textInputRef = React.useRef(null)
+
+ const { drawerOpenRatio, setKeepOpen } = useDrawerOpenRatio()
+ const [containerHeight, setContainerHeight] = useState(undefined)
+
+ const inputScale = useDerivedValue(() => drawerOpenRatio.value)
+
+ const handleLayout = useHandler((event: LayoutChangeEvent) => {
+ if (containerHeight != null) return
+ setContainerHeight(event.nativeEvent.layout.height)
+ })
+
+ const handleSearchChangeText = useHandler((text: string) => {
+ onChangeText(text)
+ })
+
+ const handleSearchBlur = useHandler(() => {
+ if (searchText === '') {
+ onDoneSearching()
+ }
+ })
+
+ const handleSearchClear = useHandler(() => {
+ if (!textInputRef.current?.isFocused()) {
+ onDoneSearching()
+ }
+ })
+
+ const handleSearchFocus = useHandler(() => {
+ onStartSearching()
+ })
+
+ useEffect(() => {
+ if (setKeepOpen != null) setKeepOpen(isSearching)
+ if (isSearching && textInputRef.current) {
+ textInputRef.current.focus()
+ }
+ if (!isSearching && textInputRef.current) {
+ textInputRef.current.blur()
+ }
+ }, [isSearching, setKeepOpen])
+
+ return (
+ <>
+
+
+
+
+
+
+ >
+ )
+}
+
+const ContainerAnimatedView = styled(Animated.View)<{
+ containerHeight?: number
+ drawerOpenRatio: SharedValue
+}>(() => ({ containerHeight, drawerOpenRatio }) => [
+ {
+ overflow: 'hidden'
+ },
+ useAnimatedStyle(() => {
+ if (containerHeight == null) return {}
+ return {
+ height: containerHeight * drawerOpenRatio.value
+ }
+ })
+])
diff --git a/src/state/SceneDrawerState.tsx b/src/state/SceneDrawerState.tsx
index b6515cc69af..0f6436abe71 100644
--- a/src/state/SceneDrawerState.tsx
+++ b/src/state/SceneDrawerState.tsx
@@ -1,4 +1,5 @@
-import { LayoutChangeEvent, Platform } from 'react-native'
+import { useEffect } from 'react'
+import { Dimensions, LayoutChangeEvent, Platform } from 'react-native'
import { runOnJS, useAnimatedReaction, useSharedValue, withTiming } from 'react-native-reanimated'
import { withContextProvider } from '../components/hoc/withContextProvider'
@@ -8,14 +9,18 @@ import { useState } from '../types/reactHooks'
import { makeUseContextValue } from '../util/makeUseContextValue'
import { useSceneScrollContext } from './SceneScrollState'
+const SCROLL_DISTANCE = Dimensions.get('window').height / 10
+
export const [SceneDrawerProvider, SceneDrawerContext] = withContextProvider(() => {
- const [isRatioDisabled, setIsRatioDisabled] = useState(false)
+ const [keepOpen, setKeepOpen] = useState(false)
+ const [tabDrawerHeight, setTabDrawerHeight] = useState(undefined)
return {
- drawerHeight: useSharedValue(0),
drawerOpenRatio: useSharedValue(1),
drawerOpenRatioStart: useSharedValue(1),
- isRatioDisabled,
- setIsRatioDisabled
+ keepOpen,
+ setKeepOpen,
+ tabDrawerHeight,
+ setTabDrawerHeight
}
})
export const useSceneDrawerState = makeUseContextValue(SceneDrawerContext)
@@ -25,7 +30,7 @@ export const useDrawerOpenRatio = () => {
const scrollYStart = useSharedValue(undefined)
const snapTo = useSharedValue(undefined)
- const { drawerHeight, drawerOpenRatio, drawerOpenRatioStart, isRatioDisabled, setIsRatioDisabled } = useSceneDrawerState()
+ const { drawerOpenRatio, drawerOpenRatioStart, keepOpen, setKeepOpen } = useSceneDrawerState()
function resetDrawerRatio() {
snapTo.value = 1
@@ -77,14 +82,14 @@ export const useDrawerOpenRatio = () => {
useAnimatedReaction(
() => {
- // Drawer height is not ready
- if (drawerHeight.value === 0) return drawerOpenRatio.value
+ // Keep it open when disabled
+ if (keepOpen) return 1
// Scrolling hasn't started yet
if (scrollYStart.value == null) return
const scrollYDelta = scrollY.value - scrollYStart.value
- const ratioDelta = scrollYDelta / drawerHeight.value / 2 // Constant is to lower jumpy-ness
+ const ratioDelta = scrollYDelta / SCROLL_DISTANCE // Constant is to lower jumpy-ness
return Math.min(1, Math.max(0, drawerOpenRatioStart.value - ratioDelta))
},
@@ -109,31 +114,55 @@ export const useDrawerOpenRatio = () => {
snapTo.value = undefined
drawerOpenRatio.value = currentValue
},
- []
+ [keepOpen]
)
useAnimatedReaction(
- () => snapTo.value,
+ () => {
+ // Keep it open when disabled
+ if (keepOpen) return 1
+
+ return snapTo.value
+ },
(currentValue, previousValue) => {
if (currentValue === previousValue) return
if (currentValue == null) return
drawerOpenRatio.value = withTiming(currentValue, { duration: 300 })
- }
+ },
+ [keepOpen]
)
- const handleDrawerLayout = useHandler((event: LayoutChangeEvent) => {
- // Only handle the initial layout (re-layout is not yet supported):
- if (drawerHeight.value !== 0) return
- drawerHeight.value = event.nativeEvent.layout.height
- })
-
return {
- drawerHeight,
drawerOpenRatio,
- isRatioDisabled,
- setIsRatioDisabled,
- handleDrawerLayout,
- resetDrawerRatio
+ resetDrawerRatio,
+
+ keepOpen,
+ setKeepOpen
}
}
+
+export const useLayoutHeightInTabBar = (): ((event: LayoutChangeEvent) => void) => {
+ const { setTabDrawerHeight } = useSceneDrawerState()
+
+ const [layoutHeight, setLayoutHeight] = useState(undefined)
+
+ // One-time layout measurement handler:
+ const handleLayout = useHandler((event: LayoutChangeEvent) => {
+ if (layoutHeight == null) {
+ const layoutHeight = event.nativeEvent.layout.height
+ setLayoutHeight((prev = 0) => prev + layoutHeight)
+ }
+ })
+
+ // Add/subtract container height to the tab-bar height when mounted/unmounted
+ useEffect(() => {
+ if (layoutHeight == null) return
+ setTabDrawerHeight((prev = 0) => prev + layoutHeight)
+ return () => {
+ setTabDrawerHeight((prev = 0) => prev - layoutHeight)
+ }
+ }, [layoutHeight, setTabDrawerHeight])
+
+ return handleLayout
+}