diff options
Diffstat (limited to 'src/view/shell/mobile')
-rw-r--r-- | src/view/shell/mobile/Menu.tsx | 253 | ||||
-rw-r--r-- | src/view/shell/mobile/TabsSelector.tsx | 327 | ||||
-rw-r--r-- | src/view/shell/mobile/index.tsx | 288 |
3 files changed, 257 insertions, 611 deletions
diff --git a/src/view/shell/mobile/Menu.tsx b/src/view/shell/mobile/Menu.tsx index ceeda8c58..23c09b82c 100644 --- a/src/view/shell/mobile/Menu.tsx +++ b/src/view/shell/mobile/Menu.tsx @@ -17,19 +17,23 @@ import {FEEDBACK_FORM_URL} from 'lib/constants' import {useStores} from 'state/index' import { HomeIcon, + HomeIconSolid, BellIcon, + BellIconSolid, UserIcon, CogIcon, - MagnifyingGlassIcon, + MagnifyingGlassIcon2, + MagnifyingGlassIcon2Solid, } from 'lib/icons' import {TabPurpose, TabPurposeMainPath} from 'state/models/navigation' import {UserAvatar} from '../../com/util/UserAvatar' import {Text} from '../../com/util/text/Text' -import {ToggleButton} from '../../com/util/forms/ToggleButton' +import {useTheme} from 'lib/ThemeContext' import {usePalette} from 'lib/hooks/usePalette' import {useAnalytics} from 'lib/analytics' export const Menu = observer(({onClose}: {onClose: () => void}) => { + const theme = useTheme() const pal = usePalette('default') const store = useStores() const {track} = useAnalytics() @@ -89,11 +93,8 @@ export const Menu = observer(({onClose}: {onClose: () => void}) => { ) : undefined} </View> <Text - type="title" - style={[ - pal.text, - bold ? styles.menuItemLabelBold : styles.menuItemLabel, - ]} + type={bold ? '2xl-bold' : '2xl'} + style={[pal.text, s.flex1]} numberOfLines={1}> {label} </Text> @@ -105,68 +106,114 @@ export const Menu = observer(({onClose}: {onClose: () => void}) => { store.shell.setDarkMode(!store.shell.darkMode) } + const isAtHome = + store.nav.tab.current.url === TabPurposeMainPath[TabPurpose.Default] + const isAtSearch = + store.nav.tab.current.url === TabPurposeMainPath[TabPurpose.Search] + const isAtNotifications = + store.nav.tab.current.url === TabPurposeMainPath[TabPurpose.Notifs] + return ( <View testID="menuView" style={[ styles.view, - pal.view, + theme.colorScheme === 'light' ? pal.view : styles.viewDarkMode, store.shell.minimalShellMode && styles.viewMinimalShell, ]}> <TouchableOpacity testID="profileCardButton" - onPress={() => onNavigate(`/profile/${store.me.handle}`)} - style={styles.profileCard}> + onPress={() => onNavigate(`/profile/${store.me.handle}`)}> <UserAvatar - size={60} + size={80} displayName={store.me.displayName} handle={store.me.handle} avatar={store.me.avatar} /> - <View style={s.flex1}> - <Text - type="title-lg" - style={[pal.text, styles.profileCardDisplayName]} - numberOfLines={1}> - {store.me.displayName || store.me.handle} - </Text> - <Text - style={[pal.textLight, styles.profileCardHandle]} - numberOfLines={1}> - @{store.me.handle} - </Text> - </View> - </TouchableOpacity> - <TouchableOpacity - testID="searchBtn" - style={[styles.searchBtn, pal.btn]} - onPress={() => onNavigate('/search')}> - <MagnifyingGlassIcon - style={pal.text as StyleProp<ViewStyle>} - size={25} - /> - <Text type="title" style={[pal.text, styles.searchBtnLabel]}> - Search + <Text + type="title-lg" + style={[pal.text, s.bold, styles.profileCardDisplayName]} + numberOfLines={1}> + {store.me.displayName || store.me.handle} + </Text> + <Text + type="2xl" + style={[pal.textLight, styles.profileCardHandle]} + numberOfLines={1}> + @{store.me.handle} </Text> </TouchableOpacity> - <View style={[styles.section, pal.border, s.pt5]}> + <View style={s.flex1} /> + <View> + <MenuItem + icon={ + isAtSearch ? ( + <MagnifyingGlassIcon2Solid + style={pal.text as StyleProp<ViewStyle>} + size={24} + strokeWidth={1.7} + /> + ) : ( + <MagnifyingGlassIcon2 + style={pal.text as StyleProp<ViewStyle>} + size={24} + strokeWidth={1.7} + /> + ) + } + label="Search" + url="/search" + bold={isAtSearch} + /> <MenuItem - icon={<HomeIcon style={pal.text as StyleProp<ViewStyle>} size="26" />} + icon={ + isAtHome ? ( + <HomeIconSolid + style={pal.text as StyleProp<ViewStyle>} + size="24" + strokeWidth={3.25} + fillOpacity={1} + /> + ) : ( + <HomeIcon + style={pal.text as StyleProp<ViewStyle>} + size="24" + strokeWidth={3.25} + /> + ) + } label="Home" url="/" + bold={isAtHome} /> <MenuItem - icon={<BellIcon style={pal.text as StyleProp<ViewStyle>} size="28" />} + icon={ + isAtNotifications ? ( + <BellIconSolid + style={pal.text as StyleProp<ViewStyle>} + size="24" + strokeWidth={1.7} + fillOpacity={1} + /> + ) : ( + <BellIcon + style={pal.text as StyleProp<ViewStyle>} + size="24" + strokeWidth={1.7} + /> + ) + } label="Notifications" url="/notifications" count={store.me.notifications.unreadCount} + bold={isAtNotifications} /> <MenuItem icon={ <UserIcon style={pal.text as StyleProp<ViewStyle>} - size="30" - strokeWidth={2} + size="26" + strokeWidth={1.5} /> } label="Profile" @@ -176,34 +223,46 @@ export const Menu = observer(({onClose}: {onClose: () => void}) => { icon={ <CogIcon style={pal.text as StyleProp<ViewStyle>} - size="30" - strokeWidth={2} + size="26" + strokeWidth={1.75} /> } label="Settings" url="/settings" /> </View> - <View style={[styles.section, pal.border]}> - <ToggleButton - label="Dark mode" - isSelected={store.shell.darkMode} - onPress={onDarkmodePress} - /> - </View> <View style={s.flex1} /> <View style={styles.footer}> - <MenuItem - icon={ - <FontAwesomeIcon - style={pal.text as FontAwesomeIconStyle} - size={24} - icon={['far', 'message']} - /> - } - label="Feedback" + <TouchableOpacity + onPress={onDarkmodePress} + style={[ + styles.footerBtn, + theme.colorScheme === 'light' ? pal.btn : styles.footerBtnDarkMode, + ]}> + <CogIcon + style={pal.text as StyleProp<ViewStyle>} + size="26" + strokeWidth={1.75} + /> + </TouchableOpacity> + <TouchableOpacity onPress={onPressFeedback} - /> + style={[ + styles.footerBtn, + styles.footerBtnFeedback, + theme.colorScheme === 'light' + ? styles.footerBtnFeedbackLight + : styles.footerBtnFeedbackDark, + ]}> + <FontAwesomeIcon + style={pal.link as FontAwesomeIconStyle} + size={19} + icon={['far', 'message']} + /> + <Text type="2xl-medium" style={[pal.link, s.pl10]}> + Feedback + </Text> + </TouchableOpacity> </View> </View> ) @@ -212,70 +271,37 @@ export const Menu = observer(({onClose}: {onClose: () => void}) => { const styles = StyleSheet.create({ view: { flex: 1, + paddingTop: 10, paddingBottom: 90, + paddingLeft: 30, + }, + viewDarkMode: { + backgroundColor: colors.gray8, }, viewMinimalShell: { paddingBottom: 50, }, - section: { - paddingHorizontal: 10, - paddingTop: 10, - paddingBottom: 10, - borderBottomWidth: 1, - }, - heading: { - paddingVertical: 8, - paddingHorizontal: 4, - }, - profileCard: { - flexDirection: 'row', - alignItems: 'center', - margin: 10, - marginBottom: 6, - }, profileCardDisplayName: { - marginLeft: 12, + marginTop: 20, }, profileCardHandle: { - marginLeft: 12, - }, - - searchBtn: { - flexDirection: 'row', - borderRadius: 8, - margin: 10, - marginBottom: 0, - paddingVertical: 10, - paddingHorizontal: 12, - }, - searchBtnLabel: { - marginLeft: 14, - fontWeight: 'normal', + marginTop: 4, }, menuItem: { flexDirection: 'row', alignItems: 'center', - paddingVertical: 6, - paddingLeft: 6, + paddingVertical: 16, paddingRight: 10, }, menuItemIconWrapper: { - width: 36, - height: 36, + width: 24, + height: 24, alignItems: 'center', justifyContent: 'center', marginRight: 12, }, - menuItemLabel: { - flex: 1, - fontWeight: 'normal', - }, - menuItemLabelBold: { - flex: 1, - fontWeight: 'bold', - }, menuItemCount: { position: 'absolute', right: -6, @@ -292,6 +318,27 @@ const styles = StyleSheet.create({ }, footer: { - paddingHorizontal: 10, + flexDirection: 'row', + justifyContent: 'space-between', + paddingRight: 30, + paddingTop: 20, + }, + footerBtn: { + flexDirection: 'row', + alignItems: 'center', + padding: 10, + borderRadius: 25, + }, + footerBtnDarkMode: { + backgroundColor: colors.black, + }, + footerBtnFeedback: { + paddingHorizontal: 24, + }, + footerBtnFeedbackLight: { + backgroundColor: '#DDEFFF', + }, + footerBtnFeedbackDark: { + backgroundColor: colors.blue6, }, }) diff --git a/src/view/shell/mobile/TabsSelector.tsx b/src/view/shell/mobile/TabsSelector.tsx deleted file mode 100644 index 92854e3b6..000000000 --- a/src/view/shell/mobile/TabsSelector.tsx +++ /dev/null @@ -1,327 +0,0 @@ -import React, {createRef, useRef, useMemo, useState} from 'react' -import {observer} from 'mobx-react-lite' -import { - Animated, - ScrollView, - Share, - StyleSheet, - TouchableWithoutFeedback, - View, -} from 'react-native' -import {useSafeAreaInsets} from 'react-native-safe-area-context' -import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' -import {Text} from '../../com/util/text/Text' -import Swipeable from 'react-native-gesture-handler/Swipeable' -import {useStores} from 'state/index' -import {s, colors} from 'lib/styles' -import {toShareUrl} from 'lib/strings/url-helpers' -import {match} from '../../routes' -import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' - -const TAB_HEIGHT = 42 - -export const TabsSelector = observer( - ({ - active, - tabMenuInterp, - onClose, - }: { - active: boolean - tabMenuInterp: Animated.Value - onClose: () => void - }) => { - const store = useStores() - const insets = useSafeAreaInsets() - const [closingTabIndex, setClosingTabIndex] = useState<number | undefined>( - undefined, - ) - const closeInterp = useAnimatedValue(0) - const tabsContainerRef = useRef<View>(null) - const tabsRef = useRef<ScrollView>(null) - const tabRefs = useMemo( - () => - Array.from({length: store.nav.tabs.length}).map(() => - createRef<View>(), - ), - [store.nav.tabs.length], - ) - - const wrapperAnimStyle = { - transform: [ - { - translateY: tabMenuInterp.interpolate({ - inputRange: [0, 1.0], - outputRange: [320, 0], - }), - }, - ], - } - - // events - // = - - const onPressNewTab = () => { - store.nav.newTab('/') - onClose() - } - const onPressCloneTab = () => { - store.nav.newTab(store.nav.tab.current.url) - onClose() - } - const onPressShareTab = () => { - onClose() - Share.share({url: toShareUrl(store.nav.tab.current.url)}) - } - const onPressChangeTab = (tabIndex: number) => { - store.nav.setActiveTab(tabIndex) - onClose() - } - const onCloseTab = (tabIndex: number) => { - setClosingTabIndex(tabIndex) - closeInterp.setValue(0) - Animated.timing(closeInterp, { - toValue: 1, - duration: 300, - useNativeDriver: false, - }).start(() => { - setClosingTabIndex(undefined) - store.nav.closeTab(tabIndex) - }) - } - const onLayout = () => { - // focus the current tab - const targetTab = tabRefs[store.nav.tabIndex] - if (tabsContainerRef.current && tabsRef.current && targetTab.current) { - targetTab.current.measureLayout?.( - tabsContainerRef.current, - (_left: number, top: number) => { - tabsRef.current?.scrollTo({y: top, animated: false}) - }, - () => {}, - ) - } - } - - // rendering - // = - - const renderSwipeActions = () => { - return <View style={[s.p2]} /> - } - - const currentTabIndex = store.nav.tabIndex - const closingTabAnimStyle = { - height: Animated.multiply(TAB_HEIGHT, Animated.subtract(1, closeInterp)), - opacity: Animated.subtract(1, closeInterp), - marginBottom: Animated.multiply(4, Animated.subtract(1, closeInterp)), - } - - if (!active) { - return <View testID="emptyView" /> - } - - return ( - <Animated.View - testID="tabsSelectorView" - style={[ - styles.wrapper, - {bottom: insets.bottom + 55}, - wrapperAnimStyle, - ]}> - <View onLayout={onLayout}> - <View style={[s.p10, styles.section]}> - <View style={styles.btns}> - <TouchableWithoutFeedback - testID="shareButton" - onPress={onPressShareTab}> - <View style={[styles.btn]}> - <View style={styles.btnIcon}> - <FontAwesomeIcon size={16} icon="share" /> - </View> - <Text style={styles.btnText}>Share</Text> - </View> - </TouchableWithoutFeedback> - <TouchableWithoutFeedback - testID="cloneButton" - onPress={onPressCloneTab}> - <View style={[styles.btn]}> - <View style={styles.btnIcon}> - <FontAwesomeIcon size={16} icon={['far', 'clone']} /> - </View> - <Text style={styles.btnText}>Clone tab</Text> - </View> - </TouchableWithoutFeedback> - <TouchableWithoutFeedback - testID="newTabButton" - onPress={onPressNewTab}> - <View style={[styles.btn]}> - <View style={styles.btnIcon}> - <FontAwesomeIcon size={16} icon="plus" /> - </View> - <Text style={styles.btnText}>New tab</Text> - </View> - </TouchableWithoutFeedback> - </View> - </View> - <View - ref={tabsContainerRef} - style={[s.p10, styles.section, styles.sectionGrayBg]}> - <ScrollView ref={tabsRef} style={styles.tabs}> - {store.nav.tabs.map((tab, tabIndex) => { - const {icon} = match(tab.current.url) - const isActive = tabIndex === currentTabIndex - const isClosing = closingTabIndex === tabIndex - return ( - <Swipeable - key={tab.id} - testID="tabsSwipable" - renderLeftActions={renderSwipeActions} - renderRightActions={renderSwipeActions} - leftThreshold={100} - rightThreshold={100} - onSwipeableWillOpen={() => onCloseTab(tabIndex)}> - <Animated.View - style={[ - styles.tabOuter, - isClosing ? closingTabAnimStyle : undefined, - ]}> - <Animated.View - // HOTFIX - // TabsSelector.test.tsx snapshot fails if the - // ref was set like this: ref={tabRefs[tabIndex]} - ref={(ref: any) => (tabRefs[tabIndex] = ref)} - style={[ - styles.tab, - styles.existing, - isActive && styles.active, - ]}> - <TouchableWithoutFeedback - testID="changeTabButton" - onPress={() => onPressChangeTab(tabIndex)}> - <View style={styles.tabInner}> - <View style={styles.tabIcon}> - <FontAwesomeIcon size={20} icon={icon} /> - </View> - <Text - ellipsizeMode="tail" - numberOfLines={1} - suppressHighlighting={true} - style={[ - styles.tabText, - isActive && styles.tabTextActive, - ]}> - {tab.current.title || tab.current.url} - </Text> - </View> - </TouchableWithoutFeedback> - <TouchableWithoutFeedback - testID="closeTabButton" - onPress={() => onCloseTab(tabIndex)}> - <View style={styles.tabClose}> - <FontAwesomeIcon - size={14} - icon="x" - style={styles.tabCloseIcon} - /> - </View> - </TouchableWithoutFeedback> - </Animated.View> - </Animated.View> - </Swipeable> - ) - })} - </ScrollView> - </View> - </View> - </Animated.View> - ) - }, -) - -const styles = StyleSheet.create({ - wrapper: { - position: 'absolute', - width: '100%', - height: 320, - borderTopColor: colors.gray2, - borderTopWidth: 1, - backgroundColor: '#fff', - opacity: 1, - }, - section: { - borderBottomColor: colors.gray2, - borderBottomWidth: 1, - }, - sectionGrayBg: { - backgroundColor: colors.gray1, - }, - tabs: { - height: 240, - }, - tabOuter: { - height: TAB_HEIGHT + 4, - overflow: 'hidden', - }, - tab: { - flexDirection: 'row', - height: TAB_HEIGHT, - backgroundColor: colors.gray1, - alignItems: 'center', - borderRadius: 4, - }, - tabInner: { - flexDirection: 'row', - flex: 1, - alignItems: 'center', - paddingLeft: 12, - paddingVertical: 12, - }, - existing: { - borderColor: colors.gray4, - borderWidth: 1, - }, - active: { - backgroundColor: colors.white, - borderColor: colors.black, - borderWidth: 1, - }, - tabIcon: {}, - tabText: { - flex: 1, - paddingHorizontal: 10, - fontSize: 16, - }, - tabTextActive: { - fontWeight: '500', - }, - tabClose: { - paddingVertical: 16, - paddingRight: 16, - }, - tabCloseIcon: { - color: '#655', - }, - btns: { - flexDirection: 'row', - paddingTop: 2, - }, - btn: { - flexDirection: 'row', - flex: 1, - alignItems: 'center', - justifyContent: 'center', - backgroundColor: colors.gray1, - borderRadius: 4, - marginRight: 5, - paddingLeft: 12, - paddingRight: 16, - paddingVertical: 10, - }, - btnIcon: { - marginRight: 8, - }, - btnText: { - fontWeight: '500', - fontSize: 16, - }, -}) diff --git a/src/view/shell/mobile/index.tsx b/src/view/shell/mobile/index.tsx index 89a834ee1..6ab19d651 100644 --- a/src/view/shell/mobile/index.tsx +++ b/src/view/shell/mobile/index.tsx @@ -2,21 +2,17 @@ import React, {useState, useEffect} from 'react' import {observer} from 'mobx-react-lite' import { Animated, - Easing, GestureResponderEvent, StatusBar, StyleSheet, TouchableOpacity, TouchableWithoutFeedback, - useColorScheme, useWindowDimensions, View, } from 'react-native' import {ScreenContainer, Screen} from 'react-native-screens' import {useSafeAreaInsets} from 'react-native-safe-area-context' -import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {IconProp} from '@fortawesome/fontawesome-svg-core' -import {TABS_ENABLED} from 'lib/build-flags' import {useStores} from 'state/index' import { NavigationModel, @@ -31,18 +27,18 @@ import {ModalsContainer} from '../../com/modals/Modal' import {Lightbox} from '../../com/lightbox/Lightbox' import {Text} from '../../com/util/text/Text' import {ErrorBoundary} from '../../com/util/ErrorBoundary' -import {TabsSelector} from './TabsSelector' import {Composer} from './Composer' import {s, colors} from 'lib/styles' import {clamp} from 'lib/numbers' import { - GridIcon, - GridIconSolid, HomeIcon, HomeIconSolid, - MagnifyingGlassIcon, + MagnifyingGlassIcon2, + MagnifyingGlassIcon2Solid, + ComposeIcon2, BellIcon, BellIconSolid, + UserIcon, } from 'lib/icons' import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' import {useTheme} from 'lib/ThemeContext' @@ -52,74 +48,14 @@ import {useAnalytics} from 'lib/analytics' const Btn = ({ icon, notificationCount, - tabCount, onPress, onLongPress, }: { - icon: - | IconProp - | 'menu' - | 'menu-solid' - | 'home' - | 'home-solid' - | 'search' - | 'search-solid' - | 'bell' - | 'bell-solid' + icon: JSX.Element notificationCount?: number - tabCount?: number onPress?: (event: GestureResponderEvent) => void onLongPress?: (event: GestureResponderEvent) => void }) => { - const pal = usePalette('default') - let iconEl - if (icon === 'menu') { - iconEl = <GridIcon style={[styles.ctrlIcon, pal.text]} /> - } else if (icon === 'menu-solid') { - iconEl = <GridIconSolid style={[styles.ctrlIcon, pal.text]} /> - } else if (icon === 'home') { - iconEl = <HomeIcon size={27} style={[styles.ctrlIcon, pal.text]} /> - } else if (icon === 'home-solid') { - iconEl = <HomeIconSolid size={27} style={[styles.ctrlIcon, pal.text]} /> - } else if (icon === 'search') { - iconEl = ( - <MagnifyingGlassIcon - size={28} - style={[styles.ctrlIcon, pal.text, styles.bumpUpOnePixel]} - /> - ) - } else if (icon === 'search-solid') { - iconEl = ( - <MagnifyingGlassIcon - size={28} - strokeWidth={3} - style={[styles.ctrlIcon, pal.text, styles.bumpUpOnePixel]} - /> - ) - } else if (icon === 'bell') { - iconEl = ( - <BellIcon - size={27} - style={[styles.ctrlIcon, pal.text, styles.bumpUpOnePixel]} - /> - ) - } else if (icon === 'bell-solid') { - iconEl = ( - <BellIconSolid - size={27} - style={[styles.ctrlIcon, pal.text, styles.bumpUpOnePixel]} - /> - ) - } else { - iconEl = ( - <FontAwesomeIcon - icon={icon} - size={24} - style={[styles.ctrlIcon, pal.text]} - /> - ) - } - return ( <TouchableOpacity style={styles.ctrl} @@ -131,12 +67,7 @@ const Btn = ({ <Text style={styles.notificationCountLabel}>{notificationCount}</Text> </View> ) : undefined} - {tabCount && tabCount > 1 ? ( - <View style={styles.tabCount}> - <Text style={styles.tabCountLabel}>{tabCount}</Text> - </View> - ) : undefined} - {iconEl} + {icon} </TouchableOpacity> ) } @@ -145,15 +76,10 @@ export const MobileShell: React.FC = observer(() => { const theme = useTheme() const pal = usePalette('default') const store = useStores() - const [isTabsSelectorActive, setTabsSelectorActive] = useState(false) const winDim = useWindowDimensions() const [menuSwipingDirection, setMenuSwipingDirection] = useState(0) const swipeGestureInterp = useAnimatedValue(0) const minimalShellInterp = useAnimatedValue(0) - const tabMenuInterp = useAnimatedValue(0) - const newTabInterp = useAnimatedValue(0) - const [isRunningNewTabAnim, setIsRunningNewTabAnim] = useState(false) - const colorScheme = useColorScheme() const safeAreaInsets = useSafeAreaInsets() const screenRenderDesc = constructScreenRenderDesc(store.nav) const {track} = useAnalytics() @@ -188,6 +114,10 @@ export const MobileShell: React.FC = observer(() => { } } } + const onPressCompose = () => { + track('MobileShell:ComposeButtonPressed') + store.shell.openComposer({}) + } const onPressNotifications = () => { track('MobileShell:NotificationsButtonPressed') if (store.nav.tab.fixedTabPurpose === TabPurpose.Notifs) { @@ -203,8 +133,10 @@ export const MobileShell: React.FC = observer(() => { } } } - const onPressTabs = () => toggleTabsMenu(!isTabsSelectorActive) - const doNewTab = (url: string) => () => store.nav.newTab(url) + const onPressProfile = () => { + track('MobileShell:ProfileButtonPressed') + store.nav.navigate(`/profile/${store.me.handle}`) + } // minimal shell animation // = @@ -229,60 +161,6 @@ export const MobileShell: React.FC = observer(() => { transform: [{translateY: Animated.multiply(minimalShellInterp, 100)}], } - // tab selector animation - // = - const toggleTabsMenu = (active: boolean) => { - if (active) { - // will trigger the animation below - setTabsSelectorActive(true) - } else { - Animated.timing(tabMenuInterp, { - toValue: 0, - duration: 100, - useNativeDriver: false, - }).start(() => { - // hide once the animation has finished - setTabsSelectorActive(false) - }) - } - } - useEffect(() => { - if (isTabsSelectorActive) { - // trigger the animation once the tabs selector is rendering - Animated.timing(tabMenuInterp, { - toValue: 1, - duration: 100, - useNativeDriver: false, - }).start() - } - }, [tabMenuInterp, isTabsSelectorActive]) - - // new tab animation - // = - useEffect(() => { - if (screenRenderDesc.hasNewTab && !isRunningNewTabAnim) { - setIsRunningNewTabAnim(true) - } - }, [isRunningNewTabAnim, screenRenderDesc.hasNewTab]) - useEffect(() => { - if (isRunningNewTabAnim) { - const reset = () => { - store.nav.tab.setIsNewTab(false) - setIsRunningNewTabAnim(false) - } - Animated.timing(newTabInterp, { - toValue: 1, - duration: 250, - easing: Easing.out(Easing.exp), - useNativeDriver: false, - }).start(() => { - reset() - }) - } else { - newTabInterp.setValue(0) - } - }, [newTabInterp, store.nav.tab, isRunningNewTabAnim]) - // navigation swipes // = const isMenuActive = store.shell.isMainMenuOpen @@ -495,20 +373,6 @@ export const MobileShell: React.FC = observer(() => { )} </HorzSwipe> </View> - {isTabsSelectorActive ? ( - <View - style={[ - styles.topBarProtector, - colorScheme === 'dark' ? styles.topBarProtectorDark : undefined, - {height: safeAreaInsets.top}, - ]} - /> - ) : undefined} - <TabsSelector - active={isTabsSelectorActive} - tabMenuInterp={tabMenuInterp} - onClose={() => toggleTabsMenu(false)} - /> <Animated.View style={[ styles.bottomBar, @@ -518,28 +382,85 @@ export const MobileShell: React.FC = observer(() => { footerMinimalShellTransform, ]}> <Btn - icon={isAtHome ? 'home-solid' : 'home'} + icon={ + isAtHome ? ( + <HomeIconSolid + strokeWidth={4} + size={24} + style={[styles.ctrlIcon, pal.text, styles.homeIcon]} + /> + ) : ( + <HomeIcon + strokeWidth={4} + size={24} + style={[styles.ctrlIcon, pal.text, styles.homeIcon]} + /> + ) + } onPress={onPressHome} - onLongPress={TABS_ENABLED ? doNewTab('/') : undefined} /> <Btn - icon={isAtSearch ? 'search-solid' : 'search'} + icon={ + isAtSearch ? ( + <MagnifyingGlassIcon2Solid + size={25} + style={[styles.ctrlIcon, pal.text, styles.searchIcon]} + strokeWidth={1.8} + /> + ) : ( + <MagnifyingGlassIcon2 + size={25} + style={[styles.ctrlIcon, pal.text, styles.searchIcon]} + strokeWidth={1.8} + /> + ) + } onPress={onPressSearch} - onLongPress={TABS_ENABLED ? doNewTab('/') : undefined} /> - {TABS_ENABLED ? ( - <Btn - icon={isTabsSelectorActive ? 'clone' : ['far', 'clone']} - onPress={onPressTabs} - tabCount={store.nav.tabCount} - /> - ) : undefined} <Btn - icon={isAtNotifications ? 'bell-solid' : 'bell'} + icon={ + <View style={styles.ctrlIconSizingWrapper}> + <ComposeIcon2 + strokeWidth={1.5} + size={29} + style={[styles.ctrlIcon, pal.text, styles.composeIcon]} + backgroundColor={pal.colors.background} + /> + </View> + } + onPress={onPressCompose} + /> + <Btn + icon={ + isAtNotifications ? ( + <BellIconSolid + size={24} + strokeWidth={1.9} + style={[styles.ctrlIcon, pal.text, styles.bellIcon]} + /> + ) : ( + <BellIcon + size={24} + strokeWidth={1.9} + style={[styles.ctrlIcon, pal.text, styles.bellIcon]} + /> + ) + } onPress={onPressNotifications} - onLongPress={TABS_ENABLED ? doNewTab('/notifications') : undefined} notificationCount={store.me.notifications.unreadCount} /> + <Btn + icon={ + <View style={styles.ctrlIconSizingWrapper}> + <UserIcon + size={28} + strokeWidth={1.5} + style={[styles.ctrlIcon, pal.text, styles.profileIcon]} + /> + </View> + } + onPress={onPressProfile} + /> </Animated.View> <ModalsContainer /> <Lightbox /> @@ -650,46 +571,51 @@ const styles = StyleSheet.create({ flexDirection: 'row', borderTopWidth: 1, paddingLeft: 5, - paddingRight: 25, + paddingRight: 10, }, ctrl: { flex: 1, - paddingTop: 12, - paddingBottom: 5, + paddingTop: 13, + paddingBottom: 4, }, notificationCount: { position: 'absolute', - left: '60%', + left: '56%', top: 10, - backgroundColor: colors.red3, + backgroundColor: colors.blue3, paddingHorizontal: 4, paddingBottom: 1, borderRadius: 8, + zIndex: 1, }, notificationCountLabel: { fontSize: 12, fontWeight: 'bold', color: colors.white, }, - tabCount: { - position: 'absolute', - left: 46, - top: 30, - }, - tabCountLabel: { - fontSize: 12, - fontWeight: 'bold', - color: colors.black, - }, ctrlIcon: { marginLeft: 'auto', marginRight: 'auto', }, + ctrlIconSizingWrapper: { + height: 27, + }, inactive: { color: colors.gray3, }, - bumpUpOnePixel: { - position: 'relative', - top: -1, + homeIcon: { + top: 0, + }, + searchIcon: { + top: -2, + }, + bellIcon: { + top: -2.5, + }, + composeIcon: { + top: -4.5, + }, + profileIcon: { + top: -4, }, }) |