Skip to content
Merged
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
63 changes: 40 additions & 23 deletions src/components/Buttons/GameOptionsButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -168,30 +168,14 @@ const GameOptionsButton: React.FunctionComponent = () => {
testID="game-options-menu"
>
<View style={styles.button}>
<View style={styles.content}>
<View style={styles.addendColumn}>
<Text style={[styles.addendText, { color: theme.text }]}>{addendOne}</Text>
<Text style={[styles.addendText, { color: theme.text }]}>{addendTwo}</Text>
</View>
<View>
{Platform.OS === 'ios' ? (
<SymbolView
name={isDial ? 'dial.min' : isSwipe ? 'hand.draw' : 'hand.point.up'}
size={30}
tintColor={theme.text}
/>
) : (
<MaterialCommunityIcons
name={isDial ? 'knob' : isSwipe ? 'gesture-swipe-up' : 'gesture-tap'}
size={30}
color={theme.text}
/>
)}
{Platform.OS === 'android' ? (
<View style={styles.androidOverflowButton}>
<MaterialCommunityIcons name="dots-vertical" size={28} color={theme.text} />
{showDialDot && (
<View testID="dial-notification-dot" style={{
position: 'absolute',
top: -2,
right: -4,
top: 6,
right: 8,
width: 8,
height: 8,
borderRadius: 4,
Expand All @@ -201,15 +185,48 @@ const GameOptionsButton: React.FunctionComponent = () => {
}} />
)}
</View>
</View>
) : (
<View style={styles.content}>
<View style={styles.addendColumn}>
<Text style={[styles.addendText, { color: theme.text }]}>{addendOne}</Text>
<Text style={[styles.addendText, { color: theme.text }]}>{addendTwo}</Text>
</View>
<View>
<SymbolView
name={isDial ? 'dial.min' : isSwipe ? 'hand.draw' : 'hand.point.up'}
size={30}
tintColor={theme.text}
/>
{showDialDot && (
<View testID="dial-notification-dot" style={{
position: 'absolute',
top: -2,
right: -4,
width: 8,
height: 8,
borderRadius: 4,
backgroundColor: theme.warning,
borderWidth: 1,
borderColor: theme.backgroundSecondary,
}} />
)}
</View>
</View>
)}
</View>
</MenuView>
);
};

const styles = StyleSheet.create({
button: {
padding: 8,
padding: Platform.OS === 'android' ? 0 : 8,
},
androidOverflowButton: {
width: 48,
height: 48,
alignItems: 'center',
justifyContent: 'center',
},
content: {
flexDirection: 'row',
Expand Down
26 changes: 23 additions & 3 deletions src/components/Buttons/HeaderButton.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';

import { TouchableOpacity, StyleSheet } from 'react-native';
import { Platform, Pressable, StyleSheet } from 'react-native';

interface Props {
children: React.ReactNode;
Expand All @@ -10,9 +10,18 @@ interface Props {

const HeaderButton: React.FunctionComponent<Props> = (props) => {
return (
<TouchableOpacity accessibilityRole='button' {...props} style={[styles.headerButton]}>
<Pressable
accessibilityRole='button'
{...props}
android_ripple={{ borderless: true }}
style={({ pressed }) => [
styles.headerButton,
Platform.OS === 'android' && styles.androidHeaderButton,
Platform.OS === 'ios' && pressed && styles.iosPressed,
]}
>
{props.children}
</TouchableOpacity>
</Pressable>
);
};

Expand All @@ -22,6 +31,17 @@ const styles = StyleSheet.create({
padding: 10,
paddingHorizontal: 15,
},
androidHeaderButton: {
width: 48,
height: 48,
alignItems: 'center',
justifyContent: 'center',
padding: 0,
paddingHorizontal: 0,
},
iosPressed: {
opacity: 0.45,
},
});

export default HeaderButton;
23 changes: 17 additions & 6 deletions src/components/GameListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,18 @@ const GameListItem: React.FunctionComponent<Props> = ({ navigation, gameId, inde
{formatCreated(dateCreated)}
</Text>
</ListItem.Content>
<Text style={[styles.badge, { color: theme.badgeBlue }]}>
{playerIds.length} <Icon color={theme.badgeBlue} name='users' type='font-awesome-5' size={13} />
</Text>
<Text style={[styles.badge, { color: theme.badgeRed }]}>
{roundCount} <Icon color={theme.badgeRed} name='circle-notch' type='font-awesome-5' size={13} />
</Text>
<View style={styles.badgeGroup}>
<Text style={[styles.badge, { color: theme.badgeBlue }]}>
{playerIds.length}
</Text>
<Icon color={theme.badgeBlue} name='users' type='font-awesome-5' size={13} />
</View>
<View style={styles.badgeGroup}>
<Text style={[styles.badge, { color: theme.badgeRed }]}>
{roundCount}
</Text>
<Icon color={theme.badgeRed} name='circle-notch' type='font-awesome-5' size={13} />
</View>
<ListItem.Chevron iconStyle={{ color: theme.textTertiary }} />
</View>
</ListItem>
Expand Down Expand Up @@ -221,6 +227,11 @@ const styles = StyleSheet.create({
badge: {
fontSize: 15,
},
badgeGroup: {
flexDirection: 'row',
alignItems: 'center',
gap: 4,
},
});

export default memo(GameListItem);
1 change: 1 addition & 0 deletions src/components/ScoreLogTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ const ScoreLogTable: React.FunctionComponent<Props> = ({ showScores = true }) =>
<ScrollView horizontal={true}
nestedScrollEnabled={true}
contentContainerStyle={{ flexDirection: 'row' }}
overScrollMode="always"
ref={scoreLogScrollViewEl}>
{roundsIterator.map((item, round) => (
<View key={round} onLayout={e => onLayoutHandler(e, round)}>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Sheets/ChooseWinnersSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ const ChooseWinnersSheet: React.FunctionComponent = () => {
/>
</View>

<BottomSheetScrollView contentContainerStyle={styles.scrollContent}>
<BottomSheetScrollView contentContainerStyle={styles.scrollContent} overScrollMode="always">
<SafeAreaView edges={['right', 'left']}>
<Text style={[styles.subtitle, { color: theme.textTertiary }]}>
Select winners (optional) to end the game.
Expand Down
29 changes: 29 additions & 0 deletions src/components/Sheets/GameSheet.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ jest.mock('@gorhom/bottom-sheet', () => {
index: number;
backdropComponent?: React.ComponentType;
backgroundStyle?: object;
handleComponent?: React.ComponentType;
handleIndicatorStyle?: object;
animatedPosition?: object;
enablePanDownToClose?: boolean;
Expand All @@ -42,6 +43,7 @@ jest.mock('@gorhom/bottom-sheet', () => {

return (
<View testID="bottom-sheet" style={{ backgroundColor: 'rgb(30,40,50)' }} topInset={props.topInset}>
{props.handleComponent ? <props.handleComponent /> : null}
{props.children}
</View>
);
Expand All @@ -63,6 +65,7 @@ jest.mock('@gorhom/bottom-sheet', () => {
return {
__esModule: true,
default: MockBottomSheet,
BottomSheetHandle: () => <View testID="bottom-sheet-default-handle" />,
BottomSheetScrollView: MockBottomSheetScrollView,
BottomSheetBackdrop: MockBottomSheetBackdrop,
};
Expand Down Expand Up @@ -292,6 +295,32 @@ describe('GameSheet', () => {
expect(getByTestId('score-log-table')).toBeTruthy();
});

it('renders a tappable sheet handle', () => {
const store = createMockStore({
settings: {
currentGameId: 'game-1',
},
games: {
entities: {
'game-1': mockGame,
},
ids: ['game-1'],
},
players: {
entities: mockPlayers,
ids: ['player-1', 'player-2'],
},
});

const { getByTestId } = render(
<Provider store={store}>
<GameSheet {...defaultProps} />
</Provider>
);

expect(getByTestId('game-sheet-handle')).toBeTruthy();
});

it('should allow full expansion up to the top safe area', () => {
const store = createMockStore({
settings: {
Expand Down
22 changes: 17 additions & 5 deletions src/components/Sheets/GameSheet.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';

import BottomSheet, { BottomSheetBackdrop, BottomSheetBackdropProps, BottomSheetScrollView } from '@gorhom/bottom-sheet';
import BottomSheet, { BottomSheetBackdrop, BottomSheetBackdropProps, BottomSheetHandle, BottomSheetHandleProps, BottomSheetScrollView } from '@gorhom/bottom-sheet';
import { ParamListBase, useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { Alert, StyleSheet, Text, TouchableWithoutFeedback, View, useWindowDimensions } from 'react-native';
import { Alert, StyleSheet, Text, TouchableOpacity, TouchableWithoutFeedback, View, useWindowDimensions } from 'react-native';
import { Button } from 'react-native-elements';
import Animated, { Extrapolate, FadeIn, interpolate, Layout, useAnimatedStyle, useSharedValue } from 'react-native-reanimated';
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
Expand Down Expand Up @@ -180,13 +180,13 @@ const GameSheet: React.FunctionComponent = () => {
/**
* Function to snap to the next point when the handle is pressed
*/
const sheetTitlePress = () => {
const sheetTitlePress = useCallback(() => {
setSnapPointIndex((prevIndex) => {
const nextIndex = (prevIndex + 1) % snapPoints.length;
gameSheetRef?.current?.snapToIndex(nextIndex);
return nextIndex;
});
};
}, [gameSheetRef, snapPoints.length]);

/**
* Animated position of the bottom sheet
Expand Down Expand Up @@ -228,6 +228,17 @@ const GameSheet: React.FunctionComponent = () => {
[]
);

const renderHandle = useCallback(
(props: BottomSheetHandleProps) => (
<TouchableOpacity activeOpacity={0.7} onPress={sheetTitlePress} testID="game-sheet-handle">
<View>
<BottomSheetHandle {...props} indicatorStyle={{ backgroundColor: theme.sheetHandle }} />
</View>
</TouchableOpacity>
),
[sheetTitlePress, theme.sheetHandle]
);

if (currentGameId == undefined) return null;

return (
Expand All @@ -239,6 +250,7 @@ const GameSheet: React.FunctionComponent = () => {
snapPoints={snapPoints}
backdropComponent={renderBackdrop}
backgroundStyle={{ backgroundColor: theme.sheetBackground }}
handleComponent={renderHandle}
handleIndicatorStyle={{ backgroundColor: theme.sheetHandle }}
animatedPosition={animatedPosition}
enablePanDownToClose={false}
Expand All @@ -247,7 +259,7 @@ const GameSheet: React.FunctionComponent = () => {
accessible={false}
accessibilityViewIsModal={false}
>
<BottomSheetScrollView>
<BottomSheetScrollView overScrollMode="always">
<SafeAreaView edges={['right', 'left']}>
<View style={styles.sheetHeaderContainer}>
<TouchableWithoutFeedback onPress={() => sheetTitlePress()}>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Sheets/GestureInfoSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const GestureInfoSheet: React.FunctionComponent = () => {
backgroundStyle={{ backgroundColor: theme.sheetBackground }}
handleIndicatorStyle={{ backgroundColor: theme.sheetHandle }}
>
<BottomSheetScrollView style={styles.container} contentContainerStyle={styles.content}>
<BottomSheetScrollView contentContainerStyle={styles.content} overScrollMode="always" style={styles.container}>
<Text style={[styles.title, { color: theme.text }]}>Tap Gesture</Text>
<Text style={[styles.paragraph, { color: theme.textSecondary }]}>
Tap the top half of a player&apos;s tile to increase their score, or the bottom half
Expand Down
6 changes: 5 additions & 1 deletion src/components/Sheets/PointValuesSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,11 @@ const PointValuesSheet: React.FunctionComponent = () => {
backdropComponent={renderBackdrop}
backgroundStyle={{ backgroundColor: theme.sheetBackground }}
handleIndicatorStyle={{ backgroundColor: theme.sheetHandle }}>
<BottomSheetScrollView style={styles.sheetContainer} contentContainerStyle={{ alignItems: 'center' }}>
<BottomSheetScrollView
contentContainerStyle={{ alignItems: 'center' }}
overScrollMode="always"
style={styles.sheetContainer}
>
<View style={{ flex: 1, width: '100%', alignItems: 'center' }}>
<Text style={{ color: theme.text, fontSize: 20 }}>Point Values</Text>
<View style={{ flex: 1, flexDirection: 'row', justifyContent: 'space-around', padding: 10 }}>
Expand Down
6 changes: 5 additions & 1 deletion src/screens/AppSettingsScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,11 @@ const AppSettingsScreen: React.FunctionComponent<Props> = ({ navigation }) => {
};

return (
<ScrollView style={{ backgroundColor: theme.background, flex: 1 }} contentContainerStyle={{ paddingBottom: 50 }}>
<ScrollView
contentContainerStyle={{ paddingBottom: 50 }}
overScrollMode="always"
style={{ backgroundColor: theme.background, flex: 1 }}
>
<View style={[styles.iconWrapper, { alignItems: 'center' }]}>
<RotatingIcon />
<Text testID="scorepad-title" style={{ color: theme.textTertiary }} onPress={alertWithVersion}>
Expand Down
1 change: 1 addition & 0 deletions src/screens/EditGameScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ const EditGameScreen: React.FunctionComponent<Props> = ({ navigation, route }) =
containerStyle={{ flex: 1 }}
ListFooterComponent={ListFooter}
data={playerIds}
overScrollMode="always"
renderItem={({ item: playerId, getIndex, drag, isActive }) => (
<ScaleDecorator activeScale={1.05}>
<PlayerListItem
Expand Down
1 change: 1 addition & 0 deletions src/screens/ListScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ const ListScreen: React.FunctionComponent<Props> = ({ navigation }) => {
<Text style={{ textAlign: 'center', padding: 10, color: theme.textSecondary }}>Tap the + button to create a new game.</Text>
</>
}
overScrollMode="always"
style={[styles.list, { backgroundColor: theme.backgroundSecondary }]}
data={gameIds}
renderItem={({ item, index }) =>
Expand Down
Loading