@fadyshawky/react-native-magic 1.0.0

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 (165) hide show
  1. package/.vscode/settings.json +7 -0
  2. package/README.md +269 -0
  3. package/package.json +36 -0
  4. package/template/.bundle/config +2 -0
  5. package/template/.env.development +5 -0
  6. package/template/.env.production +5 -0
  7. package/template/.env.staging +5 -0
  8. package/template/.eslintrc.js +4 -0
  9. package/template/.prettierrc.js +7 -0
  10. package/template/.watchmanconfig +1 -0
  11. package/template/App.tsx +34 -0
  12. package/template/Gemfile +9 -0
  13. package/template/Gemfile.lock +117 -0
  14. package/template/README.md +79 -0
  15. package/template/__tests__/App.test.tsx +17 -0
  16. package/template/android/app/build.gradle +128 -0
  17. package/template/android/app/debug.keystore +0 -0
  18. package/template/android/app/proguard-rules.pro +10 -0
  19. package/template/android/app/src/debug/AndroidManifest.xml +9 -0
  20. package/template/android/app/src/main/AndroidManifest.xml +26 -0
  21. package/template/android/app/src/main/java/com/reactnativemagic/MainActivity.kt +22 -0
  22. package/template/android/app/src/main/java/com/reactnativemagic/MainApplication.kt +44 -0
  23. package/template/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
  24. package/template/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  25. package/template/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  26. package/template/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  27. package/template/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  28. package/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  29. package/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  30. package/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  31. package/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  32. package/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  33. package/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  34. package/template/android/app/src/main/res/values/strings.xml +3 -0
  35. package/template/android/app/src/main/res/values/styles.xml +9 -0
  36. package/template/android/build.gradle +21 -0
  37. package/template/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  38. package/template/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  39. package/template/android/gradle.properties +39 -0
  40. package/template/android/gradlew +252 -0
  41. package/template/android/gradlew.bat +94 -0
  42. package/template/android/settings.gradle +6 -0
  43. package/template/app.json +4 -0
  44. package/template/babel.config.js +3 -0
  45. package/template/index.js +9 -0
  46. package/template/install-dev.sh +1 -0
  47. package/template/install.sh +1 -0
  48. package/template/ios/.xcode.env +11 -0
  49. package/template/ios/Podfile +42 -0
  50. package/template/ios/Podfile.lock +2461 -0
  51. package/template/ios/reactnativemagic/AppDelegate.h +6 -0
  52. package/template/ios/reactnativemagic/AppDelegate.mm +31 -0
  53. package/template/ios/reactnativemagic/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
  54. package/template/ios/reactnativemagic/Images.xcassets/Contents.json +6 -0
  55. package/template/ios/reactnativemagic/Info.plist +52 -0
  56. package/template/ios/reactnativemagic/LaunchScreen.storyboard +47 -0
  57. package/template/ios/reactnativemagic/PrivacyInfo.xcprivacy +46 -0
  58. package/template/ios/reactnativemagic/main.m +10 -0
  59. package/template/ios/reactnativemagic copy-Info.plist +52 -0
  60. package/template/ios/reactnativemagic.xcodeproj/project.pbxproj +836 -0
  61. package/template/ios/reactnativemagic.xcodeproj/xcshareddata/xcschemes/reactnativemagic.xcscheme +88 -0
  62. package/template/ios/reactnativemagic.xcworkspace/contents.xcworkspacedata +10 -0
  63. package/template/ios/reactnativemagic.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
  64. package/template/ios/reactnativemagicTests/Info.plist +24 -0
  65. package/template/ios/reactnativemagicTests/reactnativemagicTests.m +66 -0
  66. package/template/ios/tmp.xcconfig +2 -0
  67. package/template/jest.config.js +3 -0
  68. package/template/metro.config.js +11 -0
  69. package/template/package-lock.json +18315 -0
  70. package/template/package.json +125 -0
  71. package/template/resources/symbols/SFSymbols.ts +2614 -0
  72. package/template/src/common/ImageResources.g.ts +14 -0
  73. package/template/src/common/components/Background.tsx +34 -0
  74. package/template/src/common/components/EmptyView.tsx +31 -0
  75. package/template/src/common/components/FlatListWrapper.tsx +66 -0
  76. package/template/src/common/components/IconPlatform.tsx +17 -0
  77. package/template/src/common/components/ImageCropPickerButton.tsx +108 -0
  78. package/template/src/common/components/LoadingComponent.tsx +16 -0
  79. package/template/src/common/components/PhotoTakingButton.tsx +99 -0
  80. package/template/src/common/components/PrimaryButton.tsx +305 -0
  81. package/template/src/common/components/PrimaryTextInput.tsx +287 -0
  82. package/template/src/common/components/RadioButton.tsx +73 -0
  83. package/template/src/common/components/RadioIcon.tsx +63 -0
  84. package/template/src/common/components/Separator.tsx +39 -0
  85. package/template/src/common/components/Svg.tsx +25 -0
  86. package/template/src/common/components/TouchablePlatform.tsx +70 -0
  87. package/template/src/common/components/TryAgain.tsx +56 -0
  88. package/template/src/common/helpers/arrayHelpers.ts +29 -0
  89. package/template/src/common/helpers/calculatePage.ts +16 -0
  90. package/template/src/common/helpers/colorHelpers.ts +34 -0
  91. package/template/src/common/helpers/defaultKeyIdExtractor.ts +5 -0
  92. package/template/src/common/helpers/dialogsHelpers.ts +66 -0
  93. package/template/src/common/helpers/imageHelpers.ts +5 -0
  94. package/template/src/common/helpers/inAppReviewHelper.ts +31 -0
  95. package/template/src/common/helpers/netInfoHelpers.ts +42 -0
  96. package/template/src/common/helpers/orientationHelpers.ts +25 -0
  97. package/template/src/common/helpers/regexHelpers.ts +7 -0
  98. package/template/src/common/helpers/shareHelpers.ts +47 -0
  99. package/template/src/common/helpers/stringsHelpers.ts +15 -0
  100. package/template/src/common/hooks/useBackHandler.ts +10 -0
  101. package/template/src/common/hooks/useDebounce.ts +17 -0
  102. package/template/src/common/hooks/useEventRegister.ts +50 -0
  103. package/template/src/common/hooks/useFlatListActions.ts +31 -0
  104. package/template/src/common/hooks/usePrevious.ts +11 -0
  105. package/template/src/common/hooks/useWhyDidYouUpdate.ts +27 -0
  106. package/template/src/common/localization/dateFormatter.ts +108 -0
  107. package/template/src/common/localization/intlFormatter.ts +37 -0
  108. package/template/src/common/localization/localization.ts +51 -0
  109. package/template/src/common/localization/translations/commonLocalization.ts +29 -0
  110. package/template/src/common/localization/translations/emptyLocalization.ts +6 -0
  111. package/template/src/common/localization/translations/errorsLocalization.ts +22 -0
  112. package/template/src/common/localization/translations/homeLocalization.ts +5 -0
  113. package/template/src/common/localization/translations/loginLocalization.ts +14 -0
  114. package/template/src/common/localization/translations/onboardingLocalization.ts +13 -0
  115. package/template/src/common/localization/translations/pagesLocalization.ts +14 -0
  116. package/template/src/common/localization/translations/profileLocalization.ts +6 -0
  117. package/template/src/common/urls/baseUrlOpener.ts +31 -0
  118. package/template/src/common/urls/emailUrl.ts +20 -0
  119. package/template/src/common/urls/httpUrl.ts +19 -0
  120. package/template/src/common/urls/mapUrl.ts +22 -0
  121. package/template/src/common/urls/phoneUrl.ts +16 -0
  122. package/template/src/common/utils/createPerfectSize.ts +15 -0
  123. package/template/src/common/utils/listHandlers.ts +30 -0
  124. package/template/src/common/utils/newState.ts +5 -0
  125. package/template/src/common/utils/serializeQueryParams.ts +10 -0
  126. package/template/src/common/validations/authValidations.ts +15 -0
  127. package/template/src/common/validations/commonValidations.ts +39 -0
  128. package/template/src/common/validations/errorValidations.ts +72 -0
  129. package/template/src/common/validations/hooks/useDatesError.ts +40 -0
  130. package/template/src/common/validations/hooks/useInputError.ts +30 -0
  131. package/template/src/common/validations/profileValidations.ts +30 -0
  132. package/template/src/common/validations/validationConstants.ts +20 -0
  133. package/template/src/core/api/responseHandlers.ts +43 -0
  134. package/template/src/core/api/serverHeaders.ts +39 -0
  135. package/template/src/core/store/app/appSlice.ts +12 -0
  136. package/template/src/core/store/app/appState.ts +3 -0
  137. package/template/src/core/store/reduxHelpers.ts +11 -0
  138. package/template/src/core/store/rootReducer.ts +10 -0
  139. package/template/src/core/store/store.tsx +41 -0
  140. package/template/src/core/store/user/userActions.ts +31 -0
  141. package/template/src/core/store/user/userSlice.ts +62 -0
  142. package/template/src/core/store/user/userState.ts +44 -0
  143. package/template/src/core/theme/colors.ts +117 -0
  144. package/template/src/core/theme/commonConsts.ts +45 -0
  145. package/template/src/core/theme/commonSizes.ts +70 -0
  146. package/template/src/core/theme/commonStyles.ts +228 -0
  147. package/template/src/core/theme/fonts.ts +12 -0
  148. package/template/src/navigation/AuthStack.tsx +39 -0
  149. package/template/src/navigation/HeaderComponents.tsx +104 -0
  150. package/template/src/navigation/MainNavigation.tsx +55 -0
  151. package/template/src/navigation/MainStack.tsx +99 -0
  152. package/template/src/navigation/RootNavigation.tsx +33 -0
  153. package/template/src/navigation/TabBar.tsx +94 -0
  154. package/template/src/navigation/TopTabBar.tsx +75 -0
  155. package/template/src/navigation/types.ts +5 -0
  156. package/template/src/screens/Login/Login.tsx +114 -0
  157. package/template/src/screens/Settings/Settings.tsx +5 -0
  158. package/template/src/screens/main/Main.tsx +5 -0
  159. package/template/src/screens/profile/Profile.tsx +5 -0
  160. package/template/src/screens/splash/Splash.tsx +19 -0
  161. package/template/src/sheetManager/sheets.tsx +14 -0
  162. package/template/tsconfig.json +3 -0
  163. package/template/types/index.ts +108 -0
  164. package/template/types/react-native-config.d.ts +19 -0
  165. package/template.config.js +4 -0
@@ -0,0 +1,14 @@
1
+ /* eslint-disable */
2
+ /* tslint:disable */
3
+ import {ImageURISource} from "react-native";
4
+
5
+ /**
6
+ * This file is auto-generated by react-native-image-resource-generator
7
+ * !!! DO NOT EDIT !!!
8
+ * For more information check the documentation:
9
+ * https://github.com/svbutko/react-native-image-resource-generator
10
+ */
11
+
12
+ export class ImageResources {
13
+
14
+ }
@@ -0,0 +1,34 @@
1
+ import React from 'react';
2
+ import {ImageBackground, StyleSheet, ViewStyle} from 'react-native';
3
+ import {CommonStyles} from '../../core/theme/commonStyles';
4
+ import {ImageResources} from '../ImageResources.g';
5
+
6
+ interface BackgroundType {
7
+ children: React.ReactNode;
8
+ inverted?: boolean;
9
+ extraStyle?: ViewStyle;
10
+ }
11
+
12
+ export const Background = ({
13
+ children,
14
+ inverted,
15
+ extraStyle,
16
+ }: BackgroundType) => {
17
+ const styles = StyleSheet.create({
18
+ imageContainer: {
19
+ ...CommonStyles.flex1,
20
+ flexGrow: 1,
21
+ // justifyContent: 'flex-end',
22
+ },
23
+ });
24
+
25
+ return (
26
+ <ImageBackground
27
+ resizeMode="cover"
28
+ style={{...styles.imageContainer, ...extraStyle}}
29
+ imageStyle={inverted ? {transform: [{rotateY: '180deg'}]} : undefined}
30
+ source={0}>
31
+ {children}
32
+ </ImageBackground>
33
+ );
34
+ };
@@ -0,0 +1,31 @@
1
+ import React, {FC, memo} from 'react';
2
+ import {StyleSheet, Text, TextStyle, View} from 'react-native';
3
+ import {CommonSizes} from '../../core/theme/commonSizes';
4
+ import {CommonStyles} from '../../core/theme/commonStyles';
5
+
6
+ interface IProps {
7
+ title: string;
8
+ description: string;
9
+ }
10
+
11
+ export const EmptyView: FC<IProps> = memo(({title, description}) => {
12
+ return (
13
+ <View style={CommonStyles.flexCenter}>
14
+ <Text style={styles.title}>{title}</Text>
15
+ <Text style={styles.description}>{description}</Text>
16
+ </View>
17
+ );
18
+ });
19
+
20
+ const styles = StyleSheet.create({
21
+ title: {
22
+ ...CommonStyles.normalText,
23
+ fontWeight: '600',
24
+ textAlign: 'center',
25
+ marginBottom: CommonSizes.spacing.extraSmall,
26
+ } as TextStyle,
27
+ description: {
28
+ ...CommonStyles.normalText,
29
+ textAlign: 'center',
30
+ } as TextStyle,
31
+ });
@@ -0,0 +1,66 @@
1
+ import React, {FC, useMemo} from 'react';
2
+ import {FlatList, FlatListProps} from 'react-native';
3
+ import {LoadState} from '../../../types';
4
+ import {TryAgain} from './TryAgain';
5
+ import {Separator} from './Separator';
6
+ import {EmptyView} from './EmptyView';
7
+ import {LoadingComponent} from './LoadingComponent';
8
+ import {localization} from '../localization/localization';
9
+ import {defaultKeyIdExtractor} from '../helpers/defaultKeyIdExtractor';
10
+ import {CommonStyles} from '../../core/theme/commonStyles';
11
+
12
+ interface IProps extends FlatListProps<any> {
13
+ loadState: LoadState;
14
+ tryAgain?: () => void;
15
+ error?: string | null;
16
+ }
17
+
18
+ const FlatListWrapperProps = {
19
+ keyExtractor: defaultKeyIdExtractor,
20
+ ListEmptyComponent: (
21
+ <EmptyView
22
+ title={localization.empty.noData}
23
+ description={localization.empty.checkThisPageLater}
24
+ />
25
+ ),
26
+ onEndReachedThreshold: 1,
27
+ ItemSeparatorComponent: Separator,
28
+ };
29
+
30
+ export function FlatListWrapper({
31
+ loadState,
32
+ tryAgain,
33
+ error,
34
+ ...props
35
+ }: IProps) {
36
+ const ListEmptyComponent = useMemo(() => {
37
+ if (loadState == LoadState.error) {
38
+ return (
39
+ <TryAgain
40
+ onPress={tryAgain}
41
+ errorText={error || localization.errors.listErrorTitle}
42
+ />
43
+ );
44
+ } else {
45
+ return props.ListEmptyComponent;
46
+ }
47
+ }, [loadState, props.ListEmptyComponent, error, tryAgain]);
48
+
49
+ const refreshing = useMemo(() => {
50
+ return loadState == LoadState.pullToRefresh;
51
+ }, [loadState]);
52
+
53
+ if (loadState == LoadState.firstLoad) {
54
+ return <LoadingComponent />;
55
+ } else {
56
+ return (
57
+ <FlatList
58
+ contentContainerStyle={CommonStyles.listContentContainer}
59
+ {...FlatListWrapperProps}
60
+ {...props}
61
+ refreshing={refreshing}
62
+ ListEmptyComponent={ListEmptyComponent}
63
+ />
64
+ );
65
+ }
66
+ }
@@ -0,0 +1,17 @@
1
+ import React, {FC, memo} from 'react';
2
+ import {isAndroid, isIos} from '../../core/theme/commonConsts';
3
+ import Icon from 'react-native-vector-icons/MaterialIcons';
4
+ import {SFSymbol} from 'react-native-sfsymbols';
5
+ import {IIconPlatformProps} from '../../../types';
6
+
7
+ export const IconPlatform: FC<IIconPlatformProps> = memo(
8
+ ({iosName, androidName, ...props}) => {
9
+ if (isIos && iosName) {
10
+ return <SFSymbol {...props} name={iosName} />;
11
+ } else if (isAndroid && androidName) {
12
+ return <Icon {...props} name={androidName} />;
13
+ } else {
14
+ return null;
15
+ }
16
+ },
17
+ );
@@ -0,0 +1,108 @@
1
+ import React, {FC, memo, useCallback} from 'react';
2
+ import ImagePicker, {
3
+ Image as CropperImage,
4
+ Options,
5
+ } from 'react-native-image-crop-picker';
6
+ import {ImageStyle, ImageURISource, ViewStyle} from 'react-native';
7
+ import {ImageResources} from '../ImageResources.g';
8
+ import {PhotoTakingButton} from './PhotoTakingButton';
9
+ import {localization} from '../localization/localization';
10
+ import {showActionSheet} from '../helpers/dialogsHelpers';
11
+
12
+ interface IProps {
13
+ onImagePicked: (image: CropperImage) => void;
14
+ onRemoveImage: () => void;
15
+ onPickerError?: (error: Error) => void;
16
+ image?: ImageURISource | null;
17
+ icon?: ImageURISource;
18
+ style?: ViewStyle;
19
+ iconStyle?: ImageStyle;
20
+ imageStyle?: ImageStyle;
21
+ disabled?: boolean;
22
+ }
23
+
24
+ export const ImageCropPickerButton: FC<IProps> = memo(
25
+ ({
26
+ onPickerError,
27
+ onImagePicked,
28
+ onRemoveImage,
29
+ image,
30
+ icon = ImageResources.camera,
31
+ style,
32
+ iconStyle,
33
+ imageStyle,
34
+ disabled,
35
+ }) => {
36
+ const openGallery = useCallback(() => {
37
+ ImagePicker.openPicker(pickerOptions)
38
+ .then(imageResult => {
39
+ onImagePicked(imageResult);
40
+ })
41
+ .catch(error => {
42
+ onPickerError && onPickerError(error);
43
+ });
44
+ }, [onImagePicked, onPickerError]);
45
+
46
+ const openCamera = useCallback(() => {
47
+ ImagePicker.openCamera(pickerOptions)
48
+ .then(imageResult => {
49
+ onImagePicked(imageResult);
50
+ })
51
+ .catch(error => {
52
+ onPickerError && onPickerError(error);
53
+ });
54
+ }, [onImagePicked, onPickerError]);
55
+
56
+ const onPress = useCallback(() => {
57
+ const options = [
58
+ localization.common.cancel,
59
+ localization.common.chooseFromLibrary,
60
+ localization.common.takePhoto,
61
+ ];
62
+
63
+ if (image != null) {
64
+ options.push(localization.common.delete);
65
+ }
66
+
67
+ showActionSheet(
68
+ {
69
+ title: localization.common.selectPhoto,
70
+ options: options,
71
+ cancelButtonIndex: 0,
72
+ destructiveButtonIndex: 3,
73
+ },
74
+ optionIndex => {
75
+ if (optionIndex == 1) {
76
+ openGallery();
77
+ } else if (optionIndex == 2) {
78
+ openCamera();
79
+ } else if (optionIndex == 3) {
80
+ onRemoveImage();
81
+ }
82
+ },
83
+ );
84
+ }, [openGallery, openCamera, image, onRemoveImage]);
85
+
86
+ return (
87
+ <PhotoTakingButton
88
+ style={style}
89
+ iconStyle={iconStyle}
90
+ imageStyle={imageStyle}
91
+ onPress={onPress}
92
+ backgroundImage={image}
93
+ icon={icon}
94
+ disabled={disabled}
95
+ />
96
+ );
97
+ },
98
+ );
99
+
100
+ const pickerOptions: Options = {
101
+ height: 1000,
102
+ width: 1000,
103
+ cropping: true,
104
+ compressImageQuality: 0.5,
105
+ multiple: false,
106
+ mediaType: 'photo',
107
+ includeBase64: true,
108
+ };
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import {Image, View} from 'react-native';
3
+ import {CommonStyles} from '../../core/theme/commonStyles';
4
+ import {ImageResources} from '../ImageResources.g';
5
+
6
+ export const LoadingComponent = () => {
7
+ return (
8
+ <View style={CommonStyles.flexCenter}>
9
+ <Image
10
+ resizeMode='cover'
11
+ style={{flex: 1}}
12
+ source={ImageResources.splash}
13
+ />
14
+ </View>
15
+ );
16
+ };
@@ -0,0 +1,99 @@
1
+ import React, {FC, Fragment, memo, ReactNode} from 'react';
2
+ import {
3
+ Image,
4
+ ImageBackground,
5
+ ImageStyle,
6
+ ImageURISource,
7
+ StyleSheet,
8
+ TouchableOpacity,
9
+ ViewStyle,
10
+ } from 'react-native';
11
+ import {ImageResources} from '../ImageResources.g';
12
+ import {Image as CropperImage} from 'react-native-image-crop-picker';
13
+ import {CommonSizes} from '../../core/theme/commonSizes';
14
+ import {Colors, PlatformColorsIOS} from '../../core/theme/colors';
15
+ import {platformMixedColor} from '../helpers/colorHelpers';
16
+
17
+ interface IProps {
18
+ onPress?: () => void;
19
+ icon?: ImageURISource;
20
+ backgroundImage?: ImageURISource | CropperImage | null;
21
+ style?: ViewStyle;
22
+ iconStyle?: ImageStyle;
23
+ imageStyle?: ImageStyle;
24
+ disabled?: boolean;
25
+ }
26
+
27
+ export const PhotoTakingButton: FC<IProps> = memo(
28
+ ({
29
+ icon = ImageResources.camera,
30
+ onPress,
31
+ backgroundImage,
32
+ style,
33
+ iconStyle,
34
+ imageStyle,
35
+ disabled,
36
+ }) => {
37
+ return (
38
+ <TouchableOpacity
39
+ style={[styles.button, style] as ViewStyle}
40
+ onPress={onPress}
41
+ disabled={disabled}>
42
+ <BackgroundComponent image={backgroundImage} style={imageStyle}>
43
+ <Image style={[styles.icon, iconStyle]} source={icon!} />
44
+ </BackgroundComponent>
45
+ </TouchableOpacity>
46
+ );
47
+ },
48
+ );
49
+
50
+ interface IBackgroundComponentProps {
51
+ image?: ImageURISource | null;
52
+ style?: ImageStyle;
53
+ children?: ReactNode;
54
+ }
55
+
56
+ const BackgroundComponent: FC<IBackgroundComponentProps> = memo(
57
+ ({image, children, style}) => {
58
+ if (image != null) {
59
+ return (
60
+ <ImageBackground
61
+ source={image}
62
+ style={[styles.image, style]}
63
+ imageStyle={[styles.image, style]}>
64
+ {children}
65
+ </ImageBackground>
66
+ );
67
+ } else {
68
+ return <Fragment>{children}</Fragment>;
69
+ }
70
+ },
71
+ );
72
+
73
+ const styles = StyleSheet.create({
74
+ button: {
75
+ height: 120,
76
+ width: 120,
77
+ alignItems: 'center',
78
+ justifyContent: 'center',
79
+ borderRadius: CommonSizes.borderRadius.small,
80
+ borderWidth: 1,
81
+ borderColor: platformMixedColor(PlatformColorsIOS.systemFill, Colors.gray),
82
+ } as ViewStyle,
83
+ image: {
84
+ height: 120,
85
+ width: 120,
86
+ resizeMode: 'contain',
87
+ alignItems: 'center',
88
+ justifyContent: 'center',
89
+ borderRadius: CommonSizes.borderRadius.small,
90
+ overflow: 'hidden',
91
+ } as ImageStyle,
92
+ icon: {
93
+ width: 32,
94
+ height: 32,
95
+ resizeMode: 'contain',
96
+ tintColor: platformMixedColor(PlatformColorsIOS.label, Colors.black),
97
+ opacity: 0.8,
98
+ } as ImageStyle,
99
+ });
@@ -0,0 +1,305 @@
1
+ import React, {FC, memo, useMemo} from 'react';
2
+ import {
3
+ ActivityIndicator,
4
+ Image,
5
+ ImageStyle,
6
+ ImageURISource,
7
+ Platform,
8
+ StyleProp,
9
+ StyleSheet,
10
+ Text,
11
+ TextStyle,
12
+ ViewStyle,
13
+ } from 'react-native';
14
+ import {
15
+ ButtonType,
16
+ IIconPlatformProps,
17
+ TouchablePlatformProps,
18
+ } from '../../../types';
19
+ import {Colors} from '../../core/theme/colors';
20
+ import {CommonSizes} from '../../core/theme/commonSizes';
21
+ import {CommonStyles} from '../../core/theme/commonStyles';
22
+ import {IconPlatform} from './IconPlatform';
23
+ import {TouchablePlatform} from './TouchablePlatform';
24
+
25
+ interface IProps extends TouchablePlatformProps {
26
+ label: string;
27
+ type: ButtonType;
28
+ rounded?: boolean;
29
+ icon?: ImageURISource;
30
+ iconStyle?: StyleProp<ImageStyle>;
31
+ platformIconProps?: IIconPlatformProps;
32
+ labelStyle?: TextStyle;
33
+ isLoading?: boolean;
34
+ }
35
+
36
+ export const PrimaryButton: FC<IProps> = memo(
37
+ ({
38
+ label,
39
+ icon,
40
+ iconStyle,
41
+ type,
42
+ rounded,
43
+ labelStyle,
44
+ style,
45
+ isLoading,
46
+ platformIconProps,
47
+ ...props
48
+ }) => {
49
+ const styles = useMemo(() => {
50
+ return getStyles(type, rounded, props.disabled);
51
+ }, [type, rounded, props.disabled]);
52
+
53
+ const content = useMemo(() => {
54
+ if (isLoading) {
55
+ return (
56
+ <ActivityIndicator
57
+ animating={true}
58
+ color={activityIndicatorColor}
59
+ size={'small'}
60
+ />
61
+ );
62
+ } else {
63
+ return (
64
+ <>
65
+ <ButtonIcon
66
+ icon={icon}
67
+ iconStyle={[styles.icon, iconStyle]}
68
+ platformIconProps={platformIconProps}
69
+ />
70
+ <Text style={[styles.label, labelStyle]} numberOfLines={1}>
71
+ {label}
72
+ </Text>
73
+ </>
74
+ );
75
+ }
76
+ }, [
77
+ icon,
78
+ iconStyle,
79
+ isLoading,
80
+ label,
81
+ labelStyle,
82
+ platformIconProps,
83
+ styles.icon,
84
+ styles.label,
85
+ ]);
86
+
87
+ return (
88
+ <TouchablePlatform
89
+ style={[styles.button, style] as ViewStyle[]}
90
+ highlightColor={'rgba(0,0,0,0.05)'}
91
+ {...props}>
92
+ {content}
93
+ </TouchablePlatform>
94
+ );
95
+ },
96
+ );
97
+
98
+ const activityIndicatorColor = Colors.gray_disabled;
99
+
100
+ const ButtonIcon: FC<Pick<IProps, 'icon' | 'iconStyle' | 'platformIconProps'>> =
101
+ memo(props => {
102
+ if (props.icon != null) {
103
+ return <Image source={props.icon} style={props.iconStyle} />;
104
+ } else if (props.platformIconProps != null) {
105
+ return <IconPlatform {...props.platformIconProps} />;
106
+ } else {
107
+ return null;
108
+ }
109
+ });
110
+
111
+ function getStyles(
112
+ type: ButtonType,
113
+ rounded?: boolean,
114
+ disabled?: boolean | null,
115
+ ): IStyles {
116
+ switch (type) {
117
+ case ButtonType.solid:
118
+ return mergeStylesWithDisabled(
119
+ rounded ? smallSolidStyles : solidStyles,
120
+ disabled,
121
+ );
122
+ case ButtonType.outline:
123
+ return mergeStylesWithDisabled(
124
+ rounded ? smallOutlineStyles : outlineStyles,
125
+ disabled,
126
+ true,
127
+ );
128
+ case ButtonType.outlineNegative:
129
+ return mergeStylesWithDisabled(
130
+ rounded ? smallOutlineStyles : outlineNegativeStyles,
131
+ disabled,
132
+ true,
133
+ );
134
+ case ButtonType.borderless:
135
+ return borderlessStyles;
136
+ default:
137
+ throw new Error('Unknown button type: ' + type);
138
+ }
139
+ }
140
+
141
+ function mergeStylesWithDisabled(
142
+ styles: IStyles,
143
+ disabled?: boolean | null,
144
+ outline?: boolean,
145
+ ): IStyles {
146
+ return disabled
147
+ ? {
148
+ ...styles,
149
+ button: {
150
+ ...styles.button,
151
+ backgroundColor: Colors.gray_disabled,
152
+ borderColor: outline ? Colors.teal100 : styles.button.borderColor,
153
+ elevation: 0,
154
+ } as ViewStyle,
155
+ icon: {
156
+ ...styles.icon,
157
+ tintColor: Colors.gray,
158
+ } as ImageStyle,
159
+ label: {
160
+ ...styles.label,
161
+ color: Colors.black,
162
+ } as TextStyle,
163
+ }
164
+ : styles;
165
+ }
166
+
167
+ interface IStyles {
168
+ button: ViewStyle;
169
+ icon: ImageStyle;
170
+ label: TextStyle;
171
+ }
172
+
173
+ const commonButtonStyle: ViewStyle = {
174
+ padding: CommonSizes.spacing.medium,
175
+ alignItems: 'center',
176
+ justifyContent: 'center',
177
+ borderRadius: CommonSizes.borderRadius.extraLargePlus,
178
+ flexDirection: 'row',
179
+ backgroundColor: Colors.transparent,
180
+ width: '100%',
181
+ };
182
+
183
+ const commonLabelStyle: TextStyle = {
184
+ ...CommonStyles.h4_bold,
185
+ color: Colors.white,
186
+ textAlign: 'center',
187
+ textAlignVertical: 'center',
188
+ ...Platform.select({
189
+ android: {
190
+ textTransform: 'uppercase',
191
+ } as TextStyle,
192
+ }),
193
+ };
194
+
195
+ const commonIcon: ImageStyle = {
196
+ width: 22,
197
+ height: 22,
198
+ marginHorizontal: CommonSizes.spacing.extraSmall,
199
+ resizeMode: 'contain',
200
+ tintColor: Colors.blue100,
201
+ };
202
+
203
+ const solidStyles = StyleSheet.create({
204
+ button: {
205
+ ...commonButtonStyle,
206
+ backgroundColor: Colors.primary100,
207
+ } as ViewStyle,
208
+ label: {
209
+ ...commonLabelStyle,
210
+ } as TextStyle,
211
+ icon: {
212
+ ...commonIcon,
213
+ tintColor: Colors.white,
214
+ },
215
+ });
216
+
217
+ const outlineStyles = StyleSheet.create({
218
+ button: {
219
+ ...commonButtonStyle,
220
+ borderColor: Colors.primary100,
221
+ borderWidth: 2,
222
+ } as ViewStyle,
223
+ label: {
224
+ ...commonLabelStyle,
225
+ color: Colors.primary100,
226
+ } as TextStyle,
227
+ icon: {
228
+ ...commonIcon,
229
+ tintColor: Colors.blue100,
230
+ } as ImageStyle,
231
+ });
232
+
233
+ const outlineNegativeStyles = StyleSheet.create({
234
+ button: {
235
+ ...commonButtonStyle,
236
+ borderColor: Colors.red,
237
+ borderWidth: 2,
238
+ } as ViewStyle,
239
+ label: {
240
+ ...commonLabelStyle,
241
+ color: Colors.red,
242
+ } as TextStyle,
243
+ icon: {
244
+ ...commonIcon,
245
+ tintColor: Colors.red,
246
+ } as ImageStyle,
247
+ });
248
+
249
+ const borderlessStyles = StyleSheet.create({
250
+ button: {
251
+ ...commonButtonStyle,
252
+ borderRadius: undefined,
253
+ width: undefined,
254
+ padding: undefined,
255
+ } as ViewStyle,
256
+ label: {
257
+ ...commonLabelStyle,
258
+ color: Colors.primary100,
259
+ textDecorationLine: 'underline',
260
+ } as TextStyle,
261
+ icon: {
262
+ ...commonIcon,
263
+ tintColor: Colors.blue100,
264
+ } as ImageStyle,
265
+ });
266
+
267
+ const roundedButtonStyle: ViewStyle = {
268
+ paddingHorizontal: CommonSizes.spacing.medium,
269
+ paddingVertical: CommonSizes.spacing.extraSmall,
270
+ alignItems: 'center',
271
+ justifyContent: 'center',
272
+ borderRadius: CommonSizes.borderRadius.extraLarge,
273
+ flexDirection: 'row',
274
+ backgroundColor: Colors.transparent,
275
+ width: 175,
276
+ };
277
+
278
+ const smallSolidStyles = StyleSheet.create({
279
+ button: {
280
+ ...roundedButtonStyle,
281
+ backgroundColor: Colors.primary100,
282
+ } as ViewStyle,
283
+ label: {
284
+ ...CommonStyles.normalText,
285
+ } as TextStyle,
286
+ icon: {
287
+ ...commonIcon,
288
+ },
289
+ });
290
+
291
+ const smallOutlineStyles = StyleSheet.create({
292
+ button: {
293
+ ...roundedButtonStyle,
294
+ borderColor: Colors.blue100,
295
+ borderWidth: 1,
296
+ } as ViewStyle,
297
+ label: {
298
+ ...CommonStyles.normalText,
299
+ color: Colors.blue100,
300
+ } as TextStyle,
301
+ icon: {
302
+ ...commonIcon,
303
+ tintColor: Colors.blue100,
304
+ } as ImageStyle,
305
+ });