@fadyshawky/react-native-magic 2.1.3 → 2.1.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fadyshawky/react-native-magic",
3
- "version": "2.1.3",
3
+ "version": "2.1.5",
4
4
  "description": "Plug-and-play React Native template: TypeScript, Redux, React Navigation, scalable architecture. Init and start developing without hassle.",
5
5
  "keywords": [
6
6
  "react-native-magic",
@@ -1,79 +1,112 @@
1
- This is a new [**React Native**](https://reactnative.dev) project, bootstrapped using [`@react-native-community/cli`](https://github.com/react-native-community/cli).
1
+ # ReactNativeMagic
2
2
 
3
- # Getting Started
3
+ **Plug and play** – create your app and start developing without hassle.
4
4
 
5
- >**Note**: Make sure you have completed the [React Native - Environment Setup](https://reactnative.dev/docs/environment-setup) instructions till "Creating a new application" step, before proceeding.
5
+ A production-ready React Native template with TypeScript, Redux, React Navigation, and a scalable architecture (Uprise-style). Use it to bootstrap new apps with one command.
6
6
 
7
- ## Step 1: Start the Metro Server
7
+ ## Requirements
8
8
 
9
- First, you will need to start **Metro**, the JavaScript _bundler_ that ships _with_ React Native.
9
+ - **Node.js >= 20** ([Download](https://nodejs.org/en/download/))
10
+ - JDK >= 11 ([Download](https://www.oracle.com/java/technologies/downloads/))
11
+ - Ruby >= 2.7.5 (for iOS)
12
+ - Xcode (for iOS) / Android Studio (for Android)
10
13
 
11
- To start Metro, run the following command from the _root_ of your React Native project:
14
+ ## Quick start
12
15
 
13
16
  ```bash
14
- # using npm
15
- npm start
16
-
17
- # OR using Yarn
18
- yarn start
17
+ npx @react-native-community/cli init YourAppName --template @fadyshawky/react-native-magic
18
+ cd YourAppName
19
19
  ```
20
20
 
21
- ## Step 2: Start your Application
21
+ Optional: set your bundle ID at creation:
22
22
 
23
- Let Metro Bundler run in its _own_ terminal. Open a _new_ terminal from the _root_ of your React Native project. Run the following command to start your _Android_ or _iOS_ app:
23
+ ```bash
24
+ npx @react-native-community/cli init YourAppName --template @fadyshawky/react-native-magic --package-name com.yourcompany.yourapp
25
+ ```
24
26
 
25
- ### For Android
27
+ For iOS, install pods:
26
28
 
27
29
  ```bash
28
- # using npm
29
- npm run android
30
-
31
- # OR using Yarn
32
- yarn android
30
+ cd ios && pod install && cd ..
33
31
  ```
34
32
 
35
- ### For iOS
33
+ Then run:
36
34
 
37
35
  ```bash
38
- # using npm
39
- npm run ios
36
+ npm start
37
+ npm run ios # or an Android variant below
38
+ ```
39
+
40
+ ## First steps after creating your app
41
+
42
+ 1. **App name & bundle ID** – Set at init (or you’ll be prompted for package name if you didn’t pass `--package-name`). See [CUSTOMIZATION.md](template/docs/CUSTOMIZATION.md#app-name-and-bundle-id).
43
+ 2. **API** – Copy `.env.example` to `.env` and set `API_BASE_URL` (and other vars) for your backend.
44
+ 3. **Theme** – Edit `src/core/theme/colors.ts` (and `fonts.ts`, `commonSizes.ts` if needed) for your brand.
45
+ 4. **Config** – Optional: adjust `src/core/config/index.ts` for feature toggles or app-level constants.
46
+
47
+ ## Documentation
48
+
49
+ In your generated project you’ll have:
50
+
51
+ - **[docs/ARCHITECTURE.md](template/docs/ARCHITECTURE.md)** – Layers, folder map, data flow, SOLID.
52
+ - **[docs/CUSTOMIZATION.md](template/docs/CUSTOMIZATION.md)** – App name, bundle ID, API, theme, adding a screen/slice/language.
53
+ - **[docs/BEST_PRACTICES.md](template/docs/BEST_PRACTICES.md)** – Code style, structure, testing, security, upgrades.
54
+
55
+ ## Project structure (in your app)
40
56
 
41
- # OR using Yarn
42
- yarn ios
43
57
  ```
58
+ src/
59
+ ├── common/ # Shared components, localization, helpers, validations, utils
60
+ ├── core/ # Store (Redux), API, theme, config
61
+ ├── navigation/ # Auth stack, main stack, tabs
62
+ ├── screens/ # Feature screens
63
+ └── sheetManager/ # Action sheets
64
+ ```
65
+
66
+ ## Scripts
44
67
 
45
- If everything is set up _correctly_, you should see your new app running in your _Android Emulator_ or _iOS Simulator_ shortly provided you have set up your emulator/simulator correctly.
68
+ | Command | Description |
69
+ |---------|-------------|
70
+ | `npm start` | Start Metro bundler |
71
+ | `npm run ios` | Run on iOS |
72
+ | `npm run android:prod:debug` | Run Android (production, debug) |
73
+ | `npm run android:prod:release` | Run Android (production, release) |
74
+ | `npm run android:staging:debug` | Run Android (staging, debug) |
75
+ | `npm run android:staging:release` | Run Android (staging, release) |
76
+ | `npm run android:development:debug` | Run Android (development, debug) |
77
+ | `npm run android:development:release` | Run Android (development, release) |
78
+ | `npm test` | Run tests |
79
+ | `npm run lint` | Lint code |
46
80
 
47
- This is one way to run your app — you can also run it directly from within Android Studio and Xcode respectively.
81
+ ## Versioning
48
82
 
49
- ## Step 3: Modifying your App
83
+ - **React Native**: ^0.84.x (current stable at release).
84
+ - **React**: ^19.2.x.
85
+ - **Node**: >= 20 (LTS).
50
86
 
51
- Now that you have successfully run the app, let's modify it.
87
+ Dependencies use **caret (^)** so your app can get patch/minor updates independently.
52
88
 
53
- 1. Open `App.tsx` in your text editor of choice and edit some lines.
54
- 2. For **Android**: Press the <kbd>R</kbd> key twice or select **"Reload"** from the **Developer Menu** (<kbd>Ctrl</kbd> + <kbd>M</kbd> (on Window and Linux) or <kbd>Cmd ⌘</kbd> + <kbd>M</kbd> (on macOS)) to see your changes!
89
+ ## Common issues
55
90
 
56
- For **iOS**: Hit <kbd>Cmd ⌘</kbd> + <kbd>R</kbd> in your iOS Simulator to reload the app and see your changes!
91
+ **iOS Pod install fails**
57
92
 
58
- ## Congratulations! :tada:
93
+ ```bash
94
+ cd ios && pod deintegrate && pod install && cd ..
95
+ ```
59
96
 
60
- You've successfully run and modified your React Native App. :partying_face:
97
+ **Android Gradle / SDK**
61
98
 
62
- ### Now what?
99
+ - Run `./gradlew clean` in `android/`.
100
+ - Ensure `android/local.properties` has `sdk.dir` set to your Android SDK path.
63
101
 
64
- - If you want to add this new React Native code to an existing application, check out the [Integration guide](https://reactnative.dev/docs/integration-with-existing-apps).
65
- - If you're curious to learn more about React Native, check out the [Introduction to React Native](https://reactnative.dev/docs/getting-started).
102
+ **Upgrading React Native**
66
103
 
67
- # Troubleshooting
104
+ Use [React Native Upgrade Helper](https://react-native-community.github.io/upgrade-helper/) (select current → target version) and apply the suggested changes.
68
105
 
69
- If you can't get this to work, see the [Troubleshooting](https://reactnative.dev/docs/troubleshooting) page.
106
+ ## License
70
107
 
71
- # Learn More
108
+ MIT see [LICENSE.md](LICENSE.md).
72
109
 
73
- To learn more about React Native, take a look at the following resources:
110
+ ## Author
74
111
 
75
- - [React Native Website](https://reactnative.dev) - learn more about React Native.
76
- - [Getting Started](https://reactnative.dev/docs/environment-setup) - an **overview** of React Native and how setup your environment.
77
- - [Learn the Basics](https://reactnative.dev/docs/getting-started) - a **guided tour** of the React Native **basics**.
78
- - [Blog](https://reactnative.dev/blog) - read the latest official React Native **Blog** posts.
79
- - [`@facebook/react-native`](https://github.com/facebook/react-native) - the Open Source; GitHub **repository** for React Native.
112
+ Fady Shawky [GitHub](https://github.com/fadyshawky)
@@ -22,7 +22,7 @@ import {
22
22
  ViewStyle,
23
23
  } from 'react-native';
24
24
  import {useTheme} from '../../core/theme/ThemeProvider';
25
- import {PrimaryColors, AlertColors} from '../../core/theme/colors';
25
+ import {PrimaryColors, AlertColors, NaturalColors} from '../../core/theme/colors';
26
26
  import {isIos} from '../../core/theme/commonConsts';
27
27
  import {CommonSizes} from '../../core/theme/commonSizes';
28
28
  import {CommonStyles} from '../../core/theme/commonStyles';
@@ -93,6 +93,15 @@ export const PrimaryTextInput: FC<IProps> = memo(
93
93
  const {theme} = useTheme();
94
94
  const [regexError, setRegexError] = useState<string | null>(null);
95
95
 
96
+ // Ensure gradient colors are always a valid array (BVLinearGradient crashes on null/undefined)
97
+ const gradientColors = useMemo(
98
+ () => [
99
+ theme.colors.mutedLavender ?? NaturalColors.naturalColor_100,
100
+ theme.colors.indigoBlue ?? PrimaryColors.PlatinateBlue_400,
101
+ ],
102
+ [theme.colors.mutedLavender, theme.colors.indigoBlue],
103
+ );
104
+
96
105
  const onLocalFocus = useCallback(
97
106
  (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
98
107
  setFocused(true);
@@ -158,7 +167,7 @@ export const PrimaryTextInput: FC<IProps> = memo(
158
167
  }}>
159
168
  <GradientBorderView
160
169
  gradientProps={{
161
- colors: [theme.colors.mutedLavender, theme.colors.indigoBlue],
170
+ colors: gradientColors,
162
171
  }}
163
172
  style={{
164
173
  borderWidth: CommonSizes.borderWidth.small,
@@ -37,176 +37,3 @@ export const userLogin = createAsyncThunk(
37
37
  }
38
38
  },
39
39
  );
40
-
41
- export const verifyOTP = createAsyncThunk(
42
- 'user/verifyOTP',
43
- async (
44
- {
45
- verification_code,
46
- mobile_number,
47
- device_token,
48
- scheme_id,
49
- }: {
50
- verification_code: string;
51
- mobile_number: string;
52
- device_token?: string;
53
- scheme_id: number;
54
- },
55
- {rejectWithValue, getState, dispatch}: any,
56
- ) => {
57
- try {
58
- const data: {
59
- mobile_number: string;
60
- verification_code: string;
61
- device_token?: string;
62
- scheme_id: number;
63
- } = {
64
- mobile_number,
65
- verification_code,
66
- device_token: undefined,
67
- scheme_id: 1,
68
- };
69
-
70
- const token = (getState() as RootState).user.tempToken;
71
-
72
- const response = await post({
73
- url: 'wallet_users/verifylogin',
74
- data,
75
- config: {
76
- headers: {
77
- Authorization: `${token}`,
78
- },
79
- },
80
- });
81
-
82
- return handleFetchJsonResponse(response);
83
- } catch (e: any) {
84
- const serverError = extractServerError(e);
85
-
86
- return rejectWithValue({
87
- ...serverError,
88
- message: ensureString(serverError.message),
89
- });
90
- }
91
- },
92
- );
93
-
94
- export const getBalance = createAsyncThunk(
95
- 'user/getBalance',
96
- async (_, {rejectWithValue, getState, dispatch}: any) => {
97
- try {
98
- const token = (getState() as RootState).user.accessToken;
99
-
100
- const response = await post({
101
- url: 'wallet_users/getbalance',
102
- data: {},
103
- config: {
104
- headers: {
105
- Authorization: `${token}`,
106
- },
107
- },
108
- });
109
-
110
- return handleFetchJsonResponse(response);
111
- } catch (e: any) {
112
- const serverError = extractServerError(e);
113
-
114
- return rejectWithValue({
115
- ...serverError,
116
- message: ensureString(serverError.message),
117
- });
118
- }
119
- },
120
- );
121
-
122
- export const resetPassword = createAsyncThunk(
123
- 'user/resetPassword',
124
- async (
125
- {
126
- mpin,
127
- new_mpin,
128
- confirm_mpin,
129
- }: {mpin: string; new_mpin: string; confirm_mpin: string},
130
- {rejectWithValue, getState},
131
- ) => {
132
- try {
133
- const token = (getState() as RootState).user.accessToken;
134
-
135
- const response = await post({
136
- url: 'wallet_users/force-update-mpin',
137
- data: {mpin, new_mpin, confirm_mpin},
138
- config: {
139
- headers: {
140
- Authorization: `${token}`,
141
- },
142
- },
143
- });
144
-
145
- return handleFetchJsonResponse(response);
146
- } catch (e: any) {
147
- const serverError = extractServerError(e);
148
-
149
- return rejectWithValue({
150
- ...serverError,
151
- message: ensureString(serverError.message),
152
- });
153
- }
154
- },
155
- );
156
-
157
- export const fetchHistory = createAsyncThunk(
158
- 'user/fetchHistory',
159
- async (
160
- {data}: {data: {page: number; limit: number}},
161
- {rejectWithValue, getState},
162
- ) => {
163
- try {
164
- const token = (getState() as RootState).user.accessToken;
165
- const response = await post({
166
- url: 'wallet_users/history',
167
- data: {page: data.page, limit: data.limit},
168
- config: {
169
- headers: {
170
- Authorization: `${token}`,
171
- },
172
- },
173
- });
174
-
175
- return handleFetchJsonResponse(response);
176
- } catch (e: any) {
177
- const serverError = extractServerError(e);
178
-
179
- return rejectWithValue({
180
- ...serverError,
181
- message: ensureString(serverError.message),
182
- });
183
- }
184
- },
185
- );
186
-
187
- export const fetchHistoryDetails = createAsyncThunk(
188
- 'user/fetchHistoryDetails',
189
- async ({trxRef}: {trxRef: string}, {rejectWithValue, getState}) => {
190
- try {
191
- const token = (getState() as RootState).user.accessToken;
192
-
193
- const response = await post({
194
- url: 'wallet_users/history/details',
195
- data: {trxRef},
196
- config: {
197
- headers: {
198
- Authorization: `${token}`,
199
- },
200
- },
201
- });
202
-
203
- return handleFetchJsonResponse(response);
204
- } catch (e: any) {
205
- const serverError = extractServerError(e);
206
- return rejectWithValue({
207
- ...serverError,
208
- message: ensureString(serverError.message),
209
- });
210
- }
211
- },
212
- );
@@ -1,21 +1,15 @@
1
- import {createSlice} from '@reduxjs/toolkit';
2
- import {LoadState} from '../../../../types';
3
- import {newState} from '../../../common/utils/newState';
4
- import {handleErrorResponse} from '../../api/responseHandlers';
1
+ import { createSlice } from '@reduxjs/toolkit';
2
+ import { cloneDeep, uniqBy } from 'lodash';
3
+ import { LoadState } from '../../../../types';
4
+ import { newState } from '../../../common/utils/newState';
5
+ import { handleErrorResponse } from '../../api/responseHandlers';
5
6
  import {
6
- resetPassword,
7
- userLogin,
8
- getBalance,
9
- verifyOTP,
10
- fetchHistoryDetails,
11
- fetchHistory,
7
+ userLogin
12
8
  } from './userActions';
13
- import {UserInitialState, UserPayload, UserState} from './userState';
14
- import {cloneDeep, uniqBy} from 'lodash';
9
+ import { UserInitialState, UserState } from './userState';
15
10
 
16
- function loginHandler(state: UserState, payload: {payload: UserPayload}) {
11
+ function loginHandler(state: UserState, payload: {payload: any}) {
17
12
  return newState(state, {
18
- tempToken: payload.payload.token,
19
13
  loginLoading: LoadState['allIsLoaded'],
20
14
  });
21
15
  }
@@ -36,257 +30,24 @@ function loginErrorHandler(
36
30
  });
37
31
  }
38
32
 
39
- function verifyOTPHandler(state: UserState, payload: {payload: UserPayload}) {
40
- return newState(state, {
41
- user: {
42
- type: payload.payload.type,
43
- mobile_number: payload.payload.mobile_number,
44
- full_name: payload.payload.fullname,
45
- merchantStore: payload.payload.merchantStore,
46
- status: payload.payload.status,
47
- },
48
- accessToken: payload.payload.token,
49
- loginLoading: LoadState['allIsLoaded'],
50
- current_balance: payload?.payload?.current_balance?.find(
51
- i => i.name === 'Deposit',
52
- )?.value,
53
- daily_commission: payload?.payload?.current_balance?.find(
54
- i => i.name === 'Daily Commission',
55
- )?.value,
56
- });
57
- }
58
- function verifyOTPLoadingHandler(state: UserState) {
59
- return newState(state, {
60
- loginLoading: LoadState['pullToRefresh'],
61
- });
62
- }
63
-
64
- function verifyOTPErrorHandler(
65
- state: UserState,
66
- payload: {payload: {message: string}},
67
- ) {
68
- handleErrorResponse(
69
- (payload.payload.message as string) || 'Verify OTP failed',
70
- );
71
- return newState(state, {
72
- loginLoading: LoadState['error'],
73
- });
74
- }
75
-
76
33
  function logoutHandler(state: UserState) {
77
34
  return newState(state, UserInitialState);
78
35
  }
79
36
 
80
- function resetPasswordHandler(
81
- state: UserState,
82
- payload: {payload: UserPayload},
83
- ) {
84
- return newState(state, {
85
- user: {
86
- ...state.user,
87
- status: payload.payload.status,
88
- },
89
- loginLoading: LoadState['allIsLoaded'],
90
- });
91
- }
92
-
93
- function resetPasswordLoadingHandler(state: UserState) {
94
- return newState(state, {
95
- loginLoading: LoadState['pullToRefresh'],
96
- });
97
- }
98
-
99
- function getBalanceHandler(state: UserState, payload: {payload: UserPayload}) {
100
- return newState(state, {
101
- current_balance: payload?.payload?.balance?.find(i => i.name === 'Deposit')
102
- ?.value,
103
- daily_commission: payload?.payload?.balance?.find(
104
- i => i.name === 'Daily Commission',
105
- )?.value,
106
- loginLoading: LoadState['allIsLoaded'],
107
- });
108
- }
109
-
110
- function getBalanceLoadingHandler(state: UserState) {
111
- return newState(state, {
112
- loginLoading: LoadState['pullToRefresh'],
113
- });
114
- }
115
-
116
- function getBalanceErrorHandler(
117
- state: UserState,
118
- payload: {payload: {message: string}},
119
- ) {
120
- handleErrorResponse((payload.payload.message as string) || 'Register failed');
121
- return newState(state, {
122
- loginLoading: LoadState['error'],
123
- });
124
- }
125
-
126
- function updateHandler(state: UserState, payload: any) {
127
- return newState(state, {
128
- user: {
129
- ...state.user,
130
- ...payload.payload,
131
- },
132
- });
133
- }
134
-
135
- function resetPasswordErrorHandler(
136
- state: UserState,
137
- payload: {payload: {message: string}},
138
- ) {
139
- handleErrorResponse(
140
- (payload.payload.message as string) || 'Password reset failed',
141
- );
142
- return newState(state, {
143
- loginLoading: LoadState['error'],
144
- });
145
- }
146
-
147
- function setFavoriteCategoriesHandler(
148
- state: UserState,
149
- payload: {payload: number},
150
- ) {
151
- if (!state.favoriteCategories) {
152
- state.favoriteCategories = [];
153
- }
154
- state.favoriteCategories.push(payload.payload);
155
- }
156
-
157
- function setFavoriteProvidersHandler(
158
- state: UserState,
159
- payload: {payload: number},
160
- ) {
161
- if (!state.favoriteProviders) {
162
- state.favoriteProviders = [];
163
- }
164
- state.favoriteProviders.push(payload.payload);
165
- }
166
-
167
- function removeFavoriteCategoriesHandler(
168
- state: UserState,
169
- payload: {payload: number},
170
- ) {
171
- if (state.favoriteCategories) {
172
- state.favoriteCategories = state.favoriteCategories.filter(
173
- id => id !== payload.payload,
174
- );
175
- }
176
- }
177
-
178
- function removeFavoriteProvidersHandler(
179
- state: UserState,
180
- payload: {payload: number},
181
- ) {
182
- if (state.favoriteProviders) {
183
- state.favoriteProviders = state.favoriteProviders.filter(
184
- id => id !== payload.payload,
185
- );
186
- }
187
- }
188
-
189
- function fetchHistoryHandler(
190
- state: UserState,
191
- payload: {payload: UserPayload},
192
- ) {
193
- let tmp = cloneDeep(state.history);
194
- tmp = [...tmp, ...payload.payload.requests];
195
- tmp = uniqBy(tmp, 'transaction_reference_num');
196
- return newState(state, {
197
- history: tmp,
198
- });
199
- }
200
-
201
- function fetchHistoryLoadingHandler(state: UserState) {
202
- return newState(state, {
203
- loginLoading: LoadState['pullToRefresh'],
204
- });
205
- }
206
-
207
- function fetchHistoryErrorHandler(
208
- state: UserState,
209
- payload: {payload: {message: string}},
210
- ) {
211
- handleErrorResponse(
212
- (payload.payload.message as string) || 'Fetch history failed',
213
- );
214
- }
215
-
216
- function fetchHistoryDetailsHandler(
217
- state: UserState,
218
- payload: {payload: UserPayload},
219
- ) {
220
- return newState(state, {
221
- historyDetails: payload.payload.data,
222
- });
223
- }
224
-
225
- function fetchHistoryDetailsLoadingHandler(state: UserState) {
226
- return newState(state, {
227
- loginLoading: LoadState['pullToRefresh'],
228
- });
229
- }
230
-
231
- function fetchHistoryDetailsErrorHandler(
232
- state: UserState,
233
- payload: {payload: {message: string}},
234
- ) {
235
- handleErrorResponse(
236
- (payload.payload.message as string) || 'Fetch history details failed',
237
- );
238
- }
239
-
240
- function clearHistoryHandler(state: UserState) {
241
- return newState(state, {
242
- history: UserInitialState.history,
243
- });
244
- }
245
-
246
37
  export const {reducer: UserReducer, actions} = createSlice({
247
38
  name: 'user',
248
39
  initialState: UserInitialState,
249
40
  reducers: {
250
- setLogin: loginHandler,
251
- setLoginLoading: loginLoadingHandler,
252
- setLoginError: loginLoadingHandler,
253
41
  setLogout: logoutHandler,
254
- setUpdate: updateHandler,
255
- setFavoriteCategories: setFavoriteCategoriesHandler,
256
- setFavoriteProviders: setFavoriteProvidersHandler,
257
- removeFavoriteCategories: removeFavoriteCategoriesHandler,
258
- removeFavoriteProviders: removeFavoriteProvidersHandler,
259
- clearHistory: clearHistoryHandler,
260
42
  },
261
43
  extraReducers: builder => {
262
44
  builder
263
45
  .addCase(userLogin.fulfilled, loginHandler)
264
46
  .addCase(userLogin.rejected, loginErrorHandler)
265
- .addCase(userLogin.pending, loginLoadingHandler)
266
- .addCase(resetPassword.fulfilled, resetPasswordHandler)
267
- .addCase(resetPassword.rejected, resetPasswordErrorHandler)
268
- .addCase(resetPassword.pending, resetPasswordLoadingHandler)
269
- .addCase(getBalance.fulfilled, getBalanceHandler)
270
- .addCase(getBalance.rejected, getBalanceErrorHandler)
271
- .addCase(getBalance.pending, getBalanceLoadingHandler)
272
- .addCase(verifyOTP.fulfilled, verifyOTPHandler)
273
- .addCase(verifyOTP.rejected, verifyOTPErrorHandler)
274
- .addCase(verifyOTP.pending, verifyOTPLoadingHandler)
275
- .addCase(fetchHistory.fulfilled, fetchHistoryHandler)
276
- .addCase(fetchHistory.rejected, fetchHistoryErrorHandler)
277
- .addCase(fetchHistory.pending, fetchHistoryLoadingHandler)
278
- .addCase(fetchHistoryDetails.fulfilled, fetchHistoryDetailsHandler)
279
- .addCase(fetchHistoryDetails.rejected, fetchHistoryDetailsErrorHandler)
280
- .addCase(fetchHistoryDetails.pending, fetchHistoryDetailsLoadingHandler);
47
+ .addCase(userLogin.pending, loginLoadingHandler);
281
48
  },
282
49
  });
283
50
 
284
51
  export const {
285
52
  setLogout,
286
- setUpdate,
287
- setFavoriteCategories,
288
- setFavoriteProviders,
289
- removeFavoriteCategories,
290
- removeFavoriteProviders,
291
- clearHistory,
292
53
  } = actions;
@@ -2,105 +2,16 @@ import {LoadState} from '../../../../types';
2
2
 
3
3
  export interface UserState {
4
4
  user: User;
5
- current_balance: number;
6
- daily_commission: number;
7
5
  accessToken: string;
8
6
  loginLoading: string;
9
- tempToken: string;
10
- favoriteCategories: number[];
11
- favoriteProviders: number[];
12
- history: HistoryEntity[];
13
- historyDetails: HistoryDetailsEntity[];
14
7
  }
15
8
  export interface User {
16
9
  type: string;
17
10
  mobile_number: string;
18
11
  full_name: string;
19
- merchantStore: MerchantStore;
20
12
  status: string;
21
13
  }
22
- export interface MerchantStore {
23
- id: number;
24
- wallet_user_id: number;
25
- name: string;
26
- address: string;
27
- city: string;
28
- region: string;
29
- store_imgs?: null;
30
- tax_card?: null;
31
- commercial_registration?: null;
32
- createdAt: string;
33
- updatedAt: string;
34
- id_front: string;
35
- id_back: string;
36
- selfie?: null;
37
- }
38
- export interface HistoryEntity {
39
- id: number;
40
- transaction_reference_num: string;
41
- bee_transaction_id?: null;
42
- fawry_transaction_id?: string | null;
43
- transaction_serial_num?: string | null;
44
- amount: string | number;
45
- sender_fees: string;
46
- reciever_fees: string;
47
- sender_identifier: string;
48
- reciever_identifier: string;
49
- status: string;
50
- sender_status: string;
51
- receiver_status: string;
52
- reason?: null;
53
- transactionReason: string;
54
- display_reason?: null;
55
- sender_interchange_amount: string;
56
- receiver_interchange_amount: string;
57
- sender_commission: string;
58
- receiver_commission: string;
59
- bulk_id?: null;
60
- sender_name: string;
61
- receiver_name: string;
62
- notification_id?: null;
63
- transaction_data?: null;
64
- convenience: string;
65
- tips: string;
66
- reference1?: null;
67
- reference2?: null;
68
- meta?: null;
69
- createdAt: string;
70
- updatedAt: string;
71
- transaction_type: number;
72
- sender_id: number;
73
- reciever_id: number;
74
- sender_scheme_id: number;
75
- receiver_scheme_id: number;
76
- 'Transaction_type.id': number;
77
- 'Transaction_type.name': string;
78
- 'Transaction_type.name_ar'?: null;
79
- 'Transaction_type.request_type': string;
80
- 'Transaction_type.request_type_ar'?: null;
81
- 'Transaction_type.timeout': string;
82
- 'Transaction_type.on_off_us': string;
83
- 'Transaction_type.sender_type': string;
84
- 'Transaction_type.profile_type'?: null;
85
- 'Transaction_type.receiver_type': string;
86
- 'Transaction_type.tahweel_operation': string;
87
- 'Transaction_type.send_fees'?: string | null;
88
- 'Transaction_type.receive_fees': string;
89
- 'Transaction_type.send_limit': string;
90
- 'Transaction_type.receive_limit': string;
91
- 'Transaction_type.send_commision'?: string | null;
92
- 'Transaction_type.receive_commision'?: null;
93
- 'Transaction_type.createdAt': string;
94
- 'Transaction_type.updatedAt': string;
95
- providerName?: string | null;
96
- serviceName?: string | null;
97
- totalAmount?: number | null;
98
- }
99
- export interface HistoryDetailsEntity {
100
- value: string;
101
- type: string;
102
- label?: string;
103
- }
14
+
104
15
 
105
16
  export enum UserStatus {
106
17
  ACTIVE = 'Active',
@@ -109,25 +20,7 @@ export enum UserStatus {
109
20
  PENDING = 'Pending',
110
21
  }
111
22
 
112
- export interface UserPayload {
113
- type: string;
114
- mobile_number: string;
115
- full_name: string;
116
- merchantStore: MerchantStore;
117
- requests: HistoryEntity[];
118
- data: HistoryDetailsEntity[];
119
- balance: {
120
- name: string;
121
- value: number;
122
- }[];
123
- current_balance: {
124
- name: string;
125
- value: number;
126
- }[];
127
- status: UserStatus;
128
- fullname: string;
129
- token: string;
130
- }
23
+
131
24
 
132
25
  export const UserInitialState: UserState = {
133
26
  user: {
@@ -135,28 +28,9 @@ export const UserInitialState: UserState = {
135
28
  mobile_number: '',
136
29
  full_name: '',
137
30
  status: UserStatus.PENDING,
138
- merchantStore: {
139
- id: 0,
140
- wallet_user_id: 0,
141
- name: '',
142
- address: '',
143
- city: '',
144
- region: '',
145
- createdAt: '',
146
- updatedAt: '',
147
- id_front: '',
148
- id_back: '',
149
- },
150
31
  },
151
- current_balance: 0,
152
- daily_commission: 0,
153
32
  accessToken: '',
154
33
  loginLoading: LoadState['needLoad'],
155
- tempToken: '',
156
- favoriteCategories: [],
157
- favoriteProviders: [],
158
- history: [],
159
- historyDetails: [],
160
34
  };
161
35
 
162
36
  export interface UserEntity {
@@ -21,7 +21,6 @@ import {userLogin} from '../../core/store/user/userActions';
21
21
  import {CommonSizes} from '../../core/theme/commonSizes';
22
22
  import {Fonts} from '../../core/theme/fonts';
23
23
  import {useTheme} from '../../core/theme/ThemeProvider';
24
- import {Header} from '../../navigation/HeaderComponents';
25
24
  import type {RootStackParamList} from '../../navigation/types';
26
25
 
27
26
  export function Login(): JSX.Element {
@@ -1,54 +1,18 @@
1
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
2
  import {useAppDispatch, useAppSelector} from '../../../core/store/reduxHelpers';
6
3
  import {RootState} from '../../../core/store/rootReducer';
7
- import {getBalance} from '../../../core/store/user/userActions';
8
- import {clearSelectedProvider} from '../../../core/store/Providers/providersSlice';
9
4
  export function useHomeData() {
10
5
  const [isLoading, setIsLoading] = useState(false);
11
6
  const dispatch = useAppDispatch();
12
7
  const {categories, loadState} = useAppSelector(
13
8
  (state: RootState) => state.categories,
14
9
  );
15
- const [isPayByCodeModalVisible, setIsPayByCodeModalVisible] = useState(false);
16
- const {homeProviders} = useAppSelector((state: RootState) => state.providers);
17
10
  const {user} = useAppSelector((state: RootState) => state.user);
18
11
 
19
- const fetchData = async () => {
20
- setIsLoading(true);
21
- try {
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
- }
32
- } catch (error) {
33
- console.error('Error fetching home data:', error);
34
- } finally {
35
- setIsLoading(false);
36
- }
37
- };
38
-
39
- const refreshData = () => {
40
- fetchData();
41
- };
12
+
42
13
 
43
14
  return {
44
15
  isLoading,
45
- refreshData,
46
- fetchData,
47
- categories,
48
16
  user,
49
- homeProviders,
50
- loadState,
51
- isPayByCodeModalVisible,
52
- setIsPayByCodeModalVisible,
53
17
  };
54
18
  }
@@ -1,7 +0,0 @@
1
- export interface CarouselItem {
2
- id: string;
3
- title: string;
4
- subtitle?: string;
5
- imageUrl: string;
6
- onPress?: (item: CarouselItem) => void;
7
- }
@@ -1,129 +0,0 @@
1
- import React, {useState} from 'react';
2
- import {Modal, StyleSheet, View} from 'react-native';
3
- import {RTLAwareView} from '../../../common/components/RTLAwareView';
4
- import {CommonSizes} from '../../../core/theme/commonSizes';
5
- import {PrimaryTextInput} from '../../../common/components/PrimaryTextInput';
6
- import {useTheme} from '../../../core/theme/ThemeProvider';
7
- import {PrimaryButton} from '../../../common/components/PrimaryButton';
8
- import {ButtonType} from '../../../../types';
9
- import {useAppDispatch} from '../../../core/store/reduxHelpers';
10
- import {getServiceById} from '../../../core/store/Services/servicesActions';
11
- import {getProviderById} from '../../../core/store/Providers/providersActions';
12
- import {useNavigation} from '@react-navigation/native';
13
- import {NativeStackNavigationProp} from '@react-navigation/native-stack';
14
- import {RootStackParamList} from '../../../navigation/types';
15
- import {useTranslation} from '../../../common/localization/LocalizationProvider';
16
-
17
- export function PayByCode({
18
- isVisible,
19
- onClose,
20
- }: {
21
- isVisible: boolean;
22
- onClose: () => void;
23
- }): JSX.Element {
24
- const {theme} = useTheme();
25
- const t = useTranslation();
26
- const [code, setCode] = useState('');
27
- const [isLoading, setIsLoading] = useState(false);
28
- const dispatch = useAppDispatch();
29
- const navigation =
30
- useNavigation<NativeStackNavigationProp<RootStackParamList>>();
31
-
32
- const getService = async () => {
33
- if (!code) {
34
- return;
35
- }
36
- try {
37
- setIsLoading(true);
38
- const response = await dispatch(
39
- getServiceById({data: {BillTypeCode: code}}),
40
- );
41
- if (response.type.includes('fulfilled')) {
42
- const providerResponse = await dispatch(
43
- getProviderById({
44
- data: {BillerId: response.payload.service.BillerId},
45
- }),
46
- );
47
- if (providerResponse.type.includes('fulfilled')) {
48
- setCode('');
49
- onClose();
50
- setTimeout(() => {
51
- navigation.navigate('SingleService', {
52
- serviceID: undefined,
53
- });
54
- }, 200);
55
- }
56
- }
57
- } catch (error) {
58
- console.error(error);
59
- } finally {
60
- setIsLoading(false);
61
- }
62
- };
63
-
64
- return (
65
- <Modal
66
- onRequestClose={onClose}
67
- visible={isVisible}
68
- transparent
69
- style={styles.modal}>
70
- <RTLAwareView
71
- style={{
72
- ...styles.container,
73
- backgroundColor: theme.colors.surface,
74
- borderWidth: 1,
75
- borderColor: theme.colors.indigoBlue,
76
- }}>
77
- <PrimaryTextInput
78
- value={code}
79
- onChangeText={setCode}
80
- placeholder={t('enterCode', 'home')}
81
- />
82
- <RTLAwareView
83
- style={{
84
- flexDirection: 'row',
85
- alignItems: 'center',
86
- justifyContent: 'space-between',
87
- }}>
88
- <PrimaryButton
89
- disabled={isLoading}
90
- isLoading={isLoading}
91
- onPress={() => {
92
- onClose();
93
- setCode('');
94
- }}
95
- style={{width: '48%'}}
96
- label={t('cancel', 'home')}
97
- type={ButtonType.solid}
98
- />
99
- <PrimaryButton
100
- disabled={isLoading}
101
- isLoading={isLoading}
102
- onPress={getService}
103
- style={{width: '48%'}}
104
- label={t('search', 'home')}
105
- type={ButtonType.solid}
106
- />
107
- </RTLAwareView>
108
- </RTLAwareView>
109
- </Modal>
110
- );
111
- }
112
-
113
- const styles = StyleSheet.create({
114
- modal: {
115
- flex: 1,
116
- backgroundColor: 'red',
117
- alignItems: 'center',
118
- justifyContent: 'center',
119
- },
120
- container: {
121
- width: '95%',
122
- // minHeight: '40%',
123
- alignSelf: 'center',
124
- borderRadius: CommonSizes.borderRadius.large,
125
- padding: CommonSizes.spacing.large,
126
- gap: CommonSizes.spacing.large,
127
- marginTop: '45%',
128
- },
129
- });