create-react-native-starter-kit 0.0.0-stage → 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/LICENSE +21 -0
- package/README.md +378 -2
- package/bin/create-react-native-starter-kit.js +95 -0
- package/lib/copy-src.js +127 -0
- package/lib/creator.js +211 -0
- package/lib/deps.js +58 -0
- package/lib/patch-configs.js +318 -0
- package/lib/patch-native.js +387 -0
- package/package.json +47 -4
- package/scripts/sync-template.js +52 -0
- package/template/.env.example +3 -0
- package/template/patches/react-native-element-dropdown+2.12.4.patch +67 -0
- package/template/src/app/App.tsx +25 -0
- package/template/src/app/navigation/root.tsx +24 -0
- package/template/src/app/navigation/stacks/app.stack.tsx +67 -0
- package/template/src/app/navigation/stacks/app.tab.tsx +72 -0
- package/template/src/app/navigation/stacks/auth.stack.tsx +56 -0
- package/template/src/app/providers.tsx +43 -0
- package/template/src/assets/fonts/InterTight-Black.ttf +0 -0
- package/template/src/assets/fonts/InterTight-BlackItalic.ttf +0 -0
- package/template/src/assets/fonts/InterTight-Bold.ttf +0 -0
- package/template/src/assets/fonts/InterTight-BoldItalic.ttf +0 -0
- package/template/src/assets/fonts/InterTight-ExtraBold.ttf +0 -0
- package/template/src/assets/fonts/InterTight-ExtraBoldItalic.ttf +0 -0
- package/template/src/assets/fonts/InterTight-ExtraLight.ttf +0 -0
- package/template/src/assets/fonts/InterTight-ExtraLightItalic.ttf +0 -0
- package/template/src/assets/fonts/InterTight-Italic.ttf +0 -0
- package/template/src/assets/fonts/InterTight-Light.ttf +0 -0
- package/template/src/assets/fonts/InterTight-LightItalic.ttf +0 -0
- package/template/src/assets/fonts/InterTight-Medium.ttf +0 -0
- package/template/src/assets/fonts/InterTight-MediumItalic.ttf +0 -0
- package/template/src/assets/fonts/InterTight-Regular.ttf +0 -0
- package/template/src/assets/fonts/InterTight-SemiBold.ttf +0 -0
- package/template/src/assets/fonts/InterTight-SemiBoldItalic.ttf +0 -0
- package/template/src/assets/fonts/InterTight-Thin.ttf +0 -0
- package/template/src/assets/fonts/InterTight-ThinItalic.ttf +0 -0
- package/template/src/assets/icons/back.svg +3 -0
- package/template/src/assets/icons/calendar.svg +3 -0
- package/template/src/assets/icons/claim_upload.svg +3 -0
- package/template/src/assets/icons/clock.svg +1 -0
- package/template/src/assets/icons/downArrow.svg +3 -0
- package/template/src/assets/icons/edit.svg +3 -0
- package/template/src/assets/icons/eye.svg +4 -0
- package/template/src/assets/icons/eyeClose.svg +7 -0
- package/template/src/assets/icons/folder.svg +5 -0
- package/template/src/assets/icons/home.svg +4 -0
- package/template/src/assets/icons/index.ts +33 -0
- package/template/src/assets/icons/setting.svg +4 -0
- package/template/src/assets/icons/tabs/home/filled_dark.svg +3 -0
- package/template/src/assets/icons/tabs/home/filled_light.svg +3 -0
- package/template/src/assets/icons/tabs/home/outline.svg +4 -0
- package/template/src/assets/icons/tabs/index.ts +31 -0
- package/template/src/assets/icons/tabs/profile/filled_dark.svg +6 -0
- package/template/src/assets/icons/tabs/profile/filled_light.svg +6 -0
- package/template/src/assets/icons/tabs/profile/outline.svg +3 -0
- package/template/src/assets/icons/tooltip_dark.svg +1 -0
- package/template/src/assets/icons/tooltip_light.svg +1 -0
- package/template/src/assets/icons/upArrow.svg +3 -0
- package/template/src/assets/images/back.png +0 -0
- package/template/src/assets/images/index.ts +7 -0
- package/template/src/assets/images/placeholder/profile.png +0 -0
- package/template/src/components/shared/AppImage.tsx +190 -0
- package/template/src/components/shared/AvatarPicker.tsx +175 -0
- package/template/src/components/shared/CustomButton.tsx +273 -0
- package/template/src/components/shared/DocumentPicker.tsx +86 -0
- package/template/src/components/shared/GlobalLoader.tsx +47 -0
- package/template/src/components/shared/Icon.tsx +28 -0
- package/template/src/components/shared/PressableIcon.tsx +104 -0
- package/template/src/components/shared/bottom-sheet/BaseBottomSheet.tsx +30 -0
- package/template/src/components/shared/bottom-sheet/BaseBottomSheetModal.tsx +50 -0
- package/template/src/components/shared/bottom-sheet/sheets/ImagePickerSheet.tsx +310 -0
- package/template/src/components/shared/bottom-sheet/types.ts +4 -0
- package/template/src/components/shared/form-input/FormInput.tsx +139 -0
- package/template/src/components/shared/form-input/FormInputRenderer.tsx +82 -0
- package/template/src/components/shared/form-input/FullNameInput.tsx +243 -0
- package/template/src/components/shared/form-input/formFieldStyles.ts +43 -0
- package/template/src/components/shared/form-input/type.ts +188 -0
- package/template/src/components/shared/form-input/variants/CountryPhoneVariant.tsx +289 -0
- package/template/src/components/shared/form-input/variants/DateTimeVariant.tsx +280 -0
- package/template/src/components/shared/form-input/variants/DropdownVariant.tsx +221 -0
- package/template/src/components/shared/form-input/variants/TextInputVariant.tsx +300 -0
- package/template/src/components/shared/form-input/variants/ToggleVariant.tsx +194 -0
- package/template/src/components/shared/modal/BaseModal.tsx +83 -0
- package/template/src/components/shared/modal/common/AlertModal.tsx +173 -0
- package/template/src/components/shared/navigation/AnimatedTabIcon.tsx +37 -0
- package/template/src/components/shared/navigation/HeaderActionButton.tsx +70 -0
- package/template/src/components/shared/navigation/HeaderBackButton.tsx +38 -0
- package/template/src/components/shared/navigation/StackHeader.tsx +105 -0
- package/template/src/components/shared/navigation/tabIconRender.tsx +14 -0
- package/template/src/components/shared/offline/OfflineQueryBoundary.tsx +25 -0
- package/template/src/components/shared/offline/OfflineState.tsx +229 -0
- package/template/src/components/shared/offline/index.ts +6 -0
- package/template/src/components/shared/offline/types.ts +15 -0
- package/template/src/components/shared/offline/wifi-icon-animation/BrokenWifiAnimation.tsx +116 -0
- package/template/src/components/shared/offline/wifi-icon-animation/WifiPiece.tsx +80 -0
- package/template/src/components/shared/offline/wifi-icon-animation/geometry.ts +163 -0
- package/template/src/components/shared/offline/wifi-icon-animation/useWifiIconAnimationActive.ts +37 -0
- package/template/src/components/shared/skelton/BaseSkelton.tsx +71 -0
- package/template/src/components/shared/skelton/ShimmerHolder.ts +6 -0
- package/template/src/components/shared/skelton/holders/HomeShimmer.tsx +25 -0
- package/template/src/components/shared/skelton/holders/index.ts +1 -0
- package/template/src/components/shared/skelton/layouts/avatar.layout.ts +18 -0
- package/template/src/components/shared/skelton/layouts/home.layout.ts +26 -0
- package/template/src/components/shared/skelton/layouts/index.ts +3 -0
- package/template/src/components/shared/skelton/layouts/text.layout.ts +18 -0
- package/template/src/components/shared/text/CustomText.tsx +85 -0
- package/template/src/components/shared/text/ShimmerText.tsx +159 -0
- package/template/src/components/shared/text/types.ts +40 -0
- package/template/src/constants/device.ts +12 -0
- package/template/src/constants/endpoint.ts +11 -0
- package/template/src/constants/keyExtractor.ts +6 -0
- package/template/src/constants/limits.ts +9 -0
- package/template/src/constants/queryKey.ts +3 -0
- package/template/src/constants/regex.ts +7 -0
- package/template/src/context/NetworkContext.tsx +102 -0
- package/template/src/context/ThemeContext.tsx +56 -0
- package/template/src/hooks/app/index.ts +2 -0
- package/template/src/hooks/app/useGetProfile.ts +38 -0
- package/template/src/hooks/app/useUpdateProfile.ts +21 -0
- package/template/src/hooks/useAppDispatch.ts +6 -0
- package/template/src/hooks/useAppSelector.ts +4 -0
- package/template/src/hooks/useDebounce.ts +15 -0
- package/template/src/hooks/useDocumentPicker.ts +102 -0
- package/template/src/hooks/useImagePicker.ts +83 -0
- package/template/src/i18n/i18next.d.ts +9 -0
- package/template/src/i18n/index.ts +16 -0
- package/template/src/i18n/locales/en.json +274 -0
- package/template/src/i18n/locales/ru.json +165 -0
- package/template/src/i18n/resources.ts +11 -0
- package/template/src/redux/selectors/auth.selectors.ts +4 -0
- package/template/src/redux/slice/auth.slice.ts +23 -0
- package/template/src/redux/slice/loader.slice.ts +25 -0
- package/template/src/redux/slice/settings.slice.ts +26 -0
- package/template/src/redux/slice/user.slice.ts +25 -0
- package/template/src/screens/app/main-tab/index.tsx +73 -0
- package/template/src/screens/app/main-tab/style.ts +0 -0
- package/template/src/screens/app/profile-setup/index.tsx +161 -0
- package/template/src/screens/app/profile-setup/styles.ts +3 -0
- package/template/src/screens/auth/forgot-password/index.tsx +59 -0
- package/template/src/screens/auth/forgot-password/styles.ts +0 -0
- package/template/src/screens/auth/index.ts +4 -0
- package/template/src/screens/auth/login/index.tsx +137 -0
- package/template/src/screens/auth/login/styles.ts +3 -0
- package/template/src/screens/auth/signup/index.tsx +110 -0
- package/template/src/screens/auth/signup/styles.ts +3 -0
- package/template/src/screens/auth/verify-email/index.tsx +15 -0
- package/template/src/screens/auth/verify-email/styles.ts +3 -0
- package/template/src/screens/tab/home/index.tsx +49 -0
- package/template/src/screens/tab/home/styles.ts +3 -0
- package/template/src/screens/tab/index.ts +2 -0
- package/template/src/screens/tab/profile/index.tsx +50 -0
- package/template/src/screens/tab/profile/styles.ts +3 -0
- package/template/src/services/api/auth/auth.api.ts +22 -0
- package/template/src/services/api/auth/auth.endpoints.ts +7 -0
- package/template/src/services/api/auth/auth.types.ts +9 -0
- package/template/src/services/api/auth/index.ts +3 -0
- package/template/src/services/api/axios/axios.instance.ts +15 -0
- package/template/src/services/api/axios/axios.interceptors.ts +43 -0
- package/template/src/services/api/axios/index.ts +2 -0
- package/template/src/services/api/mock/mockAdapter.ts +97 -0
- package/template/src/services/api/user/index.ts +2 -0
- package/template/src/services/api/user/user.api.ts +67 -0
- package/template/src/services/api/user/user.endpoints.ts +0 -0
- package/template/src/services/api/user/user.types.ts +0 -0
- package/template/src/services/errors/backendErrorCodes.ts +7 -0
- package/template/src/services/errors/errorHandler.ts +94 -0
- package/template/src/services/errors/firebaseErrorMap.ts +61 -0
- package/template/src/services/errors/httpStatusMap.ts +47 -0
- package/template/src/services/errors/index.ts +4 -0
- package/template/src/services/network/index.ts +1 -0
- package/template/src/services/network/onlineManager.ts +22 -0
- package/template/src/services/query/index.ts +1 -0
- package/template/src/services/query/queryClient.ts +23 -0
- package/template/src/services/toast/ToastCard.tsx +137 -0
- package/template/src/services/toast/ToastConfig.tsx +32 -0
- package/template/src/services/toast/index.ts +3 -0
- package/template/src/services/toast/toast.ts +68 -0
- package/template/src/storage/mmkvAdapter.ts +3 -0
- package/template/src/storage/mmkvInstance.ts +16 -0
- package/template/src/store/index.ts +15 -0
- package/template/src/store/mmkv.ts +6 -0
- package/template/src/store/persist.ts +10 -0
- package/template/src/store/rootReducer.ts +26 -0
- package/template/src/theme/dark.ts +40 -0
- package/template/src/theme/fonts.ts +20 -0
- package/template/src/theme/index.ts +6 -0
- package/template/src/theme/layout.ts +22 -0
- package/template/src/theme/light.ts +41 -0
- package/template/src/theme/palette.ts +32 -0
- package/template/src/theme/spacing.ts +7 -0
- package/template/src/theme/typography.ts +80 -0
- package/template/src/types/api.types.ts +34 -0
- package/template/src/types/env.d.ts +6 -0
- package/template/src/types/index.ts +2 -0
- package/template/src/types/navigation.types.ts +94 -0
- package/template/src/utils/clearAppSession.ts +10 -0
- package/template/src/utils/date.ts +19 -0
- package/template/src/utils/documentPickerHandler.ts +96 -0
- package/template/src/utils/getFileNameFromUri.ts +18 -0
- package/template/src/utils/getInitials.ts +13 -0
- package/template/src/utils/hindiText.ts +33 -0
- package/template/src/utils/imageCompress.ts +97 -0
- package/template/src/utils/imagePickerHandler.ts +200 -0
- package/template/src/utils/maskPhone.ts +17 -0
- package/template/src/utils/normalize.ts +12 -0
- package/template/src/validation/messages.ts +11 -0
- package/template/src/validation/rules.ts +44 -0
- package/template/src/validation/schema/auth.schema.ts +53 -0
- package/template/src/validation/schema/user.schema.ts +46 -0
- package/template/src/validation/tValError.ts +13 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useQuery } from '@tanstack/react-query';
|
|
3
|
+
import { QUERY_KEYS } from '@/constants/queryKey';
|
|
4
|
+
import { useAppDispatch } from '@/hooks/useAppDispatch';
|
|
5
|
+
import { useAppSelector } from '@/hooks/useAppSelector';
|
|
6
|
+
import { selectIsAuthenticated } from '@/redux/selectors/auth.selectors';
|
|
7
|
+
import { setUserAction } from '@/redux/slice/user.slice';
|
|
8
|
+
import { getProfile } from '@/services/api/user/user.api';
|
|
9
|
+
import { ErrorHandler } from '@/services/errors';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Reference query hook: fetch the signed-in profile, cache it, and mirror it into Redux.
|
|
13
|
+
* Pass `enabled: false` when a screen should not load the profile yet.
|
|
14
|
+
*/
|
|
15
|
+
export const useGetProfile = (enabled = true) => {
|
|
16
|
+
const dispatch = useAppDispatch();
|
|
17
|
+
const isAuthenticated = useAppSelector(selectIsAuthenticated);
|
|
18
|
+
|
|
19
|
+
const query = useQuery({
|
|
20
|
+
queryKey: [QUERY_KEYS.profile],
|
|
21
|
+
queryFn: getProfile,
|
|
22
|
+
enabled: enabled && isAuthenticated,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (query.data?.data) {
|
|
27
|
+
dispatch(setUserAction(query.data.data));
|
|
28
|
+
}
|
|
29
|
+
}, [dispatch, query.data]);
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (query.error) {
|
|
33
|
+
ErrorHandler(query.error);
|
|
34
|
+
}
|
|
35
|
+
}, [query.error]);
|
|
36
|
+
|
|
37
|
+
return query;
|
|
38
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import i18n from '@/i18n';
|
|
2
|
+
import { QUERY_KEYS } from '@/constants/queryKey';
|
|
3
|
+
import { updateProfile } from '@/services/api/user/user.api';
|
|
4
|
+
import { ErrorHandler } from '@/services/errors';
|
|
5
|
+
import { SuccessToast } from '@/services/toast';
|
|
6
|
+
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
7
|
+
|
|
8
|
+
export const useUpdateProfile = () => {
|
|
9
|
+
const queryClient = useQueryClient();
|
|
10
|
+
|
|
11
|
+
return useMutation({
|
|
12
|
+
mutationFn: updateProfile,
|
|
13
|
+
onSuccess: res => {
|
|
14
|
+
SuccessToast({
|
|
15
|
+
title: res.message || i18n.t('profile.updateSuccess'),
|
|
16
|
+
});
|
|
17
|
+
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.profile] });
|
|
18
|
+
},
|
|
19
|
+
onError: ErrorHandler,
|
|
20
|
+
});
|
|
21
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
export function useDebounce<T>(value: T, delay: number = 400): T {
|
|
4
|
+
const [debouncedValue, setDebouncedValue] = useState(value);
|
|
5
|
+
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
const timer = setTimeout(() => {
|
|
8
|
+
setDebouncedValue(value);
|
|
9
|
+
}, delay);
|
|
10
|
+
|
|
11
|
+
return () => clearTimeout(timer);
|
|
12
|
+
}, [value, delay]);
|
|
13
|
+
|
|
14
|
+
return debouncedValue;
|
|
15
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { useState, useCallback, useRef } from 'react';
|
|
2
|
+
import { pickDocument } from '@/utils/documentPickerHandler';
|
|
3
|
+
import { getFileNameFromUri } from '@/utils/getFileNameFromUri';
|
|
4
|
+
import { pickImage } from '@/utils/imagePickerHandler';
|
|
5
|
+
|
|
6
|
+
export type PickedFile = {
|
|
7
|
+
uri: string;
|
|
8
|
+
name?: string;
|
|
9
|
+
type?: string | null;
|
|
10
|
+
width?: number;
|
|
11
|
+
height?: number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
interface UseDocumentPickerOptions {
|
|
15
|
+
multiple?: boolean;
|
|
16
|
+
/** Camera capture max size (photos of documents). */
|
|
17
|
+
maxSize?: number;
|
|
18
|
+
quality?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Document upload flow: files via document picker, camera via image capture.
|
|
23
|
+
*/
|
|
24
|
+
export const useDocumentPicker = (options?: UseDocumentPickerOptions) => {
|
|
25
|
+
const { multiple = false, maxSize = 1024, quality = 85 } = options || {};
|
|
26
|
+
|
|
27
|
+
const [documents, setDocuments] = useState<PickedFile[]>([]);
|
|
28
|
+
const isPickerOpen = useRef(false);
|
|
29
|
+
|
|
30
|
+
const appendOrReplace = useCallback(
|
|
31
|
+
(next: PickedFile[]) => {
|
|
32
|
+
if (!next.length) return;
|
|
33
|
+
if (multiple) {
|
|
34
|
+
setDocuments(prev => [...prev, ...next]);
|
|
35
|
+
} else {
|
|
36
|
+
setDocuments(next);
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
[multiple],
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const pickFromFiles = useCallback(async () => {
|
|
43
|
+
if (isPickerOpen.current) return;
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
isPickerOpen.current = true;
|
|
47
|
+
const result = await pickDocument({ multiple });
|
|
48
|
+
if (result.cancelled || !result.documents.length) return;
|
|
49
|
+
|
|
50
|
+
appendOrReplace(
|
|
51
|
+
result.documents.map(doc => ({
|
|
52
|
+
uri: doc.uri,
|
|
53
|
+
name: doc.name,
|
|
54
|
+
type: doc.type,
|
|
55
|
+
})),
|
|
56
|
+
);
|
|
57
|
+
} finally {
|
|
58
|
+
isPickerOpen.current = false;
|
|
59
|
+
}
|
|
60
|
+
}, [appendOrReplace, multiple]);
|
|
61
|
+
|
|
62
|
+
const captureFromCamera = useCallback(async () => {
|
|
63
|
+
if (isPickerOpen.current) return;
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
isPickerOpen.current = true;
|
|
67
|
+
const result = await pickImage({
|
|
68
|
+
source: 'camera',
|
|
69
|
+
multiple: false,
|
|
70
|
+
maxSize,
|
|
71
|
+
quality,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (result.cancelled || !result.images.length) return;
|
|
75
|
+
|
|
76
|
+
appendOrReplace(
|
|
77
|
+
result.images.map(img => ({
|
|
78
|
+
uri: img.uri,
|
|
79
|
+
width: img.width,
|
|
80
|
+
height: img.height,
|
|
81
|
+
name: getFileNameFromUri(img.uri),
|
|
82
|
+
})),
|
|
83
|
+
);
|
|
84
|
+
} finally {
|
|
85
|
+
isPickerOpen.current = false;
|
|
86
|
+
}
|
|
87
|
+
}, [appendOrReplace, maxSize, quality]);
|
|
88
|
+
|
|
89
|
+
const removeDocument = useCallback((uri: string) => {
|
|
90
|
+
setDocuments(prev => prev.filter(doc => doc.uri !== uri));
|
|
91
|
+
}, []);
|
|
92
|
+
|
|
93
|
+
const resetDocuments = useCallback(() => setDocuments([]), []);
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
documents,
|
|
97
|
+
pickFromFiles,
|
|
98
|
+
captureFromCamera,
|
|
99
|
+
removeDocument,
|
|
100
|
+
resetDocuments,
|
|
101
|
+
};
|
|
102
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { useState, useCallback, useRef } from 'react';
|
|
2
|
+
import { pickImage } from '@/utils/imagePickerHandler';
|
|
3
|
+
|
|
4
|
+
type ImageResult = {
|
|
5
|
+
uri: string;
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
interface UseImagePickerOptions {
|
|
11
|
+
maxSize?: number;
|
|
12
|
+
quality?: number;
|
|
13
|
+
|
|
14
|
+
//For multiple Image select
|
|
15
|
+
multiple?: boolean;
|
|
16
|
+
selectionLimit?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const useImagePicker = (options?: UseImagePickerOptions) => {
|
|
20
|
+
const {
|
|
21
|
+
multiple = false,
|
|
22
|
+
maxSize = 512,
|
|
23
|
+
quality = 80,
|
|
24
|
+
selectionLimit = 0, //0 means unlimited selection
|
|
25
|
+
} = options || {};
|
|
26
|
+
|
|
27
|
+
const [images, setImages] = useState<ImageResult[]>([]);
|
|
28
|
+
const isImagePickerOpen = useRef<boolean>(false);
|
|
29
|
+
|
|
30
|
+
// ─────────────────────────────────────────────
|
|
31
|
+
// Internal picker handler
|
|
32
|
+
// ─────────────────────────────────────────────
|
|
33
|
+
const handlePick = useCallback(
|
|
34
|
+
async (source: 'camera' | 'gallery') => {
|
|
35
|
+
if (isImagePickerOpen.current) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
isImagePickerOpen.current = true;
|
|
40
|
+
|
|
41
|
+
const result = await pickImage({
|
|
42
|
+
source,
|
|
43
|
+
multiple,
|
|
44
|
+
maxSize,
|
|
45
|
+
quality,
|
|
46
|
+
selectionLimit,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (!result.cancelled && result.images.length) {
|
|
50
|
+
if (multiple) {
|
|
51
|
+
setImages((prev: ImageResult[]) => [...prev, ...result.images]);
|
|
52
|
+
} else {
|
|
53
|
+
setImages(result.images);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
} finally {
|
|
57
|
+
isImagePickerOpen.current = false;
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
[multiple, maxSize, quality, selectionLimit],
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
// ─────────────────────────────────────────────
|
|
64
|
+
// Public functions
|
|
65
|
+
// ─────────────────────────────────────────────
|
|
66
|
+
const pickFromGallery = () => handlePick('gallery');
|
|
67
|
+
|
|
68
|
+
const captureFromCamera = () => handlePick('camera');
|
|
69
|
+
|
|
70
|
+
const removeImage = (uri: string) => {
|
|
71
|
+
setImages((prev: ImageResult[]) => prev.filter(img => img.uri !== uri));
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const resetImages = () => setImages([]);
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
images,
|
|
78
|
+
pickFromGallery,
|
|
79
|
+
captureFromCamera,
|
|
80
|
+
removeImage,
|
|
81
|
+
resetImages,
|
|
82
|
+
};
|
|
83
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import i18n from 'i18next';
|
|
2
|
+
import { initReactI18next } from 'react-i18next';
|
|
3
|
+
import { resources } from './resources';
|
|
4
|
+
|
|
5
|
+
i18n.use(initReactI18next).init({
|
|
6
|
+
resources,
|
|
7
|
+
lng: 'en',
|
|
8
|
+
fallbackLng: 'en',
|
|
9
|
+
supportedLngs: ['en', 'ru'],
|
|
10
|
+
interpolation: {
|
|
11
|
+
escapeValue: false,
|
|
12
|
+
},
|
|
13
|
+
compatibilityJSON: 'v4',
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export default i18n;
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
{
|
|
2
|
+
"common": {
|
|
3
|
+
"loading": "Loading...",
|
|
4
|
+
"error": "Error",
|
|
5
|
+
"success": "Success",
|
|
6
|
+
"cancel": "Cancel",
|
|
7
|
+
"confirm": "Confirm",
|
|
8
|
+
"save": "Save",
|
|
9
|
+
"delete": "Delete",
|
|
10
|
+
"edit": "Edit",
|
|
11
|
+
"done": "Done",
|
|
12
|
+
"next": "Next",
|
|
13
|
+
"back": "Back",
|
|
14
|
+
"skip": "Skip",
|
|
15
|
+
"retry": "Retry",
|
|
16
|
+
"ok": "OK",
|
|
17
|
+
"yes": "Yes",
|
|
18
|
+
"no": "No",
|
|
19
|
+
"close": "Close",
|
|
20
|
+
"search": "Search",
|
|
21
|
+
"noResults": "No results found",
|
|
22
|
+
"emptyState": "Nothing here yet",
|
|
23
|
+
"continue": "Continue",
|
|
24
|
+
"info": "Info"
|
|
25
|
+
},
|
|
26
|
+
"auth": {
|
|
27
|
+
"login": "Login",
|
|
28
|
+
"signup": "Sign Up",
|
|
29
|
+
"logout": "Logout",
|
|
30
|
+
"email": "Email",
|
|
31
|
+
"password": "Password",
|
|
32
|
+
"confirmPassword": "Confirm Password",
|
|
33
|
+
"forgotPassword": "Forgot Password?",
|
|
34
|
+
"resetPassword": "Reset Password",
|
|
35
|
+
"rememberMe": "Remember me",
|
|
36
|
+
"dontHaveAccount": "Don't have an account?",
|
|
37
|
+
"alreadyHaveAccount": "Already have an account?",
|
|
38
|
+
"createAccount": "Create Account",
|
|
39
|
+
"welcomeBack": "Welcome back!",
|
|
40
|
+
"signInToContinue": "Sign in to continue",
|
|
41
|
+
"welcome-back": "Welcome Back 👋",
|
|
42
|
+
"reset": "Reset",
|
|
43
|
+
"dont-have-an-account": "Don’t have an account?",
|
|
44
|
+
"create-account": "Create Account",
|
|
45
|
+
"by-continuing-you-agree-to-our": "By continuing, you agree to our",
|
|
46
|
+
"terms-of-service": "Terms of Service",
|
|
47
|
+
"privacy-policy": "Privacy Policy",
|
|
48
|
+
"lets-get-started": "Let’s Get Started 🚀",
|
|
49
|
+
"send-now": "Send Now"
|
|
50
|
+
},
|
|
51
|
+
"user": {
|
|
52
|
+
"name": "Name",
|
|
53
|
+
"phone-number": "Phone number",
|
|
54
|
+
"gender": "Gender",
|
|
55
|
+
"date-of-birth": "Date of Birth",
|
|
56
|
+
"current-time": "Current time",
|
|
57
|
+
"address": "Address",
|
|
58
|
+
"enter-your-address": "Enter your address"
|
|
59
|
+
},
|
|
60
|
+
"navigation": {
|
|
61
|
+
"home": "Home",
|
|
62
|
+
"search": "Search",
|
|
63
|
+
"notifications": "Notifications",
|
|
64
|
+
"profile": "Profile",
|
|
65
|
+
"settings": "Settings"
|
|
66
|
+
},
|
|
67
|
+
"home": {
|
|
68
|
+
"greeting": "Hello, {{name}}",
|
|
69
|
+
"welcomeBack": "Welcome back!",
|
|
70
|
+
"recentActivity": "Recent Activity",
|
|
71
|
+
"noActivity": "No recent activity",
|
|
72
|
+
"quickActions": "Quick Actions"
|
|
73
|
+
},
|
|
74
|
+
"profile": {
|
|
75
|
+
"editProfile": "Edit Profile",
|
|
76
|
+
"myAccount": "My Account",
|
|
77
|
+
"preferences": "Preferences",
|
|
78
|
+
"privacy": "Privacy & Security",
|
|
79
|
+
"appearance": "Appearance",
|
|
80
|
+
"notifications": "Notifications",
|
|
81
|
+
"help": "Help & Support",
|
|
82
|
+
"about": "About",
|
|
83
|
+
"termsOfService": "Terms of Service",
|
|
84
|
+
"privacyPolicy": "Privacy Policy",
|
|
85
|
+
"rateApp": "Rate App",
|
|
86
|
+
"version": "Version {{version}}",
|
|
87
|
+
"updateSuccess": "Profile updated successfully"
|
|
88
|
+
},
|
|
89
|
+
"settings": {
|
|
90
|
+
"general": "General",
|
|
91
|
+
"account": "Account",
|
|
92
|
+
"notifications": "Notifications",
|
|
93
|
+
"privacy": "Privacy",
|
|
94
|
+
"language": "Language",
|
|
95
|
+
"theme": "Theme",
|
|
96
|
+
"darkMode": "Dark Mode",
|
|
97
|
+
"lightMode": "Light Mode",
|
|
98
|
+
"systemDefault": "System Default",
|
|
99
|
+
"changePassword": "Change Password",
|
|
100
|
+
"deleteAccount": "Delete Account",
|
|
101
|
+
"deleteAccountWarning": "This action cannot be undone. All your data will be permanently deleted."
|
|
102
|
+
},
|
|
103
|
+
"validation": {
|
|
104
|
+
"required": "{{field}} is required",
|
|
105
|
+
"minLength": "{{field}} must be at least {{min}} characters",
|
|
106
|
+
"maxLength": "{{field}} must be at most {{max}} characters",
|
|
107
|
+
"invalidEmail": "Please enter a valid email address",
|
|
108
|
+
"invalidPhone": "Please enter a valid phone number",
|
|
109
|
+
"hasLetter": "{{field}} must contain at least one letter",
|
|
110
|
+
"invalidZip": "Invalid zip code",
|
|
111
|
+
"strongPassword": "Password must include upper, lower, number & symbol",
|
|
112
|
+
"selectOption": "Please select an option for {{field}}"
|
|
113
|
+
},
|
|
114
|
+
"alerts": {
|
|
115
|
+
"logoutTitle": "Logout",
|
|
116
|
+
"logoutMessage": "Are you sure you want to logout?",
|
|
117
|
+
"deleteTitle": "Delete",
|
|
118
|
+
"deleteMessage": "Are you sure you want to delete this item?",
|
|
119
|
+
"unsavedChangesTitle": "Unsaved Changes",
|
|
120
|
+
"unsavedChangesMessage": "You have unsaved changes. Are you sure you want to leave?"
|
|
121
|
+
},
|
|
122
|
+
"placeholder": {
|
|
123
|
+
"search": "Search...",
|
|
124
|
+
"enterEmail": "Enter your email",
|
|
125
|
+
"enterPassword": "Enter your password",
|
|
126
|
+
"enterName": "Enter your name",
|
|
127
|
+
"enterPhone": "Enter your phone number",
|
|
128
|
+
"select-your-gender": "Select your gender",
|
|
129
|
+
"select-your-date-of-birth": "Select your date of birth",
|
|
130
|
+
"enter-your-current-time": "Enter your current time",
|
|
131
|
+
"enterAddress": "Enter your address"
|
|
132
|
+
},
|
|
133
|
+
"apiErrors": {
|
|
134
|
+
"userNotFound": "We couldn’t find an account with this information.",
|
|
135
|
+
"invalidToken": "Your session has expired. Please log in again.",
|
|
136
|
+
"emailExists": "An account with this email already exists.",
|
|
137
|
+
"weakPassword": "Your password is too weak. Please choose a stronger one.",
|
|
138
|
+
"accountDisabled": "Your account has been disabled. Please contact support."
|
|
139
|
+
},
|
|
140
|
+
"errors": {
|
|
141
|
+
"generic": "Something went wrong. Please try again.",
|
|
142
|
+
"network": "Network error. Please check your connection.",
|
|
143
|
+
"server": "Server error. Please try again later.",
|
|
144
|
+
"unauthorized": "You are not authorized. Please log in again.",
|
|
145
|
+
"notFound": "The requested resource could not be found.",
|
|
146
|
+
"validation": "Some information is invalid. Please check your input."
|
|
147
|
+
},
|
|
148
|
+
"firebase": {
|
|
149
|
+
"tooManyRequests": "Too many attempts. Please wait a moment and try again.",
|
|
150
|
+
"claimsTooLarge": "Something went wrong while verifying your account.",
|
|
151
|
+
"emailExists": "An account with this email already exists.",
|
|
152
|
+
"userNotFound": "No account found with this email.",
|
|
153
|
+
"wrongPassword": "The password you entered is incorrect.",
|
|
154
|
+
"emailAlreadyInUse": "This email is already linked to another account.",
|
|
155
|
+
"insufficientPermission": "You don’t have permission to perform this action.",
|
|
156
|
+
"internalError": "Something went wrong. Please try again.",
|
|
157
|
+
"invalidArgument": "Some information is invalid. Please check and try again.",
|
|
158
|
+
"invalidClaims": "Your account data could not be verified.",
|
|
159
|
+
"invalidContinueUri": "This action link is no longer valid.",
|
|
160
|
+
"invalidCreationTime": "There was an issue with your account data.",
|
|
161
|
+
"invalidCredential": "Your login details are invalid.",
|
|
162
|
+
"invalidDisabledField": "Some required information is missing.",
|
|
163
|
+
"invalidDisplayName": "Please enter a valid display name.",
|
|
164
|
+
"invalidDynamicLinkDomain": "This sign-in link is not supported.",
|
|
165
|
+
"invalidEmail": "Please enter a valid email address.",
|
|
166
|
+
"invalidEmailVerified": "Your email could not be verified.",
|
|
167
|
+
"invalidHashAlgorithm": "There was a security verification issue.",
|
|
168
|
+
"invalidHashBlockSize": "There was a security verification issue.",
|
|
169
|
+
"invalidHashDerivedKeyLength": "There was a security verification issue.",
|
|
170
|
+
"invalidHashKey": "There was a security verification issue.",
|
|
171
|
+
"invalidHashMemoryCost": "There was a security verification issue.",
|
|
172
|
+
"invalidHashParallelization": "There was a security verification issue.",
|
|
173
|
+
"invalidHashRounds": "There was a security verification issue.",
|
|
174
|
+
"invalidHashSaltSeparator": "There was a security verification issue.",
|
|
175
|
+
"invalidIdToken": "Your session has expired. Please sign in again.",
|
|
176
|
+
"invalidLastSignInTime": "We couldn’t verify your last login.",
|
|
177
|
+
"invalidPageToken": "We couldn’t load your data. Please try again.",
|
|
178
|
+
"invalidPassword": "Your password is not valid.",
|
|
179
|
+
"invalidPasswordHash": "There was a security issue. Please reset your password.",
|
|
180
|
+
"invalidPasswordSalt": "There was a security issue. Please reset your password.",
|
|
181
|
+
"invalidPhoneNumber": "Please enter a valid phone number.",
|
|
182
|
+
"invalidPhotoUrl": "Your profile image is not valid.",
|
|
183
|
+
"invalidProviderData": "Your sign-in provider data is invalid.",
|
|
184
|
+
"invalidProviderId": "This sign-in method is not supported.",
|
|
185
|
+
"invalidOauthResponseType": "This sign-in method is not supported.",
|
|
186
|
+
"invalidSessionCookieDuration": "Your session settings are invalid.",
|
|
187
|
+
"invalidUid": "We couldn’t verify your account.",
|
|
188
|
+
"invalidUserImport": "There was an issue importing your account.",
|
|
189
|
+
"maximumUserCountExceeded": "User limit reached. Please contact support.",
|
|
190
|
+
"missingAndroidPkgName": "This action is not supported on your device.",
|
|
191
|
+
"missingContinueUri": "This action link is incomplete.",
|
|
192
|
+
"missingHashAlgorithm": "There was a security verification issue.",
|
|
193
|
+
"missingIosBundleId": "This action is not supported on your device.",
|
|
194
|
+
"missingUid": "We couldn’t verify your account.",
|
|
195
|
+
"missingOauthClientSecret": "Configuration issue. Please try again later.",
|
|
196
|
+
"operationNotAllowed": "This action is currently disabled.",
|
|
197
|
+
"phoneNumberExists": "This phone number is already linked to another account.",
|
|
198
|
+
"projectNotFound": "App configuration error. Please try again later.",
|
|
199
|
+
"reservedClaims": "Your account could not be verified.",
|
|
200
|
+
"sessionCookieExpired": "Your session has expired. Please sign in again.",
|
|
201
|
+
"sessionCookieRevoked": "Your session has been revoked. Please sign in again.",
|
|
202
|
+
"uidExists": "This account already exists.",
|
|
203
|
+
"unauthorizedContinueUri": "This sign-in link is not authorized.",
|
|
204
|
+
"networkFailed": "Network error. Please check your connection.",
|
|
205
|
+
"developerError": "App configuration issue. Please contact support."
|
|
206
|
+
},
|
|
207
|
+
"http": {
|
|
208
|
+
"100": "Please wait, the request is still being processed.",
|
|
209
|
+
"101": "Connection is being updated. Please try again.",
|
|
210
|
+
"200": "Request completed successfully.",
|
|
211
|
+
"201": "Created successfully.",
|
|
212
|
+
"202": "Your request is being processed.",
|
|
213
|
+
"203": "Some information may be outdated.",
|
|
214
|
+
"204": "No content available for this request.",
|
|
215
|
+
"205": "Please refresh the page.",
|
|
216
|
+
"206": "Only part of the data was loaded. Try again.",
|
|
217
|
+
"300": "Multiple options found for this request.",
|
|
218
|
+
"301": "This resource has moved permanently.",
|
|
219
|
+
"302": "This resource has moved temporarily.",
|
|
220
|
+
"303": "Please follow the new link provided.",
|
|
221
|
+
"304": "Nothing has changed since your last request.",
|
|
222
|
+
"305": "Unable to access this resource right now.",
|
|
223
|
+
"307": "This resource is temporarily unavailable.",
|
|
224
|
+
"400": "Invalid request. Please check your input.",
|
|
225
|
+
"401": "Your session has expired. Please log in again.",
|
|
226
|
+
"402": "Payment is required to continue.",
|
|
227
|
+
"403": "You do not have permission to perform this action.",
|
|
228
|
+
"404": "We couldn’t find what you were looking for.",
|
|
229
|
+
"405": "This action is not allowed.",
|
|
230
|
+
"406": "The server cannot return the requested format.",
|
|
231
|
+
"407": "Authentication is required to proceed.",
|
|
232
|
+
"408": "The request timed out. Please try again.",
|
|
233
|
+
"409": "There was a conflict. Please try again.",
|
|
234
|
+
"410": "This resource is no longer available.",
|
|
235
|
+
"411": "Required information is missing.",
|
|
236
|
+
"412": "The request conditions were not met.",
|
|
237
|
+
"413": "The data is too large to process.",
|
|
238
|
+
"414": "The request URL is too long.",
|
|
239
|
+
"415": "Unsupported file or data type.",
|
|
240
|
+
"416": "Requested range is not available.",
|
|
241
|
+
"417": "The server could not meet the request expectations.",
|
|
242
|
+
"422": "Some information is invalid. Please check your input.",
|
|
243
|
+
"426": "Please update your app to continue.",
|
|
244
|
+
"428": "Additional conditions are required to continue.",
|
|
245
|
+
"429": "Too many requests. Please slow down.",
|
|
246
|
+
"431": "Request headers are too large.",
|
|
247
|
+
"500": "Something went wrong on our side. Please try again later.",
|
|
248
|
+
"501": "This feature is not supported yet.",
|
|
249
|
+
"502": "Server connection failed. Please try again.",
|
|
250
|
+
"503": "Service is temporarily unavailable. Try again later.",
|
|
251
|
+
"504": "Server took too long to respond. Try again.",
|
|
252
|
+
"505": "Your request could not be processed due to a protocol issue."
|
|
253
|
+
},
|
|
254
|
+
"home-screen": "Home Screen",
|
|
255
|
+
"network": {
|
|
256
|
+
"unavailableTitle": "No internet connection",
|
|
257
|
+
"unavailableMessage": "Internet is not available. Please check your connection.",
|
|
258
|
+
"unavailableBody": "Please check your Wi-Fi or mobile data and try again."
|
|
259
|
+
},
|
|
260
|
+
"imagePicker": {
|
|
261
|
+
"title": "Add a photo",
|
|
262
|
+
"subtitle": "Pick a clear photo for your profile.",
|
|
263
|
+
"camera": "Take photo",
|
|
264
|
+
"cameraHint": "Capture a new picture with your camera",
|
|
265
|
+
"gallery": "Choose from gallery",
|
|
266
|
+
"galleryHint": "Select an existing photo from your device",
|
|
267
|
+
"docTitle": "Upload a file",
|
|
268
|
+
"docSubtitle": "You can upload images, PDFs, and other documents.",
|
|
269
|
+
"docCamera": "Take photo",
|
|
270
|
+
"docCameraHint": "Capture a new photo with your camera",
|
|
271
|
+
"docGallery": "Choose from device",
|
|
272
|
+
"docGalleryHint": "Select an image, PDF, or other document"
|
|
273
|
+
}
|
|
274
|
+
}
|