@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.
- package/.vscode/settings.json +7 -0
- package/README.md +269 -0
- package/package.json +36 -0
- package/template/.bundle/config +2 -0
- package/template/.env.development +5 -0
- package/template/.env.production +5 -0
- package/template/.env.staging +5 -0
- package/template/.eslintrc.js +4 -0
- package/template/.prettierrc.js +7 -0
- package/template/.watchmanconfig +1 -0
- package/template/App.tsx +34 -0
- package/template/Gemfile +9 -0
- package/template/Gemfile.lock +117 -0
- package/template/README.md +79 -0
- package/template/__tests__/App.test.tsx +17 -0
- package/template/android/app/build.gradle +128 -0
- package/template/android/app/debug.keystore +0 -0
- package/template/android/app/proguard-rules.pro +10 -0
- package/template/android/app/src/debug/AndroidManifest.xml +9 -0
- package/template/android/app/src/main/AndroidManifest.xml +26 -0
- package/template/android/app/src/main/java/com/reactnativemagic/MainActivity.kt +22 -0
- package/template/android/app/src/main/java/com/reactnativemagic/MainApplication.kt +44 -0
- package/template/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
- package/template/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/template/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/template/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/template/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/template/android/app/src/main/res/values/strings.xml +3 -0
- package/template/android/app/src/main/res/values/styles.xml +9 -0
- package/template/android/build.gradle +21 -0
- package/template/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/template/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/template/android/gradle.properties +39 -0
- package/template/android/gradlew +252 -0
- package/template/android/gradlew.bat +94 -0
- package/template/android/settings.gradle +6 -0
- package/template/app.json +4 -0
- package/template/babel.config.js +3 -0
- package/template/index.js +9 -0
- package/template/install-dev.sh +1 -0
- package/template/install.sh +1 -0
- package/template/ios/.xcode.env +11 -0
- package/template/ios/Podfile +42 -0
- package/template/ios/Podfile.lock +2461 -0
- package/template/ios/reactnativemagic/AppDelegate.h +6 -0
- package/template/ios/reactnativemagic/AppDelegate.mm +31 -0
- package/template/ios/reactnativemagic/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
- package/template/ios/reactnativemagic/Images.xcassets/Contents.json +6 -0
- package/template/ios/reactnativemagic/Info.plist +52 -0
- package/template/ios/reactnativemagic/LaunchScreen.storyboard +47 -0
- package/template/ios/reactnativemagic/PrivacyInfo.xcprivacy +46 -0
- package/template/ios/reactnativemagic/main.m +10 -0
- package/template/ios/reactnativemagic copy-Info.plist +52 -0
- package/template/ios/reactnativemagic.xcodeproj/project.pbxproj +836 -0
- package/template/ios/reactnativemagic.xcodeproj/xcshareddata/xcschemes/reactnativemagic.xcscheme +88 -0
- package/template/ios/reactnativemagic.xcworkspace/contents.xcworkspacedata +10 -0
- package/template/ios/reactnativemagic.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
- package/template/ios/reactnativemagicTests/Info.plist +24 -0
- package/template/ios/reactnativemagicTests/reactnativemagicTests.m +66 -0
- package/template/ios/tmp.xcconfig +2 -0
- package/template/jest.config.js +3 -0
- package/template/metro.config.js +11 -0
- package/template/package-lock.json +18315 -0
- package/template/package.json +125 -0
- package/template/resources/symbols/SFSymbols.ts +2614 -0
- package/template/src/common/ImageResources.g.ts +14 -0
- package/template/src/common/components/Background.tsx +34 -0
- package/template/src/common/components/EmptyView.tsx +31 -0
- package/template/src/common/components/FlatListWrapper.tsx +66 -0
- package/template/src/common/components/IconPlatform.tsx +17 -0
- package/template/src/common/components/ImageCropPickerButton.tsx +108 -0
- package/template/src/common/components/LoadingComponent.tsx +16 -0
- package/template/src/common/components/PhotoTakingButton.tsx +99 -0
- package/template/src/common/components/PrimaryButton.tsx +305 -0
- package/template/src/common/components/PrimaryTextInput.tsx +287 -0
- package/template/src/common/components/RadioButton.tsx +73 -0
- package/template/src/common/components/RadioIcon.tsx +63 -0
- package/template/src/common/components/Separator.tsx +39 -0
- package/template/src/common/components/Svg.tsx +25 -0
- package/template/src/common/components/TouchablePlatform.tsx +70 -0
- package/template/src/common/components/TryAgain.tsx +56 -0
- package/template/src/common/helpers/arrayHelpers.ts +29 -0
- package/template/src/common/helpers/calculatePage.ts +16 -0
- package/template/src/common/helpers/colorHelpers.ts +34 -0
- package/template/src/common/helpers/defaultKeyIdExtractor.ts +5 -0
- package/template/src/common/helpers/dialogsHelpers.ts +66 -0
- package/template/src/common/helpers/imageHelpers.ts +5 -0
- package/template/src/common/helpers/inAppReviewHelper.ts +31 -0
- package/template/src/common/helpers/netInfoHelpers.ts +42 -0
- package/template/src/common/helpers/orientationHelpers.ts +25 -0
- package/template/src/common/helpers/regexHelpers.ts +7 -0
- package/template/src/common/helpers/shareHelpers.ts +47 -0
- package/template/src/common/helpers/stringsHelpers.ts +15 -0
- package/template/src/common/hooks/useBackHandler.ts +10 -0
- package/template/src/common/hooks/useDebounce.ts +17 -0
- package/template/src/common/hooks/useEventRegister.ts +50 -0
- package/template/src/common/hooks/useFlatListActions.ts +31 -0
- package/template/src/common/hooks/usePrevious.ts +11 -0
- package/template/src/common/hooks/useWhyDidYouUpdate.ts +27 -0
- package/template/src/common/localization/dateFormatter.ts +108 -0
- package/template/src/common/localization/intlFormatter.ts +37 -0
- package/template/src/common/localization/localization.ts +51 -0
- package/template/src/common/localization/translations/commonLocalization.ts +29 -0
- package/template/src/common/localization/translations/emptyLocalization.ts +6 -0
- package/template/src/common/localization/translations/errorsLocalization.ts +22 -0
- package/template/src/common/localization/translations/homeLocalization.ts +5 -0
- package/template/src/common/localization/translations/loginLocalization.ts +14 -0
- package/template/src/common/localization/translations/onboardingLocalization.ts +13 -0
- package/template/src/common/localization/translations/pagesLocalization.ts +14 -0
- package/template/src/common/localization/translations/profileLocalization.ts +6 -0
- package/template/src/common/urls/baseUrlOpener.ts +31 -0
- package/template/src/common/urls/emailUrl.ts +20 -0
- package/template/src/common/urls/httpUrl.ts +19 -0
- package/template/src/common/urls/mapUrl.ts +22 -0
- package/template/src/common/urls/phoneUrl.ts +16 -0
- package/template/src/common/utils/createPerfectSize.ts +15 -0
- package/template/src/common/utils/listHandlers.ts +30 -0
- package/template/src/common/utils/newState.ts +5 -0
- package/template/src/common/utils/serializeQueryParams.ts +10 -0
- package/template/src/common/validations/authValidations.ts +15 -0
- package/template/src/common/validations/commonValidations.ts +39 -0
- package/template/src/common/validations/errorValidations.ts +72 -0
- package/template/src/common/validations/hooks/useDatesError.ts +40 -0
- package/template/src/common/validations/hooks/useInputError.ts +30 -0
- package/template/src/common/validations/profileValidations.ts +30 -0
- package/template/src/common/validations/validationConstants.ts +20 -0
- package/template/src/core/api/responseHandlers.ts +43 -0
- package/template/src/core/api/serverHeaders.ts +39 -0
- package/template/src/core/store/app/appSlice.ts +12 -0
- package/template/src/core/store/app/appState.ts +3 -0
- package/template/src/core/store/reduxHelpers.ts +11 -0
- package/template/src/core/store/rootReducer.ts +10 -0
- package/template/src/core/store/store.tsx +41 -0
- package/template/src/core/store/user/userActions.ts +31 -0
- package/template/src/core/store/user/userSlice.ts +62 -0
- package/template/src/core/store/user/userState.ts +44 -0
- package/template/src/core/theme/colors.ts +117 -0
- package/template/src/core/theme/commonConsts.ts +45 -0
- package/template/src/core/theme/commonSizes.ts +70 -0
- package/template/src/core/theme/commonStyles.ts +228 -0
- package/template/src/core/theme/fonts.ts +12 -0
- package/template/src/navigation/AuthStack.tsx +39 -0
- package/template/src/navigation/HeaderComponents.tsx +104 -0
- package/template/src/navigation/MainNavigation.tsx +55 -0
- package/template/src/navigation/MainStack.tsx +99 -0
- package/template/src/navigation/RootNavigation.tsx +33 -0
- package/template/src/navigation/TabBar.tsx +94 -0
- package/template/src/navigation/TopTabBar.tsx +75 -0
- package/template/src/navigation/types.ts +5 -0
- package/template/src/screens/Login/Login.tsx +114 -0
- package/template/src/screens/Settings/Settings.tsx +5 -0
- package/template/src/screens/main/Main.tsx +5 -0
- package/template/src/screens/profile/Profile.tsx +5 -0
- package/template/src/screens/splash/Splash.tsx +19 -0
- package/template/src/sheetManager/sheets.tsx +14 -0
- package/template/tsconfig.json +3 -0
- package/template/types/index.ts +108 -0
- package/template/types/react-native-config.d.ts +19 -0
- package/template.config.js +4 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {View, Text, TouchableOpacity, Image, StyleSheet} from 'react-native';
|
|
3
|
+
import {CommonStyles, screenWidth} from '../core/theme/commonStyles';
|
|
4
|
+
import {BottomTabHeaderProps} from '@react-navigation/bottom-tabs';
|
|
5
|
+
import {ImageResources} from '../common/ImageResources.g';
|
|
6
|
+
import {CommonSizes} from '../core/theme/commonSizes';
|
|
7
|
+
import {NativeStackNavigationProp} from '@react-navigation/native-stack';
|
|
8
|
+
import {RootStackParamList} from './types';
|
|
9
|
+
|
|
10
|
+
export function Header({title}: {title: string}) {
|
|
11
|
+
return (
|
|
12
|
+
<View style={styles.headerBase}>
|
|
13
|
+
<Text style={CommonStyles.h2_regular}>{title}</Text>
|
|
14
|
+
</View>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function HeaderBack({
|
|
19
|
+
title,
|
|
20
|
+
navigation,
|
|
21
|
+
}: {
|
|
22
|
+
title: string;
|
|
23
|
+
navigation: NativeStackNavigationProp<RootStackParamList>;
|
|
24
|
+
}) {
|
|
25
|
+
return (
|
|
26
|
+
<View style={styles.headerWithBack}>
|
|
27
|
+
<View style={styles.titleContainer}>
|
|
28
|
+
<Text style={CommonStyles.h2_regular}>{title}</Text>
|
|
29
|
+
</View>
|
|
30
|
+
<BackButton navigation={navigation} />
|
|
31
|
+
</View>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function WebViewHeader({
|
|
36
|
+
navigation,
|
|
37
|
+
}: {
|
|
38
|
+
navigation: NativeStackNavigationProp<RootStackParamList>;
|
|
39
|
+
}) {
|
|
40
|
+
return (
|
|
41
|
+
<View style={styles.webViewHeader}>
|
|
42
|
+
<BackButton navigation={navigation} />
|
|
43
|
+
</View>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function BackButton({
|
|
48
|
+
navigation,
|
|
49
|
+
}: {
|
|
50
|
+
navigation: NativeStackNavigationProp<RootStackParamList>;
|
|
51
|
+
}) {
|
|
52
|
+
return (
|
|
53
|
+
<TouchableOpacity
|
|
54
|
+
style={styles.backButton}
|
|
55
|
+
onPress={() => navigation.pop()}>
|
|
56
|
+
<Image style={styles.backIcon} source={0} />
|
|
57
|
+
</TouchableOpacity>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const styles = StyleSheet.create({
|
|
62
|
+
headerBase: {
|
|
63
|
+
width: '100%',
|
|
64
|
+
height: 80,
|
|
65
|
+
backgroundColor: 'transparent',
|
|
66
|
+
justifyContent: 'flex-end',
|
|
67
|
+
alignItems: 'center',
|
|
68
|
+
marginBottom: 25,
|
|
69
|
+
},
|
|
70
|
+
headerWithBack: {
|
|
71
|
+
width: '100%',
|
|
72
|
+
height: 80,
|
|
73
|
+
backgroundColor: 'transparent',
|
|
74
|
+
justifyContent: 'flex-start',
|
|
75
|
+
alignItems: 'center',
|
|
76
|
+
paddingHorizontal: CommonSizes.spacing.large,
|
|
77
|
+
flexDirection: 'row',
|
|
78
|
+
},
|
|
79
|
+
webViewHeader: {
|
|
80
|
+
width: '100%',
|
|
81
|
+
height: 120,
|
|
82
|
+
backgroundColor: 'white',
|
|
83
|
+
justifyContent: 'flex-start',
|
|
84
|
+
alignItems: 'center',
|
|
85
|
+
paddingHorizontal: CommonSizes.spacing.large,
|
|
86
|
+
flexDirection: 'row',
|
|
87
|
+
},
|
|
88
|
+
titleContainer: {
|
|
89
|
+
width: screenWidth,
|
|
90
|
+
alignItems: 'center',
|
|
91
|
+
position: 'absolute',
|
|
92
|
+
zIndex: 0,
|
|
93
|
+
},
|
|
94
|
+
backButton: {
|
|
95
|
+
width: 60,
|
|
96
|
+
height: 40,
|
|
97
|
+
justifyContent: 'flex-end',
|
|
98
|
+
},
|
|
99
|
+
backIcon: {
|
|
100
|
+
width: 20,
|
|
101
|
+
height: 20,
|
|
102
|
+
resizeMode: 'contain',
|
|
103
|
+
},
|
|
104
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// In App.js in a new project
|
|
2
|
+
|
|
3
|
+
import {NavigationContainer} from '@react-navigation/native';
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import {useRef} from 'react';
|
|
6
|
+
import {useAppSelector} from '../core/store/reduxHelpers';
|
|
7
|
+
import {AuthStack} from './AuthStack';
|
|
8
|
+
import {AppMainNavigator} from './MainStack';
|
|
9
|
+
import {navigationRef} from './RootNavigation';
|
|
10
|
+
|
|
11
|
+
function AppNavigator() {
|
|
12
|
+
const routeNameRef = useRef<string | undefined>(undefined);
|
|
13
|
+
const {accessToken} = useAppSelector(state => state.user);
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<NavigationContainer
|
|
17
|
+
ref={navigationRef}
|
|
18
|
+
theme={{
|
|
19
|
+
dark: false,
|
|
20
|
+
colors: {
|
|
21
|
+
primary: '#000',
|
|
22
|
+
background: 'transparent',
|
|
23
|
+
card: '#fff',
|
|
24
|
+
text: '#000',
|
|
25
|
+
border: '#000',
|
|
26
|
+
notification: '#ff0000',
|
|
27
|
+
},
|
|
28
|
+
fonts: {
|
|
29
|
+
regular: {
|
|
30
|
+
fontFamily: 'System',
|
|
31
|
+
fontWeight: 'normal',
|
|
32
|
+
},
|
|
33
|
+
medium: {
|
|
34
|
+
fontFamily: 'System',
|
|
35
|
+
fontWeight: '500',
|
|
36
|
+
},
|
|
37
|
+
bold: {
|
|
38
|
+
fontFamily: 'System',
|
|
39
|
+
fontWeight: '700',
|
|
40
|
+
},
|
|
41
|
+
heavy: {
|
|
42
|
+
fontFamily: 'System',
|
|
43
|
+
fontWeight: '900',
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
}}
|
|
47
|
+
onReady={() => {
|
|
48
|
+
routeNameRef.current = navigationRef.current?.getCurrentRoute()?.name;
|
|
49
|
+
}}>
|
|
50
|
+
{!accessToken ? <AuthStack /> : <AppMainNavigator />}
|
|
51
|
+
</NavigationContainer>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default AppNavigator;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
|
|
2
|
+
import {
|
|
3
|
+
createNativeStackNavigator,
|
|
4
|
+
NativeStackHeaderProps,
|
|
5
|
+
NativeStackNavigationProp,
|
|
6
|
+
} from '@react-navigation/native-stack';
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import {ImageResources} from '../common/ImageResources.g';
|
|
9
|
+
import {localization} from '../common/localization/localization';
|
|
10
|
+
import {Main} from '../screens/main/Main';
|
|
11
|
+
import {Profile} from '../screens/profile/Profile';
|
|
12
|
+
import {Settings} from '../screens/Settings/Settings';
|
|
13
|
+
import {Header, HeaderBack} from './HeaderComponents';
|
|
14
|
+
import {TabBar} from './TabBar';
|
|
15
|
+
import {RootStackParamList} from './types';
|
|
16
|
+
|
|
17
|
+
const MainScreens = [
|
|
18
|
+
{
|
|
19
|
+
id: 'Main',
|
|
20
|
+
component: Main,
|
|
21
|
+
options: {
|
|
22
|
+
headerShown: false,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 'Profile',
|
|
27
|
+
component: Profile,
|
|
28
|
+
options: {
|
|
29
|
+
tabBarLabel: 'Profile',
|
|
30
|
+
header: () => {
|
|
31
|
+
return <Header title={localization.profile.studentProfile} />;
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const AppStack = [
|
|
38
|
+
{
|
|
39
|
+
id: 'Home',
|
|
40
|
+
component: MainTabs,
|
|
41
|
+
options: {
|
|
42
|
+
headerShown: false,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: 'Settings',
|
|
47
|
+
component: Settings,
|
|
48
|
+
options: {
|
|
49
|
+
header: (props: NativeStackHeaderProps) => {
|
|
50
|
+
return (
|
|
51
|
+
<HeaderBack
|
|
52
|
+
title={'Settings'}
|
|
53
|
+
navigation={
|
|
54
|
+
props.navigation as NativeStackNavigationProp<RootStackParamList>
|
|
55
|
+
}
|
|
56
|
+
/>
|
|
57
|
+
);
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
const Stack = createNativeStackNavigator();
|
|
64
|
+
|
|
65
|
+
const Tab = createBottomTabNavigator();
|
|
66
|
+
|
|
67
|
+
function MainTabs() {
|
|
68
|
+
return (
|
|
69
|
+
<Tab.Navigator
|
|
70
|
+
initialRouteName="Main"
|
|
71
|
+
tabBar={props => <TabBar {...props} />}>
|
|
72
|
+
{MainScreens.map(s => (
|
|
73
|
+
<Tab.Screen
|
|
74
|
+
key={s.id}
|
|
75
|
+
name={s.id}
|
|
76
|
+
component={s.component}
|
|
77
|
+
options={s.options}
|
|
78
|
+
/>
|
|
79
|
+
))}
|
|
80
|
+
</Tab.Navigator>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function AppMainNavigator() {
|
|
85
|
+
return (
|
|
86
|
+
<Stack.Navigator screenOptions={{animation: 'none'}}>
|
|
87
|
+
{AppStack.map(s => {
|
|
88
|
+
return (
|
|
89
|
+
<Stack.Screen
|
|
90
|
+
key={s.id}
|
|
91
|
+
name={s.id}
|
|
92
|
+
component={s.component}
|
|
93
|
+
options={s.options}
|
|
94
|
+
/>
|
|
95
|
+
);
|
|
96
|
+
})}
|
|
97
|
+
</Stack.Navigator>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {createNavigationContainerRef} from '@react-navigation/native';
|
|
2
|
+
import {CommonActions} from '@react-navigation/native';
|
|
3
|
+
|
|
4
|
+
type RootStackParamList = {
|
|
5
|
+
Home: undefined;
|
|
6
|
+
// Add other screens here
|
|
7
|
+
Profile: {userId: string};
|
|
8
|
+
Settings: undefined;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const navigationRef = createNavigationContainerRef<RootStackParamList>();
|
|
12
|
+
|
|
13
|
+
export function navigate<T extends keyof RootStackParamList>(
|
|
14
|
+
name: never,
|
|
15
|
+
params?: RootStackParamList[T],
|
|
16
|
+
) {
|
|
17
|
+
if (navigationRef.isReady()) {
|
|
18
|
+
// Type assertion is not needed since name and params are already properly typed
|
|
19
|
+
navigationRef.current?.navigate(name, params);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Function to reset the navigation stack and navigate to the "Home" screen
|
|
24
|
+
export const resetToHomeScreen = () => {
|
|
25
|
+
navigationRef.dispatch(
|
|
26
|
+
CommonActions.reset({
|
|
27
|
+
index: 0,
|
|
28
|
+
routes: [{name: 'Home'}],
|
|
29
|
+
}),
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Call the function to reset the navigation stack and navigate to the "Home" screen
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {View, Text, TouchableOpacity, Image, StyleSheet} from 'react-native';
|
|
3
|
+
import {CommonStyles} from '../core/theme/commonStyles';
|
|
4
|
+
import {Colors} from '../core/theme/colors';
|
|
5
|
+
import {
|
|
6
|
+
BottomTabBarProps,
|
|
7
|
+
BottomTabNavigationOptions,
|
|
8
|
+
} from '@react-navigation/bottom-tabs';
|
|
9
|
+
import {ImageSourcePropType} from 'react-native';
|
|
10
|
+
import {toString} from 'lodash';
|
|
11
|
+
|
|
12
|
+
interface TabBarOptions extends BottomTabNavigationOptions {
|
|
13
|
+
selectedIcon: ImageSourcePropType;
|
|
14
|
+
icon: ImageSourcePropType;
|
|
15
|
+
tabBarTestID?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function TabBar({state, descriptors, navigation}: BottomTabBarProps) {
|
|
19
|
+
return (
|
|
20
|
+
<View style={styles.container}>
|
|
21
|
+
{state.routes.map((route, index) => {
|
|
22
|
+
const {options} = descriptors[route.key] as unknown as {
|
|
23
|
+
options: TabBarOptions;
|
|
24
|
+
};
|
|
25
|
+
const label =
|
|
26
|
+
options.tabBarLabel !== undefined
|
|
27
|
+
? options.tabBarLabel
|
|
28
|
+
: options.title !== undefined
|
|
29
|
+
? options.title
|
|
30
|
+
: route.name;
|
|
31
|
+
|
|
32
|
+
const isFocused = state.index === index;
|
|
33
|
+
|
|
34
|
+
const onPress = () => {
|
|
35
|
+
const event = navigation.emit({
|
|
36
|
+
type: 'tabPress',
|
|
37
|
+
target: route.key,
|
|
38
|
+
canPreventDefault: true,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (!isFocused && !event.defaultPrevented) {
|
|
42
|
+
navigation.navigate(route.name, route.params);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const onLongPress = () => {
|
|
47
|
+
navigation.emit({
|
|
48
|
+
type: 'tabLongPress',
|
|
49
|
+
target: route.key,
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<TouchableOpacity
|
|
55
|
+
key={route.key}
|
|
56
|
+
accessibilityRole="button"
|
|
57
|
+
accessibilityState={isFocused ? {selected: true} : {}}
|
|
58
|
+
accessibilityLabel={options.tabBarAccessibilityLabel}
|
|
59
|
+
testID={options.tabBarTestID}
|
|
60
|
+
onPress={onPress}
|
|
61
|
+
onLongPress={onLongPress}
|
|
62
|
+
style={styles.tabButton}>
|
|
63
|
+
<Image source={isFocused ? options.selectedIcon : options.icon} />
|
|
64
|
+
<Text style={[styles.label, isFocused && styles.labelFocused]}>
|
|
65
|
+
{toString(label)}
|
|
66
|
+
</Text>
|
|
67
|
+
</TouchableOpacity>
|
|
68
|
+
);
|
|
69
|
+
})}
|
|
70
|
+
</View>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const styles = StyleSheet.create({
|
|
75
|
+
container: {
|
|
76
|
+
flexDirection: 'row',
|
|
77
|
+
alignItems: 'center',
|
|
78
|
+
height: 100,
|
|
79
|
+
borderTopLeftRadius: 50,
|
|
80
|
+
borderTopRightRadius: 50,
|
|
81
|
+
justifyContent: 'space-evenly',
|
|
82
|
+
...CommonStyles.shadow,
|
|
83
|
+
},
|
|
84
|
+
tabButton: {
|
|
85
|
+
flex: 1,
|
|
86
|
+
alignItems: 'center',
|
|
87
|
+
},
|
|
88
|
+
label: {
|
|
89
|
+
color: Colors.gray,
|
|
90
|
+
},
|
|
91
|
+
labelFocused: {
|
|
92
|
+
color: Colors.primary100,
|
|
93
|
+
},
|
|
94
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import {Animated, View, TouchableOpacity} from 'react-native';
|
|
2
|
+
import {Colors} from '../core/theme/colors';
|
|
3
|
+
import {CommonStyles, screenWidth} from '../core/theme/commonStyles';
|
|
4
|
+
|
|
5
|
+
export function TopBar({state, descriptors, navigation, position}: any) {
|
|
6
|
+
return (
|
|
7
|
+
<View
|
|
8
|
+
style={{
|
|
9
|
+
flexDirection: 'row',
|
|
10
|
+
backgroundColor: Colors.gray,
|
|
11
|
+
width: screenWidth - 60,
|
|
12
|
+
alignSelf: 'center',
|
|
13
|
+
height: 40,
|
|
14
|
+
borderRadius: 20,
|
|
15
|
+
marginBottom: 24,
|
|
16
|
+
}}>
|
|
17
|
+
{state.routes.map((route, index) => {
|
|
18
|
+
const {options} = descriptors[route.key];
|
|
19
|
+
const label =
|
|
20
|
+
options.tabBarLabel !== undefined
|
|
21
|
+
? options.tabBarLabel
|
|
22
|
+
: options.title !== undefined
|
|
23
|
+
? options.title
|
|
24
|
+
: route.name;
|
|
25
|
+
|
|
26
|
+
const isFocused = state.index === index;
|
|
27
|
+
|
|
28
|
+
const onPress = () => {
|
|
29
|
+
const event = navigation.emit({
|
|
30
|
+
type: 'tabPress',
|
|
31
|
+
target: route.key,
|
|
32
|
+
canPreventDefault: true,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (!isFocused && !event.defaultPrevented) {
|
|
36
|
+
navigation.navigate(route.name, route.params);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const onLongPress = () => {
|
|
41
|
+
navigation.emit({
|
|
42
|
+
type: 'tabLongPress',
|
|
43
|
+
target: route.key,
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<TouchableOpacity
|
|
49
|
+
accessibilityRole="button"
|
|
50
|
+
accessibilityState={isFocused ? {selected: true} : {}}
|
|
51
|
+
accessibilityLabel={options.tabBarAccessibilityLabel}
|
|
52
|
+
testID={options.tabBarTestID}
|
|
53
|
+
onPress={onPress}
|
|
54
|
+
onLongPress={onLongPress}
|
|
55
|
+
style={{
|
|
56
|
+
flex: 1,
|
|
57
|
+
alignItems: 'center',
|
|
58
|
+
justifyContent: 'center',
|
|
59
|
+
backgroundColor: isFocused ? Colors.white : Colors.gray,
|
|
60
|
+
borderRadius: 20,
|
|
61
|
+
}}>
|
|
62
|
+
<Animated.Text
|
|
63
|
+
style={
|
|
64
|
+
isFocused
|
|
65
|
+
? {...CommonStyles.h4_bold, fontWeight: 'bold'}
|
|
66
|
+
: CommonStyles.h4_regular
|
|
67
|
+
}>
|
|
68
|
+
{label}
|
|
69
|
+
</Animated.Text>
|
|
70
|
+
</TouchableOpacity>
|
|
71
|
+
);
|
|
72
|
+
})}
|
|
73
|
+
</View>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import {useNavigation} from '@react-navigation/native';
|
|
2
|
+
import {toLower} from 'lodash';
|
|
3
|
+
import React, {useRef, useState} from 'react';
|
|
4
|
+
import {
|
|
5
|
+
NativeSyntheticEvent,
|
|
6
|
+
StyleSheet,
|
|
7
|
+
Text,
|
|
8
|
+
TextInputFocusEventData,
|
|
9
|
+
findNodeHandle,
|
|
10
|
+
} from 'react-native';
|
|
11
|
+
import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view';
|
|
12
|
+
import {ButtonType} from '../../../types';
|
|
13
|
+
import {PrimaryButton} from '../../common/components/PrimaryButton';
|
|
14
|
+
import {PrimaryTextInput} from '../../common/components/PrimaryTextInput';
|
|
15
|
+
import {localization} from '../../common/localization/localization';
|
|
16
|
+
import {useAppDispatch} from '../../core/store/reduxHelpers';
|
|
17
|
+
import {userLogin} from '../../core/store/user/userActions';
|
|
18
|
+
import {Colors} from '../../core/theme/colors';
|
|
19
|
+
import {CommonSizes} from '../../core/theme/commonSizes';
|
|
20
|
+
import {CommonStyles} from '../../core/theme/commonStyles';
|
|
21
|
+
|
|
22
|
+
export function Login(): JSX.Element {
|
|
23
|
+
const dispatch = useAppDispatch();
|
|
24
|
+
const [email, setEmail] = useState('');
|
|
25
|
+
const [password, setPassword] = useState('');
|
|
26
|
+
const [loading, setLoading] = useState(false);
|
|
27
|
+
|
|
28
|
+
async function loginUser() {
|
|
29
|
+
setLoading(true);
|
|
30
|
+
await dispatch(
|
|
31
|
+
userLogin({
|
|
32
|
+
email: toLower(email),
|
|
33
|
+
password,
|
|
34
|
+
}),
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
setLoading(false);
|
|
38
|
+
}
|
|
39
|
+
const scroll = useRef<KeyboardAwareScrollView>(null);
|
|
40
|
+
function scrollToInput(reactNode: any) {
|
|
41
|
+
// Add a 'scroll' ref to your ScrollView
|
|
42
|
+
|
|
43
|
+
// setTimeout(() => {
|
|
44
|
+
scroll.current?.scrollToFocusedInput(reactNode);
|
|
45
|
+
// }, 500);
|
|
46
|
+
}
|
|
47
|
+
return (
|
|
48
|
+
<KeyboardAwareScrollView
|
|
49
|
+
ref={scroll}
|
|
50
|
+
resetScrollToCoords={{x: 0, y: 0}}
|
|
51
|
+
scrollEnabled={true}
|
|
52
|
+
enableOnAndroid={true}
|
|
53
|
+
testID={'MainPageID'}
|
|
54
|
+
contentContainerStyle={styles.contentContainer}
|
|
55
|
+
contentInsetAdjustmentBehavior={'automatic'}
|
|
56
|
+
style={styles.container}>
|
|
57
|
+
<Text style={CommonStyles.h1_semiBold}>{localization.login.Login}</Text>
|
|
58
|
+
<PrimaryTextInput
|
|
59
|
+
onFocus={(event: NativeSyntheticEvent<TextInputFocusEventData>) => {
|
|
60
|
+
scrollToInput(findNodeHandle(event.target));
|
|
61
|
+
}}
|
|
62
|
+
autoCapitalize="none"
|
|
63
|
+
value={email}
|
|
64
|
+
onChangeText={setEmail}
|
|
65
|
+
containerStyle={CommonStyles.textInputContainer}
|
|
66
|
+
label={localization.login.Email}
|
|
67
|
+
placeholder={localization.login.EnterEmail}
|
|
68
|
+
/>
|
|
69
|
+
<PrimaryTextInput
|
|
70
|
+
onFocus={(event: NativeSyntheticEvent<TextInputFocusEventData>) => {
|
|
71
|
+
scrollToInput(findNodeHandle(event.target));
|
|
72
|
+
}}
|
|
73
|
+
clearButtonMode="never"
|
|
74
|
+
value={password}
|
|
75
|
+
secureTextEntry
|
|
76
|
+
onChangeText={setPassword}
|
|
77
|
+
containerStyle={CommonStyles.textInputContainer}
|
|
78
|
+
label={localization.login.Password}
|
|
79
|
+
placeholder={localization.login.EnterPassword}
|
|
80
|
+
/>
|
|
81
|
+
<PrimaryButton
|
|
82
|
+
label={localization.login.forgetPassword}
|
|
83
|
+
type={ButtonType.borderless}
|
|
84
|
+
style={{alignSelf: 'flex-end'}}
|
|
85
|
+
/>
|
|
86
|
+
<PrimaryButton
|
|
87
|
+
isLoading={loading}
|
|
88
|
+
onPress={loginUser}
|
|
89
|
+
label={localization.login.continue}
|
|
90
|
+
type={ButtonType.solid}
|
|
91
|
+
/>
|
|
92
|
+
</KeyboardAwareScrollView>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const styles = StyleSheet.create({
|
|
97
|
+
imageContainer: {
|
|
98
|
+
...CommonStyles.flex1,
|
|
99
|
+
justifyContent: 'flex-end',
|
|
100
|
+
},
|
|
101
|
+
container: {
|
|
102
|
+
flexGrow: 1,
|
|
103
|
+
backgroundColor: Colors.white,
|
|
104
|
+
borderTopRightRadius: CommonSizes.spacing.large,
|
|
105
|
+
borderTopLeftRadius: CommonSizes.spacing.large,
|
|
106
|
+
},
|
|
107
|
+
contentContainer: {
|
|
108
|
+
justifyContent: 'center',
|
|
109
|
+
alignItems: 'center',
|
|
110
|
+
paddingHorizontal: CommonSizes.spacing.large,
|
|
111
|
+
paddingVertical: 26,
|
|
112
|
+
gap: 16,
|
|
113
|
+
},
|
|
114
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {useNavigation} from '@react-navigation/native';
|
|
2
|
+
import React, {useEffect} from 'react';
|
|
3
|
+
import {LoadingComponent} from '../../common/components/LoadingComponent';
|
|
4
|
+
import {useAppSelector} from '../../core/store/reduxHelpers';
|
|
5
|
+
|
|
6
|
+
export function Splash(): JSX.Element {
|
|
7
|
+
const isUserLoggedIn = useAppSelector(state => state.user.accessToken);
|
|
8
|
+
const navigation = useNavigation();
|
|
9
|
+
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (isUserLoggedIn) {
|
|
12
|
+
navigation.navigate('Main' as never);
|
|
13
|
+
} else {
|
|
14
|
+
navigation.navigate('Login' as never);
|
|
15
|
+
}
|
|
16
|
+
}, [isUserLoggedIn]);
|
|
17
|
+
|
|
18
|
+
return <LoadingComponent />;
|
|
19
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// We extend some of the types here to give us great intellisense
|
|
2
|
+
|
|
3
|
+
import {registerSheet, SheetDefinition} from 'react-native-actions-sheet';
|
|
4
|
+
|
|
5
|
+
type Sheet = SheetDefinition;
|
|
6
|
+
|
|
7
|
+
// across the app for all registered sheets.
|
|
8
|
+
declare module 'react-native-actions-sheet' {
|
|
9
|
+
interface Sheets {
|
|
10
|
+
'first-sheet': Sheet;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export {};
|