Use a custom tabbar

This commit is contained in:
2024-01-04 15:26:28 +01:00
committed by Clément Le Bihan
parent 42a947dfb0
commit 038918c212
5 changed files with 72 additions and 70 deletions

View File

@@ -35,13 +35,16 @@ import DiscoveryView from './views/V2/DiscoveryView';
import MusicView from './views/MusicView'; import MusicView from './views/MusicView';
import Leaderboardiew from './views/LeaderboardView'; import Leaderboardiew from './views/LeaderboardView';
import { LinearGradient } from 'expo-linear-gradient'; import { LinearGradient } from 'expo-linear-gradient';
import ScaffoldMobileCC from './components/UI/ScaffoldMobileCC';
const Stack = createNativeStackNavigator<AppRouteParams & { Loading: never; Oops: never }>(); const Stack = createNativeStackNavigator<AppRouteParams & { Loading: never; Oops: never }>();
const Tab = createBottomTabNavigator<AppRouteParams & { Loading: never; Oops: never }>(); const Tab = createBottomTabNavigator<AppRouteParams & { Loading: never; Oops: never }>();
const Tabs = () => { const Tabs = () => {
return ( return (
<Tab.Navigator> <Tab.Navigator
tabBar={ScaffoldMobileCC}
>
{Object.entries(tabRoutes).map(([name, route], routeIndex) => ( {Object.entries(tabRoutes).map(([name, route], routeIndex) => (
<Tab.Screen <Tab.Screen
key={'route-' + routeIndex} key={'route-' + routeIndex}

View File

@@ -11,6 +11,7 @@ interface ButtonProps {
title?: string; title?: string;
style?: StyleProp<ViewStyle>; style?: StyleProp<ViewStyle>;
onPress?: () => void | Promise<void>; onPress?: () => void | Promise<void>;
onLongPress?: () => void | Promise<void>;
isDisabled?: boolean; isDisabled?: boolean;
icon?: Icon; icon?: Icon;
iconVariant?: 'Bold' | 'Outline'; iconVariant?: 'Bold' | 'Outline';
@@ -22,6 +23,7 @@ const ButtonBase: React.FC<ButtonProps> = ({
title, title,
style, style,
onPress, onPress,
onLongPress,
isDisabled, isDisabled,
icon, icon,
iconImage, iconImage,
@@ -123,6 +125,18 @@ const ButtonBase: React.FC<ButtonProps> = ({
} }
} }
}} }}
onLongPress={async () => {
if (onLongPress && !isDisabled) {
setLoading(true);
try {
await onLongPress();
} catch (error) {
console.error(error);
} finally {
setLoading(false);
}
}
}}
isDisabled={isDisabled} isDisabled={isDisabled}
isOutlined={type === 'outlined'} isOutlined={type === 'outlined'}
> >

View File

@@ -5,6 +5,7 @@ import { Animated, StyleProp, ViewStyle } from 'react-native';
interface InteractiveBaseProps { interface InteractiveBaseProps {
children?: React.ReactNode; children?: React.ReactNode;
onPress?: () => Promise<void>; onPress?: () => Promise<void>;
onLongPress?: () => Promise<void>;
isDisabled?: boolean; isDisabled?: boolean;
isOutlined?: boolean; isOutlined?: boolean;
focusable?: boolean; focusable?: boolean;
@@ -44,6 +45,7 @@ interface InteractiveBaseProps {
const InteractiveBase: React.FC<InteractiveBaseProps> = ({ const InteractiveBase: React.FC<InteractiveBaseProps> = ({
children, children,
onPress, onPress,
onLongPress,
style, style,
styleAnimate, styleAnimate,
isDisabled = false, isDisabled = false,
@@ -183,10 +185,6 @@ const InteractiveBase: React.FC<InteractiveBaseProps> = ({
useNativeDriver: false, useNativeDriver: false,
}), }),
]).start(); ]).start();
if (onPress && !isDisabled) {
onPress();
}
}; };
// Mouse Leave // Mouse Leave
const handleMouseLeave = () => { const handleMouseLeave = () => {
@@ -248,6 +246,8 @@ const InteractiveBase: React.FC<InteractiveBaseProps> = ({
onPressIn={handlePressIn} onPressIn={handlePressIn}
onPressOut={handlePressOut} onPressOut={handlePressOut}
onHoverOut={handleMouseLeave} onHoverOut={handleMouseLeave}
onPress={onPress}
onLongPress={onLongPress}
> >
{children} {children}
</Pressable> </Pressable>

View File

@@ -28,8 +28,6 @@ type ScaffoldCCProps = {
const ScaffoldCC = ({ const ScaffoldCC = ({
children, children,
routeName, routeName,
withPadding = true,
enableScroll = true,
}: ScaffoldCCProps) => { }: ScaffoldCCProps) => {
const userQuery = useQuery(API.getUserInfo); const userQuery = useQuery(API.getUserInfo);
const screenSize = useBreakpointValue({ base: 'small', md: 'big' }); const screenSize = useBreakpointValue({ base: 'small', md: 'big' });
@@ -48,12 +46,8 @@ const ScaffoldCC = ({
<Flex style={{ flex: 1, backgroundColor: '#cdd4fd' }}> <Flex style={{ flex: 1, backgroundColor: '#cdd4fd' }}>
{screenSize === 'small' ? ( {screenSize === 'small' ? (
<ScaffoldMobileCC <ScaffoldMobileCC
enableScroll={enableScroll}
user={userQuery.data}
logo={logo?.at(0)?.uri ?? ''}
routeName={routeName} routeName={routeName}
menu={menu} menu={menu}
widthPadding={withPadding}
> >
{children} {children}
</ScaffoldMobileCC> </ScaffoldMobileCC>
@@ -63,7 +57,7 @@ const ScaffoldCC = ({
logo={logo?.at(0)?.uri ?? ''} logo={logo?.at(0)?.uri ?? ''}
routeName={routeName} routeName={routeName}
menu={menu} menu={menu}
widthPadding={withPadding} widthPadding={true}
> >
{children} {children}
</ScaffoldDesktopCC> </ScaffoldDesktopCC>

View File

@@ -1,79 +1,70 @@
/* eslint-disable no-mixed-spaces-and-tabs */
import { View } from 'react-native'; import { View } from 'react-native';
import { Flex, useMediaQuery, useTheme } from 'native-base'; import { Flex, useMediaQuery, useTheme } from 'native-base';
import ButtonBase from './ButtonBase'; import ButtonBase from './ButtonBase';
import { Icon } from 'iconsax-react-native'; import { Discover } from 'iconsax-react-native';
import { useNavigation } from '../../Navigation';
import User from '../../models/User';
import { translate } from '../../i18n/i18n'; import { translate } from '../../i18n/i18n';
import { BottomTabBarProps } from '@react-navigation/bottom-tabs';
import { ComponentProps } from 'react';
type ScaffoldMobileCCProps = { const ScaffoldMobileCC = ({ state, descriptors, navigation }: BottomTabBarProps) => {
children?: React.ReactNode;
user: User;
logo: string;
routeName: string;
widthPadding: boolean;
enableScroll: boolean;
menu: readonly {
type: 'main' | 'sub';
title:
| 'menuDiscovery'
| 'menuProfile'
| 'menuMusic'
| 'menuSearch'
| 'menuLeaderBoard'
| 'menuSettings';
icon: Icon;
link: string;
}[];
};
const ScaffoldMobileCC = (props: ScaffoldMobileCCProps) => {
const navigation = useNavigation();
const [isSmallScreen] = useMediaQuery({ maxWidth: 400 }); const [isSmallScreen] = useMediaQuery({ maxWidth: 400 });
const { colors } = useTheme(); const { colors } = useTheme();
return ( return (
<View style={{ height: '100%', flexDirection: 'column', overflow: 'hidden' }}> <View style={{ padding: 8, paddingTop: 0 }}>
<View <Flex
style={{ style={{
flex: 1, width: '100%',
maxHeight: '100%', flexDirection: 'row',
padding: props.widthPadding ? 8 : 0, backgroundColor: colors.coolGray[500],
padding: 8,
justifyContent: 'space-between',
borderRadius: 8,
}} }}
> >
{props.children} {state.routes.map((route, index) => {
</View> const { options } = descriptors[route.key]!;
<View style={{ padding: 8, paddingTop: 0 }}> const label = options.title !== undefined ? options.title : route.name;
<Flex const isFocused = state.index === index;
style={{
width: '100%', return (
flexDirection: 'row',
backgroundColor: colors.coolGray[500],
padding: 8,
justifyContent: 'space-between',
borderRadius: 8,
}}
>
{props.menu.map((value) => (
<ButtonBase <ButtonBase
key={'key-menu-link-' + value.title} key={'key-menu-link-' + label}
type="menu" type="menu"
icon={value.icon} icon={Discover}
title={ title={
props.routeName === value.link && !isSmallScreen isFocused && !isSmallScreen ? translate(label as any) : undefined
? translate(value.title)
: undefined
} }
isDisabled={props.routeName === value.link} isDisabled={isFocused}
iconVariant={props.routeName === value.link ? 'Bold' : 'Outline'} iconVariant={isFocused ? 'Bold' : 'Outline'}
onPress={async () => navigation.navigate(value.link as never)} onPress={() => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
});
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name, route.params);
}
}}
onLongPress={() => {
navigation.emit({
type: 'tabLongPress',
target: route.key,
});
}}
/> />
))} );
</Flex> })}
</View> </Flex>
</View> </View>
); );
}; };
export default ScaffoldMobileCC; // This is needed to bypass a bug in react-navigation that calls custom tabBars weirdly
const Wrapper = (props: ComponentProps<typeof ScaffoldMobileCC>) => {
return <ScaffoldMobileCC {...props} />;
}
export default Wrapper;