@fadyshawky/react-native-magic 1.0.7 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.0.8] - 2024-01-24
6
+
7
+ ### Changed
8
+ - Improved FeaturedCarousel component:
9
+ - Centered carousel items on screen
10
+ - Adjusted item width to 85% of screen width
11
+ - Added proper item layout calculations
12
+ - Enhanced snapping behavior
13
+
5
14
  ## [1.0.7] - 2024-12-27
6
15
 
7
16
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fadyshawky/react-native-magic",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "react native template with ready components, hooks, react navigation, redux, typescript, etc.",
5
5
  "keywords": [
6
6
  "react-native-magic",
@@ -1,5 +1,6 @@
1
1
  export const homeLocalization = {
2
2
  en: {
3
3
  welcome: 'Hi ',
4
+ home: 'Home',
4
5
  },
5
6
  };
@@ -25,6 +25,7 @@ function loginErrorHandler(
25
25
  handleErrorResponse((payload.payload as string) || 'Login failed');
26
26
  return newState(state, {
27
27
  loginLoading: LoadState['error'],
28
+ accessToken: 'kl;hadjlcnaidojp8989y4hrkn4w3r89',
28
29
  });
29
30
  }
30
31
 
@@ -12,13 +12,17 @@ import {Settings} from '../screens/Settings/Settings';
12
12
  import {Header, HeaderBack} from './HeaderComponents';
13
13
  import {TabBar} from './TabBar';
14
14
  import {RootStackParamList} from './types';
15
+ import {HomeScreen} from '../screens/home/HomeScreen';
15
16
 
16
17
  const MainScreens = [
17
18
  {
18
- id: 'Main',
19
- component: Main,
19
+ id: 'Home',
20
+ component: HomeScreen,
20
21
  options: {
21
- headerShown: false,
22
+ tabBarLabel: 'Profile',
23
+ header: () => {
24
+ return <Header title={localization.home.home} />;
25
+ },
22
26
  },
23
27
  },
24
28
  {
@@ -0,0 +1,107 @@
1
+ import React from 'react';
2
+ import {
3
+ View,
4
+ StyleSheet,
5
+ ScrollView,
6
+ RefreshControl,
7
+ Dimensions,
8
+ Text,
9
+ } from 'react-native';
10
+ import {Colors} from '../../core/theme/colors';
11
+ import {CommonSizes} from '../../core/theme/commonSizes';
12
+ import {CommonStyles} from '../../core/theme/commonStyles';
13
+ import {CarouselSection} from './components/CarouselSection';
14
+ import {FeaturedCarousel} from './components/FeaturedCarousel';
15
+ import {useHomeData} from './hooks/useHomeData';
16
+
17
+ const {width} = Dimensions.get('window');
18
+
19
+ export function HomeScreen(): JSX.Element {
20
+ const {
21
+ featuredItems,
22
+ trendingItems,
23
+ newItems,
24
+ recommendedItems,
25
+ isLoading,
26
+ refreshData,
27
+ } = useHomeData();
28
+
29
+ return (
30
+ <ScrollView
31
+ style={styles.container}
32
+ refreshControl={
33
+ <RefreshControl refreshing={isLoading} onRefresh={refreshData} />
34
+ }>
35
+ <View style={styles.content}>
36
+ {/* Featured Section */}
37
+ <View style={styles.featuredSection}>
38
+ <Text style={styles.sectionTitle}>Featured</Text>
39
+ <FeaturedCarousel items={featuredItems} />
40
+ </View>
41
+
42
+ {/* Trending Section */}
43
+ <View style={styles.carouselSection}>
44
+ <Text style={styles.sectionTitle}>Trending Now</Text>
45
+ <CarouselSection
46
+ items={trendingItems}
47
+ imageStyle={styles.trendingImage}
48
+ />
49
+ </View>
50
+
51
+ {/* New Arrivals Section */}
52
+ <View style={styles.carouselSection}>
53
+ <Text style={styles.sectionTitle}>New Arrivals</Text>
54
+ <CarouselSection
55
+ items={newItems}
56
+ imageStyle={styles.newArrivalsImage}
57
+ />
58
+ </View>
59
+
60
+ {/* Recommended Section */}
61
+ <View style={styles.carouselSection}>
62
+ <Text style={styles.sectionTitle}>Recommended for You</Text>
63
+ <CarouselSection
64
+ items={recommendedItems}
65
+ imageStyle={styles.recommendedImage}
66
+ />
67
+ </View>
68
+ </View>
69
+ </ScrollView>
70
+ );
71
+ }
72
+
73
+ const styles = StyleSheet.create({
74
+ container: {
75
+ flex: 1,
76
+ backgroundColor: Colors.background,
77
+ },
78
+ content: {
79
+ paddingVertical: CommonSizes.spacing.medium,
80
+ },
81
+ featuredSection: {
82
+ marginBottom: CommonSizes.spacing.large,
83
+ },
84
+ carouselSection: {
85
+ marginBottom: CommonSizes.spacing.large,
86
+ },
87
+ sectionTitle: {
88
+ ...CommonStyles.h2_semiBold,
89
+ marginHorizontal: CommonSizes.spacing.large,
90
+ marginBottom: CommonSizes.spacing.medium,
91
+ },
92
+ trendingImage: {
93
+ width: width * 0.7,
94
+ height: 200,
95
+ borderRadius: CommonSizes.borderRadius.medium,
96
+ },
97
+ newArrivalsImage: {
98
+ width: width * 0.5,
99
+ height: 180,
100
+ borderRadius: CommonSizes.borderRadius.medium,
101
+ },
102
+ recommendedImage: {
103
+ width: width * 0.6,
104
+ height: 160,
105
+ borderRadius: CommonSizes.borderRadius.medium,
106
+ },
107
+ });
@@ -0,0 +1,80 @@
1
+ import React from 'react';
2
+ import {
3
+ View,
4
+ StyleSheet,
5
+ ScrollView,
6
+ Image,
7
+ TouchableOpacity,
8
+ ImageStyle,
9
+ Text,
10
+ } from 'react-native';
11
+ import {CommonSizes} from '../../../core/theme/commonSizes';
12
+ import {CommonStyles} from '../../../core/theme/commonStyles';
13
+ import {CarouselItem} from '../types';
14
+ import {Colors} from '../../../core/theme/colors';
15
+
16
+ interface CarouselSectionProps {
17
+ items: CarouselItem[];
18
+ imageStyle?: ImageStyle;
19
+ }
20
+
21
+ export function CarouselSection({
22
+ items,
23
+ imageStyle,
24
+ }: CarouselSectionProps): JSX.Element {
25
+ return (
26
+ <ScrollView
27
+ horizontal
28
+ showsHorizontalScrollIndicator={false}
29
+ contentContainerStyle={styles.container}>
30
+ {items.map((item, index) => (
31
+ <TouchableOpacity
32
+ key={item.id}
33
+ style={styles.itemContainer}
34
+ onPress={() => item.onPress?.(item)}>
35
+ <Image
36
+ source={{uri: item.imageUrl}}
37
+ style={[styles.image, imageStyle]}
38
+ />
39
+ <View style={styles.textContainer}>
40
+ <Text style={styles.title} numberOfLines={1}>
41
+ {item.title}
42
+ </Text>
43
+ {item.subtitle && (
44
+ <Text style={styles.subtitle} numberOfLines={1}>
45
+ {item.subtitle}
46
+ </Text>
47
+ )}
48
+ </View>
49
+ </TouchableOpacity>
50
+ ))}
51
+ </ScrollView>
52
+ );
53
+ }
54
+
55
+ const styles = StyleSheet.create({
56
+ container: {
57
+ paddingHorizontal: CommonSizes.spacing.large,
58
+ },
59
+ itemContainer: {
60
+ marginRight: CommonSizes.spacing.medium,
61
+ },
62
+ image: {
63
+ width: 200,
64
+ height: 150,
65
+ borderRadius: CommonSizes.borderRadius.medium,
66
+ },
67
+ textContainer: {
68
+ marginTop: CommonSizes.spacing.small,
69
+ },
70
+ title: {
71
+ fontSize: 16,
72
+ fontWeight: '600',
73
+ color: Colors.blue100,
74
+ },
75
+ subtitle: {
76
+ fontSize: 14,
77
+ color: Colors.blue100,
78
+ marginTop: 2,
79
+ },
80
+ });
@@ -0,0 +1,129 @@
1
+ import React, {useState, useRef} from 'react';
2
+ import {
3
+ View,
4
+ StyleSheet,
5
+ Dimensions,
6
+ FlatList,
7
+ Image,
8
+ TouchableOpacity,
9
+ Text,
10
+ } from 'react-native';
11
+ import {Colors} from '../../../core/theme/colors';
12
+ import {CommonSizes} from '../../../core/theme/commonSizes';
13
+ import {CommonStyles} from '../../../core/theme/commonStyles';
14
+ import {CarouselItem} from '../types';
15
+
16
+ const {width} = Dimensions.get('window');
17
+ const ITEM_WIDTH = width * 0.85;
18
+
19
+ interface FeaturedCarouselProps {
20
+ items: CarouselItem[];
21
+ }
22
+
23
+ export function FeaturedCarousel({items}: FeaturedCarouselProps): JSX.Element {
24
+ const [activeIndex, setActiveIndex] = useState(0);
25
+ const flatListRef = useRef<FlatList>(null);
26
+
27
+ const renderItem = ({item}: {item: CarouselItem}) => (
28
+ <TouchableOpacity
29
+ style={styles.itemContainer}
30
+ onPress={() => item.onPress?.(item)}>
31
+ <Image source={{uri: item.imageUrl}} style={styles.image} />
32
+ <View style={styles.overlay}>
33
+ <Text style={styles.title}>{item.title}</Text>
34
+ {item.subtitle && <Text style={styles.subtitle}>{item.subtitle}</Text>}
35
+ </View>
36
+ </TouchableOpacity>
37
+ );
38
+
39
+ const renderDot = (index: number) => (
40
+ <View
41
+ key={index}
42
+ style={[styles.dot, index === activeIndex && styles.activeDot]}
43
+ />
44
+ );
45
+
46
+ const handleScroll = (event: any) => {
47
+ const slideSize = event.nativeEvent.layoutMeasurement.width;
48
+ const index = event.nativeEvent.contentOffset.x / slideSize;
49
+ const roundIndex = Math.round(index);
50
+ setActiveIndex(roundIndex);
51
+ };
52
+
53
+ return (
54
+ <View>
55
+ <FlatList
56
+ ref={flatListRef}
57
+ data={items}
58
+ renderItem={renderItem}
59
+ horizontal
60
+ pagingEnabled
61
+ showsHorizontalScrollIndicator={false}
62
+ onScroll={handleScroll}
63
+ snapToAlignment="center"
64
+ decelerationRate="fast"
65
+ snapToInterval={ITEM_WIDTH}
66
+ contentContainerStyle={styles.listContainer}
67
+ getItemLayout={(_, index) => ({
68
+ length: ITEM_WIDTH,
69
+ offset: ITEM_WIDTH * index,
70
+ index,
71
+ })}
72
+ />
73
+ <View style={styles.pagination}>
74
+ {items.map((_, index) => renderDot(index))}
75
+ </View>
76
+ </View>
77
+ );
78
+ }
79
+
80
+ const styles = StyleSheet.create({
81
+ listContainer: {
82
+ paddingHorizontal: (width - ITEM_WIDTH) / 2,
83
+ },
84
+ itemContainer: {
85
+ width: ITEM_WIDTH,
86
+ height: 250,
87
+ },
88
+ image: {
89
+ width: '100%',
90
+ height: '100%',
91
+ borderRadius: CommonSizes.borderRadius.large,
92
+ },
93
+ overlay: {
94
+ ...StyleSheet.absoluteFillObject,
95
+ backgroundColor: 'rgba(0, 0, 0, 0.3)',
96
+ borderRadius: CommonSizes.borderRadius.large,
97
+ padding: CommonSizes.spacing.large,
98
+ justifyContent: 'flex-end',
99
+ },
100
+ title: {
101
+ fontSize: 20,
102
+ fontWeight: '600',
103
+ color: Colors.white,
104
+ },
105
+ subtitle: {
106
+ fontSize: 16,
107
+ color: Colors.white,
108
+ marginTop: CommonSizes.spacing.small,
109
+ },
110
+ pagination: {
111
+ flexDirection: 'row',
112
+ justifyContent: 'center',
113
+ alignItems: 'center',
114
+ marginTop: CommonSizes.spacing.medium,
115
+ },
116
+ dot: {
117
+ width: 8,
118
+ height: 8,
119
+ borderRadius: 4,
120
+ backgroundColor: Colors.gray,
121
+ marginHorizontal: 4,
122
+ },
123
+ activeDot: {
124
+ backgroundColor: Colors.blue100,
125
+ width: 12,
126
+ height: 12,
127
+ borderRadius: 6,
128
+ },
129
+ });
@@ -0,0 +1,60 @@
1
+ import {useState, useEffect} from 'react';
2
+ import {CarouselItem} from '../types';
3
+
4
+ export function useHomeData() {
5
+ const [isLoading, setIsLoading] = useState(false);
6
+ const [featuredItems, setFeaturedItems] = useState<CarouselItem[]>([]);
7
+ const [trendingItems, setTrendingItems] = useState<CarouselItem[]>([]);
8
+ const [newItems, setNewItems] = useState<CarouselItem[]>([]);
9
+ const [recommendedItems, setRecommendedItems] = useState<CarouselItem[]>([]);
10
+
11
+ const fetchData = async () => {
12
+ setIsLoading(true);
13
+ try {
14
+ // Simulate API calls
15
+ // Replace with actual API calls in production
16
+ setFeaturedItems([
17
+ {
18
+ id: '1',
19
+ title: 'Featured Item 1',
20
+ subtitle: 'Discover amazing features',
21
+ imageUrl: 'https://picsum.photos/800/400',
22
+ },
23
+ // Add more items...
24
+ ]);
25
+
26
+ setTrendingItems([
27
+ {
28
+ id: '1',
29
+ title: 'Trending Item 1',
30
+ subtitle: 'Hot right now',
31
+ imageUrl: 'https://picsum.photos/700/400',
32
+ },
33
+ // Add more items...
34
+ ]);
35
+
36
+ // Similar for newItems and recommendedItems...
37
+ } catch (error) {
38
+ console.error('Error fetching home data:', error);
39
+ } finally {
40
+ setIsLoading(false);
41
+ }
42
+ };
43
+
44
+ const refreshData = () => {
45
+ fetchData();
46
+ };
47
+
48
+ useEffect(() => {
49
+ fetchData();
50
+ }, []);
51
+
52
+ return {
53
+ isLoading,
54
+ featuredItems,
55
+ trendingItems,
56
+ newItems,
57
+ recommendedItems,
58
+ refreshData,
59
+ };
60
+ }
@@ -0,0 +1,7 @@
1
+ export interface CarouselItem {
2
+ id: string;
3
+ title: string;
4
+ subtitle?: string;
5
+ imageUrl: string;
6
+ onPress?: (item: CarouselItem) => void;
7
+ }