@fadyshawky/react-native-magic 2.0.4 → 2.0.5
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/package.json +1 -1
- package/template/src/common/ImageResources.g.ts +33 -1
- package/template/src/common/components/Background.tsx +3 -1
- package/template/src/common/components/Container.tsx +1 -1
- package/template/src/common/components/OTPInput.tsx +3 -2
- package/template/src/common/components/PrimaryButton.tsx +23 -23
- package/template/src/common/components/PrimaryTextInput.tsx +189 -199
- package/template/src/common/components/RadioIcon.tsx +4 -4
- package/template/src/common/components/SafeText.tsx +41 -0
- package/template/src/common/components/SearchBar.tsx +19 -17
- package/template/src/common/components/TryAgain.tsx +3 -3
- package/template/src/common/localization/localization.ts +10 -0
- package/template/src/common/localization/translations/commonLocalization.ts +97 -0
- package/template/src/common/localization/translations/homeLocalization.ts +24 -0
- package/template/src/common/localization/translations/loginLocalization.ts +28 -2
- package/template/src/common/localization/translations/mainNavigationLocalization.ts +30 -0
- package/template/src/common/localization/translations/navigationLocalization.ts +48 -0
- package/template/src/common/localization/translations/otpLocalization.ts +28 -0
- package/template/src/common/localization/translations/passwordLocalization.ts +54 -0
- package/template/src/common/localization/translations/posLocalization.ts +196 -0
- package/template/src/common/utils/FeesCaalculation.tsx +37 -0
- package/template/src/common/utils/index.tsx +11 -0
- package/template/src/common/utils/printData.tsx +161 -0
- package/template/src/common/validations/errorValidations.ts +3 -2
- package/template/src/components/PrinterExample.js +226 -0
- package/template/src/core/api/serverHeaders.ts +62 -1
- package/template/src/core/store/Categories/categoryActions.ts +33 -0
- package/template/src/core/store/Categories/categorySlice.ts +75 -0
- package/template/src/core/store/Categories/categoryState.ts +41 -0
- package/template/src/core/store/Providers/providersActions.ts +102 -0
- package/template/src/core/store/Providers/providersSlice.ts +136 -0
- package/template/src/core/store/Providers/providersState.ts +37 -0
- package/template/src/core/store/Services/servicesActions.ts +191 -0
- package/template/src/core/store/Services/servicesSlice.ts +205 -0
- package/template/src/core/store/Services/servicesState.ts +466 -0
- package/template/src/core/store/app/appSlice.ts +13 -5
- package/template/src/core/store/app/appState.ts +10 -2
- package/template/src/core/store/rootReducer.ts +6 -1
- package/template/src/core/store/store.tsx +55 -2
- package/template/src/core/store/user/userActions.ts +164 -26
- package/template/src/core/store/user/userSlice.ts +193 -21
- package/template/src/core/store/user/userState.ts +148 -25
- package/template/src/core/theme/colors.ts +70 -94
- package/template/src/core/theme/commonConsts.ts +1 -1
- package/template/src/core/theme/commonSizes.ts +94 -119
- package/template/src/core/theme/commonStyles.ts +22 -22
- package/template/src/core/theme/fonts.ts +14 -13
- package/template/src/core/theme/themes.ts +75 -386
- package/template/src/core/theme/types.ts +15 -201
- package/template/src/core/utils/stringUtils.ts +114 -0
- package/template/src/modules/SunmiCard.js +212 -0
- package/template/src/modules/SunmiPrepaid.ts +122 -0
- package/template/src/navigation/AuthStack.tsx +8 -0
- package/template/src/navigation/HeaderComponents.tsx +76 -1
- package/template/src/navigation/MainNavigation.tsx +3 -1
- package/template/src/navigation/MainStack.tsx +130 -56
- package/template/src/navigation/TabBar.tsx +111 -59
- package/template/src/navigation/types.ts +24 -0
- package/template/src/screens/Categories/Categories.tsx +141 -0
- package/template/src/screens/Categories/hooks/useCategoriesData.ts +33 -0
- package/template/src/screens/Categories/types.ts +7 -0
- package/template/src/screens/Favorites/Favorites.tsx +130 -0
- package/template/src/screens/ForceChangePassword/ForceChangePasswordScreen.tsx +155 -0
- package/template/src/screens/History/History.tsx +430 -0
- package/template/src/screens/History/hooks/useHistoryData.ts +49 -0
- package/template/src/screens/History/types.ts +7 -0
- package/template/src/screens/InquiredBill/InquiredBill.tsx +443 -0
- package/template/src/screens/InquiredBill/hooks/useInquiredData.ts +91 -0
- package/template/src/screens/Login/Login.tsx +85 -85
- package/template/src/screens/OTP/OTPScreen.tsx +170 -0
- package/template/src/screens/PaymentConfirmation/PaymentConfirmation.tsx +326 -0
- package/template/src/screens/Providers/Providers.tsx +166 -0
- package/template/src/screens/Providers/hooks/useProvidersData.ts +33 -0
- package/template/src/screens/Providers/types.ts +7 -0
- package/template/src/screens/ReceiptScreen/ReceiptScreen.tsx +181 -0
- package/template/src/screens/ReceiptScreen/hooks/useReceiptData.ts +46 -0
- package/template/src/screens/ReceiptScreen/utils/utils.tsx +156 -0
- package/template/src/screens/Services/Services.tsx +144 -0
- package/template/src/screens/Services/hooks/useServicesData.ts +41 -0
- package/template/src/screens/SingleService/Components/FawryInputs.tsx +446 -0
- package/template/src/screens/SingleService/SingleService.tsx +229 -0
- package/template/src/screens/SingleService/hooks/useServiceData.ts +164 -0
- package/template/src/screens/home/Components/PayByCode.tsx +129 -0
- package/template/src/screens/home/HomeScreen.tsx +268 -77
- package/template/src/screens/home/hooks/useHomeData.ts +32 -38
- package/template/src/screens/index.tsx +24 -0
- package/template/src/screens/profile/Profile.tsx +290 -2
- package/template/src/services/SunmiPrinterInternal.js +268 -0
- package/template/src/types/sunmiPrepaid.d.ts +20 -0
- package/template/src/utils/SunmiPrinter.ts +442 -0
- package/template/src/utils/feesCalculator.ts +92 -0
- package/template/src/common/components/Stepper.tsx +0 -153
- package/template/src/common/components/Svg.tsx +0 -25
- package/template/src/common/hooks/useDebounce.ts +0 -17
- package/template/src/common/hooks/usePrevious.ts +0 -11
- package/template/src/common/localization/intlFormatter.ts +0 -37
- package/template/src/common/urls/emailUrl.ts +0 -20
- package/template/src/common/urls/mapUrl.ts +0 -22
- package/template/src/common/utils/listHandlers.ts +0 -30
- package/template/src/common/utils/serializeQueryParams.ts +0 -10
- package/template/src/common/validations/hooks/useDatesError.ts +0 -40
- package/template/src/common/validations/profileValidations.ts +0 -30
- package/template/src/core/theme/shadows.ts +0 -135
- package/template/src/navigation/TopTabBar.tsx +0 -77
- package/template/src/screens/Settings/Settings.tsx +0 -5
- package/template/src/screens/home/components/CarouselSection.tsx +0 -79
- package/template/src/screens/home/components/FeaturedCarousel.tsx +0 -128
- package/template/src/screens/main/Main.tsx +0 -5
- package/template/src/screens/registration/RegistrationScreen.tsx +0 -198
- package/template/src/screens/resetPassword/ForgotPasswordScreen.tsx +0 -129
|
@@ -1,39 +1,34 @@
|
|
|
1
|
-
import {useState
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import {useState} from 'react';
|
|
2
|
+
import {getCategories} from '../../../core/store/Categories/categoryActions';
|
|
3
|
+
import {clearSelectedCategory} from '../../../core/store/Categories/categorySlice';
|
|
4
|
+
import {getHomeProviders} from '../../../core/store/Providers/providersActions';
|
|
5
|
+
import {useAppDispatch, useAppSelector} from '../../../core/store/reduxHelpers';
|
|
6
|
+
import {RootState} from '../../../core/store/rootReducer';
|
|
7
|
+
import {getBalance} from '../../../core/store/user/userActions';
|
|
8
|
+
import {clearSelectedProvider} from '../../../core/store/Providers/providersSlice';
|
|
4
9
|
export function useHomeData() {
|
|
5
10
|
const [isLoading, setIsLoading] = useState(false);
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
const dispatch = useAppDispatch();
|
|
12
|
+
const {categories, loadState} = useAppSelector(
|
|
13
|
+
(state: RootState) => state.categories,
|
|
14
|
+
);
|
|
15
|
+
const [isPayByCodeModalVisible, setIsPayByCodeModalVisible] = useState(false);
|
|
16
|
+
const {homeProviders} = useAppSelector((state: RootState) => state.providers);
|
|
17
|
+
const {user} = useAppSelector((state: RootState) => state.user);
|
|
10
18
|
|
|
11
19
|
const fetchData = async () => {
|
|
12
20
|
setIsLoading(true);
|
|
13
21
|
try {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
]);
|
|
25
|
-
|
|
26
|
-
setTrendingItems([
|
|
27
|
-
{
|
|
28
|
-
id: '1',
|
|
29
|
-
title: 'Trending Item 1',
|
|
30
|
-
subtitle: 'Hot right now',
|
|
31
|
-
imageUrl: 'https://picsum.photos/700/400',
|
|
32
|
-
},
|
|
33
|
-
// Add more items...
|
|
34
|
-
]);
|
|
35
|
-
|
|
36
|
-
// Similar for newItems and recommendedItems...
|
|
22
|
+
const balance = await dispatch(getBalance());
|
|
23
|
+
if (balance.type.includes('fulfilled')) {
|
|
24
|
+
const categoryResponse = await dispatch(
|
|
25
|
+
getCategories({data: {limit: 30}}),
|
|
26
|
+
);
|
|
27
|
+
if (categoryResponse.type.includes('fulfilled')) {
|
|
28
|
+
const category_id = categoryResponse?.payload?.categories?.[0]?.id;
|
|
29
|
+
await dispatch(getHomeProviders({data: {category_id: category_id}}));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
37
32
|
} catch (error) {
|
|
38
33
|
console.error('Error fetching home data:', error);
|
|
39
34
|
} finally {
|
|
@@ -45,16 +40,15 @@ export function useHomeData() {
|
|
|
45
40
|
fetchData();
|
|
46
41
|
};
|
|
47
42
|
|
|
48
|
-
useEffect(() => {
|
|
49
|
-
fetchData();
|
|
50
|
-
}, []);
|
|
51
|
-
|
|
52
43
|
return {
|
|
53
44
|
isLoading,
|
|
54
|
-
featuredItems,
|
|
55
|
-
trendingItems,
|
|
56
|
-
newItems,
|
|
57
|
-
recommendedItems,
|
|
58
45
|
refreshData,
|
|
46
|
+
fetchData,
|
|
47
|
+
categories,
|
|
48
|
+
user,
|
|
49
|
+
homeProviders,
|
|
50
|
+
loadState,
|
|
51
|
+
isPayByCodeModalVisible,
|
|
52
|
+
setIsPayByCodeModalVisible,
|
|
59
53
|
};
|
|
60
54
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Authentication Screens
|
|
2
|
+
export {Login} from './Login/Login';
|
|
3
|
+
export {OTPScreen as OTP} from './OTP/OTPScreen';
|
|
4
|
+
export {ForceChangePasswordScreen as ForceChangePassword} from './ForceChangePassword/ForceChangePasswordScreen';
|
|
5
|
+
|
|
6
|
+
// Main Navigation Screens
|
|
7
|
+
export {HomeScreen as Home} from './home/HomeScreen';
|
|
8
|
+
export {Profile} from './profile/Profile';
|
|
9
|
+
export {Splash} from './splash/Splash';
|
|
10
|
+
|
|
11
|
+
// Service Related Screens
|
|
12
|
+
export {Categories} from './Categories/Categories';
|
|
13
|
+
export {Services} from './Services/Services';
|
|
14
|
+
export {SingleService} from './SingleService/SingleService';
|
|
15
|
+
export {Providers} from './Providers/Providers';
|
|
16
|
+
export {InquiredBill} from './InquiredBill/InquiredBill';
|
|
17
|
+
export {PaymentConfirmation} from './PaymentConfirmation/PaymentConfirmation';
|
|
18
|
+
|
|
19
|
+
// History and Receipt Screens
|
|
20
|
+
export {History} from './History/History';
|
|
21
|
+
export {Favorites} from './Favorites/Favorites';
|
|
22
|
+
|
|
23
|
+
// Device and Card Related Screens
|
|
24
|
+
export {ReceiptScreen} from './ReceiptScreen/ReceiptScreen';
|
|
@@ -1,5 +1,293 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, {useState} from 'react';
|
|
2
|
+
import {
|
|
3
|
+
ScrollView,
|
|
4
|
+
StyleSheet,
|
|
5
|
+
Switch,
|
|
6
|
+
TouchableOpacity,
|
|
7
|
+
View,
|
|
8
|
+
Text,
|
|
9
|
+
Alert,
|
|
10
|
+
Image,
|
|
11
|
+
} from 'react-native';
|
|
12
|
+
import {ImageResources} from '../../common/ImageResources.g';
|
|
13
|
+
import {Container} from '../../common/components/Container';
|
|
14
|
+
import {RTLAwareText} from '../../common/components/RTLAwareText';
|
|
15
|
+
import {RTLAwareView} from '../../common/components/RTLAwareView';
|
|
16
|
+
import {
|
|
17
|
+
useLocalization,
|
|
18
|
+
useRTL,
|
|
19
|
+
useTranslation,
|
|
20
|
+
} from '../../common/localization/LocalizationProvider';
|
|
21
|
+
import {Languages} from '../../common/localization/localization';
|
|
22
|
+
import {useTheme} from '../../core/theme/ThemeProvider';
|
|
23
|
+
import {Theme} from '../../core/theme/types';
|
|
24
|
+
import {scaleWidth, scaleHeight} from '../../core/theme/scaling';
|
|
25
|
+
import {useAppDispatch, useAppSelector} from '../../core/store/reduxHelpers';
|
|
26
|
+
import {setProvidersLogout} from '../../core/store/Providers/providersSlice';
|
|
27
|
+
import {setServiceLogout} from '../../core/store/Services/servicesSlice';
|
|
28
|
+
import {setCategoriesLogout} from '../../core/store/Categories/categorySlice';
|
|
29
|
+
import {setLogout} from '../../core/store/user/userSlice';
|
|
30
|
+
import DeviceInfo from 'react-native-device-info';
|
|
31
|
+
|
|
32
|
+
function SwitchButton({
|
|
33
|
+
onValueChange,
|
|
34
|
+
value,
|
|
35
|
+
}: {
|
|
36
|
+
onValueChange: () => void;
|
|
37
|
+
value: boolean;
|
|
38
|
+
}) {
|
|
39
|
+
const {theme} = useTheme();
|
|
40
|
+
return (
|
|
41
|
+
<TouchableOpacity
|
|
42
|
+
onPressIn={() => {
|
|
43
|
+
onValueChange();
|
|
44
|
+
}}
|
|
45
|
+
style={{
|
|
46
|
+
width: scaleWidth(100),
|
|
47
|
+
height: scaleHeight(53),
|
|
48
|
+
backgroundColor: theme.colors.mutedLavender,
|
|
49
|
+
borderRadius: theme.borderRadius.xl,
|
|
50
|
+
paddingHorizontal: scaleWidth(5),
|
|
51
|
+
justifyContent: 'center',
|
|
52
|
+
alignItems: 'center',
|
|
53
|
+
}}>
|
|
54
|
+
<View
|
|
55
|
+
style={{
|
|
56
|
+
width: scaleWidth(42),
|
|
57
|
+
height: scaleWidth(42),
|
|
58
|
+
backgroundColor: theme.colors.indigoBlue,
|
|
59
|
+
borderRadius: theme.borderRadius.xl,
|
|
60
|
+
alignSelf: value ? 'flex-end' : 'flex-start',
|
|
61
|
+
}}
|
|
62
|
+
/>
|
|
63
|
+
</TouchableOpacity>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
2
66
|
|
|
3
67
|
export function Profile(): JSX.Element {
|
|
4
|
-
|
|
68
|
+
const {theme, toggleTheme} = useTheme();
|
|
69
|
+
const {currentLanguage, changeLanguage} = useLocalization();
|
|
70
|
+
const t = useTranslation();
|
|
71
|
+
const isRTL = useRTL();
|
|
72
|
+
const isArabic = currentLanguage === Languages.ar;
|
|
73
|
+
const isDarkMode = theme.mode === 'dark';
|
|
74
|
+
const {language: storedLanguage} = useAppSelector(state => state.app);
|
|
75
|
+
const dispatch = useAppDispatch();
|
|
76
|
+
|
|
77
|
+
const styles = createStyles(theme);
|
|
78
|
+
|
|
79
|
+
const handleLanguageToggle = () => {
|
|
80
|
+
const newLanguage = isArabic ? Languages.en : Languages.ar;
|
|
81
|
+
|
|
82
|
+
Alert.alert(
|
|
83
|
+
t('languageChangeTitle', 'profile'),
|
|
84
|
+
t('languageChangeMessage', 'profile'),
|
|
85
|
+
[
|
|
86
|
+
{
|
|
87
|
+
text: t('cancel', 'common'),
|
|
88
|
+
style: 'cancel',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
text: t('change', 'common'),
|
|
92
|
+
onPress: () => changeLanguage(newLanguage),
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
{cancelable: true},
|
|
96
|
+
);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
function logout(): any {
|
|
100
|
+
throw new Error('Function not implemented.');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<Container
|
|
105
|
+
backgroundImage={ImageResources[`${theme.mode}_background_1`]}
|
|
106
|
+
withoutPadding
|
|
107
|
+
backgroundColor={theme.colors.background}>
|
|
108
|
+
<ScrollView showsVerticalScrollIndicator={false}>
|
|
109
|
+
<View style={styles.titleSection}>
|
|
110
|
+
<RTLAwareText style={styles.mainTitle}>
|
|
111
|
+
{t('account', 'profile')}
|
|
112
|
+
</RTLAwareText>
|
|
113
|
+
</View>
|
|
114
|
+
|
|
115
|
+
<View style={styles.menuSection}>
|
|
116
|
+
<TouchableOpacity style={styles.menuItem}>
|
|
117
|
+
<View style={styles.menuItemLeft}>
|
|
118
|
+
<Image source={ImageResources.account} style={styles.menuIcon} />
|
|
119
|
+
<RTLAwareText style={theme.text.header2}>
|
|
120
|
+
{t('editProfile', 'profile')}
|
|
121
|
+
</RTLAwareText>
|
|
122
|
+
</View>
|
|
123
|
+
</TouchableOpacity>
|
|
124
|
+
|
|
125
|
+
<RTLAwareView style={styles.menuItem}>
|
|
126
|
+
<View
|
|
127
|
+
style={{
|
|
128
|
+
flexDirection: 'row',
|
|
129
|
+
alignItems: 'center',
|
|
130
|
+
justifyContent: 'space-between',
|
|
131
|
+
}}>
|
|
132
|
+
<Image
|
|
133
|
+
source={ImageResources.en}
|
|
134
|
+
style={{
|
|
135
|
+
...styles.menuIcon,
|
|
136
|
+
marginRight: scaleWidth(10),
|
|
137
|
+
width: scaleWidth(48),
|
|
138
|
+
height: scaleWidth(48),
|
|
139
|
+
}}
|
|
140
|
+
/>
|
|
141
|
+
<SwitchButton
|
|
142
|
+
onValueChange={handleLanguageToggle}
|
|
143
|
+
value={isArabic}
|
|
144
|
+
/>
|
|
145
|
+
<Image
|
|
146
|
+
tintColor={theme.colors.tintColor}
|
|
147
|
+
source={ImageResources.ar}
|
|
148
|
+
style={{
|
|
149
|
+
...styles.menuIcon,
|
|
150
|
+
marginLeft: scaleWidth(10),
|
|
151
|
+
width: scaleWidth(48),
|
|
152
|
+
height: scaleWidth(48),
|
|
153
|
+
}}
|
|
154
|
+
/>
|
|
155
|
+
</View>
|
|
156
|
+
<View style={styles.menuItemLeft}>
|
|
157
|
+
<Image
|
|
158
|
+
tintColor={theme.colors.tintColor}
|
|
159
|
+
source={ImageResources.language}
|
|
160
|
+
style={styles.menuIcon}
|
|
161
|
+
/>
|
|
162
|
+
<RTLAwareText style={theme.text.header2}>
|
|
163
|
+
{t('language', 'profile')}
|
|
164
|
+
</RTLAwareText>
|
|
165
|
+
</View>
|
|
166
|
+
</RTLAwareView>
|
|
167
|
+
|
|
168
|
+
<RTLAwareView style={styles.menuItem}>
|
|
169
|
+
<View
|
|
170
|
+
style={{
|
|
171
|
+
flexDirection: 'row',
|
|
172
|
+
alignItems: 'center',
|
|
173
|
+
justifyContent: 'space-between',
|
|
174
|
+
}}>
|
|
175
|
+
<Image
|
|
176
|
+
tintColor={theme.colors.tintColor}
|
|
177
|
+
source={ImageResources.light}
|
|
178
|
+
style={{
|
|
179
|
+
...styles.menuIcon,
|
|
180
|
+
marginRight: scaleWidth(10),
|
|
181
|
+
width: scaleWidth(48),
|
|
182
|
+
height: scaleWidth(48),
|
|
183
|
+
}}
|
|
184
|
+
/>
|
|
185
|
+
<SwitchButton onValueChange={toggleTheme} value={isDarkMode} />
|
|
186
|
+
<Image
|
|
187
|
+
tintColor={theme.colors.tintColor}
|
|
188
|
+
source={ImageResources.dark}
|
|
189
|
+
style={{
|
|
190
|
+
...styles.menuIcon,
|
|
191
|
+
marginLeft: scaleWidth(10),
|
|
192
|
+
width: scaleWidth(48),
|
|
193
|
+
height: scaleWidth(48),
|
|
194
|
+
}}
|
|
195
|
+
/>
|
|
196
|
+
</View>
|
|
197
|
+
<View style={styles.menuItemLeft}>
|
|
198
|
+
<Image
|
|
199
|
+
tintColor={theme.colors.tintColor}
|
|
200
|
+
source={ImageResources.display}
|
|
201
|
+
style={styles.menuIcon}
|
|
202
|
+
/>
|
|
203
|
+
<RTLAwareText style={theme.text.header2}>
|
|
204
|
+
{t('darkMode', 'profile')}
|
|
205
|
+
</RTLAwareText>
|
|
206
|
+
</View>
|
|
207
|
+
</RTLAwareView>
|
|
208
|
+
|
|
209
|
+
<TouchableOpacity
|
|
210
|
+
onPressIn={() => {
|
|
211
|
+
dispatch(setProvidersLogout());
|
|
212
|
+
dispatch(setServiceLogout());
|
|
213
|
+
dispatch(setCategoriesLogout());
|
|
214
|
+
dispatch(setLogout());
|
|
215
|
+
}}
|
|
216
|
+
style={styles.menuItem}>
|
|
217
|
+
<View style={styles.menuItemLeft}>
|
|
218
|
+
<Image
|
|
219
|
+
source={ImageResources.services}
|
|
220
|
+
style={[styles.menuIcon, styles.logoutIcon]}
|
|
221
|
+
/>
|
|
222
|
+
<RTLAwareText style={[theme.text.header2, styles.logoutText]}>
|
|
223
|
+
{t('logout', 'profile')}
|
|
224
|
+
</RTLAwareText>
|
|
225
|
+
</View>
|
|
226
|
+
</TouchableOpacity>
|
|
227
|
+
</View>
|
|
228
|
+
<RTLAwareText style={{...theme.text.body1, alignSelf: 'center'}}>
|
|
229
|
+
{DeviceInfo.getVersion()}
|
|
230
|
+
</RTLAwareText>
|
|
231
|
+
</ScrollView>
|
|
232
|
+
</Container>
|
|
233
|
+
);
|
|
5
234
|
}
|
|
235
|
+
|
|
236
|
+
const createStyles = (theme: Theme) =>
|
|
237
|
+
StyleSheet.create({
|
|
238
|
+
titleSection: {
|
|
239
|
+
paddingHorizontal: scaleWidth(20),
|
|
240
|
+
paddingTop: scaleWidth(20),
|
|
241
|
+
paddingBottom: scaleWidth(10),
|
|
242
|
+
},
|
|
243
|
+
mainTitle: {
|
|
244
|
+
...theme.text.header1,
|
|
245
|
+
marginBottom: scaleWidth(10),
|
|
246
|
+
},
|
|
247
|
+
menuSection: {
|
|
248
|
+
paddingHorizontal: scaleWidth(20),
|
|
249
|
+
paddingVertical: scaleWidth(10),
|
|
250
|
+
},
|
|
251
|
+
menuItem: {
|
|
252
|
+
flexDirection: 'row',
|
|
253
|
+
justifyContent: 'space-between',
|
|
254
|
+
alignItems: 'center',
|
|
255
|
+
paddingVertical: scaleWidth(15),
|
|
256
|
+
borderBottomWidth: 1,
|
|
257
|
+
borderBottomColor: theme.colors.shadow,
|
|
258
|
+
},
|
|
259
|
+
menuItemLeft: {
|
|
260
|
+
flexDirection: 'row',
|
|
261
|
+
alignItems: 'center',
|
|
262
|
+
},
|
|
263
|
+
menuIcon: {
|
|
264
|
+
width: scaleWidth(71),
|
|
265
|
+
height: scaleWidth(71),
|
|
266
|
+
marginRight: scaleWidth(15),
|
|
267
|
+
tintColor: theme.colors.tintColor,
|
|
268
|
+
resizeMode: 'contain',
|
|
269
|
+
},
|
|
270
|
+
menuText: {
|
|
271
|
+
...theme.text.body1,
|
|
272
|
+
},
|
|
273
|
+
logoutText: {
|
|
274
|
+
color: theme.colors.red,
|
|
275
|
+
},
|
|
276
|
+
logoutIcon: {
|
|
277
|
+
tintColor: theme.colors.red,
|
|
278
|
+
},
|
|
279
|
+
languageOptions: {
|
|
280
|
+
flexDirection: 'row',
|
|
281
|
+
marginTop: scaleWidth(10),
|
|
282
|
+
},
|
|
283
|
+
languageOption: {
|
|
284
|
+
marginRight: scaleWidth(20),
|
|
285
|
+
paddingVertical: scaleWidth(5),
|
|
286
|
+
paddingHorizontal: scaleWidth(10),
|
|
287
|
+
borderRadius: theme.borderRadius.sm,
|
|
288
|
+
},
|
|
289
|
+
activeLanguage: {
|
|
290
|
+
backgroundColor: theme.colors.indigoBlue,
|
|
291
|
+
color: theme.colors.white,
|
|
292
|
+
},
|
|
293
|
+
});
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import {NativeModules, Platform} from 'react-native';
|
|
2
|
+
|
|
3
|
+
const {SunmiPrinterInternal} = NativeModules;
|
|
4
|
+
|
|
5
|
+
// Create a wrapper with error handling
|
|
6
|
+
const SunmiPrinterInternalWrapper = {
|
|
7
|
+
// Check if the module is available
|
|
8
|
+
isAvailable: () => {
|
|
9
|
+
return (
|
|
10
|
+
!!SunmiPrinterInternal &&
|
|
11
|
+
typeof SunmiPrinterInternal === 'object' &&
|
|
12
|
+
Object.keys(SunmiPrinterInternal).length > 0
|
|
13
|
+
);
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
// Get the status of the printer
|
|
17
|
+
getStatus: async () => {
|
|
18
|
+
if (!SunmiPrinterInternalWrapper.isAvailable()) {
|
|
19
|
+
console.error('SunmiPrinterInternal module is not available');
|
|
20
|
+
return {
|
|
21
|
+
connected: false,
|
|
22
|
+
error: 'SunmiPrinterInternal module is not available',
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
if (SunmiPrinterInternal.getStatus) {
|
|
28
|
+
return await SunmiPrinterInternal.getStatus();
|
|
29
|
+
} else {
|
|
30
|
+
return {
|
|
31
|
+
connected: false,
|
|
32
|
+
error: 'getStatus method is not available',
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error('Error getting printer status:', error);
|
|
37
|
+
return {
|
|
38
|
+
connected: false,
|
|
39
|
+
error: error.message || 'Unknown error',
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
// Print text
|
|
45
|
+
printText: async text => {
|
|
46
|
+
if (!SunmiPrinterInternalWrapper.isAvailable()) {
|
|
47
|
+
console.error('SunmiPrinterInternal module is not available');
|
|
48
|
+
throw new Error('SunmiPrinterInternal module is not available');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!SunmiPrinterInternal.printText) {
|
|
52
|
+
console.error('printText method is not available');
|
|
53
|
+
throw new Error('printText method is not available');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
return await SunmiPrinterInternal.printText(text);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error('Error printing text:', error);
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
// Print text with font
|
|
65
|
+
printTextWithFont: async (text, typeface = '', fontSize = 24) => {
|
|
66
|
+
if (!SunmiPrinterInternalWrapper.isAvailable()) {
|
|
67
|
+
console.error('SunmiPrinterInternal module is not available');
|
|
68
|
+
throw new Error('SunmiPrinterInternal module is not available');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!SunmiPrinterInternal.printTextWithFont) {
|
|
72
|
+
console.error('printTextWithFont method is not available');
|
|
73
|
+
throw new Error('printTextWithFont method is not available');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
return await SunmiPrinterInternal.printTextWithFont(
|
|
78
|
+
text,
|
|
79
|
+
typeface,
|
|
80
|
+
fontSize,
|
|
81
|
+
);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error('Error printing text with font:', error);
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
// Print bold text
|
|
89
|
+
printBoldText: async text => {
|
|
90
|
+
if (!SunmiPrinterInternalWrapper.isAvailable()) {
|
|
91
|
+
console.error('SunmiPrinterInternal module is not available');
|
|
92
|
+
throw new Error('SunmiPrinterInternal module is not available');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!SunmiPrinterInternal.printBoldText) {
|
|
96
|
+
console.error('printBoldText method is not available');
|
|
97
|
+
throw new Error('printBoldText method is not available');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
return await SunmiPrinterInternal.printBoldText(text);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.error('Error printing bold text:', error);
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
// Print barcode
|
|
109
|
+
printBarCode: async (
|
|
110
|
+
data,
|
|
111
|
+
symbology = 8,
|
|
112
|
+
height = 80,
|
|
113
|
+
width = 2,
|
|
114
|
+
textPosition = 2,
|
|
115
|
+
) => {
|
|
116
|
+
if (!SunmiPrinterInternalWrapper.isAvailable()) {
|
|
117
|
+
console.error('SunmiPrinterInternal module is not available');
|
|
118
|
+
throw new Error('SunmiPrinterInternal module is not available');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!SunmiPrinterInternal.printBarCode) {
|
|
122
|
+
console.error('printBarCode method is not available');
|
|
123
|
+
throw new Error('printBarCode method is not available');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
return await SunmiPrinterInternal.printBarCode(
|
|
128
|
+
data,
|
|
129
|
+
symbology,
|
|
130
|
+
height,
|
|
131
|
+
width,
|
|
132
|
+
textPosition,
|
|
133
|
+
);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.error('Error printing barcode:', error);
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
// Print QR code
|
|
141
|
+
printQRCode: async (data, moduleSize = 8, errorLevel = 0) => {
|
|
142
|
+
if (!SunmiPrinterInternalWrapper.isAvailable()) {
|
|
143
|
+
console.error('SunmiPrinterInternal module is not available');
|
|
144
|
+
throw new Error('SunmiPrinterInternal module is not available');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (!SunmiPrinterInternal.printQRCode) {
|
|
148
|
+
console.error('printQRCode method is not available');
|
|
149
|
+
throw new Error('printQRCode method is not available');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
return await SunmiPrinterInternal.printQRCode(
|
|
154
|
+
data,
|
|
155
|
+
moduleSize,
|
|
156
|
+
errorLevel,
|
|
157
|
+
);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
console.error('Error printing QR code:', error);
|
|
160
|
+
throw error;
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
// Print image
|
|
165
|
+
printImage: async base64Image => {
|
|
166
|
+
if (!SunmiPrinterInternalWrapper.isAvailable()) {
|
|
167
|
+
console.error('SunmiPrinterInternal module is not available');
|
|
168
|
+
throw new Error('SunmiPrinterInternal module is not available');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (!SunmiPrinterInternal.printImage) {
|
|
172
|
+
console.error('printImage method is not available');
|
|
173
|
+
throw new Error('printImage method is not available');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
return await SunmiPrinterInternal.printImage(base64Image);
|
|
178
|
+
} catch (error) {
|
|
179
|
+
console.error('Error printing image:', error);
|
|
180
|
+
throw error;
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
// Line wrap
|
|
185
|
+
lineWrap: async (lines = 3) => {
|
|
186
|
+
if (!SunmiPrinterInternalWrapper.isAvailable()) {
|
|
187
|
+
console.error('SunmiPrinterInternal module is not available');
|
|
188
|
+
throw new Error('SunmiPrinterInternal module is not available');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (!SunmiPrinterInternal.lineWrap) {
|
|
192
|
+
console.error('lineWrap method is not available');
|
|
193
|
+
throw new Error('lineWrap method is not available');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
return await SunmiPrinterInternal.lineWrap(lines);
|
|
198
|
+
} catch (error) {
|
|
199
|
+
console.error('Error line wrapping:', error);
|
|
200
|
+
throw error;
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
// Cut paper
|
|
205
|
+
cutPaper: async () => {
|
|
206
|
+
if (!SunmiPrinterInternalWrapper.isAvailable()) {
|
|
207
|
+
console.error('SunmiPrinterInternal module is not available');
|
|
208
|
+
throw new Error('SunmiPrinterInternal module is not available');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (!SunmiPrinterInternal.cutPaper) {
|
|
212
|
+
console.error('cutPaper method is not available');
|
|
213
|
+
throw new Error('cutPaper method is not available');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
try {
|
|
217
|
+
return await SunmiPrinterInternal.cutPaper();
|
|
218
|
+
} catch (error) {
|
|
219
|
+
console.error('Error cutting paper:', error);
|
|
220
|
+
throw error;
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
|
|
224
|
+
// Print receipt
|
|
225
|
+
printReceipt: async receiptData => {
|
|
226
|
+
if (!SunmiPrinterInternalWrapper.isAvailable()) {
|
|
227
|
+
console.error('SunmiPrinterInternal module is not available');
|
|
228
|
+
throw new Error('SunmiPrinterInternal module is not available');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
// Print header
|
|
233
|
+
await SunmiPrinterInternalWrapper.printBoldText(receiptData.header);
|
|
234
|
+
await SunmiPrinterInternalWrapper.lineWrap(1);
|
|
235
|
+
|
|
236
|
+
// Print items
|
|
237
|
+
for (const item of receiptData.items) {
|
|
238
|
+
await SunmiPrinterInternalWrapper.printText(
|
|
239
|
+
`${item.name} x${item.quantity} $${item.price}`,
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
await SunmiPrinterInternalWrapper.lineWrap(1);
|
|
244
|
+
|
|
245
|
+
// Print total
|
|
246
|
+
await SunmiPrinterInternalWrapper.printBoldText(
|
|
247
|
+
`Total: $${receiptData.total}`,
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
// Print footer
|
|
251
|
+
await SunmiPrinterInternalWrapper.lineWrap(2);
|
|
252
|
+
await SunmiPrinterInternalWrapper.printText(receiptData.footer);
|
|
253
|
+
|
|
254
|
+
// Cut paper
|
|
255
|
+
await SunmiPrinterInternalWrapper.lineWrap(3);
|
|
256
|
+
if (SunmiPrinterInternal.cutPaper) {
|
|
257
|
+
await SunmiPrinterInternalWrapper.cutPaper();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return true;
|
|
261
|
+
} catch (error) {
|
|
262
|
+
console.error('Error printing receipt:', error);
|
|
263
|
+
throw error;
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
export default SunmiPrinterInternalWrapper;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
declare module 'SunmiPrepaid' {
|
|
2
|
+
interface PrepaidResponse {
|
|
3
|
+
success: boolean;
|
|
4
|
+
message: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface ServiceStatus {
|
|
8
|
+
isInitialized: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface SunmiPrepaidInterface {
|
|
12
|
+
initializePrepaid(): Promise<PrepaidResponse>;
|
|
13
|
+
initializeUtilityType(utilityType: string): Promise<PrepaidResponse>;
|
|
14
|
+
forceCardFacadeDetection(): Promise<PrepaidResponse>;
|
|
15
|
+
getServiceStatus(): Promise<ServiceStatus>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const SunmiPrepaid: SunmiPrepaidInterface;
|
|
19
|
+
export default SunmiPrepaid;
|
|
20
|
+
}
|