diff --git a/src/view/shell/mobile/TabsSelector.tsx b/src/view/shell/mobile/TabsSelector.tsx index a3da5fa197..1210da91f2 100644 --- a/src/view/shell/mobile/TabsSelector.tsx +++ b/src/view/shell/mobile/TabsSelector.tsx @@ -7,8 +7,10 @@ import { TouchableWithoutFeedback, View, } from 'react-native' +import {useSafeAreaInsets} from 'react-native-safe-area-context' import Animated, { interpolate, + SharedValue, useSharedValue, useAnimatedStyle, withTiming, @@ -24,12 +26,20 @@ import {LinkActionsModel} from '../../../state/models/shell-ui' const TAB_HEIGHT = 42 export const TabsSelector = observer( - ({active, onClose}: {active: boolean; onClose: () => void}) => { + ({ + active, + tabMenuInterp, + onClose, + }: { + active: boolean + tabMenuInterp: SharedValue + onClose: () => void + }) => { const store = useStores() + const insets = useSafeAreaInsets() const [closingTabIndex, setClosingTabIndex] = useState( undefined, ) - const initInterp = useSharedValue(0) const closeInterp = useSharedValue(0) const tabsRef = useRef(null) const tabRefs = useMemo( @@ -40,15 +50,10 @@ export const TabsSelector = observer( [store.nav.tabs.length], ) - useEffect(() => { - if (active) { - initInterp.value = withTiming(1, {duration: 150}) - } else { - initInterp.value = 0 - } - }, [initInterp, active]) const wrapperAnimStyle = useAnimatedStyle(() => ({ - bottom: interpolate(initInterp.value, [0, 1.0], [50, 75]), + transform: [ + {translateY: interpolate(tabMenuInterp.value, [0, 1.0], [320, 0])}, + ], })) // events @@ -118,124 +123,116 @@ export const TabsSelector = observer( } return ( - <> - - - - - - - - - - - - - Share + + + + + + + + - - - - - - - Clone tab + Share + + + + + + - - - - - - - New tab + Clone tab + + + + + + - - - - - - {store.nav.tabs.map((tab, tabIndex) => { - const {icon} = match(tab.current.url) - const isActive = tabIndex === currentTabIndex - const isClosing = closingTabIndex === tabIndex - return ( - onCloseTab(tabIndex)}> - - - onPressChangeTab(tabIndex)}> - - - - - - {tab.current.title || tab.current.url} - - - - onCloseTab(tabIndex)}> - - - - - - - - ) - })} - + New tab + + - - + + + {store.nav.tabs.map((tab, tabIndex) => { + const {icon} = match(tab.current.url) + const isActive = tabIndex === currentTabIndex + const isClosing = closingTabIndex === tabIndex + return ( + onCloseTab(tabIndex)}> + + + onPressChangeTab(tabIndex)}> + + + + + + {tab.current.title || tab.current.url} + + + + onCloseTab(tabIndex)}> + + + + + + + + ) + })} + + + + ) }, ) const styles = StyleSheet.create({ - bg: { - position: 'absolute', - top: 0, - right: 0, - bottom: 0, - left: 0, - backgroundColor: '#000', - opacity: 0.2, - }, wrapper: { position: 'absolute', - // bottom: 75, width: '100%', + height: 320, + borderTopColor: colors.gray2, + borderTopWidth: 1, backgroundColor: '#fff', - borderRadius: 8, opacity: 1, }, section: { @@ -244,45 +241,6 @@ const styles = StyleSheet.create({ }, sectionGrayBg: { backgroundColor: colors.gray1, - borderBottomLeftRadius: 8, - borderBottomRightRadius: 8, - }, - fatMenuItems: { - flexDirection: 'row', - marginTop: 10, - marginBottom: 10, - }, - fatMenuItem: { - width: 80, - alignItems: 'center', - marginRight: 6, - }, - fatMenuItemMargin: { - marginRight: 14, - }, - fatMenuItemIconWrapper: { - borderRadius: 6, - width: 60, - height: 60, - justifyContent: 'center', - alignItems: 'center', - marginBottom: 5, - shadowColor: '#000', - shadowOpacity: 0.2, - shadowOffset: {width: 0, height: 2}, - shadowRadius: 2, - }, - fatMenuItemIcon: { - color: colors.white, - }, - fatMenuImage: { - borderRadius: 30, - width: 60, - height: 60, - marginBottom: 5, - }, - fatMenuItemLabel: { - fontSize: 13, }, tabs: { height: 240, diff --git a/src/view/shell/mobile/index.tsx b/src/view/shell/mobile/index.tsx index 94407599f8..83bab5e9f5 100644 --- a/src/view/shell/mobile/index.tsx +++ b/src/view/shell/mobile/index.tsx @@ -115,6 +115,7 @@ export const MobileShell: React.FC = observer(() => { const scrollElRef = useRef() const winDim = useWindowDimensions() const swipeGestureInterp = useSharedValue(0) + const tabMenuInterp = useSharedValue(0) const screenRenderDesc = constructScreenRenderDesc(store.nav) const onPressHome = () => { @@ -127,7 +128,26 @@ export const MobileShell: React.FC = observer(() => { const onPressSearch = () => store.nav.navigate('/search') const onPressMenu = () => setMainMenuActive(true) const onPressNotifications = () => store.nav.navigate('/notifications') - const onPressTabs = () => setTabsSelectorActive(true) + const onPressTabs = () => toggleTabsMenu(!isTabsSelectorActive) + + const closeTabsSelector = () => setTabsSelectorActive(false) + const toggleTabsMenu = (active: boolean) => { + if (active) { + // will trigger the animation below + setTabsSelectorActive(true) + } else { + tabMenuInterp.value = withTiming(0, {duration: 100}, () => { + // hide once the animation has finished + runOnJS(closeTabsSelector)() + }) + } + } + useEffect(() => { + if (isTabsSelectorActive) { + // trigger the animation once the tabs selector is rendering + tabMenuInterp.value = withTiming(1, {duration: 100}) + } + }, [isTabsSelectorActive]) const goBack = () => store.nav.tab.goBack() const swipeGesture = Gesture.Pan() @@ -159,6 +179,9 @@ export const MobileShell: React.FC = observer(() => { const swipeOpacity = useAnimatedStyle(() => ({ opacity: interpolate(swipeGestureInterp.value, [0, 1.0], [0.6, 0.0]), })) + const tabMenuTransform = useAnimatedStyle(() => ({ + transform: [{translateY: tabMenuInterp.value * -320}], + })) if (!store.session.isAuthed) { return ( @@ -205,7 +228,9 @@ export const MobileShell: React.FC = observer(() => { style={[ s.flex1, styles.screen, - current ? swipeTransform : undefined, + current + ? [swipeTransform, tabMenuTransform] + : undefined, ]}> { + toggleTabsMenu(false)} + /> @@ -236,10 +266,6 @@ export const MobileShell: React.FC = observer(() => { onClose={() => setMainMenuActive(false)} /> - setTabsSelectorActive(false)} - /> store.shell.closeComposer()}