@fadyshawky/react-native-magic 1.0.7 → 1.0.9

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.
Files changed (66) hide show
  1. package/.vscode/settings.json +7 -0
  2. package/CHANGELOG.md +9 -0
  3. package/package.json +1 -1
  4. package/template/App.tsx +30 -9
  5. package/template/package-lock.json +170 -123
  6. package/template/package.json +1 -0
  7. package/template/src/common/ImageResources.g.ts +3 -5
  8. package/template/src/common/components/Background.tsx +66 -28
  9. package/template/src/common/components/Cards.tsx +116 -0
  10. package/template/src/common/components/Container.tsx +145 -0
  11. package/template/src/common/components/FlatListWrapper.tsx +1 -0
  12. package/template/src/common/components/ImageCropPickerButton.tsx +1 -1
  13. package/template/src/common/components/OTPInput.tsx +107 -0
  14. package/template/src/common/components/PhotoTakingButton.tsx +1 -4
  15. package/template/src/common/components/PrimaryButton.tsx +171 -157
  16. package/template/src/common/components/RTLAwareText.tsx +42 -0
  17. package/template/src/common/components/RTLAwareView.tsx +179 -0
  18. package/template/src/common/components/RadioButton.tsx +1 -3
  19. package/template/src/common/components/RadioIcon.tsx +1 -2
  20. package/template/src/common/components/SearchBar.tsx +179 -0
  21. package/template/src/common/components/Separator.tsx +7 -4
  22. package/template/src/common/components/TouchablePlatform.tsx +1 -3
  23. package/template/src/common/components/TryAgain.tsx +3 -3
  24. package/template/src/common/helpers/inAppReviewHelper.ts +0 -1
  25. package/template/src/common/helpers/stringsHelpers.ts +10 -0
  26. package/template/src/common/hooks/useFlatListActions.ts +1 -1
  27. package/template/src/common/localization/LocalizationProvider.tsx +152 -0
  28. package/template/src/common/localization/README.md +488 -0
  29. package/template/src/common/localization/localization.ts +12 -0
  30. package/template/src/common/localization/translations/homeLocalization.ts +1 -0
  31. package/template/src/common/localization/translations/profileLocalization.ts +24 -0
  32. package/template/src/common/validations/errorValidations.ts +1 -6
  33. package/template/src/common/validations/examples/TextInputWithValidation.tsx +229 -0
  34. package/template/src/common/validations/index.ts +28 -0
  35. package/template/src/common/validations/regex.js +83 -0
  36. package/template/src/common/validations/regexValidator.ts +240 -0
  37. package/template/src/common/validations/validationConstants.ts +2 -2
  38. package/template/src/core/api/errorHandler.ts +39 -0
  39. package/template/src/core/api/responseHandlers.ts +1 -26
  40. package/template/src/core/api/serverHeaders.ts +13 -23
  41. package/template/src/core/store/app/appSlice.ts +1 -2
  42. package/template/src/core/store/user/userSlice.ts +1 -0
  43. package/template/src/core/theme/ThemeProvider.tsx +63 -0
  44. package/template/src/core/theme/colors.ts +31 -42
  45. package/template/src/core/theme/commonConsts.ts +1 -1
  46. package/template/src/core/theme/commonStyles.ts +267 -210
  47. package/template/src/core/theme/fonts.ts +17 -1
  48. package/template/src/core/theme/scaling.ts +101 -0
  49. package/template/src/core/theme/themes.ts +214 -0
  50. package/template/src/core/theme/types.ts +51 -0
  51. package/template/src/navigation/AuthStack.tsx +25 -30
  52. package/template/src/navigation/HeaderComponents.tsx +18 -58
  53. package/template/src/navigation/MainNavigation.tsx +5 -6
  54. package/template/src/navigation/MainStack.tsx +6 -27
  55. package/template/src/navigation/RootNavigation.tsx +1 -7
  56. package/template/src/navigation/TabBar.tsx +2 -2
  57. package/template/src/navigation/TopTabBar.tsx +1 -1
  58. package/template/src/screens/Login/Login.tsx +3 -3
  59. package/template/src/screens/home/HomeScreen.tsx +107 -0
  60. package/template/src/screens/home/components/CarouselSection.tsx +79 -0
  61. package/template/src/screens/home/components/FeaturedCarousel.tsx +128 -0
  62. package/template/src/screens/home/hooks/useHomeData.ts +60 -0
  63. package/template/src/screens/home/types.ts +7 -0
  64. package/template/src/screens/registration/RegistrationScreen.tsx +2 -2
  65. package/template/src/screens/resetPassword/ForgotPasswordScreen.tsx +2 -2
  66. package/template/src/utils/stringBuilder.js +25 -0
@@ -19,6 +19,7 @@
19
19
  "test": "jest"
20
20
  },
21
21
  "dependencies": {
22
+ "@d11/react-native-fast-image": "^8.9.2",
22
23
  "@react-native-async-storage/async-storage": "^2.1.0",
23
24
  "@react-native-camera-roll/camera-roll": "^7.9.0",
24
25
  "@react-native-community/datetimepicker": "^8.2.0",
@@ -1,14 +1,12 @@
1
1
  /* eslint-disable */
2
2
  /* tslint:disable */
3
- import {ImageURISource} from "react-native";
3
+ import {ImageURISource} from 'react-native';
4
4
 
5
5
  /**
6
6
  * This file is auto-generated by react-native-image-resource-generator
7
7
  * !!! DO NOT EDIT !!!
8
8
  * For more information check the documentation:
9
9
  * https://github.com/svbutko/react-native-image-resource-generator
10
- */
10
+ */
11
11
 
12
- export class ImageResources {
13
-
14
- }
12
+ export class ImageResources {}
@@ -1,33 +1,71 @@
1
- import React from 'react';
2
- import {ImageBackground, StyleSheet, ViewStyle} from 'react-native';
3
- import {CommonStyles} from '../../core/theme/commonStyles';
1
+ import React, {FC, memo} from 'react';
2
+ import {ImageBackground, StyleSheet, View, ViewStyle} from 'react-native';
3
+ import {useTheme} from '../../core/theme/ThemeProvider';
4
+ import {ImageResources} from '../ImageResources.g';
5
+ import {Container} from './Container';
6
+ import {KeyboardAwareScrollViewProps} from 'react-native-keyboard-aware-scroll-view';
4
7
 
5
- interface BackgroundType {
8
+ interface BackgroundProps
9
+ extends Omit<KeyboardAwareScrollViewProps, 'contentContainerStyle'> {
6
10
  children: React.ReactNode;
7
- inverted?: boolean;
8
- extraStyle?: ViewStyle;
11
+ useSafeArea?: boolean;
12
+ style?: ViewStyle;
13
+ contentContainerStyle?: ViewStyle;
14
+ withoutPadding?: boolean;
15
+ withoutScroll?: boolean;
16
+ withoutBackgroundImage?: boolean;
9
17
  }
10
18
 
11
- export const Background = ({
12
- children,
13
- inverted,
14
- extraStyle,
15
- }: BackgroundType) => {
16
- const styles = StyleSheet.create({
17
- imageContainer: {
18
- ...CommonStyles.flex1,
19
- flexGrow: 1,
20
- // justifyContent: 'flex-end',
21
- },
22
- });
19
+ export const Background: FC<BackgroundProps> = memo(
20
+ ({
21
+ children,
22
+ useSafeArea = true,
23
+ style,
24
+ contentContainerStyle,
25
+ withoutPadding = false,
26
+ withoutScroll = false,
27
+ withoutBackgroundImage = false,
28
+ ...scrollViewProps
29
+ }) => {
30
+ const {theme} = useTheme();
23
31
 
24
- return (
25
- <ImageBackground
26
- resizeMode="cover"
27
- style={{...styles.imageContainer, ...extraStyle}}
28
- imageStyle={inverted ? {transform: [{rotateY: '180deg'}]} : undefined}
29
- source={0}>
30
- {children}
31
- </ImageBackground>
32
- );
33
- };
32
+ const content = (
33
+ <Container
34
+ useSafeArea={useSafeArea}
35
+ style={style}
36
+ contentContainerStyle={contentContainerStyle}
37
+ withoutPadding={withoutPadding}
38
+ withoutScroll={withoutScroll}
39
+ {...scrollViewProps}>
40
+ {children}
41
+ </Container>
42
+ );
43
+
44
+ if (withoutBackgroundImage) {
45
+ return (
46
+ <View
47
+ style={[
48
+ styles.container,
49
+ {backgroundColor: theme.colors.background},
50
+ ]}>
51
+ {content}
52
+ </View>
53
+ );
54
+ }
55
+
56
+ return (
57
+ <View
58
+ style={[styles.container, {backgroundColor: theme.colors.background}]}>
59
+ <ImageBackground source={0} style={styles.container}>
60
+ {content}
61
+ </ImageBackground>
62
+ </View>
63
+ );
64
+ },
65
+ );
66
+
67
+ const styles = StyleSheet.create({
68
+ container: {
69
+ flex: 1,
70
+ },
71
+ });
@@ -0,0 +1,116 @@
1
+ import React from 'react';
2
+ import {
3
+ Image,
4
+ StyleSheet,
5
+ Text,
6
+ TouchableOpacity,
7
+ View,
8
+ ViewStyle,
9
+ } from 'react-native';
10
+ import {CommonSizes} from '../../core/theme/commonSizes';
11
+ import {createThemedStyles} from '../../core/theme/commonStyles';
12
+ import {scaleHeight, scaleWidth} from '../../core/theme/scaling';
13
+ import {useTheme} from '../../core/theme/ThemeProvider';
14
+ import FastImage from '@d11/react-native-fast-image';
15
+
16
+ interface CardProps {
17
+ icon: {uri: string};
18
+ title: string;
19
+ onPress?: () => void;
20
+ marginRight?: number;
21
+ cardStyle?: ViewStyle;
22
+ }
23
+
24
+ export const Card = ({
25
+ icon,
26
+ title,
27
+ onPress,
28
+ marginRight,
29
+ cardStyle,
30
+ }: CardProps): JSX.Element => {
31
+ const {theme} = useTheme();
32
+ return (
33
+ <TouchableOpacity
34
+ style={[
35
+ styles.card,
36
+ {backgroundColor: theme.colors.surface},
37
+ createThemedStyles(theme).dropShadow,
38
+ {marginRight: marginRight},
39
+ {overflow: 'hidden'},
40
+ cardStyle,
41
+ ]}
42
+ onPress={() => {
43
+ onPress?.();
44
+ }}>
45
+ <View style={styles.iconContainer}>
46
+ {icon && icon.uri && (
47
+ <FastImage
48
+ source={{uri: icon.uri, cache: FastImage.cacheControl.immutable}}
49
+ style={{
50
+ width: '100%',
51
+ height: '100%',
52
+ }}
53
+ resizeMode={FastImage.resizeMode.contain}
54
+ />
55
+ )}
56
+ </View>
57
+ <Text style={[theme.text.cards, {textAlign: 'center'}]}>{title}</Text>
58
+ </TouchableOpacity>
59
+ );
60
+ };
61
+
62
+ const styles = StyleSheet.create({
63
+ card: {
64
+ padding: CommonSizes.spacing.medium,
65
+ borderRadius: CommonSizes.borderRadius.large,
66
+ alignItems: 'center',
67
+ justifyContent: 'flex-start',
68
+ gap: scaleHeight(10),
69
+ width: scaleWidth(187),
70
+ height: scaleHeight(280),
71
+ },
72
+ blurContainer: {
73
+ flex: 1,
74
+ padding: CommonSizes.spacing.medium,
75
+ alignItems: 'center',
76
+ justifyContent: 'center',
77
+ },
78
+ iconContainer: {
79
+ width: '100%',
80
+ height: scaleHeight(100),
81
+ },
82
+ title: {
83
+ fontSize: CommonSizes.fontSize.body2,
84
+ textAlign: 'center',
85
+ marginBottom: CommonSizes.spacing.small,
86
+ },
87
+ dotsContainer: {
88
+ flexDirection: 'row',
89
+ gap: 4,
90
+ },
91
+ dot: {
92
+ width: 4,
93
+ height: 4,
94
+ borderRadius: 2,
95
+ },
96
+ whiteDropShadow: {
97
+ // iOS shadow
98
+ shadowColor: '#FFF',
99
+ shadowOffset: {
100
+ width: 0,
101
+ height: 4,
102
+ },
103
+ shadowOpacity: 0.25,
104
+ shadowRadius: 8,
105
+ // Android shadow
106
+ elevation: 5,
107
+ // Add a very subtle border to enhance the white shadow effect
108
+ borderWidth: 0.5,
109
+ borderColor: 'rgba(255, 255, 255, 0.1)',
110
+ },
111
+ favoriteContainer: {
112
+ position: 'absolute',
113
+ top: scaleHeight(17),
114
+ left: scaleWidth(12),
115
+ },
116
+ });
@@ -0,0 +1,145 @@
1
+ import React, {forwardRef} from 'react';
2
+ import {
3
+ StyleSheet,
4
+ ViewStyle,
5
+ Platform,
6
+ KeyboardAvoidingView,
7
+ ScrollView,
8
+ View,
9
+ ImageBackground,
10
+ ImageSourcePropType,
11
+ } from 'react-native';
12
+ import {
13
+ KeyboardAwareScrollView,
14
+ KeyboardAwareScrollViewProps,
15
+ } from 'react-native-keyboard-aware-scroll-view';
16
+ import {useTheme} from '../../core/theme/ThemeProvider';
17
+ import {SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-context';
18
+ import {ImageResources} from '../ImageResources.g';
19
+
20
+ interface ContainerProps extends Partial<KeyboardAwareScrollViewProps> {
21
+ children: React.ReactNode;
22
+ useSafeArea?: boolean;
23
+ style?: ViewStyle;
24
+ contentContainerStyle?: ViewStyle;
25
+ withoutPadding?: boolean;
26
+ withoutScroll?: boolean;
27
+ backgroundColor?: string;
28
+ withoutBackgroundImage?: boolean;
29
+ backgroundImage?: ImageSourcePropType;
30
+ extendedBackground?: boolean;
31
+ }
32
+
33
+ export const Container = forwardRef<KeyboardAwareScrollView, ContainerProps>(
34
+ (
35
+ {
36
+ children,
37
+ useSafeArea = true,
38
+ style,
39
+ contentContainerStyle,
40
+ withoutPadding = false,
41
+ withoutScroll = false,
42
+ backgroundColor,
43
+ withoutBackgroundImage = false,
44
+ backgroundImage = 0,
45
+ extendedBackground = false,
46
+ ...scrollViewProps
47
+ },
48
+ ref,
49
+ ) => {
50
+ const {theme} = useTheme();
51
+ const insets = useSafeAreaInsets();
52
+ const Wrapper = useSafeArea ? SafeAreaView : View;
53
+ const bgColor = backgroundColor || theme.colors.background;
54
+
55
+ const content = (
56
+ <Wrapper
57
+ style={[
58
+ styles.container,
59
+ !withoutPadding && styles.padding,
60
+ style,
61
+ extendedBackground && {
62
+ marginTop: -insets.top,
63
+ paddingTop: insets.top,
64
+ },
65
+ ]}>
66
+ {children}
67
+ </Wrapper>
68
+ );
69
+
70
+ const wrappedContent = withoutBackgroundImage ? (
71
+ <View style={[styles.container, {backgroundColor: bgColor}]}>
72
+ {content}
73
+ </View>
74
+ ) : (
75
+ <ImageBackground
76
+ source={backgroundImage}
77
+ style={[
78
+ styles.container,
79
+ extendedBackground && {
80
+ marginTop: -insets.top,
81
+ },
82
+ ]}
83
+ resizeMode="cover">
84
+ {content}
85
+ </ImageBackground>
86
+ );
87
+
88
+ if (withoutScroll) {
89
+ return (
90
+ <KeyboardAvoidingView
91
+ style={[
92
+ styles.container,
93
+ {backgroundColor: bgColor},
94
+ extendedBackground && {
95
+ marginTop: -insets.top,
96
+ },
97
+ ]}
98
+ behavior={undefined}
99
+ enabled>
100
+ {wrappedContent}
101
+ </KeyboardAvoidingView>
102
+ );
103
+ }
104
+
105
+ return (
106
+ <KeyboardAwareScrollView
107
+ ref={ref}
108
+ style={[
109
+ styles.container,
110
+ {backgroundColor: bgColor},
111
+ extendedBackground && {
112
+ marginTop: -insets.top,
113
+ },
114
+ ]}
115
+ contentContainerStyle={[
116
+ styles.contentContainer,
117
+ !withoutPadding && styles.padding,
118
+ contentContainerStyle,
119
+ ]}
120
+ keyboardShouldPersistTaps="handled"
121
+ showsVerticalScrollIndicator={false}
122
+ enableOnAndroid
123
+ enableResetScrollToCoords={false}
124
+ extraScrollHeight={20}
125
+ keyboardOpeningTime={0}
126
+ keyboardDismissMode="on-drag"
127
+ enableAutomaticScroll={true}
128
+ {...scrollViewProps}>
129
+ {wrappedContent}
130
+ </KeyboardAwareScrollView>
131
+ );
132
+ },
133
+ );
134
+
135
+ const styles = StyleSheet.create({
136
+ container: {
137
+ flex: 1,
138
+ },
139
+ contentContainer: {
140
+ flexGrow: 1,
141
+ },
142
+ padding: {
143
+ padding: 16, // Using a fixed value here as theme isn't available in StyleSheet
144
+ },
145
+ });
@@ -60,6 +60,7 @@ export function FlatListWrapper({
60
60
  {...props}
61
61
  refreshing={refreshing}
62
62
  ListEmptyComponent={ListEmptyComponent}
63
+ removeClippedSubviews={true}
63
64
  />
64
65
  );
65
66
  }
@@ -87,7 +87,7 @@ export const ImageCropPickerButton: FC<IProps> = memo(
87
87
  style={style}
88
88
  iconStyle={iconStyle}
89
89
  imageStyle={imageStyle}
90
- onPress={onPress}
90
+ onPressIn={onPress}
91
91
  backgroundImage={image}
92
92
  icon={icon}
93
93
  disabled={disabled}
@@ -0,0 +1,107 @@
1
+ import React, {useRef, useState} from 'react';
2
+ import {
3
+ NativeSyntheticEvent,
4
+ StyleSheet,
5
+ TextInput,
6
+ TextInputKeyPressEventData,
7
+ ViewStyle,
8
+ } from 'react-native';
9
+ import {useTheme} from '../../core/theme/ThemeProvider';
10
+ import {CommonSizes} from '../../core/theme/commonSizes';
11
+ import {scaleWidth} from '../../core/theme/scaling';
12
+ import {PrimaryTextInput} from './PrimaryTextInput';
13
+ import {RTLAwareView} from './RTLAwareView';
14
+
15
+ interface OTPInputProps {
16
+ value: string;
17
+ onChange: (value: string) => void;
18
+ error?: string | null;
19
+ style?: ViewStyle;
20
+ }
21
+
22
+ export const OTPInput: React.FC<OTPInputProps> = ({
23
+ value,
24
+ onChange,
25
+ error,
26
+ style,
27
+ }) => {
28
+ const {theme} = useTheme();
29
+ const inputRefs = useRef<Array<TextInput | null>>([]);
30
+ const [focused, setFocused] = useState<number>(-1);
31
+
32
+ const handleChange = (text: string, index: number) => {
33
+ const newValue = value.split('');
34
+ newValue[index] = text;
35
+ const finalValue = newValue.join('');
36
+ onChange(finalValue);
37
+
38
+ if (text.length > 0 && index < 5) {
39
+ inputRefs.current[index + 1]?.focus();
40
+ }
41
+ };
42
+
43
+ const handleKeyPress = (
44
+ e: NativeSyntheticEvent<TextInputKeyPressEventData>,
45
+ index: number,
46
+ ) => {
47
+ if (e.nativeEvent.key === 'Backspace' && index > 0 && !value[index]) {
48
+ inputRefs.current[index - 1]?.focus();
49
+ }
50
+ };
51
+
52
+ const handleFocus = (index: number) => {
53
+ setFocused(index);
54
+ };
55
+
56
+ const handleBlur = () => {
57
+ setFocused(-1);
58
+ };
59
+
60
+ return (
61
+ <RTLAwareView style={{...styles.container, ...style}}>
62
+ {[0, 1, 2, 3, 4, 5].map(index => (
63
+ <PrimaryTextInput
64
+ width={scaleWidth(78)}
65
+ key={index}
66
+ inputRef={ref => (inputRefs.current[index] = ref)}
67
+ style={[
68
+ styles.input,
69
+ {
70
+ borderColor: error
71
+ ? theme.colors.mutedLavender
72
+ : focused === index
73
+ ? theme.colors.indigoBlue
74
+ : theme.colors.mutedLavender30,
75
+ backgroundColor: theme.colors.backgroundOpacity,
76
+ },
77
+ ]}
78
+ maxLength={1}
79
+ keyboardType="number-pad"
80
+ value={value[index] || ''}
81
+ onChangeText={text => handleChange(text, index)}
82
+ onKeyPress={e => handleKeyPress(e, index)}
83
+ onFocus={() => handleFocus(index)}
84
+ onBlur={handleBlur}
85
+ selectTextOnFocus
86
+ />
87
+ ))}
88
+ </RTLAwareView>
89
+ );
90
+ };
91
+
92
+ const styles = StyleSheet.create({
93
+ container: {
94
+ flexDirection: 'row',
95
+ justifyContent: 'space-between',
96
+ width: '100%',
97
+ gap: CommonSizes.spacing.small,
98
+ paddingHorizontal: CommonSizes.spacing.large,
99
+ },
100
+ input: {
101
+ flex: 1,
102
+ height: CommonSizes.spacing.xxl,
103
+ borderWidth: 1,
104
+ borderRadius: CommonSizes.borderRadius.medium,
105
+ textAlign: 'center',
106
+ },
107
+ });
@@ -9,7 +9,6 @@ import {
9
9
  ViewStyle,
10
10
  } from 'react-native';
11
11
  import {Image as CropperImage} from 'react-native-image-crop-picker';
12
- import {Colors} from '../../core/theme/colors';
13
12
  import {CommonSizes} from '../../core/theme/commonSizes';
14
13
 
15
14
  interface IProps {
@@ -35,7 +34,7 @@ export const PhotoTakingButton: FC<IProps> = memo(
35
34
  return (
36
35
  <TouchableOpacity
37
36
  style={[styles.button, style]}
38
- onPress={onPress}
37
+ onPressIn={onPress}
39
38
  disabled={disabled}>
40
39
  <BackgroundComponent image={backgroundImage} style={imageStyle}>
41
40
  <Image style={[styles.icon, iconStyle]} source={icon!} />
@@ -76,7 +75,6 @@ const styles = StyleSheet.create({
76
75
  justifyContent: 'center',
77
76
  borderRadius: CommonSizes.borderRadius.small,
78
77
  borderWidth: 1,
79
- borderColor: Colors.gray,
80
78
  } as ViewStyle,
81
79
  image: {
82
80
  height: 120,
@@ -91,7 +89,6 @@ const styles = StyleSheet.create({
91
89
  width: 32,
92
90
  height: 32,
93
91
  resizeMode: 'contain',
94
- tintColor: Colors.black,
95
92
  opacity: 0.8,
96
93
  } as ImageStyle,
97
94
  });