Skip to content

Commit

Permalink
completed search screen, minus composability refactor wip
Browse files Browse the repository at this point in the history
  • Loading branch information
roncodes committed Nov 24, 2024
1 parent 35c7d4e commit 3773d97
Show file tree
Hide file tree
Showing 30 changed files with 1,993 additions and 221 deletions.
49 changes: 49 additions & 0 deletions src/components/CategoryProductSlider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React, { useState } from 'react';
import { ScrollView, Pressable } from 'react-native';
import { YStack, XStack, Text, Spinner } from 'tamagui';
import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome';
import { faArrowRight } from '@fortawesome/free-solid-svg-icons';
import { useNavigation } from '@react-navigation/native';
import useStorefrontData from '../hooks/use-storefront-data';
import ProductCard from './ProductCard';

const CategoryProductSlider = ({ category, style = {}, onPressCategory }) => {
const navigation = useNavigation();
const { data: products, loading: isLoadingProducts } = useStorefrontData((storefront) => storefront.products.query({ category: category.id }), {
defaultValue: [],
persistKey: `${category.id}_products`,
});

const handleCategoryPress = () => {
if (typeof onPressCategory === 'function') {
onPressCategory(category);
}
};

return (
<YStack space='$3' style={style}>
<XStack justiftContent='space-between' paddingHorizontal='$4'>
<XStack space='$4' alignItems='center'>
<Pressable onPress={handleCategoryPress} hitSlop={20} style={({ pressed }) => [{ opacity: pressed ? 0.5 : 1.0, scale: pressed ? 0.8 : 1.0 }]}>
<XStack alignItems='center'>
<Text fontWeight='bold' fontSize='$7'>
{category.getAttribute('name')}
</Text>
<FontAwesomeIcon icon={faArrowRight} color='black' size={15} style={{ marginLeft: 7 }} />
</XStack>
</Pressable>
{isLoadingProducts && <Spinner size='sm' color='$color' />}
</XStack>
</XStack>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
<XStack space='$4' paddingVertical='$1' paddingHorizontal='$4'>
{products.map((product, index) => (
<ProductCard key={index} product={product} sliderHeight={135} style={{ width: 190 }} />
))}
</XStack>
</ScrollView>
</YStack>
);
};

export default CategoryProductSlider;
36 changes: 21 additions & 15 deletions src/components/LocationPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import useStorage from '../hooks/use-storage';

const LocationPicker = ({ ...props }) => {
const navigation = useNavigation();
const { onPressAddNewLocation } = props;
const { onPressAddNewLocation, wrapperStyle = {}, triggerWrapperStyle = {}, triggerStyle = {}, triggerTextStyle = {}, triggerArrowStyle = {}, triggerProps = {} } = props;
const { storefront, adapter } = useStorefront();
const [currentLocation, setCurrentLocation] = useStorage('location');
const [savedLocations, setSavedLocations] = useState([]);
Expand Down Expand Up @@ -70,32 +70,38 @@ const LocationPicker = ({ ...props }) => {
};

return (
<YStack space='$3' {...props}>
<YStack space='$3' style={wrapperStyle} {...props}>
<TouchableOpacity
ref={triggerRef}
onPress={toggleDropdown}
activeOpacity={0.7}
style={{
backgroundColor: isDropdownOpen ? '$surface' : 'transparent',
}}
style={[
{
backgroundColor: isDropdownOpen ? '$surface' : 'transparent',
},
triggerWrapperStyle,
]}
>
<XStack alignItems='center' space='$2'>
<XStack alignItems='center' space='$2' style={triggerStyle} {...triggerProps}>
<FontAwesomeIcon icon={faMapMarkerAlt} size={14} color='#4b5563' />
<Text
color='$primary'
fontWeight='bold'
fontSize='$5'
numberOfLines={1}
style={{
maxWidth: 200,
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
}}
style={[
{
maxWidth: 200,
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
},
triggerTextStyle,
]}
>
{currentLocation ? currentLocation.name : 'Loading...'}
</Text>
<Text style={{ fontSize: 14, color: '#4b5563' }}></Text>
<Text style={[{ fontSize: 14, color: '#4b5563' }, triggerArrowStyle]}></Text>
</XStack>
</TouchableOpacity>

Expand All @@ -113,7 +119,7 @@ const LocationPicker = ({ ...props }) => {
backgroundColor='transparent'
width={dropdownWidth}
position='absolute'
top={triggerPosition.height + 80}
top={triggerPosition.height + 60}
left={triggerPosition.x - 15}
zIndex={1}
enterStyle={{
Expand All @@ -135,7 +141,7 @@ const LocationPicker = ({ ...props }) => {
originY={0}
>
<BlurView style={StyleSheet.absoluteFillObject} blurType='light' blurAmount={10} borderRadius={10} reducedTransparencyFallbackColor='rgba(255, 255, 255, 0.8)' />
<YStack space='$2' padding='$3' borderRadius='$4'>
<YStack space='$2' padding='$2' borderRadius='$4'>
{savedLocations.map((location) => (
<TouchableOpacity
key={location.id}
Expand Down
Empty file.
189 changes: 114 additions & 75 deletions src/components/ProductCard.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import React, { useState } from 'react';
import React, { useState, useCallback } from 'react';
import { Dimensions, TouchableOpacity } from 'react-native';
import Carousel, { Pagination } from 'react-native-snap-carousel';
import { Card, Text, YStack, XStack, H2, Paragraph, Button, Image, useTheme } from 'tamagui';
import { Spinner, Card, Text, YStack, XStack, H2, Paragraph, Button, Image, useTheme } from 'tamagui';
import { useNavigation } from '@react-navigation/native';
import { toast, ToastPosition } from '@backpackapp-io/react-native-toast';
import { formatCurrency } from '../utils/format';
import { productHasOptions } from '../utils/product';
import { usePromiseWithLoading } from '../hooks/use-promise-with-loading';
import QuantityButton from './QuantityButton';
import ImageSlider from './ImageSlider';
import useCart from '../hooks/use-cart';
Expand All @@ -14,6 +18,7 @@ const ProductCard = ({
onPress,
onAddToCart,
style = {},
wrapperStyle = {},
headerStyle = {},
headerProps = {},
footerStyle = {},
Expand All @@ -22,102 +27,136 @@ const ProductCard = ({
carouselStyle = {},
buttonStyle = {},
quantityButtonStyle = {},
sliderHeight = 175,
}) => {
const theme = useTheme();
const navigation = useNavigation();
const { runWithLoading, isLoading } = usePromiseWithLoading();
const [cardWidth, setCardWidth] = useState(0);
const [cart, updateCart] = useCart();
const [quantity, setQuantity] = useState(1);

const handlePress = () => {
if (typeof onPress === 'function') {
onPress(product);
if (isLoading('addToCart')) {
return;
}

navigation.navigate('Product', { product: product.serialize(), quantity });
};

const handleAddToCart = async () => {
if (typeof onAddToCart === 'function') {
onAddToCart(product);
if (isLoading('addToCart')) {
return;
}

if (productHasOptions(product)) {
return navigation.navigate('Product', { product: product.serialize(), quantity });
}

try {
const updatedCart = await cart.add(product.id, quantity, { addons: [], variants: [] });
const updatedCart = await runWithLoading(cart.add(product.id, quantity), 'addToCart');
updateCart(updatedCart);
console.log(`Product ${product.name} added to cart!`);
setQuantity(1);
toast.success(`${product.getAttribute('name')} added to cart.`, { position: ToastPosition.BOTTOM });
} catch (error) {
console.log('Cart Error: ' + error.message);
console.log('Error Adding to Cart', error.message);
}
};

return (
<TouchableOpacity
onPress={handlePress}
style={style}
onLayout={({
nativeEvent: {
layout: { width },
},
}) => {
setCardWidth(width);
}}
>
<Card bordered>
<Card.Header style={[{ paddingHorizontal: 0, paddingVertical: 0 }, headerStyle]} padding={0} {...headerProps}>
<YStack position='relative'>
<ImageSlider
images={product.getAttribute('images')}
sliderWidth={cardWidth}
sliderHeight={175}
sliderStyle={{ borderTopRightRadius: 10, borderTopLeftRadius: 10 }}
autoplay
/>
<XStack position='absolute' top='$2' right='$2' zIndex={10} alignItems='center' justifyContent='flex-end' space='$2'>
{/* {favoriteIcon} */}
</XStack>
</YStack>
</Card.Header>

<Card.Footer style={footerStyle} {...footerProps}>
<YStack space='$2' padding='$2'>
<YStack>
<Text color='$color' fontWeight='$9' fontSize='$7' numberOfLines={1}>
{product.getAttribute('name')}
</Text>
{product.isAttributeFilled('description') && (
<Text numberOfLines={2} color='$secondary' fontSize='$4'>
{product.getAttribute('description')}
</Text>
)}
<YStack wrapperStyle={wrapperStyle}>
<TouchableOpacity
onPress={handlePress}
style={style}
disabled={isLoading('addToCart')}
onLayout={({
nativeEvent: {
layout: { width },
},
}) => {
setCardWidth((prevWidth) => (prevWidth !== width ? width : prevWidth));
}}
>
<Card bordered>
<Card.Header style={[{ paddingHorizontal: 0, paddingVertical: 0 }, headerStyle]} padding={0} {...headerProps}>
<YStack position='relative'>
<ImageSlider
images={product.getAttribute('images')}
sliderWidth={cardWidth}
sliderHeight={sliderHeight}
sliderStyle={{ borderTopRightRadius: 10, borderTopLeftRadius: 10 }}
autoplay
/>
<XStack position='absolute' top='$2' right='$2' zIndex={10} alignItems='center' justifyContent='flex-end' space='$2'></XStack>
</YStack>

{product.getAttribute('on_sale') ? (
<YStack>
<Text fontSize='$6' color='$success' fontWeight='bold'>
{formatCurrency(product.getAttribute('sale_price'), product.getAttribute('currency'))}
</Text>
<Text fontSize='$5' color='$secondary' textDecorationLine='line-through'>
{formatCurrency(product.getAttribute('price'), product.getAttribute('currency'))}
</Text>
</Card.Header>
<Card.Footer style={footerStyle} {...footerProps}>
<YStack space='$2' padding='$2'>
<YStack minHeight={90}>
<YStack>
<Text color='$color' fontWeight='$9' fontSize='$7' numberOfLines={1}>
{product.getAttribute('name')}
</Text>
{product.isAttributeFilled('description') && (
<Text numberOfLines={2} color='$color' fontSize='$4'>
{product.getAttribute('description')}
</Text>
)}
</YStack>
<YStack mt='$2'>
{product.getAttribute('on_sale') ? (
<YStack>
<Text fontSize='$6' color='$success' fontWeight='bold'>
{formatCurrency(product.getAttribute('sale_price'), product.getAttribute('currency'))}
</Text>
<Text fontSize='$5' color='$secondary' textDecorationLine='line-through'>
{formatCurrency(product.getAttribute('price'), product.getAttribute('currency'))}
</Text>
</YStack>
) : (
<Text fontSize='$5' fontColor='$success' fontWeight='bold'>
{formatCurrency(product.getAttribute('price'), product.getAttribute('currency'))}
</Text>
)}
</YStack>
</YStack>
) : (
<Text fontSize='$5' fontColor='$success' fontWeight='bold'>
{formatCurrency(product.getAttribute('price'), product.getAttribute('currency'))}
</Text>
)}

{/* Quantity Button */}
<QuantityButton style={quantityButtonStyle} onChange={(quantity) => console.log('Selected Quantity:', quantity)} />
<QuantityButton style={quantityButtonStyle} onChange={setQuantity} wrapperProps={{ minHeight: 35, width: '100%', flex: 1 }} />
<XStack>
<Button
animation='bouncy'
onPress={handleAddToCart}
size='$4'
style={buttonStyle}
alignSelf='center'
borderRadius='$4'
bg='$primary'
color='white'
width='100%'
hoverStyle={{
scale: 0.75,
opacity: 0.5,
}}
pressStyle={{
scale: 0.75,
opacity: 0.5,
}}
>
{isLoading('addToCart') && (
<Button.Icon>
<Spinner />
</Button.Icon>
)}

<XStack>
<Button onPress={handleAddToCart} size='$4' style={buttonStyle} alignSelf='center' borderRadius='$4' bg='$primary' color='white' width='100%'>
<Button.Text fontSize='$6' fontWeight='$5'>
Add to Cart
</Button.Text>
</Button>
</XStack>
</YStack>
</Card.Footer>
</Card>
</TouchableOpacity>
<Button.Text fontSize='$6' fontWeight='$5'>
Add to Cart
</Button.Text>
</Button>
</XStack>
</YStack>
</Card.Footer>
</Card>
</TouchableOpacity>
</YStack>
);
};

Expand Down
Loading

0 comments on commit 3773d97

Please sign in to comment.