Use a custom tabbar
This commit is contained in:
@@ -35,13 +35,16 @@ import DiscoveryView from './views/V2/DiscoveryView';
|
||||
import MusicView from './views/MusicView';
|
||||
import Leaderboardiew from './views/LeaderboardView';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import ScaffoldMobileCC from './components/UI/ScaffoldMobileCC';
|
||||
|
||||
const Stack = createNativeStackNavigator<AppRouteParams & { Loading: never; Oops: never }>();
|
||||
const Tab = createBottomTabNavigator<AppRouteParams & { Loading: never; Oops: never }>();
|
||||
|
||||
const Tabs = () => {
|
||||
return (
|
||||
<Tab.Navigator>
|
||||
<Tab.Navigator
|
||||
tabBar={ScaffoldMobileCC}
|
||||
>
|
||||
{Object.entries(tabRoutes).map(([name, route], routeIndex) => (
|
||||
<Tab.Screen
|
||||
key={'route-' + routeIndex}
|
||||
|
||||
@@ -11,6 +11,7 @@ interface ButtonProps {
|
||||
title?: string;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
onPress?: () => void | Promise<void>;
|
||||
onLongPress?: () => void | Promise<void>;
|
||||
isDisabled?: boolean;
|
||||
icon?: Icon;
|
||||
iconVariant?: 'Bold' | 'Outline';
|
||||
@@ -22,6 +23,7 @@ const ButtonBase: React.FC<ButtonProps> = ({
|
||||
title,
|
||||
style,
|
||||
onPress,
|
||||
onLongPress,
|
||||
isDisabled,
|
||||
icon,
|
||||
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}
|
||||
isOutlined={type === 'outlined'}
|
||||
>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Animated, StyleProp, ViewStyle } from 'react-native';
|
||||
interface InteractiveBaseProps {
|
||||
children?: React.ReactNode;
|
||||
onPress?: () => Promise<void>;
|
||||
onLongPress?: () => Promise<void>;
|
||||
isDisabled?: boolean;
|
||||
isOutlined?: boolean;
|
||||
focusable?: boolean;
|
||||
@@ -44,6 +45,7 @@ interface InteractiveBaseProps {
|
||||
const InteractiveBase: React.FC<InteractiveBaseProps> = ({
|
||||
children,
|
||||
onPress,
|
||||
onLongPress,
|
||||
style,
|
||||
styleAnimate,
|
||||
isDisabled = false,
|
||||
@@ -183,10 +185,6 @@ const InteractiveBase: React.FC<InteractiveBaseProps> = ({
|
||||
useNativeDriver: false,
|
||||
}),
|
||||
]).start();
|
||||
|
||||
if (onPress && !isDisabled) {
|
||||
onPress();
|
||||
}
|
||||
};
|
||||
// Mouse Leave
|
||||
const handleMouseLeave = () => {
|
||||
@@ -248,6 +246,8 @@ const InteractiveBase: React.FC<InteractiveBaseProps> = ({
|
||||
onPressIn={handlePressIn}
|
||||
onPressOut={handlePressOut}
|
||||
onHoverOut={handleMouseLeave}
|
||||
onPress={onPress}
|
||||
onLongPress={onLongPress}
|
||||
>
|
||||
{children}
|
||||
</Pressable>
|
||||
|
||||
@@ -28,8 +28,6 @@ type ScaffoldCCProps = {
|
||||
const ScaffoldCC = ({
|
||||
children,
|
||||
routeName,
|
||||
withPadding = true,
|
||||
enableScroll = true,
|
||||
}: ScaffoldCCProps) => {
|
||||
const userQuery = useQuery(API.getUserInfo);
|
||||
const screenSize = useBreakpointValue({ base: 'small', md: 'big' });
|
||||
@@ -48,12 +46,8 @@ const ScaffoldCC = ({
|
||||
<Flex style={{ flex: 1, backgroundColor: '#cdd4fd' }}>
|
||||
{screenSize === 'small' ? (
|
||||
<ScaffoldMobileCC
|
||||
enableScroll={enableScroll}
|
||||
user={userQuery.data}
|
||||
logo={logo?.at(0)?.uri ?? ''}
|
||||
routeName={routeName}
|
||||
menu={menu}
|
||||
widthPadding={withPadding}
|
||||
>
|
||||
{children}
|
||||
</ScaffoldMobileCC>
|
||||
@@ -63,7 +57,7 @@ const ScaffoldCC = ({
|
||||
logo={logo?.at(0)?.uri ?? ''}
|
||||
routeName={routeName}
|
||||
menu={menu}
|
||||
widthPadding={withPadding}
|
||||
widthPadding={true}
|
||||
>
|
||||
{children}
|
||||
</ScaffoldDesktopCC>
|
||||
|
||||
@@ -1,49 +1,16 @@
|
||||
/* eslint-disable no-mixed-spaces-and-tabs */
|
||||
import { View } from 'react-native';
|
||||
import { Flex, useMediaQuery, useTheme } from 'native-base';
|
||||
import ButtonBase from './ButtonBase';
|
||||
import { Icon } from 'iconsax-react-native';
|
||||
import { useNavigation } from '../../Navigation';
|
||||
import User from '../../models/User';
|
||||
import { Discover } from 'iconsax-react-native';
|
||||
import { translate } from '../../i18n/i18n';
|
||||
import { BottomTabBarProps } from '@react-navigation/bottom-tabs';
|
||||
import { ComponentProps } from 'react';
|
||||
|
||||
type ScaffoldMobileCCProps = {
|
||||
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 ScaffoldMobileCC = ({ state, descriptors, navigation }: BottomTabBarProps) => {
|
||||
const [isSmallScreen] = useMediaQuery({ maxWidth: 400 });
|
||||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
<View style={{ height: '100%', flexDirection: 'column', overflow: 'hidden' }}>
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
maxHeight: '100%',
|
||||
padding: props.widthPadding ? 8 : 0,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</View>
|
||||
<View style={{ padding: 8, paddingTop: 0 }}>
|
||||
<Flex
|
||||
style={{
|
||||
@@ -55,25 +22,49 @@ const ScaffoldMobileCC = (props: ScaffoldMobileCCProps) => {
|
||||
borderRadius: 8,
|
||||
}}
|
||||
>
|
||||
{props.menu.map((value) => (
|
||||
{state.routes.map((route, index) => {
|
||||
const { options } = descriptors[route.key]!;
|
||||
const label = options.title !== undefined ? options.title : route.name;
|
||||
const isFocused = state.index === index;
|
||||
|
||||
return (
|
||||
<ButtonBase
|
||||
key={'key-menu-link-' + value.title}
|
||||
key={'key-menu-link-' + label}
|
||||
type="menu"
|
||||
icon={value.icon}
|
||||
icon={Discover}
|
||||
title={
|
||||
props.routeName === value.link && !isSmallScreen
|
||||
? translate(value.title)
|
||||
: undefined
|
||||
isFocused && !isSmallScreen ? translate(label as any) : undefined
|
||||
}
|
||||
isDisabled={props.routeName === value.link}
|
||||
iconVariant={props.routeName === value.link ? 'Bold' : 'Outline'}
|
||||
onPress={async () => navigation.navigate(value.link as never)}
|
||||
isDisabled={isFocused}
|
||||
iconVariant={isFocused ? 'Bold' : 'Outline'}
|
||||
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>
|
||||
</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;
|
||||
|
||||
Reference in New Issue
Block a user