@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,13 @@
|
|
|
1
|
+
export const onboardingLocalization = {
|
|
2
|
+
en: {
|
|
3
|
+
welcomeToApp: 'Welcome to\nApp',
|
|
4
|
+
firstHeader: 'Welcome',
|
|
5
|
+
firstBody: 'Thank you for choosing react-native-template-strong',
|
|
6
|
+
secondHeader: 'Learn',
|
|
7
|
+
secondBody:
|
|
8
|
+
'Some things are already done, to know more check the contents of pages and tabs',
|
|
9
|
+
thirdHeader: 'Star',
|
|
10
|
+
thirdBody:
|
|
11
|
+
'Star the repo if you find this project useful and participate in its development',
|
|
12
|
+
},
|
|
13
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const pagesLocalization = {
|
|
2
|
+
en: {
|
|
3
|
+
main: 'Home',
|
|
4
|
+
login: 'Login',
|
|
5
|
+
search: 'Search',
|
|
6
|
+
pickup: 'Pickup',
|
|
7
|
+
profile: 'Profile',
|
|
8
|
+
reports: 'Reports',
|
|
9
|
+
learnings: 'Learnings',
|
|
10
|
+
settings: 'Settings',
|
|
11
|
+
selectDate: 'Select date',
|
|
12
|
+
storybook: 'Storybook',
|
|
13
|
+
},
|
|
14
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {Linking} from 'react-native';
|
|
2
|
+
|
|
3
|
+
export abstract class BaseUrlOpener {
|
|
4
|
+
protected constructor() {
|
|
5
|
+
this.tryOpen = this.tryOpen.bind(this);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
tryOpen(): void {
|
|
9
|
+
const url = this.generateUrl();
|
|
10
|
+
if (url == null) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
this.tryOpenUrl(url);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async open(): Promise<void> {
|
|
18
|
+
const url = this.generateUrl();
|
|
19
|
+
if (url == null) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
await this.tryOpenUrl(url);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
protected async tryOpenUrl(url: string): Promise<void> {
|
|
27
|
+
Linking.openURL(url);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
protected abstract generateUrl(): string | null;
|
|
31
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {BaseUrlOpener} from './baseUrlOpener';
|
|
2
|
+
|
|
3
|
+
export class EmailUrl extends BaseUrlOpener {
|
|
4
|
+
constructor(private email: string | null) {
|
|
5
|
+
super();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
protected generateUrl(): string | null {
|
|
9
|
+
if (this.email == null) {
|
|
10
|
+
return null;
|
|
11
|
+
} else {
|
|
12
|
+
let url = this.email;
|
|
13
|
+
if (!url.startsWith('mailto')) {
|
|
14
|
+
url = 'mailto://' + url;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return url;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {BaseUrlOpener} from './baseUrlOpener';
|
|
2
|
+
|
|
3
|
+
export class HttpUrl extends BaseUrlOpener {
|
|
4
|
+
constructor(private httpUrl: string | null) {
|
|
5
|
+
super();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
protected generateUrl(): string | null {
|
|
9
|
+
if (this.httpUrl == null) {
|
|
10
|
+
return null;
|
|
11
|
+
} else {
|
|
12
|
+
if (this.httpUrl.startsWith('http')) {
|
|
13
|
+
return this.httpUrl;
|
|
14
|
+
} else {
|
|
15
|
+
return 'https://' + this.httpUrl;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {isIos} from '../../core/theme/commonConsts';
|
|
2
|
+
import {BaseUrlOpener} from './baseUrlOpener';
|
|
3
|
+
|
|
4
|
+
export class MapUrl extends BaseUrlOpener {
|
|
5
|
+
constructor(
|
|
6
|
+
private latitude: number,
|
|
7
|
+
private longitude: number,
|
|
8
|
+
private address: string,
|
|
9
|
+
) {
|
|
10
|
+
super();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
protected generateUrl(): string | null {
|
|
14
|
+
if (isIos) {
|
|
15
|
+
return `https://maps.apple.com/?q=${encodeURIComponent(
|
|
16
|
+
this.address,
|
|
17
|
+
)}&sll=${this.latitude},${this.longitude}`;
|
|
18
|
+
} else {
|
|
19
|
+
return `geo:${this.latitude}, ${this.longitude}`;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {isIos} from '../../core/theme/commonConsts';
|
|
2
|
+
import {BaseUrlOpener} from './baseUrlOpener';
|
|
3
|
+
|
|
4
|
+
export class PhoneUrl extends BaseUrlOpener {
|
|
5
|
+
constructor(private phone: string | null) {
|
|
6
|
+
super();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
protected generateUrl(): string | null {
|
|
10
|
+
if (this.phone == null) {
|
|
11
|
+
return null;
|
|
12
|
+
} else {
|
|
13
|
+
return (isIos ? 'telprompt:' : 'tel:') + this.phone;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {Dimensions} from 'react-native';
|
|
2
|
+
|
|
3
|
+
const windowDimensions = Dimensions.get('window');
|
|
4
|
+
|
|
5
|
+
const currentResolution = Math.sqrt(
|
|
6
|
+
windowDimensions.height * windowDimensions.height +
|
|
7
|
+
windowDimensions.width * windowDimensions.width,
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
export const createPerfectSize = (width: number, height: number) => {
|
|
11
|
+
const designResolution = Math.sqrt(height * height + width * width);
|
|
12
|
+
const resolutionsProportions = currentResolution / designResolution;
|
|
13
|
+
|
|
14
|
+
return (size: number) => resolutionsProportions * size;
|
|
15
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {LoadState} from '../../../types';
|
|
2
|
+
import {DEFAULT_PAGE_SIZE} from '../helpers/calculatePage';
|
|
3
|
+
|
|
4
|
+
export function commonListFulfilledHandler<T>(
|
|
5
|
+
loadState: LoadState,
|
|
6
|
+
payloadList: T[],
|
|
7
|
+
storedList: T[],
|
|
8
|
+
) {
|
|
9
|
+
const newLoadState =
|
|
10
|
+
payloadList.length >= DEFAULT_PAGE_SIZE
|
|
11
|
+
? LoadState.idle
|
|
12
|
+
: LoadState.allIsLoaded;
|
|
13
|
+
|
|
14
|
+
let newData: T[] = [];
|
|
15
|
+
|
|
16
|
+
switch (loadState) {
|
|
17
|
+
case LoadState.firstLoad:
|
|
18
|
+
case LoadState.pullToRefresh:
|
|
19
|
+
case LoadState.refreshing:
|
|
20
|
+
newData = [...payloadList];
|
|
21
|
+
break;
|
|
22
|
+
case LoadState.loadingMore:
|
|
23
|
+
newData = [...storedList].concat(payloadList);
|
|
24
|
+
break;
|
|
25
|
+
default:
|
|
26
|
+
throw new Error(`LoadState ${loadState} is not valid in this context.`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return {data: newData, error: null, loadState: newLoadState};
|
|
30
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function serializeQueryParams(obj: any): string {
|
|
2
|
+
const str = [];
|
|
3
|
+
for (const key in obj) {
|
|
4
|
+
if (obj.hasOwnProperty(key) && obj[key] !== undefined) {
|
|
5
|
+
str.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]));
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
return str.join('&');
|
|
10
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {localization} from '../localization/localization';
|
|
2
|
+
import {emptyValidation} from './commonValidations';
|
|
3
|
+
import {validationConstants} from './validationConstants';
|
|
4
|
+
|
|
5
|
+
export function phoneValidations(checkValue: string): string | null {
|
|
6
|
+
const isEmpty = emptyValidation(checkValue);
|
|
7
|
+
|
|
8
|
+
if (isEmpty != null) {
|
|
9
|
+
return isEmpty;
|
|
10
|
+
} else {
|
|
11
|
+
return checkValue.length >= validationConstants.phone.minLength
|
|
12
|
+
? null
|
|
13
|
+
: localization.errors.invalidPhoneNumber;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {localization} from '../localization/localization';
|
|
2
|
+
|
|
3
|
+
export function emptyValidation(checkValue: string): string | null {
|
|
4
|
+
return checkValue != null && checkValue != ''
|
|
5
|
+
? null
|
|
6
|
+
: localization.errors.thisFieldIsRequired;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function datesValidation(
|
|
10
|
+
from: Date,
|
|
11
|
+
to: Date,
|
|
12
|
+
fromLabel: string,
|
|
13
|
+
toLabel: string,
|
|
14
|
+
currentField: 'from' | 'to',
|
|
15
|
+
): string | null {
|
|
16
|
+
const fromTime = from.getTime();
|
|
17
|
+
const toTime = to.getTime();
|
|
18
|
+
|
|
19
|
+
if (fromTime == toTime) {
|
|
20
|
+
return localization.errors.datesCantBeEqual(fromLabel, toLabel);
|
|
21
|
+
} else {
|
|
22
|
+
const isFromTimeLater = fromTime > toTime;
|
|
23
|
+
|
|
24
|
+
switch (currentField) {
|
|
25
|
+
case 'from':
|
|
26
|
+
return isFromTimeLater
|
|
27
|
+
? localization.errors.invalidFromDate(fromLabel, toLabel)
|
|
28
|
+
: null;
|
|
29
|
+
case 'to':
|
|
30
|
+
return isFromTimeLater
|
|
31
|
+
? localization.errors.invalidToDate(fromLabel, toLabel)
|
|
32
|
+
: null;
|
|
33
|
+
default:
|
|
34
|
+
throw new Error(
|
|
35
|
+
`Unknown dates validation current field: ${currentField}`,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import {localization} from '../localization/localization';
|
|
2
|
+
import {unwrapResult} from '@reduxjs/toolkit';
|
|
3
|
+
import {Alert} from 'react-native';
|
|
4
|
+
import {IErrorResult, ErrorRepresentationType} from '../../../types';
|
|
5
|
+
import Snackbar from 'react-native-snackbar';
|
|
6
|
+
import {Colors} from '../../core/theme/colors';
|
|
7
|
+
|
|
8
|
+
export function handlePromiseResult(
|
|
9
|
+
promiseAction: Promise<any>,
|
|
10
|
+
successMessage?: string,
|
|
11
|
+
successAction?: () => void,
|
|
12
|
+
processError?: (error: Error) => IErrorResult,
|
|
13
|
+
setError?: (errorMessage: string) => void,
|
|
14
|
+
) {
|
|
15
|
+
promiseAction
|
|
16
|
+
.then(unwrapResult)
|
|
17
|
+
.then(() => {
|
|
18
|
+
successMessage &&
|
|
19
|
+
Snackbar.show({
|
|
20
|
+
text: successMessage,
|
|
21
|
+
duration: Snackbar.LENGTH_SHORT,
|
|
22
|
+
textColor: Colors.white,
|
|
23
|
+
backgroundColor: Colors.green,
|
|
24
|
+
});
|
|
25
|
+
successAction && successAction();
|
|
26
|
+
})
|
|
27
|
+
.catch((handledError: Error) => {
|
|
28
|
+
processError &&
|
|
29
|
+
handleErrorPostProcessing(processError(handledError), setError);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function handleErrorPostProcessing(
|
|
34
|
+
error: IErrorResult,
|
|
35
|
+
setError?: (errorMessage: string) => void,
|
|
36
|
+
) {
|
|
37
|
+
switch (error.visualRepresentation) {
|
|
38
|
+
case ErrorRepresentationType.alert:
|
|
39
|
+
Alert.alert(localization.errors.error, error.message);
|
|
40
|
+
break;
|
|
41
|
+
case ErrorRepresentationType.input:
|
|
42
|
+
setError && setError(error.message);
|
|
43
|
+
break;
|
|
44
|
+
case ErrorRepresentationType.toast:
|
|
45
|
+
Snackbar.show({
|
|
46
|
+
text: error.message,
|
|
47
|
+
duration: Snackbar.LENGTH_SHORT,
|
|
48
|
+
textColor: Colors.white,
|
|
49
|
+
backgroundColor: Colors.red,
|
|
50
|
+
});
|
|
51
|
+
break;
|
|
52
|
+
default:
|
|
53
|
+
throw new Error(
|
|
54
|
+
`Unknown error representation type: ${error.visualRepresentation}`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function recheckAllValidations(
|
|
60
|
+
recheckFunctions: (() => string | null)[],
|
|
61
|
+
): boolean {
|
|
62
|
+
let isNull: boolean = true;
|
|
63
|
+
|
|
64
|
+
recheckFunctions.forEach(recheckFunc => {
|
|
65
|
+
const result = recheckFunc() == null;
|
|
66
|
+
if (!result) {
|
|
67
|
+
isNull = false;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return isNull;
|
|
72
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import {useCallback, useEffect, useRef, useState} from 'react';
|
|
2
|
+
import {datesValidation} from '../commonValidations';
|
|
3
|
+
|
|
4
|
+
export function useDatesError(
|
|
5
|
+
fromDate: Date,
|
|
6
|
+
toDate: Date,
|
|
7
|
+
fromLabel: string,
|
|
8
|
+
toLabel: string,
|
|
9
|
+
currentField: 'from' | 'to',
|
|
10
|
+
) {
|
|
11
|
+
const [error, setError] = useState<string | null>(null);
|
|
12
|
+
const didMountRef = useRef(false);
|
|
13
|
+
|
|
14
|
+
const recheckValue = useCallback(() => {
|
|
15
|
+
const result = datesValidation(
|
|
16
|
+
fromDate,
|
|
17
|
+
toDate,
|
|
18
|
+
fromLabel,
|
|
19
|
+
toLabel,
|
|
20
|
+
currentField,
|
|
21
|
+
);
|
|
22
|
+
setError(result);
|
|
23
|
+
|
|
24
|
+
return result;
|
|
25
|
+
}, [currentField, fromDate, fromLabel, toDate, toLabel]);
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (didMountRef.current) {
|
|
29
|
+
recheckValue();
|
|
30
|
+
} else {
|
|
31
|
+
didMountRef.current = true;
|
|
32
|
+
}
|
|
33
|
+
}, [recheckValue]);
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
error,
|
|
37
|
+
setError,
|
|
38
|
+
recheckValue,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {useCallback, useEffect, useRef, useState} from 'react';
|
|
2
|
+
|
|
3
|
+
export function useInputError(
|
|
4
|
+
checkValue: string,
|
|
5
|
+
checkFunction: (valueToCheck: string) => string | null,
|
|
6
|
+
) {
|
|
7
|
+
const [error, setError] = useState<string | null>(null);
|
|
8
|
+
const didMountRef = useRef(false);
|
|
9
|
+
|
|
10
|
+
const recheckValue = useCallback(() => {
|
|
11
|
+
const result = checkFunction(checkValue);
|
|
12
|
+
setError(result);
|
|
13
|
+
|
|
14
|
+
return result;
|
|
15
|
+
}, [checkFunction, checkValue]);
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (didMountRef.current) {
|
|
19
|
+
recheckValue();
|
|
20
|
+
} else {
|
|
21
|
+
didMountRef.current = true;
|
|
22
|
+
}
|
|
23
|
+
}, [recheckValue]);
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
error,
|
|
27
|
+
setError,
|
|
28
|
+
recheckValue,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {emptyValidation} from './commonValidations';
|
|
2
|
+
import {isEmail} from '../helpers/regexHelpers';
|
|
3
|
+
import {validationConstants} from './validationConstants';
|
|
4
|
+
import {localization} from '../localization/localization';
|
|
5
|
+
|
|
6
|
+
export function emailValidations(checkValue: string): string | null {
|
|
7
|
+
const isEmpty = emptyValidation(checkValue);
|
|
8
|
+
|
|
9
|
+
if (isEmpty != null) {
|
|
10
|
+
return isEmpty;
|
|
11
|
+
} else if (!isEmail(checkValue)) {
|
|
12
|
+
return localization.errors.invalidEmail;
|
|
13
|
+
} else {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function fullNameValidations(checkValue: string): string | null {
|
|
19
|
+
const isEmpty = emptyValidation(checkValue);
|
|
20
|
+
|
|
21
|
+
if (isEmpty != null) {
|
|
22
|
+
return isEmpty;
|
|
23
|
+
} else if (
|
|
24
|
+
checkValue.trim().length < validationConstants.fullName.minLength
|
|
25
|
+
) {
|
|
26
|
+
return localization.errors.invalidFullName;
|
|
27
|
+
} else {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const validationConstants = {
|
|
2
|
+
phone: {
|
|
3
|
+
minLength: 8,
|
|
4
|
+
maxLength: 18,
|
|
5
|
+
},
|
|
6
|
+
fullName: {
|
|
7
|
+
minLength: 3,
|
|
8
|
+
maxLength: 64,
|
|
9
|
+
},
|
|
10
|
+
comment: {
|
|
11
|
+
maxLength: 280,
|
|
12
|
+
},
|
|
13
|
+
email: {
|
|
14
|
+
maxLength: 254,
|
|
15
|
+
},
|
|
16
|
+
licensePlate: {
|
|
17
|
+
minLength: 4,
|
|
18
|
+
maxLength: 10,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {AxiosResponse} from 'axios';
|
|
2
|
+
import Snackbar from 'react-native-snackbar';
|
|
3
|
+
import {Colors} from '../theme/colors';
|
|
4
|
+
import {setLogout} from '../store/user/userSlice';
|
|
5
|
+
import {store} from '../store/store';
|
|
6
|
+
|
|
7
|
+
export const handleFetchJsonResponse = async (
|
|
8
|
+
response: AxiosResponse,
|
|
9
|
+
showSuccessMessage?: boolean,
|
|
10
|
+
) => {
|
|
11
|
+
if (response.status === 401) {
|
|
12
|
+
store.dispatch(setLogout());
|
|
13
|
+
} else if (
|
|
14
|
+
!response.status ||
|
|
15
|
+
response.status < 200 ||
|
|
16
|
+
(response.status >= 300 && response.status !== 401)
|
|
17
|
+
) {
|
|
18
|
+
return Snackbar.show({
|
|
19
|
+
text: response?.data?.message,
|
|
20
|
+
duration: Snackbar.LENGTH_SHORT,
|
|
21
|
+
textColor: Colors.white,
|
|
22
|
+
backgroundColor: Colors.red,
|
|
23
|
+
});
|
|
24
|
+
} else if (showSuccessMessage) {
|
|
25
|
+
Snackbar.show({
|
|
26
|
+
text: 'Success',
|
|
27
|
+
duration: Snackbar.LENGTH_SHORT,
|
|
28
|
+
textColor: Colors.white,
|
|
29
|
+
backgroundColor: Colors.green,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return response.data;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const handleErrorResponse = async (message: string) => {
|
|
37
|
+
return Snackbar.show({
|
|
38
|
+
text: message,
|
|
39
|
+
duration: Snackbar.LENGTH_SHORT,
|
|
40
|
+
textColor: Colors.white,
|
|
41
|
+
backgroundColor: Colors.red,
|
|
42
|
+
});
|
|
43
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import Config from 'react-native-config';
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
import {AxiosDefaults} from 'axios';
|
|
4
|
+
|
|
5
|
+
export const defaultHeaders: HeadersInit_ = {
|
|
6
|
+
Connection: 'keep-alive',
|
|
7
|
+
'Content-Type': 'application/json',
|
|
8
|
+
};
|
|
9
|
+
declare type MethodData = {
|
|
10
|
+
url: AxiosDefaults['httpsAgent'];
|
|
11
|
+
data?: AxiosDefaults['data'];
|
|
12
|
+
config?: any;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const instance = axios.create({
|
|
16
|
+
baseURL: Config.API_BASE_URL,
|
|
17
|
+
timeout: 5000,
|
|
18
|
+
maxRate: 100,
|
|
19
|
+
headers: {
|
|
20
|
+
Connection: 'keep-alive',
|
|
21
|
+
'Content-Type': 'application/json',
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export const post = ({url, data, config}: MethodData) => {
|
|
26
|
+
return instance.post(url, data, config);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const get = ({url, config}: MethodData) => {
|
|
30
|
+
return instance.get(url, config);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const put = ({url, data, config}: MethodData) => {
|
|
34
|
+
return instance.put(url, data, config);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const deleteApi = ({url, config}: MethodData) => {
|
|
38
|
+
return instance.delete(url, config);
|
|
39
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {createSlice} from '@reduxjs/toolkit';
|
|
2
|
+
import {newState} from '../../../common/utils/newState';
|
|
3
|
+
|
|
4
|
+
import {appInitialState, AppInitialEntity} from './appState';
|
|
5
|
+
|
|
6
|
+
export const {reducer: AppReducer, actions} = createSlice({
|
|
7
|
+
name: 'app',
|
|
8
|
+
initialState: appInitialState,
|
|
9
|
+
reducers: {},
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export const {} = actions;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import {TypedUseSelectorHook, useDispatch, useSelector} from 'react-redux';
|
|
2
|
+
import {RootState} from './rootReducer';
|
|
3
|
+
import {createAsyncThunk} from '@reduxjs/toolkit';
|
|
4
|
+
import {AppDispatch} from './store';
|
|
5
|
+
|
|
6
|
+
export const useAppDispatch = () => useDispatch<AppDispatch>();
|
|
7
|
+
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
|
|
8
|
+
export const createAppAsyncThunk = createAsyncThunk.withTypes<{
|
|
9
|
+
state: RootState;
|
|
10
|
+
dispatch: AppDispatch;
|
|
11
|
+
}>();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import {combineReducers} from '@reduxjs/toolkit';
|
|
2
|
+
import {AppReducer} from './app/appSlice';
|
|
3
|
+
import {UserReducer} from './user/userSlice';
|
|
4
|
+
|
|
5
|
+
export const rootReducer = combineReducers({
|
|
6
|
+
app: AppReducer,
|
|
7
|
+
user: UserReducer,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export type RootState = ReturnType<typeof rootReducer>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {Action, configureStore, ThunkAction} from '@reduxjs/toolkit';
|
|
3
|
+
import {PersistConfig, persistReducer, persistStore} from 'redux-persist';
|
|
4
|
+
import {rootReducer, RootState} from './rootReducer';
|
|
5
|
+
import {Provider} from 'react-redux';
|
|
6
|
+
import {PersistGate} from 'redux-persist/integration/react';
|
|
7
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
8
|
+
import {createWhitelistFilter} from 'redux-persist-transform-filter';
|
|
9
|
+
|
|
10
|
+
const persistConfig: PersistConfig<RootState> = {
|
|
11
|
+
key: 'root',
|
|
12
|
+
storage: AsyncStorage,
|
|
13
|
+
version: 1,
|
|
14
|
+
timeout: 1000,
|
|
15
|
+
transforms: [createWhitelistFilter('user', ['accessToken', 'user'])],
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const persistedReducer = persistReducer(persistConfig, rootReducer);
|
|
19
|
+
|
|
20
|
+
export const store = configureStore({
|
|
21
|
+
reducer: persistedReducer,
|
|
22
|
+
middleware: getDefaultMiddleware =>
|
|
23
|
+
getDefaultMiddleware({
|
|
24
|
+
serializableCheck: false,
|
|
25
|
+
}),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export const persistor = persistStore(store);
|
|
29
|
+
|
|
30
|
+
export type AppDispatch = typeof store.dispatch;
|
|
31
|
+
export type AppThunk = ThunkAction<void, RootState, unknown, Action<string>>;
|
|
32
|
+
|
|
33
|
+
export const reduxProvider = (Component: any) => (props: any) => {
|
|
34
|
+
return (
|
|
35
|
+
<Provider store={store}>
|
|
36
|
+
<PersistGate loading={null} persistor={persistor}>
|
|
37
|
+
<Component {...props} />
|
|
38
|
+
</PersistGate>
|
|
39
|
+
</Provider>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import moment from 'moment';
|
|
2
|
+
import {handleFetchJsonResponse} from '../../api/responseHandlers';
|
|
3
|
+
import {get, post, put} from '../../api/serverHeaders';
|
|
4
|
+
import {createAsyncThunk} from '@reduxjs/toolkit';
|
|
5
|
+
|
|
6
|
+
export const userLogin = createAsyncThunk(
|
|
7
|
+
'user/login',
|
|
8
|
+
async (
|
|
9
|
+
{email, password}: {email: string; password: string},
|
|
10
|
+
{rejectWithValue, getState, dispatch}: any,
|
|
11
|
+
) => {
|
|
12
|
+
try {
|
|
13
|
+
const data: {email: string; password: string} = {
|
|
14
|
+
email,
|
|
15
|
+
password,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const response = await post({
|
|
19
|
+
url: '/login',
|
|
20
|
+
data,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
if (response.data?.user?.roleName !== 'Parent') {
|
|
24
|
+
return rejectWithValue('Not Authorized');
|
|
25
|
+
}
|
|
26
|
+
return handleFetchJsonResponse(response);
|
|
27
|
+
} catch (e: any) {
|
|
28
|
+
return rejectWithValue(e?.response);
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
);
|