Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16,453 changes: 16,453 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"@react-navigation/bottom-tabs": "^6.5.7",
"@react-navigation/native": "^6.1.6",
"@react-navigation/native-stack": "^6.9.12",
"expo": "^48.0.0",
"expo": "^48.0.10",
"expo-asset": "~8.9.1",
"expo-blur": "~12.2.2",
"expo-font": "~11.1.1",
Expand Down
147 changes: 48 additions & 99 deletions src/components/CustomTabBar.js
Original file line number Diff line number Diff line change
@@ -1,111 +1,60 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import { colors, device, gStyle } from '../constants';
import React from 'react';
import { View, TouchableOpacity, StyleSheet } from 'react-native';
import { colors } from '../constants';

// components
import BarMusicPlayer from './BarMusicPlayer';

// context
import Context from '../context';

// https://reactnavigation.org/docs/5.x/bottom-tab-navigator/#tabbar
function CustomTabBar({ descriptors, navigation, state }) {
// get main app state
const { currentSongData, showMusicBar } = React.useContext(Context);

const focusedOptions = descriptors[state.routes[state.index].key].options;

if (focusedOptions.tabBarVisible === false) {
return null;
}
export default function CustomTabBar({ state, descriptors, navigation, variant }) {
const isSidebar = variant === 'sidebar';

return (
<React.Fragment>
{showMusicBar && <BarMusicPlayer song={currentSongData} />}

<View style={styles.container}>
{state.routes.map((route, index) => {
const { options } = descriptors[route.key];

// default label
const defaultLabl =
options.title !== undefined ? options.title : route.name;
// label set
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: defaultLabl;

const isFocused = state.index === index;
const color = isFocused ? colors.white : colors.greyInactive;

// custom icon
const Icon = options.tabBarIcon;

const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true
});

if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name);
}
};

const onLongPress = () => {
navigation.emit({
type: 'tabLongPress',
target: route.key
});
};

return (
<TouchableOpacity
key={route.key}
accessibilityRole="button"
accessibilityState={isFocused ? { selected: true } : {}}
accessibilityLabel={options.tabBarAccessibilityLabel}
activeOpacity={gStyle.activeOpacity}
testID={options.tabBarTestID}
onPress={onPress}
onLongPress={onLongPress}
style={styles.containerTab}
>
<Icon active={isFocused} />
<Text style={[styles.label, { color }]}>{label}</Text>
</TouchableOpacity>
);
})}
</View>
</React.Fragment>
<View style={[styles.container, isSidebar && styles.sidebar]}>
{state.routes.map((route, index) => {
const isFocused = state.index === index;

const { options } = descriptors[route.key];
const icon = options.tabBarIcon?.({ active: isFocused });

const onPress = () => {
navigation.navigate(route.name);
};

return (
<TouchableOpacity
key={route.key}
onPress={onPress}
style={[styles.item, isFocused && styles.active]}
>
{icon}
</TouchableOpacity>
);
})}
</View>
);
}

CustomTabBar.propTypes = {
// required
descriptors: PropTypes.object.isRequired,
navigation: PropTypes.object.isRequired,
state: PropTypes.object.isRequired
};

const styles = StyleSheet.create({
container: {
...gStyle.flexRowCenterAlign,
backgroundColor: colors.grey,
paddingBottom: device.iPhoneNotch ? 24 : 16,
paddingTop: 12
flexDirection: 'row',
paddingVertical: 10,
justifyContent: 'space-between',
paddingHorizontal: 20
},

sidebar: {
width: 80,
flexDirection: 'column',
justifyContent: 'center',
paddingVertical: 20,
},

item: {
padding: 16,
alignItems: 'center',
justifyContent: 'center',
},
containerTab: {
...gStyle.flex1,
...gStyle.flexCenter

active: {
backgroundColor: colors.grey,
borderRadius: 8,
},
label: {
...gStyle.textSpotify12,
paddingTop: 4
}
});

export default CustomTabBar;
33 changes: 33 additions & 0 deletions src/components/TabLayout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import { View, StyleSheet, Platform } from 'react-native';
import TabNavigator from '../navigation/TabNavigation';
import { useIsWebSidebar } from '../utils/useIsWebSidebar';
import WebSidebar from './WebSideBar';


export default function TabLayout() {
const isWebSidebar = useIsWebSidebar();

if (!isWebSidebar) {
return <TabNavigator />;
}

return (
<View style={styles.root}>
<WebSidebar />
<View style={styles.content}>
<TabNavigator />
</View>
</View>
);
}

const styles = StyleSheet.create({
root: {
flex: 1,
flexDirection: 'row',
},
content: {
flex: 1,
},
});
40 changes: 40 additions & 0 deletions src/components/WebSideBar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import { View, TouchableOpacity, StyleSheet } from 'react-native';
import { useNavigation } from '@react-navigation/native';

import SvgTabHome from '../icons/Svg.TabHome';
import SvgTabSearch from '../icons/Svg.TabSearch';
import SvgTabLibrary from '../icons/Svg.TabLibrary';
import { colors } from '../constants';

const SIDEBAR_WIDTH = 80;

export default function WebSidebar() {
const navigation = useNavigation();

return (
<View style={styles.sidebar}>
<TouchableOpacity onPress={() => navigation.navigate('StackHome')}>
<SvgTabHome />
</TouchableOpacity>

<TouchableOpacity onPress={() => navigation.navigate('StackSearch')}>
<SvgTabSearch />
</TouchableOpacity>

<TouchableOpacity onPress={() => navigation.navigate('StackLibrary')}>
<SvgTabLibrary />
</TouchableOpacity>
</View>
);
}

const styles = StyleSheet.create({
sidebar: {
width: SIDEBAR_WIDTH,
backgroundColor: colors.black,
alignItems: 'center',
gap: 24,
paddingTop: 20,
},
});
3 changes: 2 additions & 1 deletion src/navigation/RootStack.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import TabNavigation from './TabNavigation';
// screens
import ModalMusicPlayer from '../screens/ModalMusicPlayer';
import ModalMoreOptions from '../screens/ModalMoreOptions';
import TabLayout from '../components/TabLayout';

const Stack = createNativeStackNavigator();

Expand All @@ -21,7 +22,7 @@ function RootStack() {
>
<Stack.Screen
name="TabNavigation"
component={TabNavigation}
component={TabLayout}
options={{
headerShown: false
}}
Expand Down
54 changes: 16 additions & 38 deletions src/navigation/TabNavigation.js
Original file line number Diff line number Diff line change
@@ -1,66 +1,44 @@
import * as React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { colors } from '../constants';

// navigation stacks
import StackHome from './StackHome';
import StackSearch from './StackSearch';
import StackLibrary from './StackLibrary';

// components
import CustomTabBar from '../components/CustomTabBar';

// icons
import SvgTabHome from '../icons/Svg.TabHome';
import SvgTabLibrary from '../icons/Svg.TabLibrary';
import SvgTabSearch from '../icons/Svg.TabSearch';
import SvgTabLibrary from '../icons/Svg.TabLibrary';

import CustomTabBar from '../components/CustomTabBar';
import { useIsWebSidebar } from '../utils/useIsWebSidebar';

const Tab = createBottomTabNavigator();

function TabNavigation() {
export default function TabNavigator() {
const isWebSidebar = useIsWebSidebar();

return (
<Tab.Navigator
screenOptions={({ route }) => ({
screenOptions={({ route }) => ({
headerShown: false,
tabBarIcon: ({ active }) => {
let icon = <SvgTabHome active={active} />;

if (route.name === 'StackSearch') {
icon = <SvgTabSearch active={active} />;
} else if (route.name === 'StackLibrary') {
icon = <SvgTabLibrary active={active} />;
}

return icon;
},
tabBarActiveTintColor: colors.white,
tabBarInactiveTintColor: colors.greyInactive
}
})}
tabBar={(props) => <CustomTabBar {...props} />}
tabBar={(props) => {
if (isWebSidebar) return null;
return <CustomTabBar {...props} />;
}}
>
<Tab.Screen
name="StackHome"
component={StackHome}
options={{
tabBarLabel: 'Home'
}}
/>
<Tab.Screen
name="StackSearch"
component={StackSearch}
options={{
tabBarLabel: 'Search'
}}
/>
<Tab.Screen
name="StackLibrary"
component={StackLibrary}
options={{
tabBarLabel: 'Library'
}}
/>
<Tab.Screen name="StackHome" component={StackHome} />
<Tab.Screen name="StackSearch" component={StackSearch} />
<Tab.Screen name="StackLibrary" component={StackLibrary} />
</Tab.Navigator>
);
}

export default TabNavigation;
7 changes: 7 additions & 0 deletions src/utils/useIsWebSidebar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Platform, useWindowDimensions } from 'react-native';

export function useIsWebSidebar() {
const { width } = useWindowDimensions();

return Platform.OS === 'web' && width >= 768;
}
Loading