Skip to content

Commit

Permalink
Merge pull request #340 from wyne/SCORE-76-bottom-sheet-buttons
Browse files Browse the repository at this point in the history
Score 76 bottom sheet buttons
  • Loading branch information
wyne authored Nov 15, 2023
2 parents cfa8c18 + 216a32e commit 6f7dd68
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 60 deletions.
2 changes: 1 addition & 1 deletion src/components/Buttons/MenuButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ interface Props {
const MenuButton: React.FunctionComponent<Props> = ({ navigation }) => {
return (
<HeaderButton accessibilityLabel='Home' onPress={async () => {
navigation.navigate('List');
navigation.goBack();
await analytics().logEvent('menu');
}}>
<Icon name="bars"
Expand Down
138 changes: 138 additions & 0 deletions src/components/GameBottomSheet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import React, { useMemo, useRef, useState } from 'react';
import { View, StyleSheet, Text } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { ParamListBase } from '@react-navigation/native';
import BottomSheet, { BottomSheetScrollView } from '@gorhom/bottom-sheet';

import { useAppSelector } from '../../redux/hooks';
import Rounds from '../components/Rounds';
import { selectGameById } from '../../redux/GamesSlice';
import { systemBlue } from '../constants';
import Animated, { Extrapolate, interpolate, useAnimatedStyle, useSharedValue } from 'react-native-reanimated';
import { TouchableOpacity } from 'react-native-gesture-handler';
import { Icon } from 'react-native-elements';

/**
* Height of the bottom sheet
*/
export const bottomSheetHeight = 80;

interface Props {
navigation: NativeStackNavigationProp<ParamListBase, string, undefined>;
containerHeight: number;
}

const GameBottomSheet: React.FunctionComponent<Props> = ({ navigation, containerHeight }) => {
const currentGameId = useAppSelector(state => state.settings.currentGameId);
if (typeof currentGameId == 'undefined') return null;

const fullscreen = useAppSelector(state => state.settings.home_fullscreen);
const currentGame = useAppSelector(state => selectGameById(state, state.settings.currentGameId));

if (currentGame == undefined) return null;

// ref
const bottomSheetRef = useRef<BottomSheet>(null);

// variables
const snapPoints = useMemo(() => [bottomSheetHeight, '60%', '100%'], []);

// State variable for the current snap point index
const [snapPointIndex, setSnapPointIndex] = useState(1);

// Function to cycle through the snap points
const cycleSnapPoints = () => {
setSnapPointIndex((prevIndex) => {
const nextIndex = prevIndex + 1;
return nextIndex < snapPoints.length ? nextIndex : 0;
});
};

// Function to snap to the next point when the button is pressed
const handleButtonPress = () => {
cycleSnapPoints();
bottomSheetRef.current?.snapToIndex(snapPointIndex);
};

const animatedPosition = useSharedValue(0);

const newStyles = useAnimatedStyle(() => {
const snapPoint0: number = typeof snapPoints[0] === 'string'
? parseFloat(snapPoints[0]) / 100 * containerHeight
: containerHeight - snapPoints[0];

const delta = snapPoint0 - animatedPosition.value;

const i = interpolate(delta, [0, 30], [0, 1], Extrapolate.CLAMP);

return {
opacity: i
};
});

return (

<BottomSheet
ref={bottomSheetRef}
index={0}
snapPoints={snapPoints}
backgroundStyle={{ backgroundColor: 'rgb(30,40,50)' }}
handleIndicatorStyle={{ backgroundColor: 'white' }}
animatedPosition={animatedPosition}
>
<BottomSheetScrollView >
<SafeAreaView edges={['right', 'left']}>
<View style={styles.sheetHeaderContainer}>
<Text style={styles.sheetHeader} onPress={() => handleButtonPress()}>
{currentGame.title}
</Text>
<Text style={styles.sheetHeaderButton} onPress={() => navigation.navigate('Settings')}>
Edit
</Text>
</View>

<Animated.View style={[styles.sheetContent, newStyles]}>
<Rounds navigation={navigation} show={!fullscreen} />
<Text style={{ color: 'white' }}>
Tap on a column to set the current round.
</Text>

<View style={{ flexDirection: 'row', justifyContent: 'space-around', paddingVertical: 10 }}>
<TouchableOpacity onPress={() => navigation.navigate('Share')}>
<View style={{ margin: 5, padding: 10, paddingHorizontal: 20, backgroundColor: 'rgba(0,0,0,.2)', borderRadius: 10, alignItems: 'center' }}>
<Icon name="share-outline" type="ionicon" size={30} color={systemBlue} />
<Text style={{ color: systemBlue, fontSize: 15, paddingTop: 5 }}>Share</Text>
</View>
</TouchableOpacity>
</View>
</Animated.View>
</SafeAreaView>
</BottomSheetScrollView>
</BottomSheet>
);
};

const styles = StyleSheet.create({
sheetHeaderContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
paddingHorizontal: 10,
},
sheetHeader: {
color: 'white',
fontSize: 20,
paddingTop: 0,
fontWeight: 'bold'
},
sheetHeaderButton: {
paddingHorizontal: 10,
fontSize: 20,
color: systemBlue,
},
sheetContent: {
padding: 10,
},
});

export default GameBottomSheet;
2 changes: 2 additions & 0 deletions src/components/Headers/GameHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import analytics from '@react-native-firebase/analytics';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { ParamListBase } from '@react-navigation/native';

import FullscreenButton from '../Buttons/FullscreenButton';
import { roundNext, roundPrevious } from '../../../redux/GamesSlice';
import { selectGameById } from '../../../redux/GamesSlice';
import { systemBlue } from '../../constants';
Expand Down Expand Up @@ -91,6 +92,7 @@ const GameHeader: React.FunctionComponent<Props> = ({ navigation }) => {
<CustomHeader navigation={navigation}
headerLeft={<>
<MenuButton navigation={navigation} />
<FullscreenButton />
</>}
headerCenter={<>
<PrevRoundButton prevRoundHandler={prevRoundHandler} roundCurrent={roundCurrent} />
Expand Down
4 changes: 2 additions & 2 deletions src/components/ScoreLog/PlayerNameColumn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const PlayerNameColumn: React.FunctionComponent<Props> = ({ navigation, disabled
).sort((a, b) => currentGame.playerIds.indexOf(a.id) - currentGame.playerIds.indexOf(b.id));

return (
<TouchableOpacity style={{ padding: 10 }} onPress={async () => {
<TouchableOpacity style={{ paddingVertical: 10 }} onPress={async () => {
if (disabled) return;

await analytics().logEvent('edit_game', {
Expand All @@ -39,7 +39,7 @@ const PlayerNameColumn: React.FunctionComponent<Props> = ({ navigation, disabled
&nbsp;
<Icon name="users"
type="font-awesome-5"
size={20}
size={19}
color={disabled ? 'white' : systemBlue} />
</Text>
{players.map((player, index) => (
Expand Down
65 changes: 18 additions & 47 deletions src/screens/GameScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { View, StyleSheet, LayoutChangeEvent, Text } from 'react-native';
import React, { useCallback, useEffect, useState } from 'react';
import { View, StyleSheet, LayoutChangeEvent } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { getContrastRatio } from 'colorsheet';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { ParamListBase } from '@react-navigation/native';
import BottomSheet, { BottomSheetScrollView } from '@gorhom/bottom-sheet';

import { useAppSelector } from '../../redux/hooks';
import PlayerTile from '../components/PlayerTile';
import Rounds from '../components/Rounds';
import { selectGameById } from '../../redux/GamesSlice';
import { systemBlue } from '../constants';
import GameBottomSheet, { bottomSheetHeight } from '../components/GameBottomSheet';


interface Props {
navigation: NativeStackNavigationProp<ParamListBase, string, undefined>;
Expand Down Expand Up @@ -93,25 +92,22 @@ const ScoreBoardScreen: React.FunctionComponent<Props> = ({ navigation }) => {
return dims;
};

// ref
const bottomSheetRef = useRef<BottomSheet>(null);

// variables
const snapPoints = useMemo(() => [73, '60%', '100%'], []);
const [windowHeight, setWindowHeight] = useState<number>(0);

// callbacks
const handleSheetChanges = useCallback((index: number) => {
console.log('handleSheetChanges', index);
}, []);

const handleSnapPress = useCallback((index: number) => {
bottomSheetRef.current?.snapToIndex(index);
const onLayout = useCallback((event: LayoutChangeEvent) => {
const { height } = event.nativeEvent.layout;
setWindowHeight(height);
}, []);

return (
<View style={{ flex: 1 }}>
<View style={[StyleSheet.absoluteFillObject]}>
<SafeAreaView edges={['left', 'right']} style={styles.contentStyle} onLayout={layoutHandler} >
<View style={[StyleSheet.absoluteFillObject]} onLayout={onLayout}>
<SafeAreaView edges={['left', 'right']} style={
[styles.contentStyle,
{
paddingBottom: fullscreen ? 20 : bottomSheetHeight + 2, // Add 2 to account for the border
}]
} onLayout={layoutHandler} >
{playerIds.map((id, index) => (
width != null && height != null && rows != 0 && cols != 0 &&
<PlayerTile
Expand All @@ -127,33 +123,9 @@ const ScoreBoardScreen: React.FunctionComponent<Props> = ({ navigation }) => {
/>
))}
</SafeAreaView>

<BottomSheet
ref={bottomSheetRef}
index={0}
snapPoints={snapPoints}
onChange={handleSheetChanges}
backgroundStyle={{ backgroundColor: 'rgb(30,40,50)' }}
handleIndicatorStyle={{ backgroundColor: 'white' }}
>
<BottomSheetScrollView>
<SafeAreaView edges={['right', 'left']}>

<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
<Text style={{ color: 'white', fontSize: 20, padding: 20, paddingTop: 0, fontWeight: 'bold' }} onPress={() => handleSnapPress(1)}>
{currentGame.title}
</Text>
<Text style={{ paddingHorizontal: 20, fontSize: 20, color: systemBlue }} onPress={() => navigation.navigate('Settings')}>
Edit
</Text>
</View>
<Rounds navigation={navigation} show={!fullscreen} />
<Text style={{ color: 'white', padding: 10 }}>
Tap on a column to set the current round.
</Text>
</SafeAreaView>
</BottomSheetScrollView>
</BottomSheet>
{!fullscreen &&
<GameBottomSheet navigation={navigation} containerHeight={windowHeight} />
}
</View>
</View>
);
Expand All @@ -168,7 +140,6 @@ const styles = StyleSheet.create({
flexDirection: 'row',
maxWidth: '100%',
backgroundColor: '#000000',
paddingBottom: 75,
},
contentContainer: {
flex: 1,
Expand Down
15 changes: 5 additions & 10 deletions src/screens/ShareScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const ShareScreen: React.FunctionComponent<Props> = ({ navigation }) => {

return (
<SafeAreaView edges={['right', 'left']}
style={[styles.scoreTableContainer, { height: 'auto' }]}>
style={[styles.contentContainer, { height: 'auto' }]}>
<ScrollView>

<Text style={{ color: 'white', paddingVertical: 20 }}>
Expand All @@ -79,14 +79,14 @@ const ShareScreen: React.FunctionComponent<Props> = ({ navigation }) => {
contentContainerStyle={{
backgroundColor: 'black',
flexDirection: 'column',
padding: 10,
padding: 20,
}}
ref={roundsScrollViewEl}>
<View style={{ flexDirection: 'column' }}>
<Text style={{ color: 'white', fontSize: 20, fontWeight: 'bold', padding: 10 }}>
<Text style={{ color: 'white', fontSize: 20, fontWeight: 'bold', paddingBottom: 10 }}>
{currentGame?.title}
</Text>
<Text style={{ color: 'white', paddingHorizontal: 10 }}>
<Text style={{ color: 'white' }}>
Created: {new Date(currentGame.dateCreated).toLocaleDateString()}
&nbsp; {new Date(currentGame.dateCreated).toLocaleTimeString()}
</Text>
Expand All @@ -105,11 +105,6 @@ const ShareScreen: React.FunctionComponent<Props> = ({ navigation }) => {
</View>
))}
</View>
<View>
<Text>
Created with ScorePad
</Text>
</View>
</ScrollView>
</View>
<Button style={{ padding: 20 }} type='clear'
Expand All @@ -121,7 +116,7 @@ const ShareScreen: React.FunctionComponent<Props> = ({ navigation }) => {
};

const styles = StyleSheet.create({
scoreTableContainer: {
contentContainer: {
backgroundColor: 'black',
flex: 1,
padding: 10,
Expand Down

0 comments on commit 6f7dd68

Please sign in to comment.