Skip to content

Commit e16d654

Browse files
misc(app): Deduplicate button and product cards components (#65)
1 parent 997bffe commit e16d654

File tree

10 files changed

+394
-683
lines changed

10 files changed

+394
-683
lines changed

src/App.tsx

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import ManualTrackerScreen from './screens/ManualTrackerScreen';
1717
import PerformanceTimingScreen from './screens/PerformanceTimingScreen';
1818
import EndToEndTestsScreen from './screens/EndToEndTestsScreen';
1919
import ReduxScreen from './screens/ReduxScreen';
20-
import EmpowerPlant from './screens/EmpowerPlant';
2120
import CartScreen from './screens/CartScreen';
2221
import CheckoutScreen from './screens/CheckoutScreen';
2322
import Toast from 'react-native-toast-message';
@@ -26,7 +25,8 @@ import {store} from './reduxApp';
2625
import {DSN} from './config';
2726
import {SE} from '@env'; // SE is undefined if no .env file is set
2827
import {RootStackParamList} from './navigation';
29-
import { GestureHandlerRootView } from 'react-native-gesture-handler';
28+
import {GestureHandlerRootView} from 'react-native-gesture-handler';
29+
import {StyleSheet} from 'react-native';
3030
console.log('> SE', SE);
3131

3232
const reactNavigationInstrumentation =
@@ -104,7 +104,7 @@ const App = () => {
104104

105105
return (
106106
<Provider store={store}>
107-
<GestureHandlerRootView style={{ flex: 1 }}>
107+
<GestureHandlerRootView style={styles.gestureHandlerRootView}>
108108
<NavigationContainer
109109
ref={navigation}
110110
onReady={() => {
@@ -115,14 +115,19 @@ const App = () => {
115115
<Stack.Navigator>
116116
<Stack.Screen name="Home" component={HomeScreen} />
117117
<Stack.Screen name="Tracker" component={TrackerScreen} />
118-
<Stack.Screen name="ManualTracker" component={ManualTrackerScreen} />
118+
<Stack.Screen
119+
name="ManualTracker"
120+
component={ManualTrackerScreen}
121+
/>
119122
<Stack.Screen
120123
name="PerformanceTiming"
121124
component={PerformanceTimingScreen}
122125
/>
123126
<Stack.Screen name="Redux" component={ReduxScreen} />
124-
<Stack.Screen name="EndToEndTests" component={EndToEndTestsScreen} />
125-
<Stack.Screen name="Products" component={EmpowerPlant} />
127+
<Stack.Screen
128+
name="EndToEndTests"
129+
component={EndToEndTestsScreen}
130+
/>
126131
<Stack.Screen name="Cart" component={CartScreen} />
127132
<Stack.Screen name="ListApp" component={ListApp} />
128133
<Stack.Screen name="Checkout" component={CheckoutScreen} />
@@ -134,6 +139,12 @@ const App = () => {
134139
);
135140
};
136141

142+
const styles = StyleSheet.create({
143+
gestureHandlerRootView: {
144+
flex: 1,
145+
},
146+
});
147+
137148
export default Sentry.wrap(App, {
138149
touchEventBoundaryProps: {
139150
ignoreNames: ['Provider', 'UselessName', /^SomeRegex/],

src/components/StyledButton.tsx

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import React, {useState} from 'react';
2+
import {
3+
ActivityIndicator,
4+
GestureResponderEvent,
5+
Pressable,
6+
PressableProps,
7+
StyleSheet,
8+
Text,
9+
ViewStyle,
10+
} from 'react-native';
11+
12+
export const StyledButton = ({
13+
onPress,
14+
title,
15+
style,
16+
isLoading,
17+
}: {
18+
style?: {
19+
default?: ViewStyle;
20+
pressed?: ViewStyle;
21+
defaultText?: ViewStyle;
22+
};
23+
title: string;
24+
onPress: null | ((event: GestureResponderEvent) => void) | undefined;
25+
isLoading?: boolean;
26+
}): React.ReactElement => {
27+
const [isPressed, setIsPressed] = useState(false);
28+
const pressableStyle: PressableProps['style'] = ({pressed}) =>
29+
pressed
30+
? {
31+
...defaultStyles.pressed,
32+
...(style && style.pressed),
33+
}
34+
: {
35+
...defaultStyles.default,
36+
...(style && style.default),
37+
};
38+
39+
const InnerLoader = (
40+
<ActivityIndicator
41+
size="small"
42+
color="#FFFFFF"
43+
style={defaultStyles.loader}
44+
/>
45+
);
46+
const InnerText = (
47+
<Text
48+
style={{
49+
...(isPressed ? defaultStyles.pressedText : defaultStyles.defaultText),
50+
...(style && style.defaultText),
51+
}}>
52+
{title}
53+
</Text>
54+
);
55+
56+
const InnerContent = isLoading ? InnerLoader : InnerText;
57+
return (
58+
<Pressable
59+
onPress={onPress}
60+
style={pressableStyle}
61+
onPressIn={() => setIsPressed(true)}
62+
onPressOut={() => setIsPressed(false)}>
63+
{InnerContent}
64+
</Pressable>
65+
);
66+
};
67+
68+
const defaultStyles = StyleSheet.create({
69+
default: {
70+
paddingLeft: 20,
71+
paddingRight: 20,
72+
borderRadius: 2,
73+
backgroundColor: '#002626',
74+
},
75+
pressed: {
76+
paddingLeft: 20,
77+
paddingRight: 20,
78+
borderRadius: 2,
79+
backgroundColor: '#f6cfb2',
80+
},
81+
defaultText: {
82+
textAlign: 'center',
83+
fontSize: 17,
84+
margin: 8,
85+
color: 'white',
86+
},
87+
pressedText: {
88+
textAlign: 'center',
89+
fontSize: 17,
90+
margin: 8,
91+
color: '#002626',
92+
},
93+
loader: {
94+
margin: 8,
95+
},
96+
});
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import * as React from 'react';
2+
import {View, Text, StyleSheet} from 'react-native';
3+
import {AppDispatch} from '../reduxApp';
4+
import {StyledButton} from './StyledButton';
5+
import * as Sentry from '@sentry/react-native';
6+
import {selectImage} from './imageFromAssets';
7+
8+
export const StyledCartProductCard = (props: {
9+
imgcropped: string;
10+
id: number;
11+
quantity: number;
12+
price: number;
13+
appDispatch: AppDispatch;
14+
title: string;
15+
}): React.ReactElement => {
16+
const deleteItem = (id: string) => {
17+
props.appDispatch({type: 'DELETE_FROM_CART', payload: id});
18+
};
19+
20+
return (
21+
<View style={styles.cardContainer}>
22+
{selectImage(props.imgcropped, {
23+
width: '100%',
24+
height: 200,
25+
marginBottom: 10,
26+
})}
27+
<Text style={styles.itemTitle}>
28+
{props.title.charAt(0).toUpperCase() + props.title.slice(1)}
29+
</Text>
30+
31+
<Text style={styles.itemPrice}>
32+
{'$' + props.price + ` (${props.quantity})`}
33+
</Text>
34+
<StyledButton
35+
title={'Delete'}
36+
onPress={() => deleteItem(props.id.toString())}
37+
/>
38+
</View>
39+
);
40+
};
41+
42+
export const ProfiledStyledCartProductCard = Sentry.withProfiler(
43+
StyledCartProductCard,
44+
);
45+
46+
const styles = StyleSheet.create({
47+
cardContainer: {
48+
width: '100%',
49+
padding: 12,
50+
borderBottomWidth: 1,
51+
52+
borderColor: '#dbdbdb',
53+
flex: 1,
54+
flexDirection: 'column',
55+
alignItems: 'center',
56+
justifyContent: 'space-around',
57+
},
58+
itemTitle: {
59+
marginBottom: 10,
60+
fontSize: 24,
61+
fontWeight: '500',
62+
color: '#002626',
63+
},
64+
itemPrice: {
65+
fontSize: 22,
66+
fontWeight: '400',
67+
color: '#002626',
68+
marginBottom: 20,
69+
},
70+
});
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import React from 'react';
2+
import {View, Text, StyleSheet} from 'react-native';
3+
import Toast from 'react-native-toast-message';
4+
import {AppDispatch} from '../reduxApp';
5+
import {StyledButton} from './StyledButton';
6+
import * as Sentry from '@sentry/react-native';
7+
import {selectImage} from './imageFromAssets';
8+
9+
export const StyledProductCard = (props: {
10+
id: number;
11+
type: string;
12+
price: number;
13+
title: string;
14+
imgcropped: string;
15+
appDispatch: AppDispatch;
16+
}): React.ReactElement => {
17+
const onAddToCartPressed = () => {
18+
props.appDispatch({
19+
type: 'ADD_TO_CART',
20+
payload: {
21+
id: props.id,
22+
title: props.title,
23+
price: props.price,
24+
quantity: 1,
25+
imgcropped: props.imgcropped,
26+
},
27+
});
28+
Toast.show({
29+
type: 'success',
30+
position: 'bottom',
31+
text1: 'Added to Cart',
32+
visibilityTime: 0.5,
33+
});
34+
};
35+
36+
return (
37+
<View style={styles.cardContainer}>
38+
<View style={styles.cardHero}>{selectImage(props.imgcropped)}</View>
39+
<View style={styles.cardDetail}>
40+
<View style={styles.cardDetailContent}>
41+
<Text style={styles.cardTitle}>{props.title}</Text>
42+
<Text style={styles.itemPrice}>${props.price}</Text>
43+
</View>
44+
<View style={styles.cardDetailAction}>
45+
<StyledButton
46+
title="Add to cart"
47+
onPress={onAddToCartPressed}
48+
style={{
49+
default: styles.addToCartButtonDefault,
50+
pressed: styles.addToCartButtonDefault,
51+
}}
52+
/>
53+
</View>
54+
</View>
55+
</View>
56+
);
57+
};
58+
59+
export const ProfiledStyledProductCard = Sentry.withProfiler(StyledProductCard);
60+
61+
const styles = StyleSheet.create({
62+
cardTitle: {
63+
marginBottom: 5,
64+
fontSize: 24,
65+
fontWeight: '500',
66+
color: '#002626',
67+
},
68+
itemPrice: {
69+
fontSize: 22,
70+
fontWeight: '400',
71+
color: '#002626',
72+
},
73+
cardHero: {
74+
width: '40%',
75+
height: '100%',
76+
77+
backgroundColor: '#f5f5f5',
78+
alignItems: 'center',
79+
justifyContent: 'center',
80+
},
81+
cardDetail: {
82+
flex: 1,
83+
height: '100%',
84+
flexDirection: 'column',
85+
alignContent: 'space-between',
86+
},
87+
cardDetailContent: {
88+
padding: 10,
89+
flex: 1,
90+
flexDirection: 'column',
91+
justifyContent: 'space-between',
92+
paddingBottom: 10,
93+
},
94+
cardDetailAction: {
95+
flex: 0,
96+
},
97+
cardContainer: {
98+
width: '100%',
99+
height: 200,
100+
101+
borderWidth: 1,
102+
borderColor: '#dbdbdb',
103+
borderRadius: 6,
104+
backgroundColor: '#ffffff',
105+
flex: 1,
106+
flexDirection: 'row',
107+
alignItems: 'center',
108+
justifyContent: 'space-between',
109+
marginVertical: 5,
110+
},
111+
addToCartButtonDefault: {
112+
margin: 10,
113+
},
114+
});

0 commit comments

Comments
 (0)