From 5f6013a4bf2962b0752474177fa7bef0b29fcdc6 Mon Sep 17 00:00:00 2001 From: EngincanV Date: Wed, 25 Sep 2024 15:36:47 +0300 Subject: [PATCH] Remove oss react-native template --- templates/app/react-native/.eslintrc.json | 25 -- .../app/react-native/.expo-shared/assets.json | 4 - templates/app/react-native/.gitignore | 14 - templates/app/react-native/.prettierrc | 9 - .../app/react-native/.vscode/extensions.json | 3 - templates/app/react-native/App.js | 101 ------- templates/app/react-native/Environment.js | 32 --- templates/app/react-native/app.json | 34 --- .../app/react-native/assets/adaptive-icon.png | Bin 17547 -> 0 bytes templates/app/react-native/assets/avatar.png | Bin 255 -> 0 bytes templates/app/react-native/assets/icon.png | Bin 3073 -> 0 bytes templates/app/react-native/assets/logo.png | Bin 1017 -> 0 bytes templates/app/react-native/assets/splash.png | Bin 39550 -> 0 bytes templates/app/react-native/babel.config.js | 7 - templates/app/react-native/package.json | 44 --- templates/app/react-native/src/api/API.js | 11 - .../app/react-native/src/api/AccountAPI.js | 62 ---- .../src/api/ApplicationConfigurationAPI.js | 30 -- .../app/react-native/src/api/IdentityAPI.js | 27 -- .../src/api/TenantManagementAPI.js | 21 -- .../src/components/AddIcon/AddIcon.js | 16 -- .../src/components/DataList/DataList.js | 143 --------- .../components/DrawerContent/DrawerContent.js | 104 ------- .../src/components/FormButtons/FormButtons.js | 78 ----- .../src/components/FormButtons/index.js | 1 - .../components/HamburgerIcon/HamburgerIcon.js | 17 -- .../src/components/Loading/Loading.js | 62 ---- .../components/LoadingButton/LoadingButton.js | 22 -- .../src/components/TenantBox/TenantBox.js | 143 --------- .../ValidationMessage/ValidationMessage.js | 16 -- .../src/contexts/LocalizationContext.js | 3 - .../react-native/src/hocs/PermissionHOC.js | 17 -- .../react-native/src/hooks/UsePermission.js | 16 -- .../src/interceptors/APIInterceptor.js | 100 ------- .../src/navigators/AuthNavigator.js | 22 -- .../src/navigators/DrawerNavigator.js | 43 --- .../src/navigators/HomeNavigator.js | 17 -- .../src/navigators/SettingsNavigator.js | 41 --- .../src/navigators/TenantsNavigator.js | 35 --- .../src/navigators/UsersNavigator.js | 34 --- .../ChangePassword/ChangePasswordForm.js | 121 -------- .../ChangePassword/ChangePasswordScreen.js | 33 --- .../CreateUpdateTenantForm.js | 144 ---------- .../CreateUpdateTenantScreen.js | 81 ------ .../CreateUpdateUser/CreateUpdateUserForm.js | 272 ------------------ .../CreateUpdateUserScreen.js | 69 ----- .../src/screens/CreateUpdateUser/UserRoles.js | 58 ---- .../src/screens/Home/HomeScreen.js | 30 -- .../src/screens/Login/LoginScreen.js | 154 ---------- .../ManageProfile/ManageProfileForm.js | 153 ---------- .../ManageProfile/ManageProfileScreen.js | 50 ---- .../src/screens/Settings/SettingsScreen.js | 157 ---------- .../src/screens/Tenants/TenantsScreen.js | 51 ---- .../src/screens/Users/UsersScreen.js | 62 ---- .../src/store/actions/AppActions.js | 26 -- .../src/store/actions/LoadingActions.js | 13 - .../store/actions/PersistentStorageActions.js | 13 - templates/app/react-native/src/store/index.js | 25 -- .../src/store/reducers/AppReducer.js | 12 - .../src/store/reducers/LoadingReducer.js | 23 -- .../reducers/PersistentStorageReducer.js | 17 -- .../react-native/src/store/reducers/index.js | 12 - .../react-native/src/store/sagas/AppSaga.js | 50 ---- .../app/react-native/src/store/sagas/index.js | 6 - .../src/store/selectors/AppSelectors.js | 19 -- .../src/store/selectors/LoadingSelectors.js | 11 - .../selectors/PersistentStorageSelectors.js | 11 - .../react-native/src/utils/DateExtensions.js | 5 - .../app/react-native/src/utils/Debounce.js | 14 - .../app/react-native/src/utils/RandomColor.js | 12 - .../react-native/src/utils/ReduxConnect.js | 9 - .../app/react-native/src/utils/TokenUtils.js | 7 - 72 files changed, 3074 deletions(-) delete mode 100644 templates/app/react-native/.eslintrc.json delete mode 100644 templates/app/react-native/.expo-shared/assets.json delete mode 100644 templates/app/react-native/.gitignore delete mode 100644 templates/app/react-native/.prettierrc delete mode 100644 templates/app/react-native/.vscode/extensions.json delete mode 100644 templates/app/react-native/App.js delete mode 100644 templates/app/react-native/Environment.js delete mode 100644 templates/app/react-native/app.json delete mode 100644 templates/app/react-native/assets/adaptive-icon.png delete mode 100644 templates/app/react-native/assets/avatar.png delete mode 100644 templates/app/react-native/assets/icon.png delete mode 100644 templates/app/react-native/assets/logo.png delete mode 100644 templates/app/react-native/assets/splash.png delete mode 100644 templates/app/react-native/babel.config.js delete mode 100644 templates/app/react-native/package.json delete mode 100644 templates/app/react-native/src/api/API.js delete mode 100644 templates/app/react-native/src/api/AccountAPI.js delete mode 100644 templates/app/react-native/src/api/ApplicationConfigurationAPI.js delete mode 100644 templates/app/react-native/src/api/IdentityAPI.js delete mode 100644 templates/app/react-native/src/api/TenantManagementAPI.js delete mode 100644 templates/app/react-native/src/components/AddIcon/AddIcon.js delete mode 100644 templates/app/react-native/src/components/DataList/DataList.js delete mode 100644 templates/app/react-native/src/components/DrawerContent/DrawerContent.js delete mode 100644 templates/app/react-native/src/components/FormButtons/FormButtons.js delete mode 100644 templates/app/react-native/src/components/FormButtons/index.js delete mode 100644 templates/app/react-native/src/components/HamburgerIcon/HamburgerIcon.js delete mode 100644 templates/app/react-native/src/components/Loading/Loading.js delete mode 100644 templates/app/react-native/src/components/LoadingButton/LoadingButton.js delete mode 100644 templates/app/react-native/src/components/TenantBox/TenantBox.js delete mode 100644 templates/app/react-native/src/components/ValidationMessage/ValidationMessage.js delete mode 100644 templates/app/react-native/src/contexts/LocalizationContext.js delete mode 100644 templates/app/react-native/src/hocs/PermissionHOC.js delete mode 100644 templates/app/react-native/src/hooks/UsePermission.js delete mode 100644 templates/app/react-native/src/interceptors/APIInterceptor.js delete mode 100644 templates/app/react-native/src/navigators/AuthNavigator.js delete mode 100644 templates/app/react-native/src/navigators/DrawerNavigator.js delete mode 100644 templates/app/react-native/src/navigators/HomeNavigator.js delete mode 100644 templates/app/react-native/src/navigators/SettingsNavigator.js delete mode 100644 templates/app/react-native/src/navigators/TenantsNavigator.js delete mode 100644 templates/app/react-native/src/navigators/UsersNavigator.js delete mode 100644 templates/app/react-native/src/screens/ChangePassword/ChangePasswordForm.js delete mode 100644 templates/app/react-native/src/screens/ChangePassword/ChangePasswordScreen.js delete mode 100644 templates/app/react-native/src/screens/CreateUpdateTenant/CreateUpdateTenantForm.js delete mode 100644 templates/app/react-native/src/screens/CreateUpdateTenant/CreateUpdateTenantScreen.js delete mode 100644 templates/app/react-native/src/screens/CreateUpdateUser/CreateUpdateUserForm.js delete mode 100644 templates/app/react-native/src/screens/CreateUpdateUser/CreateUpdateUserScreen.js delete mode 100644 templates/app/react-native/src/screens/CreateUpdateUser/UserRoles.js delete mode 100644 templates/app/react-native/src/screens/Home/HomeScreen.js delete mode 100644 templates/app/react-native/src/screens/Login/LoginScreen.js delete mode 100644 templates/app/react-native/src/screens/ManageProfile/ManageProfileForm.js delete mode 100644 templates/app/react-native/src/screens/ManageProfile/ManageProfileScreen.js delete mode 100644 templates/app/react-native/src/screens/Settings/SettingsScreen.js delete mode 100644 templates/app/react-native/src/screens/Tenants/TenantsScreen.js delete mode 100644 templates/app/react-native/src/screens/Users/UsersScreen.js delete mode 100644 templates/app/react-native/src/store/actions/AppActions.js delete mode 100644 templates/app/react-native/src/store/actions/LoadingActions.js delete mode 100644 templates/app/react-native/src/store/actions/PersistentStorageActions.js delete mode 100644 templates/app/react-native/src/store/index.js delete mode 100644 templates/app/react-native/src/store/reducers/AppReducer.js delete mode 100644 templates/app/react-native/src/store/reducers/LoadingReducer.js delete mode 100644 templates/app/react-native/src/store/reducers/PersistentStorageReducer.js delete mode 100644 templates/app/react-native/src/store/reducers/index.js delete mode 100644 templates/app/react-native/src/store/sagas/AppSaga.js delete mode 100644 templates/app/react-native/src/store/sagas/index.js delete mode 100644 templates/app/react-native/src/store/selectors/AppSelectors.js delete mode 100644 templates/app/react-native/src/store/selectors/LoadingSelectors.js delete mode 100644 templates/app/react-native/src/store/selectors/PersistentStorageSelectors.js delete mode 100644 templates/app/react-native/src/utils/DateExtensions.js delete mode 100644 templates/app/react-native/src/utils/Debounce.js delete mode 100644 templates/app/react-native/src/utils/RandomColor.js delete mode 100644 templates/app/react-native/src/utils/ReduxConnect.js delete mode 100644 templates/app/react-native/src/utils/TokenUtils.js diff --git a/templates/app/react-native/.eslintrc.json b/templates/app/react-native/.eslintrc.json deleted file mode 100644 index 6702dcefd6..0000000000 --- a/templates/app/react-native/.eslintrc.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "extends": ["airbnb", "prettier", "prettier/react"], - "parser": "babel-eslint", - "env": { - "jest": true - }, - "rules": { - "no-use-before-define": 0, - "react/jsx-filename-extension": 0, - "react/prop-types": ["error", { "ignore": ["navigation", "children"] }], - "react/require-default-props": 0, - "react/jsx-props-no-spreading": 0, - "react/forbid-prop-types": 0, - "import/prefer-default-export": 0, - "comma-dangle": 0, - "no-underscore-dangle": 1, - "no-plusplus": ["error", { "allowForLoopAfterthoughts": true }], - "no-param-reassign": 0, - "operator-linebreak": 0, - "global-require": 0 - }, - "globals": { - "fetch": false - } -} diff --git a/templates/app/react-native/.expo-shared/assets.json b/templates/app/react-native/.expo-shared/assets.json deleted file mode 100644 index 1e6decfbb5..0000000000 --- a/templates/app/react-native/.expo-shared/assets.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true, - "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true -} diff --git a/templates/app/react-native/.gitignore b/templates/app/react-native/.gitignore deleted file mode 100644 index ec8a36a257..0000000000 --- a/templates/app/react-native/.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -node_modules/ -.expo/ -dist/ -npm-debug.* -*.jks -*.p8 -*.p12 -*.key -*.mobileprovision -*.orig.* -web-build/ - -# macOS -.DS_Store diff --git a/templates/app/react-native/.prettierrc b/templates/app/react-native/.prettierrc deleted file mode 100644 index 471fac2824..0000000000 --- a/templates/app/react-native/.prettierrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "trailingComma": "all", - "singleQuote": true, - "jsxSingleQuote": false, - "printWidth": 80, - "semi": true, - "jsxBracketSameLine": true, - "arrowParens": "avoid" -} diff --git a/templates/app/react-native/.vscode/extensions.json b/templates/app/react-native/.vscode/extensions.json deleted file mode 100644 index d7df89c9cd..0000000000 --- a/templates/app/react-native/.vscode/extensions.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "recommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint"] -} diff --git a/templates/app/react-native/App.js b/templates/app/react-native/App.js deleted file mode 100644 index 6505569f2b..0000000000 --- a/templates/app/react-native/App.js +++ /dev/null @@ -1,101 +0,0 @@ -import { NavigationContainer } from '@react-navigation/native'; -import { createNativeStackNavigator } from '@react-navigation/native-stack'; -import i18n from 'i18n-js'; -import { NativeBaseProvider } from 'native-base'; -import React, { useEffect, useMemo, useState } from 'react'; -import { enableScreens } from 'react-native-screens'; -import { Provider } from 'react-redux'; -import { PersistGate } from 'redux-persist/integration/react'; -import { getEnvVars } from './Environment'; -import Loading from './src/components/Loading/Loading'; -import { LocalizationContext } from './src/contexts/LocalizationContext'; -import { initAPIInterceptor } from './src/interceptors/APIInterceptor'; -import AuthNavigator from './src/navigators/AuthNavigator'; -import DrawerNavigator from './src/navigators/DrawerNavigator'; -import { persistor, store } from './src/store'; -import AppActions from './src/store/actions/AppActions'; -import PersistentStorageActions from './src/store/actions/PersistentStorageActions'; -import { createLanguageSelector } from './src/store/selectors/AppSelectors'; -import { createTokenSelector } from './src/store/selectors/PersistentStorageSelectors'; -import { connectToRedux } from './src/utils/ReduxConnect'; -import { isTokenValid } from './src/utils/TokenUtils'; - -const Stack = createNativeStackNavigator(); - -const { localization } = getEnvVars(); - -i18n.defaultSeparator = '::'; - -const cloneT = i18n.t; -i18n.t = (key, ...args) => { - if (key.slice(0, 2) === '::') { - key = localization.defaultResourceName + key; - } - return cloneT(key, ...args); -}; - -enableScreens(); -initAPIInterceptor(store); - -export default function App() { - const language = createLanguageSelector()(store.getState()); - const [isReady, setIsReady] = useState(false); - - const localizationContextValue = useMemo( - () => ({ - t: i18n.t, - locale: (language || {}).cultureName, - }), - [language] - ); - - useEffect(() => { - store.dispatch( - AppActions.fetchAppConfigAsync({ - callback: () => setIsReady(true), - showLoading: true, - }) - ); - }, []); - - return ( - - - - - {isReady ? ( - - - - ) : null} - - - - - - ); -} - -function AppContainer({token, setToken}) { - const isValid = useMemo(() => isTokenValid(token), [token]); - - useEffect(() => { - if (!isValid && token && token.access_token) { - setToken({}) - } - }, [isValid]); - - - return isValid ? : -} - - -const ConnectedAppContainer = connectToRedux({ - component: AppContainer, - stateProps: (state) => ({ - token: createTokenSelector()(state), - }), - dispatchProps: { - setToken: PersistentStorageActions.setToken, - }, -}); diff --git a/templates/app/react-native/Environment.js b/templates/app/react-native/Environment.js deleted file mode 100644 index a4a23995f7..0000000000 --- a/templates/app/react-native/Environment.js +++ /dev/null @@ -1,32 +0,0 @@ -const yourIP = 'localhost'; // See the docs https://docs.abp.io/en/abp/latest/Getting-Started-React-Native?Tiered=No -const port = 44305; -const apiUrl = `http://${yourIP}:${port}`; -const ENV = { - dev: { - apiUrl: apiUrl, - oAuthConfig: { - issuer: apiUrl, - clientId: 'MyProjectName_App', - scope: 'offline_access MyProjectName', - }, - localization: { - defaultResourceName: 'MyProjectName', - }, - }, - prod: { - apiUrl: 'http://localhost:44305', - oAuthConfig: { - issuer: 'http://localhost:44305', - clientId: 'MyProjectName_App', - scope: 'offline_access MyProjectName', - }, - localization: { - defaultResourceName: 'MyProjectName', - }, - }, -}; - -export const getEnvVars = () => { - // eslint-disable-next-line no-undef - return __DEV__ ? ENV.dev : ENV.prod; -}; diff --git a/templates/app/react-native/app.json b/templates/app/react-native/app.json deleted file mode 100644 index 5b78afa519..0000000000 --- a/templates/app/react-native/app.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "expo": { - "name": "MyProjectName", - "slug": "MyProjectName", - "version": "1.0.0", - "orientation": "portrait", - "icon": "./assets/icon.png", - "splash": { - "image": "./assets/splash.png", - "resizeMode": "cover", - "backgroundColor": "#38003c" - }, - "updates": { - "fallbackToCacheTimeout": 0 - }, - "assetBundlePatterns": ["**/*"], - "ios": { - "supportsTablet": true, - "bundleIdentifier": "com.MyCompanyName.MyProjectName", - "buildNumber": "1.0.0" - }, - "android": { - "package": "com.MyCompanyName.MyProjectName", - "versionCode": 1, - "adaptiveIcon": { - "foregroundImage": "./assets/adaptive-icon.png", - "backgroundColor": "#FFFFFF" - } - }, - "web": { - "favicon": "./assets/icon.png" - } - } -} diff --git a/templates/app/react-native/assets/adaptive-icon.png b/templates/app/react-native/assets/adaptive-icon.png deleted file mode 100644 index 03d6f6b6c6727954aec1d8206222769afd178d8d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17547 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 diff --git a/templates/app/react-native/assets/icon.png b/templates/app/react-native/assets/icon.png deleted file mode 100644 index 528c8711d73860915d65549b5b556eeb8fa522c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3073 zcmWkw2{aUX7r!%RvQIOQu@z=UAx6ZI^2u1nR%9K^l*k%VJ=wRhR4D6{N{S?kv5rVt z#!D$#L#4&qL`5`0qI~o2Id?hdf9^f^o_qhl^Gma{wGbDP69E8mD@!v6zUTa>LRdo*{eBo^6kEJ^|h4n#G5S<)4!Lx6d(G%^@#biyrkIP-qoiJXL#J1W7pJw1w0D< z>WvXY)aG1Yh!rYdEPs2wrlmJg3D!S8zffddzG#?X0-4sdb1Gt}3F{mIZeV)C8=w11 z;1u0R^K#w7tXPi@$q|Cp6;vYFtqCI$1mH(lNTPxeRGFig*4-masKuqILE-Ocz2QM8 z>&@`>uC??&%I&dj|GEG|8*c^6b9A+!|3(mdqxbbOX zzl7Bbean0S%{8R+liPu`#*t#*!ds+U6{HkE!Hdk>Q`5<{6nJ&x>2P~|+7CmrFw^1! zF)TdmpRXlsvh~flxnB<0hT!gYY1n$PZ*N4qZiGWrN1X_Yi>PH~D<>Wsj0vclbFFqC zA*zZ~eRWF8WnafI%z(Lnw%)UaY}4rD)5G* zsfLB=^D|zJv6|1rm7s8W(j}Q(NgZ+uq`+NHo$`j<@Gd zqv9kMvO^mp^8{;@1LFL0PPdmIj~Lp~lysr-Apze;lW8Y{?UMojEgZJYW&O@Zk{e?W zc~{+3GuPyMvKhbfF+FvlUpP`_hEvzV(g!szod?M`yvX;%B}ausZyi(0khWRc$wp*< z*jC9s*O*?Ep27qfgN6$EF%5dnvp;hhn=dKHb|>?C=rYvI;X;k|5S)2tn+O@rxy18V zE6@#PJkmoc*##cntp-ldhYLnmSowW-Tjk~U?1@?BcRq(p;mz1j1iiNTf}cO$=?JOG zWk^u({dG4^>v$b+W2J13l-f<>c6}D@bS|cVQ#|WHj_Gc>NSh%!W~omM`2ace_$kB^ zMrmIdCa|EJSsLIJ-}A?qfwo3cxn18>B4MRq6Dt{VHp>6AX@XD*+2jNG>~_0qytY;I zasE>N#&wHVQr1N{V3tb#__@7CEdp2Rx8Qn$-aL!+RCWpF%A9?1H4b;NGp9AK{Juj} z)`;(`;^y=N zqRxiet39PHmp@elG-u9ObRS2J*E~vCj1B3(AzUyXF`FDvu+>0IS7_7&t?gS_=vi`s51(S(H@$?Qco|IHCt4RF)vIh>bVg~L~6{dfb@`l*>d0cT92=Z`7^_g z-*u_aq^hyHQH|X?=EHNFUD6RFc?m)7EkEd=otYqsq%zi4t5MgsJEJ%gZgL}NJO1lf zCH0h#Izly2m+J7x5F5GN!MZp4PN`51YGy7)#9s@kC>*ntozSUf z_kDY9mA=i8-XFN*o1i>%)Uulgkue8# z{^$SkC07-`wVxTfquDKxt)VA)Vj!U{UP%mUPTHLkmO$1LAg4QP3un$Y39!sz=()L5 z7!a1?oM5X^WHuJkUa~N-wF3ozsf8dtKRz51aB{XH#&1w3CD?1F!!wgqieR z86M9)cbgte8LixqL$kXDio-O6YMO%^k!TXED#TTPoLd+f9~+r%EL9tcg__N|Fq!m=DtE*pDfp?Q|EMHk{g*e=}_rMZf`@QPS$rx>6ww?qi--?O$_bhl2FL+$BX8w{&yWxn@i{ z^D1~oNffPI@IiCj8#eZh>C~@^M4SklJ#sFK^Q9)|I8Wng~1Ty>Znr`dI0xS1yD75Y)Lil$>`nX=uTHW7; zdczn3)ag{edXgr!#~}m)x(b5gR1vjH_3p8bKGcbO??0V?FWbf1ikkQmeMP6_n)?a7 z%-wyNDu{tYxr6R#A!aYRvTB1qIav(^a@sR+Rn2H}vp1_OOt%P0`Ly&11}lnUvOF*6fQLDZ_qS35YTYDjwq z;gY0+P~wmtb;;NM6OAoGnCjHyVKkzjzVW_CfJk;5<;+cD@JS0FA-HHdmTD4%(Zi1$ z^|((n%uS8tOf3EagTbEp+P*ylGOz7VsIr9-1Nf6e_brlo=N^2^IsdX*>QfsYF|hKU z@nfjYG*S-gSx6SueF8vg^6B%*wufE>Uq@f4NBs`bodCd8^vdp&1?7Q$yfdjf^Q&n+2Z`ol5sp zovb?x95Fb!^8qVQ9`WpQkXHRcnfXw0dj!V;=l3{pRY6ji(~_H!e^&AHQfq{0setE# zp#yuNX+Ox}DrCv00>LTS(-%t45={zLG+EmE>d0cE4%f*Virp+;YHwW`j;ZFel%l>* zvU$KU-9=fli84N)8SvhVz6CU&)W(i~0YYZjF3>h1)L$07C&Z-PIWHDP^n%D;mQD^) zQb1|>qJycvgIE-sBEf;*w~p#JQ9!y0k0;)hWJ~;R^+jO6XC6Il2^GxsTqds3D01DfgG_=$TH#wycQ# z8bDHLstou*zG}~UJj@O;+V+-uD~f*q@57@-S1W_3i$@GJSyI=XFo5g-c}wu&(0Z>eJ(}Kom1q#9yyF~=?MuY9LX-0XDD8quI@iX^} w71GwA>Y`AsYxpvEMK#QH7m8^@1j+RL4S`jX$oH}+A7kK)70uSH+Qc*I|MZSNQ2+n{ diff --git a/templates/app/react-native/assets/logo.png b/templates/app/react-native/assets/logo.png deleted file mode 100644 index b9b1e36c50f14695921242abd3e11bf5bf6475df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1017 zcmV2J(j z;a`C(a0RZ=EAXk5NS5U|4upRCNhA-2;JEFJvEx7#MNu-zNXXoFnWUVgn`8j`26gOp z?&zQ|pQN3n2fAK*ekLh=+cl13WRt0V1=*x{h56f{3(Hf%!?mM=%%vRx{V1z;$=x zdBP#-s}Q^ zl6EbGIa5-B&k~q#2qf69;CQCK6V#2z)^c)yxdIa@Qxm2EIwUabaqnQvgQJc3_j{}j zv(%GvFn1Q1=QNRB7MQz*Fza?uFw{br?*OJ2c{L618Qq0y0caAKZ)kGp`~%)4BrwC4 zsblF(8_!^JuL|?%5oVtSoN+@i<6}0MYbB}bdqd|MM)y*fHF#Ilrx8vV>fF^~GMH@K zB+SW1VNOpXeM6NI|y?exlkn7`N?kuuJm{SzE3NwyJBPO|Og)q0CVdiXvF>^!Qq5cKTwo>$#V209wIF|#mKymT|eU`#Zfl7gr zIe2E@FmpCysZj0^V16^q8kQ6-D{(;Tl>?l~-*y~kr0-hIcy|K07R+lWfRO;^D-#0T zLF`@6^qNZ&Kq-uCCV=%PEzKPrZQ=U%9|&`bmat#}rDB%mU|jtlm*(EKn(>aJNiqZG zEfh^`e-=)FS^`v9#5keM9u`efOLFB>0hAU^s4^dc+A){UfO!k$Z0K*i)kAy{BLjhRyi_X0aq7!{#1O>LRztriRG5^H&Ybfmz3(zFq=z nQ6P5AWRSotwkx~@=5ys27{JSJ7yUQu00000NkvXXu0mjfU!U6n diff --git a/templates/app/react-native/assets/splash.png b/templates/app/react-native/assets/splash.png deleted file mode 100644 index 0d5f0ec026eca79831a56efbaab3c57a8966f8b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39550 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�bVP#4S^k+^=!J0?!;cfs9u)18T}>_8%XS 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 diff --git a/templates/app/react-native/babel.config.js b/templates/app/react-native/babel.config.js deleted file mode 100644 index d715221e05..0000000000 --- a/templates/app/react-native/babel.config.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = function(api) { - api.cache(true); - return { - presets: ['babel-preset-expo'], - plugins: ['react-native-reanimated/plugin'], - }; -}; \ No newline at end of file diff --git a/templates/app/react-native/package.json b/templates/app/react-native/package.json deleted file mode 100644 index 9fbe23ea4b..0000000000 --- a/templates/app/react-native/package.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "myprojectname", - "version": "1.0.0", - "main": "node_modules/expo/AppEntry.js", - "scripts": { - "start": "expo start --port 19000", - "android": "expo start --android", - "ios": "expo start --ios", - "web": "expo start --web", - "eject": "expo eject" - }, - "dependencies": { - "@react-native-async-storage/async-storage": "1.18.2", - "@react-navigation/drawer": "^6.5.4", - "@react-navigation/native": "^6.1.0", - "@react-navigation/native-stack": "^6.9.5", - "@reduxjs/toolkit": "^1.7.1", - "axios": "~1.2.1", - "expo": "^49.0.0", - "expo-status-bar": "~1.6.0", - "formik": "^2.2.9", - "i18n-js": "^3.8.0", - "native-base": "^3.4.25", - "prop-types": "^15.8.1", - "react": "18.2.0", - "react-dom": "18.2.0", - "react-native": "0.72.10", - "react-native-chart-kit": "^6.11.0", - "react-native-gesture-handler": "~2.12.0", - "react-native-reanimated": "~3.3.0", - "react-native-safe-area-context": "4.6.3", - "react-native-screens": "~3.22.0", - "react-native-svg": "13.9.0", - "react-native-web": "~0.19.6", - "react-redux": "^8.0.5", - "redux-persist": "^6.0.0", - "redux-saga": "^1.2.1", - "yup": "^0.32.11" - }, - "devDependencies": { - "@babel/core": "^7.19.3" - }, - "private": true -} diff --git a/templates/app/react-native/src/api/API.js b/templates/app/react-native/src/api/API.js deleted file mode 100644 index dc604f62e9..0000000000 --- a/templates/app/react-native/src/api/API.js +++ /dev/null @@ -1,11 +0,0 @@ -import axios from 'axios'; -import { getEnvVars } from '../../Environment'; - -const { apiUrl } = getEnvVars(); - -const axiosInstance = axios.create({ - baseURL: apiUrl, - withCredentials: false -}); - -export default axiosInstance; diff --git a/templates/app/react-native/src/api/AccountAPI.js b/templates/app/react-native/src/api/AccountAPI.js deleted file mode 100644 index ddf49579d3..0000000000 --- a/templates/app/react-native/src/api/AccountAPI.js +++ /dev/null @@ -1,62 +0,0 @@ -import api from './API'; -import { getEnvVars } from '../../Environment'; - -const { oAuthConfig } = getEnvVars(); - -getLoginData = (username, password) => { - const formData = { - grant_type: 'password', - scope: oAuthConfig.scope, - username: username, - password: password, - client_id: oAuthConfig.clientId, - }; - - if (oAuthConfig.clientSecret) - formData['client_secret'] = oAuthConfig.clientSecret; - - return Object.entries(formData) - .map(([key, value]) => `${key}=${encodeURIComponent(value)}`) - .join('&'); -}; - -export const login = ({ username, password }) => - api({ - method: 'POST', - url: '/connect/token', - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - data: getLoginData(username, password), - baseURL: oAuthConfig.issuer, - }).then(({ data }) => data); - -export const Logout = ( - input = { client_id: '', token: '', token_type_hint: '' }, -) => { - if (!input.token_type_hint) { - input.token_type_hint = 'access_token'; - } - - const _data = Object.entries(input) - .map(([key, value]) => `${key}=${encodeURIComponent(value)}`) - .join('&'); - - return api({ - method: 'POST', - url: '/connect/revocat', - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - data: _data, - baseURL: oAuthConfig.issuer, - }).then(({ data }) => data); -}; - -export const getTenant = tenantName => - api({ - method: 'GET', - url: `/api/abp/multi-tenancy/tenants/by-name/${tenantName}`, - }).then(({ data }) => data); - -export const getTenantById = tenantId => - api({ - method: 'GET', - url: `/api/abp/multi-tenancy/tenants/by-id/${tenantId}`, - }).then(({ data }) => data); diff --git a/templates/app/react-native/src/api/ApplicationConfigurationAPI.js b/templates/app/react-native/src/api/ApplicationConfigurationAPI.js deleted file mode 100644 index 96fcbea5cd..0000000000 --- a/templates/app/react-native/src/api/ApplicationConfigurationAPI.js +++ /dev/null @@ -1,30 +0,0 @@ -import i18n from 'i18n-js'; -import api from './API'; - -export const getApplicationConfiguration = () => - api - .get('/api/abp/application-configuration') - .then(({ data }) => data) - .then(async config => { - const { cultureName } = config.localization.currentCulture; - i18n.locale = cultureName; - - Object.keys(config.localization.values).forEach(key => { - const resource = config.localization.values[key]; - - if (typeof resource !== 'object') return; - - Object.keys(resource).forEach(key2 => { - if (/'{|{/g.test(resource[key2])) { - resource[key2] = resource[key2].replace(/'{|{/g, '{{').replace(/}'|}/g, '}}'); - } - }); - }); - - i18n.translations[cultureName] = { - ...config.localization.values, - ...(i18n.translations[cultureName] || {}), - }; - - return config; - }); diff --git a/templates/app/react-native/src/api/IdentityAPI.js b/templates/app/react-native/src/api/IdentityAPI.js deleted file mode 100644 index 9ae81ad2b5..0000000000 --- a/templates/app/react-native/src/api/IdentityAPI.js +++ /dev/null @@ -1,27 +0,0 @@ -import api from './API'; - - -export const getAllRoles = () => api.get('/api/identity/roles/all').then(({ data }) => data.items); - -export const getUserRoles = id => - api.get(`/api/identity/users/${id}/roles`).then(({ data }) => data.items); - -export const getUsers = (params = { maxResultCount: 10, skipCount: 0 }) => - api.get('/api/identity/users', { params }).then(({ data }) => data); - -export const getUserById = id => api.get(`/api/identity/users/${id}`).then(({ data }) => data); - -export const createUser = body => api.post('/api/identity/users', body).then(({ data }) => data); - -export const updateUser = (body, id) => - api.put(`/api/identity/users/${id}`, body).then(({ data }) => data); - -export const removeUser = id => api.delete(`/api/identity/users/${id}`); - -export const getProfileDetail = () => api.get('/api/account/my-profile').then(({ data }) => data); - -export const updateProfileDetail = body => - api.put('/api/account/my-profile', body).then(({ data }) => data); - -export const changePassword = body => - api.post('/api/account/my-profile/change-password', body).then(({ data }) => data); diff --git a/templates/app/react-native/src/api/TenantManagementAPI.js b/templates/app/react-native/src/api/TenantManagementAPI.js deleted file mode 100644 index f60ed96450..0000000000 --- a/templates/app/react-native/src/api/TenantManagementAPI.js +++ /dev/null @@ -1,21 +0,0 @@ -import api from './API'; - -export function getTenants(params = {}) { - return api.get('/api/multi-tenancy/tenants', { params }).then(({ data }) => data); -} - -export function createTenant(body) { - return api.post('/api/multi-tenancy/tenants', body).then(({ data }) => data); -} - -export function getTenantById(id) { - return api.get(`/api/multi-tenancy/tenants/${id}`).then(({ data }) => data); -} - -export function updateTenant(body, id) { - return api.put(`/api/multi-tenancy/tenants/${id}`, body).then(({ data }) => data); -} - -export function removeTenant(id) { - return api.delete(`/api/multi-tenancy/tenants/${id}`).then(({ data }) => data); -} diff --git a/templates/app/react-native/src/components/AddIcon/AddIcon.js b/templates/app/react-native/src/components/AddIcon/AddIcon.js deleted file mode 100644 index b4cc26f36f..0000000000 --- a/templates/app/react-native/src/components/AddIcon/AddIcon.js +++ /dev/null @@ -1,16 +0,0 @@ -import { Ionicons } from '@expo/vector-icons'; -import { Icon } from 'native-base'; -import React from 'react'; - -export default function AddIcon({ onPress, ...iconProps }) { - return ( - - ); -} diff --git a/templates/app/react-native/src/components/DataList/DataList.js b/templates/app/react-native/src/components/DataList/DataList.js deleted file mode 100644 index 9dce6d9562..0000000000 --- a/templates/app/react-native/src/components/DataList/DataList.js +++ /dev/null @@ -1,143 +0,0 @@ -import { Ionicons } from '@expo/vector-icons'; -import { useFocusEffect } from '@react-navigation/native'; -import i18n from 'i18n-js'; -import { Box, Center, FlatList, Icon, Input, Spinner, Text } from 'native-base'; -import PropTypes from 'prop-types'; -import React, { forwardRef, useCallback, useEffect, useState } from 'react'; -import { StyleSheet, View } from 'react-native'; -import LoadingActions from '../../store/actions/LoadingActions'; -import { debounce } from '../../utils/Debounce'; -import { connectToRedux } from '../../utils/ReduxConnect'; -import LoadingButton from '../LoadingButton/LoadingButton'; - -function DataList({ - navigation, - fetchFn, - render, - maxResultCount = 15, - debounceTime = 350, - ...props -}) { - const [records, setRecords] = useState([]); - const [totalCount, setTotalCount] = useState(0); - const [loading, setLoading] = useState(false); - const [searchLoading, setSearchLoading] = useState(false); - const [buttonLoading, setButtonLoading] = useState(false); - const [skipCount, setSkipCount] = useState(0); - const [filter, setFilter] = useState(''); - - const fetch = (skip = 0, isRefreshingActive = true) => { - if (isRefreshingActive) setLoading(true); - return fetchFn({ filter, maxResultCount, skipCount: skip }) - .then(({ items, totalCount: total }) => { - setTotalCount(total); - setRecords(skip ? [...records, ...items] : items); - setSkipCount(skip); - }) - .finally(() => { - if (isRefreshingActive) setLoading(false); - }); - }; - - const fetchPartial = () => { - if (loading || records.length === totalCount) return; - - setButtonLoading(true); - fetch(skipCount + maxResultCount, false).finally(() => - setButtonLoading(false) - ); - }; - - useFocusEffect( - useCallback(() => { - setSkipCount(0); - fetch(0, false); - }, []) - ); - - useEffect(() => { - function searchFetch() { - setSearchLoading(true); - return fetch(0, false).finally(() => - setTimeout(() => setSearchLoading(false), 150) - ); - } - debounce(searchFetch, debounceTime)(); - }, [filter]); - - return ( -
- - - ) : ( - - ) - } - /> - ( - <> - {render(...args)} - {args.index + 1 === skipCount + maxResultCount && - totalCount > records.length ? ( - - fetchPartial()} - > - {i18n.t('AbpUi::LoadMore')} - - - ) : null} - - )} - {...props} - /> - -
- ); -} - -DataList.propTypes = { - ...FlatList.propTypes, - fetchFn: PropTypes.func.isRequired, - render: PropTypes.func.isRequired, - maxResultCount: PropTypes.number, - debounceTime: PropTypes.number, -}; - -const styles = StyleSheet.create({ - container: { flex: 1 }, - list: {}, -}); - -const Forwarded = forwardRef((props, ref) => ( - -)); - -export default connectToRedux({ - component: Forwarded, - dispatchProps: { - startLoading: LoadingActions.start, - stopLoading: LoadingActions.stop, - }, -}); diff --git a/templates/app/react-native/src/components/DrawerContent/DrawerContent.js b/templates/app/react-native/src/components/DrawerContent/DrawerContent.js deleted file mode 100644 index 4f7792fab5..0000000000 --- a/templates/app/react-native/src/components/DrawerContent/DrawerContent.js +++ /dev/null @@ -1,104 +0,0 @@ -import { Ionicons } from '@expo/vector-icons'; -import Constants from 'expo-constants'; -import i18n from 'i18n-js'; -import { List, Text, View } from 'native-base'; -import PropTypes from 'prop-types'; -import React from 'react'; -import { Image, StyleSheet } from 'react-native'; -import { SafeAreaView } from 'react-native-safe-area-context'; -import { withPermission } from '../../hocs/PermissionHOC'; - -const screens = { - HomeStack: { label: '::Menu:Home', iconName: 'home' }, - UsersStack: { - label: 'AbpIdentity::Users', - iconName: 'people', - requiredPolicy: 'AbpIdentity.Users', - }, - TenantsStack: { - label: 'AbpTenantManagement::Tenants', - iconName: 'book-outline', - requiredPolicy: 'AbpTenantManagement.Tenants', - }, - SettingsStack: { label: 'AbpSettingManagement::Settings', iconName: 'cog' }, -}; - -const ListItemWithPermission = withPermission(List.Item); - -function DrawerContent({ - navigation, - state: { routeNames, index: currentScreenIndex }, -}) { - const navigate = (screen) => { - navigation.navigate(screen); - navigation.closeDrawer(); - }; - - return ( - - - - - - - {routeNames.map((name) => ( - navigate(name)} - my="0" - > - - {i18n.t(screens[name].label)} - - ))} - - - - - © MyProjectName - - - v{Constants.expoConfig.version} - - - - ); -} - -const styles = StyleSheet.create({ - container: { - flexGrow: 1, - }, - logo: { - marginTop: 20, - marginBottom: 15, - }, - headerView: { - alignItems: 'center', - }, - footer: { - backgroundColor: '#eee', - flexDirection: 'row', - justifyContent: 'space-between', - }, - copyRight: { - margin: 15, - }, - version: { - margin: 15, - }, -}); - -DrawerContent.propTypes = { - state: PropTypes.object.isRequired, -}; - -export default DrawerContent; diff --git a/templates/app/react-native/src/components/FormButtons/FormButtons.js b/templates/app/react-native/src/components/FormButtons/FormButtons.js deleted file mode 100644 index d844f7747a..0000000000 --- a/templates/app/react-native/src/components/FormButtons/FormButtons.js +++ /dev/null @@ -1,78 +0,0 @@ -import i18n from 'i18n-js'; -import { Button, Text } from 'native-base'; -import PropTypes from 'prop-types'; -import React, { forwardRef } from 'react'; -import { Alert, StyleSheet, View } from 'react-native'; - -function FormButtons({ - submit, - remove, - removeMessage, - isRemoveDisabled, - isSubmitDisabled, - isShowRemove = false, - isShowSubmit = true, -}) { - const confirmation = () => { - Alert.alert( - i18n.t('AbpUi::AreYouSure'), - removeMessage, - [ - { - text: i18n.t('AbpUi::Cancel'), - style: 'cancel', - }, - { text: i18n.t('AbpUi::Yes'), onPress: () => remove() }, - ], - { cancelable: true }, - ); - }; - - return ( - - {isShowRemove ? ( - - ) : null} - {isShowSubmit ? ( - - ) : null} - - ); -} - -FormButtons.propTypes = { - submit: PropTypes.func.isRequired, - remove: PropTypes.func, - removeMessage: PropTypes.string, - style: PropTypes.any, - isRemoveDisabled: PropTypes.bool, - isSubmitDisabled: PropTypes.bool, - isShowRemove: PropTypes.bool, - isShowSubmit: PropTypes.bool, -}; - -const styles = StyleSheet.create({ - container: { - width: '100%', - justifyContent: 'center', - alignItems: 'center', - position: 'absolute', - bottom: 0, - flexDirection: 'row', - }, -}); - -const Forwarded = forwardRef((props, ref) => ); - -export default Forwarded; diff --git a/templates/app/react-native/src/components/FormButtons/index.js b/templates/app/react-native/src/components/FormButtons/index.js deleted file mode 100644 index bf8864e7e0..0000000000 --- a/templates/app/react-native/src/components/FormButtons/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default as FormButtons } from './FormButtons'; diff --git a/templates/app/react-native/src/components/HamburgerIcon/HamburgerIcon.js b/templates/app/react-native/src/components/HamburgerIcon/HamburgerIcon.js deleted file mode 100644 index 2d165f8249..0000000000 --- a/templates/app/react-native/src/components/HamburgerIcon/HamburgerIcon.js +++ /dev/null @@ -1,17 +0,0 @@ -import { Ionicons } from '@expo/vector-icons'; -import { Icon } from 'native-base'; -import React from 'react'; -import { Platform } from 'react-native'; - -export default function HamburgerIcon({ navigation, ...iconProps }) { - return ( - navigation.openDrawer()} - as={Ionicons} - name={Platform.OS ? 'ios-menu' : 'md-menu'} - size="8" - marginLeft={2} - {...iconProps} - /> - ); -} diff --git a/templates/app/react-native/src/components/Loading/Loading.js b/templates/app/react-native/src/components/Loading/Loading.js deleted file mode 100644 index bd8f5ff180..0000000000 --- a/templates/app/react-native/src/components/Loading/Loading.js +++ /dev/null @@ -1,62 +0,0 @@ -import { Spinner, View } from 'native-base'; -import PropTypes from 'prop-types'; -import React, { forwardRef } from 'react'; -import { StyleSheet } from 'react-native'; -import { - createLoadingSelector, - createOpacitySelector -} from '../../store/selectors/LoadingSelectors'; -import { connectToRedux } from '../../utils/ReduxConnect'; - -function Loading({ loading, opacity }) { - return loading ? ( - - - - - ) : null; -} -const Forwarded = forwardRef((props, ref) => ); - -const backdropStyle = { - position: 'absolute', - top: 0, - left: 0, - width: '100%', - height: '100%', - backgroundColor: '#fff', -}; - -export const styles = StyleSheet.create({ - container: { - ...backdropStyle, - backgroundColor: 'transparent', - // zIndex: activeTheme.zIndex.indicator, // TODO - alignItems: 'center', - justifyContent: 'center', - }, - backdrop: backdropStyle, - spinner: { - // color: activeTheme.brandPrimary, // TODO - fontSize: 100, - }, -}); - -Loading.propTypes = { - style: PropTypes.objectOf(PropTypes.any), - loading: PropTypes.bool, - opacity: PropTypes.number, -}; - -export default connectToRedux({ - component: Forwarded, - stateProps: state => ({ - loading: createLoadingSelector()(state), - opacity: createOpacitySelector()(state), - }), -}); diff --git a/templates/app/react-native/src/components/LoadingButton/LoadingButton.js b/templates/app/react-native/src/components/LoadingButton/LoadingButton.js deleted file mode 100644 index d36c7fc68c..0000000000 --- a/templates/app/react-native/src/components/LoadingButton/LoadingButton.js +++ /dev/null @@ -1,22 +0,0 @@ -import { Button, Spinner } from 'native-base'; -import PropTypes from 'prop-types'; -import React from 'react'; -import { StyleSheet } from 'react-native'; - -export default function LoadingButton({ loading = false, style, children, ...props }) { - return ( - - ); -} - -LoadingButton.propTypes = { - ...Button.propTypes, - loading: PropTypes.bool.isRequired, -}; - -const styles = StyleSheet.create({ - button: { marginTop: 20, marginBottom: 30, height: 30 }, -}); \ No newline at end of file diff --git a/templates/app/react-native/src/components/TenantBox/TenantBox.js b/templates/app/react-native/src/components/TenantBox/TenantBox.js deleted file mode 100644 index 939efb3c88..0000000000 --- a/templates/app/react-native/src/components/TenantBox/TenantBox.js +++ /dev/null @@ -1,143 +0,0 @@ -import i18n from 'i18n-js'; -import { Box, Button, FormControl, Input, Text } from 'native-base'; -import PropTypes from 'prop-types'; -import React, { forwardRef, useState } from 'react'; -import { Alert, StyleSheet, View } from 'react-native'; -import { getTenant } from '../../api/AccountAPI'; -import PersistentStorageActions from '../../store/actions/PersistentStorageActions'; -import { createTenantSelector } from '../../store/selectors/PersistentStorageSelectors'; -import { connectToRedux } from '../../utils/ReduxConnect'; - -function TenantBox({ - tenant = {}, - setTenant, - showTenantSelection, - toggleTenantSelection, -}) { - const [tenantName, setTenantName] = useState(tenant.name); - - const findTenant = () => { - if (!tenantName) { - setTenant({}); - toggleTenantSelection(); - return; - } - - getTenant(tenantName).then(({ success, ...data }) => { - if (!success) { - Alert.alert( - i18n.t('AbpUi::Error'), - i18n.t('AbpUiMultiTenancy::GivenTenantIsNotAvailable', { - 0: tenantName, - }), - [{ text: i18n.t('AbpUi::Ok') }] - ); - return; - } - setTenant(data); - toggleTenantSelection(); - }); - }; - - return ( - <> - - - - {i18n.t('AbpUiMultiTenancy::Tenant')} - - - {tenant.name - ? tenant.name - : i18n.t('AbpUiMultiTenancy::NotSelected')} - - - - - {showTenantSelection ? ( - - - - {i18n.t('AbpUiMultiTenancy::Name')} - - - - - {i18n.t('AbpUiMultiTenancy::SwitchTenantHint')} - - - - - - - ) : null} - - ); -} - -TenantBox.propTypes = { - setTenant: PropTypes.func.isRequired, - showTenantSelection: PropTypes.bool.isRequired, - toggleTenantSelection: PropTypes.func.isRequired, - tenant: PropTypes.object.isRequired, -}; - -const styles = StyleSheet.create({ - button: { marginTop: 20, width: '49%' }, - - tenant: { color: '#777' }, - title: { - marginRight: 10, - fontSize: 13, - fontWeight: '600', - textTransform: 'uppercase', - }, - hint: { color: '#bbb', textAlign: 'left' }, -}); - -const Forwarded = forwardRef((props, ref) => ( - -)); - -export default connectToRedux({ - component: Forwarded, - dispatchProps: { - setTenant: PersistentStorageActions.setTenant, - }, - stateProps: (state) => ({ - tenant: createTenantSelector()(state), - }), -}); \ No newline at end of file diff --git a/templates/app/react-native/src/components/ValidationMessage/ValidationMessage.js b/templates/app/react-native/src/components/ValidationMessage/ValidationMessage.js deleted file mode 100644 index c2a398f379..0000000000 --- a/templates/app/react-native/src/components/ValidationMessage/ValidationMessage.js +++ /dev/null @@ -1,16 +0,0 @@ -import i18n from 'i18n-js'; -import React, { forwardRef } from 'react'; -import { Text } from 'react-native'; - -const ValidationMessage = ({ children, ...props }) => - children ? {i18n.t(children)} : null; - -const styles = { - fontSize: 12, - marginTop: 3, - color: '#ed2f2f', -}; - -const Forwarded = forwardRef((props, ref) => ); - -export default Forwarded diff --git a/templates/app/react-native/src/contexts/LocalizationContext.js b/templates/app/react-native/src/contexts/LocalizationContext.js deleted file mode 100644 index 83c68ba2bc..0000000000 --- a/templates/app/react-native/src/contexts/LocalizationContext.js +++ /dev/null @@ -1,3 +0,0 @@ -import React from 'react'; - -export const LocalizationContext = React.createContext(); diff --git a/templates/app/react-native/src/hocs/PermissionHOC.js b/templates/app/react-native/src/hocs/PermissionHOC.js deleted file mode 100644 index 6cf8025428..0000000000 --- a/templates/app/react-native/src/hocs/PermissionHOC.js +++ /dev/null @@ -1,17 +0,0 @@ -import React, { forwardRef } from 'react'; -import PropTypes from 'prop-types'; -import { usePermission } from '../hooks/UsePermission'; - -export function withPermission(Component, policyKey) { - const Forwarded = forwardRef((props, ref) => { - const isGranted = - policyKey || props.policyKey ? usePermission(policyKey || props.policyKey) : true; - return isGranted ? : null; - }); - - Forwarded.propTypes = { - policyKey: PropTypes.string, - }; - - return Forwarded; -} diff --git a/templates/app/react-native/src/hooks/UsePermission.js b/templates/app/react-native/src/hooks/UsePermission.js deleted file mode 100644 index a50d8f7fa1..0000000000 --- a/templates/app/react-native/src/hooks/UsePermission.js +++ /dev/null @@ -1,16 +0,0 @@ -import { useEffect, useState } from 'react'; -import { store } from '../store'; -import { createGrantedPolicySelector } from '../store/selectors/AppSelectors'; - -export function usePermission(key) { - const [permission, setPermission] = useState(false); - - const state = store.getState(); - const policy = createGrantedPolicySelector(key)(state); - - useEffect(() => { - setPermission(policy); - }, [policy]); - - return permission; -} diff --git a/templates/app/react-native/src/interceptors/APIInterceptor.js b/templates/app/react-native/src/interceptors/APIInterceptor.js deleted file mode 100644 index b5ffc56a3c..0000000000 --- a/templates/app/react-native/src/interceptors/APIInterceptor.js +++ /dev/null @@ -1,100 +0,0 @@ -import i18n from 'i18n-js'; -import { Toast } from 'native-base'; -import api from '../api/API'; -import LoadingActions from '../store/actions/LoadingActions'; -import PersistentStorageActions from '../store/actions/PersistentStorageActions'; - -export function initAPIInterceptor(store) { - api.interceptors.request.use( - async request => { - const { - persistentStorage: { token, language, tenant }, - } = store.getState(); - - if (!request.headers.Authorization && token && token.access_token) { - request.headers.Authorization = `${token.token_type} ${token.access_token}`; - } - - if (!request.headers['Content-Type']) { - request.headers['Content-Type'] = 'application/json'; - } - - if (!request.headers['Accept-Language'] && language) { - request.headers['Accept-Language'] = language; - } - - if (!request.headers.__tenant && tenant && tenant.tenantId) { - request.headers.__tenant = tenant.tenantId; - } - - return request; - }, - error => console.error(error), - ); - - api.interceptors.response.use( - response => response, - error => { - store.dispatch(LoadingActions.clear()); - const errorRes = error.response; - if (errorRes) { - if (errorRes.headers._abperrorformat && errorRes.status === 401) { - store.dispatch(PersistentStorageActions.setToken({})); - } - - showError({ error: errorRes.data.error || {}, status: errorRes.status }); - } else { - Toast.show({ - title: 'An unexpected error has occurred', - isClosable: true, - duration: 10000, - backgroundColor: 'danger.500', - }); - } - - return Promise.reject(error); - }, - ); -} - -function showError({ error = {}, status }) { - let message = ''; - let title = i18n.t('AbpAccount::DefaultErrorMessage'); - - if (typeof error === 'string') { - message = error; - } else if (error.details) { - message = error.details; - title = error.message; - } else if (error.message) { - message = error.message; - } else { - switch (status) { - case 401: - title = i18n.t('AbpAccount::DefaultErrorMessage401'); - message = i18n.t('AbpAccount::DefaultErrorMessage401Detail'); - break; - case 403: - title = i18n.t('AbpAccount::DefaultErrorMessage403'); - message = i18n.t('AbpAccount::DefaultErrorMessage403Detail'); - break; - case 404: - title = i18n.t('AbpAccount::DefaultErrorMessage404'); - message = i18n.t('AbpAccount::DefaultErrorMessage404Detail'); - break; - case 500: - title = i18n.t('AbpAccount::500Message'); - message = i18n.t('AbpAccount::InternalServerErrorMessage'); - break; - default: - break; - } - } - - Toast.show({ - title: `${title}\n${message}`, - isClosable: true, - duration: 10000, - backgroundColor: 'danger.500', - }); -} diff --git a/templates/app/react-native/src/navigators/AuthNavigator.js b/templates/app/react-native/src/navigators/AuthNavigator.js deleted file mode 100644 index 0311556deb..0000000000 --- a/templates/app/react-native/src/navigators/AuthNavigator.js +++ /dev/null @@ -1,22 +0,0 @@ -import { createNativeStackNavigator } from '@react-navigation/native-stack'; -import React, { useContext } from 'react'; -import { LocalizationContext } from '../contexts/LocalizationContext'; -import LoginScreen from '../screens/Login/LoginScreen'; - -const Stack = createNativeStackNavigator(); - -export default function AuthNavigator() { - const {t} = useContext(LocalizationContext); - - return ( - - ({ - title: t('AbpAccount::Login'), - })} - /> - - ); -} diff --git a/templates/app/react-native/src/navigators/DrawerNavigator.js b/templates/app/react-native/src/navigators/DrawerNavigator.js deleted file mode 100644 index 3cd83c8a13..0000000000 --- a/templates/app/react-native/src/navigators/DrawerNavigator.js +++ /dev/null @@ -1,43 +0,0 @@ -import { createDrawerNavigator } from '@react-navigation/drawer'; -import React from 'react'; -import DrawerContent from '../components/DrawerContent/DrawerContent'; -import HamburgerIcon from '../components/HamburgerIcon/HamburgerIcon'; -import { LocalizationContext } from '../contexts/LocalizationContext'; -import HomeStackNavigator from './HomeNavigator'; -import SettingsStackNavigator from './SettingsNavigator'; -import TenantsStackNavigator from './TenantsNavigator'; -import UsersStackNavigator from './UsersNavigator'; - -const Drawer = createDrawerNavigator(); - -export default function DrawerNavigator() { - const { t } = React.useContext(LocalizationContext); - - return ( - - ({ - title: t('::Menu:Home'), - headerLeft: () => , - })} - /> - null }} - /> - null }} - /> - null }} - /> - - ); -} diff --git a/templates/app/react-native/src/navigators/HomeNavigator.js b/templates/app/react-native/src/navigators/HomeNavigator.js deleted file mode 100644 index 8f9e588edd..0000000000 --- a/templates/app/react-native/src/navigators/HomeNavigator.js +++ /dev/null @@ -1,17 +0,0 @@ -import { createNativeStackNavigator } from '@react-navigation/native-stack'; -import React from 'react'; -import HomeScreen from '../screens/Home/HomeScreen'; - -const Stack = createNativeStackNavigator(); - -export default function HomeStackNavigator() { - return ( - - null}} - /> - - ); -} diff --git a/templates/app/react-native/src/navigators/SettingsNavigator.js b/templates/app/react-native/src/navigators/SettingsNavigator.js deleted file mode 100644 index 370da97b93..0000000000 --- a/templates/app/react-native/src/navigators/SettingsNavigator.js +++ /dev/null @@ -1,41 +0,0 @@ -import { createNativeStackNavigator } from '@react-navigation/native-stack'; -import i18n from 'i18n-js'; -import React from 'react'; -import HamburgerIcon from '../components/HamburgerIcon/HamburgerIcon'; -import { LocalizationContext } from '../contexts/LocalizationContext'; -import ChangePasswordScreen from '../screens/ChangePassword/ChangePasswordScreen'; -import ManageProfileScreen from '../screens/ManageProfile/ManageProfileScreen'; -import SettingsScreen from '../screens/Settings/SettingsScreen'; - -const Stack = createNativeStackNavigator(); - -export default function SettingsStackNavigator() { - const { t } = React.useContext(LocalizationContext); - - return ( - - ({ - headerLeft: () => , - title: t('AbpSettingManagement::Settings'), - })} - /> - - - - ); -} diff --git a/templates/app/react-native/src/navigators/TenantsNavigator.js b/templates/app/react-native/src/navigators/TenantsNavigator.js deleted file mode 100644 index 94ae711d42..0000000000 --- a/templates/app/react-native/src/navigators/TenantsNavigator.js +++ /dev/null @@ -1,35 +0,0 @@ - -import { createNativeStackNavigator } from '@react-navigation/native-stack'; -import React from 'react'; -import AddIcon from '../components/AddIcon/AddIcon'; -import HamburgerIcon from '../components/HamburgerIcon/HamburgerIcon'; -import { LocalizationContext } from '../contexts/LocalizationContext'; -import CreateUpdateTenantScreen from '../screens/CreateUpdateTenant/CreateUpdateTenantScreen'; -import TenantsScreen from '../screens/Tenants/TenantsScreen'; - -const Stack = createNativeStackNavigator(); - -export default function TenantsStackNavigator() { - const { t } = React.useContext(LocalizationContext); - - return ( - - ({ - title: t('AbpTenantManagement::Tenants'), - headerLeft: () => , - headerRight: () => navigation.navigate('CreateUpdateTenant')}/>, - })} - /> - ({ - title: t(route.params?.tenantId ? 'AbpTenantManagement::Edit' : 'AbpTenantManagement::NewTenant'), - })} - /> - - ); -} diff --git a/templates/app/react-native/src/navigators/UsersNavigator.js b/templates/app/react-native/src/navigators/UsersNavigator.js deleted file mode 100644 index 537129be14..0000000000 --- a/templates/app/react-native/src/navigators/UsersNavigator.js +++ /dev/null @@ -1,34 +0,0 @@ -import { createNativeStackNavigator } from '@react-navigation/native-stack'; -import React from 'react'; -import AddIcon from '../components/AddIcon/AddIcon'; -import HamburgerIcon from '../components/HamburgerIcon/HamburgerIcon'; -import { LocalizationContext } from '../contexts/LocalizationContext'; -import CreateUpdateUserScreen from '../screens/CreateUpdateUser/CreateUpdateUserScreen'; -import UsersScreen from '../screens/Users/UsersScreen'; - -const Stack = createNativeStackNavigator(); - -export default function UsersStackNavigator() { - const { t } = React.useContext(LocalizationContext); - - return ( - - ({ - title: t('AbpIdentity::Users'), - headerLeft: () => , - headerRight: () => navigation.navigate('CreateUpdateUser')}/>, - })} - /> - ({ - title: t(route.params?.userId ? 'AbpIdentity::Edit' : 'AbpIdentity::NewUser'), - })} - /> - - ); -} diff --git a/templates/app/react-native/src/screens/ChangePassword/ChangePasswordForm.js b/templates/app/react-native/src/screens/ChangePassword/ChangePasswordForm.js deleted file mode 100644 index 8621d00a99..0000000000 --- a/templates/app/react-native/src/screens/ChangePassword/ChangePasswordForm.js +++ /dev/null @@ -1,121 +0,0 @@ -import { useFormik } from 'formik'; -import i18n from 'i18n-js'; -import { - Box, - FormControl, - Input, - KeyboardAvoidingView, - Stack, - Icon -} from 'native-base'; -import PropTypes from 'prop-types'; -import React, { useRef, useState } from 'react'; -import * as Yup from 'yup'; -import { FormButtons } from '../../components/FormButtons'; -import ValidationMessage from '../../components/ValidationMessage/ValidationMessage'; -import { Ionicons } from '@expo/vector-icons'; - -const ValidationSchema = Yup.object().shape({ - currentPassword: Yup.string().required('AbpAccount::ThisFieldIsRequired.'), - newPassword: Yup.string().required('AbpAccount::ThisFieldIsRequired.'), -}); - -function ChangePasswordForm({ submit, cancel }) { - const [showCurrentPassword, setShowCurrentPassword] = useState(false); - const [showNewPassword, setShowNewPassword] = useState(false); - - const currentPasswordRef = useRef(); - const newPasswordRef = useRef(); - - const onSubmit = (values) => { - submit({ - ...values, - newPasswordConfirm: values.newPassword, - }); - }; - - const formik = useFormik({ - enableReinitialize: true, - validationSchema: ValidationSchema, - initialValues: { - currentPassword: '', - newPassword: '', - }, - onSubmit, - }); - - return ( - <> - - - - - {i18n.t('AbpIdentity::DisplayName:CurrentPassword')} - - newPasswordRef?.current?.focus()} - returnKeyType="next" - onChangeText={formik.handleChange('currentPassword')} - onBlur={formik.handleBlur('currentPassword')} - value={formik.values.currentPassword} - textContentType="password" - secureTextEntry={!showCurrentPassword} - InputRightElement={ - setShowCurrentPassword(!showCurrentPassword)} - /> - } - /> - - {formik.errors.currentPassword} - - - - - - - - {i18n.t('AbpIdentity::DisplayName:NewPassword')} - - setShowNewPassword(!showNewPassword)} - /> - } - /> - {formik.errors.newPassword} - - - - - - ); -} - -ChangePasswordForm.propTypes = { - submit: PropTypes.func.isRequired, - cancel: PropTypes.func.isRequired, -}; - -export default ChangePasswordForm; diff --git a/templates/app/react-native/src/screens/ChangePassword/ChangePasswordScreen.js b/templates/app/react-native/src/screens/ChangePassword/ChangePasswordScreen.js deleted file mode 100644 index 196c762a38..0000000000 --- a/templates/app/react-native/src/screens/ChangePassword/ChangePasswordScreen.js +++ /dev/null @@ -1,33 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { changePassword } from '../../api/IdentityAPI'; -import LoadingActions from '../../store/actions/LoadingActions'; -import { connectToRedux } from '../../utils/ReduxConnect'; -import ChangePasswordForm from './ChangePasswordForm'; - -function ChangePasswordScreen({ navigation, startLoading, stopLoading }) { - const submit = data => { - startLoading({ key: 'changePassword' }); - - changePassword(data) - .then(() => { - navigation.goBack(); - }) - .finally(() => stopLoading({ key: 'changePassword' })); - }; - - return navigation.goBack()} />; -} - -ChangePasswordScreen.propTypes = { - startLoading: PropTypes.func.isRequired, - stopLoading: PropTypes.func.isRequired, -}; - -export default connectToRedux({ - component: ChangePasswordScreen, - dispatchProps: { - startLoading: LoadingActions.start, - stopLoading: LoadingActions.stop, - }, -}); diff --git a/templates/app/react-native/src/screens/CreateUpdateTenant/CreateUpdateTenantForm.js b/templates/app/react-native/src/screens/CreateUpdateTenant/CreateUpdateTenantForm.js deleted file mode 100644 index b76f356062..0000000000 --- a/templates/app/react-native/src/screens/CreateUpdateTenant/CreateUpdateTenantForm.js +++ /dev/null @@ -1,144 +0,0 @@ -import { Ionicons } from '@expo/vector-icons'; -import { useFormik } from 'formik'; -import i18n from 'i18n-js'; -import { Box, FormControl, Icon, Input, Stack } from 'native-base'; -import PropTypes from 'prop-types'; -import React, { useRef, useState } from 'react'; -import * as Yup from 'yup'; -import { FormButtons } from '../../components/FormButtons'; -import ValidationMessage from '../../components/ValidationMessage/ValidationMessage'; -import { usePermission } from '../../hooks/UsePermission'; - -const validations = { - name: Yup.string().required('AbpAccount::ThisFieldIsRequired.'), -}; - -function CreateUpdateTenantForm({ editingTenant = {}, submit, remove }) { - const tenantNameRef = useRef(); - const adminEmailRef = useRef(); - const adminPasswordRef = useRef(); - - const [showAdminPassword, setShowAdminPassword] = useState(false); - const hasRemovePermission = usePermission('AbpTenantManagement.Tenants.Delete'); - - const adminEmailAddressValidation = Yup.lazy(() => - Yup.string() - .required('AbpAccount::ThisFieldIsRequired.') - .email('AbpAccount::ThisFieldIsNotAValidEmailAddress.'), - ); - - const adminPasswordValidation = Yup.lazy(() => - Yup.string().required('AbpAccount::ThisFieldIsRequired.'), - ); - - const onSubmit = values => { - submit({ - ...editingTenant, - ...values, - }); - }; - - const formik = useFormik({ - enableReinitialize: true, - validationSchema: Yup.object().shape({ - ...validations, - ...(!editingTenant.id && { - adminEmailAddress: adminEmailAddressValidation, - adminPassword: adminPasswordValidation, - }), - }), - initialValues: { - lockoutEnabled: false, - twoFactorEnabled: false, - ...editingTenant, - }, - onSubmit, - }); - - return ( - <> - - - - {i18n.t('AbpTenantManagement::TenantName')} - - {formik.errors.name} - - - - {!editingTenant.id ? ( - <> - - - - {i18n.t('AbpTenantManagement::DisplayName:AdminEmailAddress')} - - adminPasswordRef?.current?.focus()} - returnKeyType="next" - /> - {formik.errors.adminEmailAddress} - - - - - - - {i18n.t('AbpTenantManagement::DisplayName:AdminPassword')} - - setShowAdminPassword(!showAdminPassword)} - /> - } - /> - {formik.errors.adminPassword} - - - - ) : null} - - - - ); -} - -CreateUpdateTenantForm.propTypes = { - editingTenant: PropTypes.object, - submit: PropTypes.func.isRequired, - remove: PropTypes.func.isRequired, -}; - -export default CreateUpdateTenantForm; diff --git a/templates/app/react-native/src/screens/CreateUpdateTenant/CreateUpdateTenantScreen.js b/templates/app/react-native/src/screens/CreateUpdateTenant/CreateUpdateTenantScreen.js deleted file mode 100644 index a3f59a8cf7..0000000000 --- a/templates/app/react-native/src/screens/CreateUpdateTenant/CreateUpdateTenantScreen.js +++ /dev/null @@ -1,81 +0,0 @@ -import { useFocusEffect } from '@react-navigation/native'; -import PropTypes from 'prop-types'; -import React, { useCallback, useState } from 'react'; -import { - createTenant, - getTenantById, - removeTenant, - updateTenant -} from '../../api/TenantManagementAPI'; -import LoadingActions from '../../store/actions/LoadingActions'; -import { createLoadingSelector } from '../../store/selectors/LoadingSelectors'; -import { connectToRedux } from '../../utils/ReduxConnect'; -import CreateUpdateTenantForm from './CreateUpdateTenantForm'; - -function CreateUpdateTenantScreen({ navigation, route, startLoading, stopLoading }) { - const [tenant, setTenant] = useState(); - const tenantId = route.params?.tenantId; - - const remove = () => { - startLoading({ key: 'removeTenant' }); - removeTenant(tenantId) - .then(() => navigation.goBack()) - .finally(() => stopLoading({ key: 'removeTenant' })); - }; - - useFocusEffect( - useCallback(() => { - if (tenantId) { - getTenantById(tenantId).then((data = {}) => setTenant(data)); - } - }, []), - ); - - const submit = data => { - startLoading({ key: 'saveTenant' }); - let request; - if (data.id) { - request = updateTenant(data, tenantId); - } else { - request = createTenant(data); - } - - request - .then(() => { - navigation.goBack(); - }) - .finally(() => stopLoading({ key: 'saveTenant' })); - }; - - const renderForm = () => ( - - ); - - if (tenantId && tenant) { - return renderForm(); - } - - if (!tenantId) { - return renderForm(); - } - - return null; -} - -CreateUpdateTenantScreen.propTypes = { - startLoading: PropTypes.func.isRequired, - stopLoading: PropTypes.func.isRequired, -}; - -export default connectToRedux({ - component: CreateUpdateTenantScreen, - stateProps: state => ({ loading: createLoadingSelector()(state) }), - dispatchProps: { - startLoading: LoadingActions.start, - stopLoading: LoadingActions.stop, - }, -}); diff --git a/templates/app/react-native/src/screens/CreateUpdateUser/CreateUpdateUserForm.js b/templates/app/react-native/src/screens/CreateUpdateUser/CreateUpdateUserForm.js deleted file mode 100644 index bb394232ba..0000000000 --- a/templates/app/react-native/src/screens/CreateUpdateUser/CreateUpdateUserForm.js +++ /dev/null @@ -1,272 +0,0 @@ -import { Ionicons } from '@expo/vector-icons'; -import { useFormik } from 'formik'; -import i18n from 'i18n-js'; -import { - Box, - Button, - Checkbox, - FormControl, - Icon, - Input, - KeyboardAvoidingView, - Stack -} from 'native-base'; -import PropTypes from 'prop-types'; -import React, { useRef, useState } from 'react'; -import { Platform } from 'react-native'; -import * as Yup from 'yup'; -import { FormButtons } from '../../components/FormButtons'; -import ValidationMessage from '../../components/ValidationMessage/ValidationMessage'; -import { usePermission } from '../../hooks/UsePermission'; -import UserRoles from './UserRoles'; - -const validations = { - userName: Yup.string().required('AbpAccount::ThisFieldIsRequired.'), - email: Yup.string() - .email('AbpAccount::ThisFieldIsNotAValidEmailAddress.') - .required('AbpAccount::ThisFieldIsRequired.'), -}; - -let roleNames = []; - -function onChangeRoles(roles) { - roleNames = roles; -} - -function CreateUpdateUserForm({ editingUser = {}, submit, remove }) { - const [selectedTab, setSelectedTab] = useState(0); - const [showPassword, setShowPassword] = useState(false); - - const usernameRef = useRef(); - const nameRef = useRef(); - const surnameRef = useRef(); - const emailRef = useRef(); - const phoneNumberRef = useRef(); - const passwordRef = useRef(); - - const hasRemovePermission = usePermission('AbpIdentity.Users.Delete'); - - const onSubmit = (values) => { - submit({ - ...editingUser, - ...values, - roleNames, - }); - }; - - const passwordValidation = Yup.lazy(() => { - if (editingUser.id) { - return Yup.string(); - } - return Yup.string().required('AbpAccount::ThisFieldIsRequired.'); - }); - - const formik = useFormik({ - enableReinitialize: true, - validationSchema: Yup.object().shape({ - ...validations, - password: passwordValidation, - }), - initialValues: { - lockoutEnabled: false, - ...editingUser, - }, - onSubmit, - }); - - return ( - <> - - - - - - - - {selectedTab === 0 ? ( - <> - - - - {i18n.t('AbpIdentity::UserName')} - - nameRef.current.focus()} - returnKeyType="next" - onChangeText={formik.handleChange('userName')} - onBlur={formik.handleBlur('userName')} - value={formik.values.userName} - autoCapitalize="none" - /> - - {formik.errors.userName} - - - - - - - - {i18n.t('AbpIdentity::DisplayName:Name')} - - surnameRef.current.focus()} - returnKeyType="next" - onChangeText={formik.handleChange('name')} - onBlur={formik.handleBlur('name')} - value={formik.values.name} - /> - - - - - - - {i18n.t('AbpIdentity::DisplayName:Surname')} - - emailRef.current.focus()} - returnKeyType="next" - onChangeText={formik.handleChange('surname')} - onBlur={formik.handleBlur('surname')} - value={formik.values.surname} - /> - - - - - - - {i18n.t('AbpIdentity::EmailAddress')} - - phoneNumberRef.current.focus()} - returnKeyType="next" - onChangeText={formik.handleChange('email')} - onBlur={formik.handleBlur('email')} - value={formik.values.email} - autoCapitalize="none" - /> - {formik.errors.email} - - - - - - - {i18n.t('AbpIdentity::PhoneNumber')} - - passwordRef?.current?.focus()} - returnKeyType={!editingUser.id ? 'next' : 'default'} - onChangeText={formik.handleChange('phoneNumber')} - onBlur={formik.handleBlur('phoneNumber')} - value={formik.values.phoneNumber} - /> - - - - {!editingUser.id ? ( - - - - {i18n.t('AbpIdentity::Password')} - - setShowPassword(!showPassword)} - /> - } - /> - - {formik.errors.password} - - - - ) : null} - - - - - formik.setFieldValue( - 'lockoutEnabled', - !formik.values.lockoutEnabled - ) - } - > - {i18n.t('AbpIdentity::DisplayName:LockoutEnabled')} - - - - - ) : ( - - )} - - - - - ); -} - -CreateUpdateUserForm.propTypes = { - editingUser: PropTypes.object, - submit: PropTypes.func.isRequired, - remove: PropTypes.func.isRequired, -}; - -export default CreateUpdateUserForm; diff --git a/templates/app/react-native/src/screens/CreateUpdateUser/CreateUpdateUserScreen.js b/templates/app/react-native/src/screens/CreateUpdateUser/CreateUpdateUserScreen.js deleted file mode 100644 index e900fde8fc..0000000000 --- a/templates/app/react-native/src/screens/CreateUpdateUser/CreateUpdateUserScreen.js +++ /dev/null @@ -1,69 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { useEffect, useState } from 'react'; -import { createUser, getUserById, removeUser, updateUser } from '../../api/IdentityAPI'; -import LoadingActions from '../../store/actions/LoadingActions'; -import { createLoadingSelector } from '../../store/selectors/LoadingSelectors'; -import { connectToRedux } from '../../utils/ReduxConnect'; -import CreateUpdateUserForm from './CreateUpdateUserForm'; - -function CreateUpdateUserScreen({ navigation, route, startLoading, stopLoading }) { - const [user, setUser] = useState(); - const userId = route.params?.userId; - - const remove = () => { - startLoading({ key: 'remove user' }); - removeUser(userId) - .then(() => navigation.goBack()) - .finally(() => stopLoading({ key: 'remove user' })); - }; - - useEffect(() => { - if (userId) { - getUserById(userId).then((data = {}) => setUser(data)); - } - }, []); - - const submit = data => { - startLoading({ key: 'saveUser' }); - let request; - if (data.id) { - request = updateUser(data, userId); - } else { - request = createUser(data); - } - - request - .then(() => { - navigation.goBack(); - }) - .finally(() => stopLoading({ key: 'saveUser' })); - }; - - const renderForm = () => ( - - ); - - if (userId && user) { - return renderForm(); - } - - if (!userId) { - return renderForm(); - } - - return null; -} - -CreateUpdateUserScreen.propTypes = { - startLoading: PropTypes.func.isRequired, - stopLoading: PropTypes.func.isRequired, -}; - -export default connectToRedux({ - component: CreateUpdateUserScreen, - stateProps: state => ({ loading: createLoadingSelector()(state) }), - dispatchProps: { - startLoading: LoadingActions.start, - stopLoading: LoadingActions.stop, - }, -}); diff --git a/templates/app/react-native/src/screens/CreateUpdateUser/UserRoles.js b/templates/app/react-native/src/screens/CreateUpdateUser/UserRoles.js deleted file mode 100644 index 25db76afee..0000000000 --- a/templates/app/react-native/src/screens/CreateUpdateUser/UserRoles.js +++ /dev/null @@ -1,58 +0,0 @@ -import { Box, Checkbox, List } from 'native-base'; -import PropTypes from 'prop-types'; -import React, { useEffect, useState } from 'react'; -import { getAllRoles, getUserRoles } from '../../api/IdentityAPI'; - -function UserRoles({ editingUser = {}, onChangeRoles }) { - const [roles, setRoles] = useState([]); - - const onPress = index => { - setRoles( - roles.map((role, i) => ({ - ...role, - isSelected: index === i ? !role.isSelected : role.isSelected, - })), - ); - }; - - useEffect(() => { - const requests = [getAllRoles()]; - if (editingUser.id) requests.push(getUserRoles(editingUser.id)); - - Promise.all(requests).then(([allRoles = [], userRoles = []]) => { - setRoles( - allRoles.map(role => ({ - ...role, - isSelected: editingUser.id - ? !!userRoles?.find(userRole => userRole?.id === role?.id) - : role.isDefault, - })), - ); - }); - }, []); - - useEffect(() => { - onChangeRoles(roles.filter(role => role.isSelected).map(role => role.name)); - }, [roles]); - - return ( - - - {roles.map((role, index) => ( - - onPress(index)} > - {role.name} - - - ))} - - - ); -} - -UserRoles.propTypes = { - editingUser: PropTypes.objectOf(PropTypes.any).isRequired, - onChangeRoles: PropTypes.func.isRequired, -}; - -export default UserRoles; diff --git a/templates/app/react-native/src/screens/Home/HomeScreen.js b/templates/app/react-native/src/screens/Home/HomeScreen.js deleted file mode 100644 index 82ca221e2d..0000000000 --- a/templates/app/react-native/src/screens/Home/HomeScreen.js +++ /dev/null @@ -1,30 +0,0 @@ -import i18n from 'i18n-js'; -import { Box, Center, Heading, Text } from 'native-base'; -import React from 'react'; -import { StyleSheet } from 'react-native'; - -function HomeScreen() { - return ( -
- - {i18n.t('::Welcome')} - - {i18n.t('::LongWelcomeMessage')} - - -
- ); -} - -const styles = StyleSheet.create({ - centeredText: { - textAlign: 'center', - marginBottom: 5 - }, -}); - -export default HomeScreen; diff --git a/templates/app/react-native/src/screens/Login/LoginScreen.js b/templates/app/react-native/src/screens/Login/LoginScreen.js deleted file mode 100644 index acb72076fa..0000000000 --- a/templates/app/react-native/src/screens/Login/LoginScreen.js +++ /dev/null @@ -1,154 +0,0 @@ -import { useFormik } from 'formik'; -import i18n from 'i18n-js'; -import { - Box, - Button, - Center, - FormControl, Image, Input, - Stack, - WarningOutlineIcon -} from 'native-base'; -import PropTypes from 'prop-types'; -import React, { useRef, useState } from 'react'; -import { View } from 'react-native'; -import { object, string } from 'yup'; -import { login } from '../../api/AccountAPI'; -import TenantBox from '../../components/TenantBox/TenantBox'; -import ValidationMessage from '../../components/ValidationMessage/ValidationMessage'; -import AppActions from '../../store/actions/AppActions'; -import LoadingActions from '../../store/actions/LoadingActions'; -import PersistentStorageActions from '../../store/actions/PersistentStorageActions'; -import { connectToRedux } from '../../utils/ReduxConnect'; - -const ValidationSchema = object().shape({ - username: string().required('AbpAccount::ThisFieldIsRequired.'), - password: string().required('AbpAccount::ThisFieldIsRequired.'), -}); - -function LoginScreen({ startLoading, stopLoading, setToken, fetchAppConfig }) { - const [showTenantSelection, setShowTenantSelection] = useState(false); - const passwordRef = useRef(null); - - const toggleTenantSelection = () => { - setShowTenantSelection(!showTenantSelection); - }; - - const submit = ({ username, password }) => { - startLoading({ key: 'login' }); - login({ username, password }) - .then((data) => - setToken({ - ...data, - expire_time: new Date().valueOf() + data.expires_in, - scope: undefined, - }) - ) - .then( - () => - new Promise((resolve) => - fetchAppConfig({ - showLoading: false, - callback: () => resolve(true), - }) - ) - ) - .finally(() => stopLoading({ key: 'login' })); - }; - - const formik = useFormik({ - validationSchema: ValidationSchema, - initialValues: { username: '', password: '' }, - onSubmit: submit, - }); - - return ( -
- - Image - - - - - - - - {i18n.t('AbpAccount::UserNameOrEmailAddress')} - - passwordRef?.current?.focus()} - size="lg" - /> - {formik.errors.username} - - - - - - - {i18n.t('AbpAccount::Password')} - - - } - > - {formik.errors.password} - - - - - - - - -
- ); -} - -LoginScreen.propTypes = { - startLoading: PropTypes.func.isRequired, - stopLoading: PropTypes.func.isRequired, - setToken: PropTypes.func.isRequired, - fetchAppConfig: PropTypes.func.isRequired, -}; - -export default connectToRedux({ - component: LoginScreen, - dispatchProps: { - startLoading: LoadingActions.start, - stopLoading: LoadingActions.stop, - fetchAppConfig: AppActions.fetchAppConfigAsync, - setToken: PersistentStorageActions.setToken, - }, -}); diff --git a/templates/app/react-native/src/screens/ManageProfile/ManageProfileForm.js b/templates/app/react-native/src/screens/ManageProfile/ManageProfileForm.js deleted file mode 100644 index 2dffd3624b..0000000000 --- a/templates/app/react-native/src/screens/ManageProfile/ManageProfileForm.js +++ /dev/null @@ -1,153 +0,0 @@ -import { useFormik } from 'formik'; -import i18n from 'i18n-js'; -import { - Box, - FormControl, - Input, - KeyboardAvoidingView, - Stack, -} from 'native-base'; -import PropTypes from 'prop-types'; -import React, { useRef } from 'react'; -import * as Yup from 'yup'; -import FormButtons from '../../components/FormButtons/FormButtons'; -import ValidationMessage from '../../components/ValidationMessage/ValidationMessage'; - -const ValidationSchema = Yup.object().shape({ - userName: Yup.string().required('AbpAccount::ThisFieldIsRequired.'), - email: Yup.string() - .required('AbpAccount::ThisFieldIsRequired.') - .email('AbpAccount::ThisFieldIsNotAValidEmailAddress.'), -}); - -function ManageProfileForm({ editingUser = {}, submit, cancel }) { - const usernameRef = useRef(); - const nameRef = useRef(); - const surnameRef = useRef(); - const emailRef = useRef(); - const phoneNumberRef = useRef(); - - const onSubmit = (values) => { - submit({ - ...editingUser, - ...values, - }); - }; - - const formik = useFormik({ - enableReinitialize: true, - validationSchema: ValidationSchema, - initialValues: { - ...editingUser, - }, - onSubmit, - }); - - return ( - <> - - - - - - {i18n.t('AbpIdentity::UserName')} - - nameRef?.current?.focus()} - returnKeyType="next" - onChangeText={formik.handleChange('userName')} - onBlur={formik.handleBlur('userName')} - value={formik.values.userName} - /> - {formik.errors.userName} - - - - - - - {i18n.t('AbpIdentity::DisplayName:Name')} - - surnameRef?.current?.focus()} - returnKeyType="next" - onChangeText={formik.handleChange('name')} - onBlur={formik.handleBlur('name')} - value={formik.values.name} - /> - - - - - - - {i18n.t('AbpIdentity::DisplayName:Surname')} - - phoneNumberRef?.current?.focus()} - returnKeyType="next" - onChangeText={formik.handleChange('surname')} - onBlur={formik.handleBlur('surname')} - value={formik.values.surname} - /> - - - - - - - {i18n.t('AbpIdentity::PhoneNumber')} - - emailRef?.current?.focus()} - returnKeyType="next" - onChangeText={formik.handleChange('phoneNumber')} - onBlur={formik.handleBlur('phoneNumber')} - value={formik.values.phoneNumber} - /> - - - - - - - {i18n.t('AbpIdentity::EmailAddress')} - - - {formik.errors.email} - - - - - - - ); -} - -ManageProfileForm.propTypes = { - editingUser: PropTypes.object.isRequired, - submit: PropTypes.func.isRequired, - cancel: PropTypes.func.isRequired, -}; - -export default ManageProfileForm; diff --git a/templates/app/react-native/src/screens/ManageProfile/ManageProfileScreen.js b/templates/app/react-native/src/screens/ManageProfile/ManageProfileScreen.js deleted file mode 100644 index 1f2e577a37..0000000000 --- a/templates/app/react-native/src/screens/ManageProfile/ManageProfileScreen.js +++ /dev/null @@ -1,50 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import PropTypes from 'prop-types'; -import { updateProfileDetail, getProfileDetail } from '../../api/IdentityAPI'; -import ManageProfileForm from './ManageProfileForm'; -import LoadingActions from '../../store/actions/LoadingActions'; -import { connectToRedux } from '../../utils/ReduxConnect'; - -function ManageProfileScreen({ navigation, startLoading, stopLoading }) { - const [user, setUser] = useState(); - - useEffect(() => { - if (!user) { - startLoading({ key: 'manageProfile' }); - getProfileDetail() - .then((data = {}) => setUser(data)) - .finally(() => stopLoading({ key: 'manageProfile' })); - } - }); - - const submit = data => { - startLoading({ key: 'manageProfile' }); - - updateProfileDetail(data) - .then(() => { - navigation.goBack(); - }) - .finally(() => stopLoading({ key: 'manageProfile' })); - }; - - return ( - <> - {user ? ( - navigation.goBack()} /> - ) : null} - - ); -} - -ManageProfileScreen.propTypes = { - startLoading: PropTypes.func.isRequired, - stopLoading: PropTypes.func.isRequired, -}; - -export default connectToRedux({ - component: ManageProfileScreen, - dispatchProps: { - startLoading: LoadingActions.start, - stopLoading: LoadingActions.stop, - }, -}); diff --git a/templates/app/react-native/src/screens/Settings/SettingsScreen.js b/templates/app/react-native/src/screens/Settings/SettingsScreen.js deleted file mode 100644 index 58ede9cbb5..0000000000 --- a/templates/app/react-native/src/screens/Settings/SettingsScreen.js +++ /dev/null @@ -1,157 +0,0 @@ -import { Ionicons } from '@expo/vector-icons'; -import { useFocusEffect } from '@react-navigation/native'; -import i18n from 'i18n-js'; -import { - Avatar, - Button, - Divider, - FormControl, - List, - Select, - Stack, - Text, - View, -} from 'native-base'; -import PropTypes from 'prop-types'; -import React, { useCallback, useState } from 'react'; -import { getProfileDetail } from '../../api/IdentityAPI'; -import AppActions from '../../store/actions/AppActions'; -import { - createLanguageSelector, - createLanguagesSelector, -} from '../../store/selectors/AppSelectors'; -import { createTenantSelector } from '../../store/selectors/PersistentStorageSelectors'; -import { connectToRedux } from '../../utils/ReduxConnect'; -import { store } from '../../store/index'; -import { getEnvVars } from '../../../Environment'; - -const env = getEnvVars(); - -function SettingsScreen({ - navigation, - language, - languages, - setLanguageAsync, - logoutAsync, - tenant = {}, -}) { - const [user, setUser] = useState({}); - const [token, setToken] = useState(store.getState().persistentStorage.token); - - const fetchUser = () => { - getProfileDetail().then(data => { - setUser(data || {}); - }); - }; - - useFocusEffect( - useCallback(() => { - fetchUser(); - }, []), - ); - - const logout = () => { - const { clientId } = env.oAuthConfig; - const { access_token, refresh_token } = token; - - logoutAsync({ - client_id: clientId, - token: access_token, - refresh_token: refresh_token, - }); - }; - - return ( - - - navigation.navigate('ManageProfile')}> - - - - - {tenant.name ? `${tenant.name}/` : ''} - {user.userName ? `${user.userName}` : ''} - - - - - - - - navigation.navigate('ChangePassword')}> - - {i18n.t('AbpUi::ChangePassword')} - - - - - - - - - {i18n.t('AbpUi::Language')} - - - - - - - - - ); -} - -SettingsScreen.propTypes = { - setLanguageAsync: PropTypes.func.isRequired, - logoutAsync: PropTypes.func.isRequired, - language: PropTypes.object.isRequired, - languages: PropTypes.array.isRequired, - tenant: PropTypes.object.isRequired, -}; - -export default connectToRedux({ - component: SettingsScreen, - stateProps: state => ({ - languages: createLanguagesSelector()(state), - language: createLanguageSelector()(state), - tenant: createTenantSelector()(state), - }), - dispatchProps: { - setLanguageAsync: AppActions.setLanguageAsync, - logoutAsync: AppActions.logoutAsync, - }, -}); diff --git a/templates/app/react-native/src/screens/Tenants/TenantsScreen.js b/templates/app/react-native/src/screens/Tenants/TenantsScreen.js deleted file mode 100644 index da3dac53af..0000000000 --- a/templates/app/react-native/src/screens/Tenants/TenantsScreen.js +++ /dev/null @@ -1,51 +0,0 @@ -import { Box, HStack, Pressable, Text } from 'native-base'; -import React from 'react'; -import { getTenants } from '../../api/TenantManagementAPI'; -import DataList from '../../components/DataList/DataList'; -import { LocalizationContext } from '../../contexts/LocalizationContext'; - -function TenantsScreen({ navigation }) { - const { t } = React.useContext(LocalizationContext); - - return ( - - ( - navigateToCreateUpdateTenantScreen(navigation, item)} - > - - - {t('AbpTenantManagement::TenantName')}: - - {item.name} - - - - - )} - /> - - ); -} - -const navigateToCreateUpdateTenantScreen = (navigation, tenant = {}) => { - navigation.navigate('CreateUpdateTenant', { - tenantId: tenant.id, - }); -}; - -export default TenantsScreen; diff --git a/templates/app/react-native/src/screens/Users/UsersScreen.js b/templates/app/react-native/src/screens/Users/UsersScreen.js deleted file mode 100644 index b49658cb24..0000000000 --- a/templates/app/react-native/src/screens/Users/UsersScreen.js +++ /dev/null @@ -1,62 +0,0 @@ -import { Box, HStack, Pressable, Text } from 'native-base'; -import React from 'react'; -import { getUsers } from '../../api/IdentityAPI'; -import DataList from '../../components/DataList/DataList'; -import { LocalizationContext } from '../../contexts/LocalizationContext'; - -function UsersScreen({ navigation }) { - const { t } = React.useContext(LocalizationContext); - - return ( - ( - navigateToCreateUpdateUserScreen(navigation, item)} - > - - - {t('AbpIdentity::UserName')}: - - {item.userName} - - - - {t('AbpIdentity::EmailAddress')}: - - {item.email} - - - - {t('AbpIdentity::DisplayName:Name')}: - - {item.name} - - - - {t('AbpIdentity::DisplayName:Surname')}: - - {item.surname} - - - - - )} - /> - ); -} - -const navigateToCreateUpdateUserScreen = (navigation, user = {}) => { - navigation.navigate('CreateUpdateUser', { - userId: user.id, - }); -}; - -export default UsersScreen; diff --git a/templates/app/react-native/src/store/actions/AppActions.js b/templates/app/react-native/src/store/actions/AppActions.js deleted file mode 100644 index 413d923129..0000000000 --- a/templates/app/react-native/src/store/actions/AppActions.js +++ /dev/null @@ -1,26 +0,0 @@ -import { createAction } from '@reduxjs/toolkit'; - -const fetchAppConfigAsync = createAction( - 'app/fetchAppConfigAsync', - ({ callback = () => {}, showLoading = true } = {}) => ({ - payload: { callback, showLoading }, - }), -); - -const setAppConfig = createAction('app/setAppConfig'); - -const setLanguageAsync = createAction('app/setLanguageAsync'); - -const logoutAsync = createAction( - 'app/logoutAsync', - ({ client_id = '', token = '', refresh_token = '' } = {}) => ({ - payload: { client_id, token, refresh_token }, - }), -); - -export default { - fetchAppConfigAsync, - setAppConfig, - setLanguageAsync, - logoutAsync, -}; diff --git a/templates/app/react-native/src/store/actions/LoadingActions.js b/templates/app/react-native/src/store/actions/LoadingActions.js deleted file mode 100644 index 9705dc4e0f..0000000000 --- a/templates/app/react-native/src/store/actions/LoadingActions.js +++ /dev/null @@ -1,13 +0,0 @@ -import { createAction } from '@reduxjs/toolkit'; - -const start = createAction('loading/start'); - -const stop = createAction('loading/stop'); - -const clear = createAction('loading/clear'); - -export default { - start, - stop, - clear, -}; diff --git a/templates/app/react-native/src/store/actions/PersistentStorageActions.js b/templates/app/react-native/src/store/actions/PersistentStorageActions.js deleted file mode 100644 index 4b32d9ff31..0000000000 --- a/templates/app/react-native/src/store/actions/PersistentStorageActions.js +++ /dev/null @@ -1,13 +0,0 @@ -import { createAction } from '@reduxjs/toolkit'; - -const setToken = createAction('persistentStorage/setToken'); - -const setLanguage = createAction('persistentStorage/setLanguage'); - -const setTenant = createAction('persistentStorage/setTenant'); - -export default { - setToken, - setLanguage, - setTenant, -}; diff --git a/templates/app/react-native/src/store/index.js b/templates/app/react-native/src/store/index.js deleted file mode 100644 index d30a0890dd..0000000000 --- a/templates/app/react-native/src/store/index.js +++ /dev/null @@ -1,25 +0,0 @@ -import AsyncStorage from '@react-native-async-storage/async-storage'; -import { configureStore } from '@reduxjs/toolkit'; -import { persistReducer, persistStore } from 'redux-persist'; -import createSagaMiddleware from 'redux-saga'; -import rootReducer from './reducers'; -import { rootSaga } from './sagas'; - -const sagaMiddleware = createSagaMiddleware(); - -const persistConfig = { - key: 'root', - storage: AsyncStorage, - whitelist: ['persistentStorage'], -}; - -const persistedReducer = persistReducer(persistConfig, rootReducer); - -export const store = configureStore({ - reducer: persistedReducer, - middleware: [sagaMiddleware], -}); - -export const persistor = persistStore(store); - -sagaMiddleware.run(rootSaga); diff --git a/templates/app/react-native/src/store/reducers/AppReducer.js b/templates/app/react-native/src/store/reducers/AppReducer.js deleted file mode 100644 index a628f7f802..0000000000 --- a/templates/app/react-native/src/store/reducers/AppReducer.js +++ /dev/null @@ -1,12 +0,0 @@ -import { createReducer } from '@reduxjs/toolkit'; -import AppActions from '../actions/AppActions'; - -const initialState = { - appConfig: {}, -}; - -export default createReducer(initialState, builder => - builder.addCase(AppActions.setAppConfig, (state, action) => { - state.appConfig = action.payload; - }), -); diff --git a/templates/app/react-native/src/store/reducers/LoadingReducer.js b/templates/app/react-native/src/store/reducers/LoadingReducer.js deleted file mode 100644 index 99ccb14149..0000000000 --- a/templates/app/react-native/src/store/reducers/LoadingReducer.js +++ /dev/null @@ -1,23 +0,0 @@ -import { createReducer } from '@reduxjs/toolkit'; -import LoadingActions from '../actions/LoadingActions'; - -const initialState = { activeLoadings: {}, loading: false }; - -export default createReducer(initialState, builder => - builder - .addCase(LoadingActions.start, (state, action) => { - const { key, opacity } = action.payload; - return { - ...state, - actives: { ...state.activeLoadings, [key]: action }, - loading: true, - opacity, - }; - }) - .addCase(LoadingActions.stop, (state, action) => { - delete state.activeLoadings[action.payload.key]; - - state.loading = !!Object.keys(state.activeLoadings).length; - }) - .addCase(LoadingActions.clear, () => ({})), -); diff --git a/templates/app/react-native/src/store/reducers/PersistentStorageReducer.js b/templates/app/react-native/src/store/reducers/PersistentStorageReducer.js deleted file mode 100644 index d4d8f9a32b..0000000000 --- a/templates/app/react-native/src/store/reducers/PersistentStorageReducer.js +++ /dev/null @@ -1,17 +0,0 @@ -import { createReducer } from '@reduxjs/toolkit'; -import PersistentStorageActions from '../actions/PersistentStorageActions'; - -const initialState = { token: {}, language: null, tenant: {} }; - -export default createReducer(initialState, builder => - builder - .addCase(PersistentStorageActions.setToken, (state, action) => { - state.token = action.payload; - }) - .addCase(PersistentStorageActions.setLanguage, (state, action) => { - state.language = action.payload; - }) - .addCase(PersistentStorageActions.setTenant, (state, action) => { - state.tenant = action.payload; - }), -); diff --git a/templates/app/react-native/src/store/reducers/index.js b/templates/app/react-native/src/store/reducers/index.js deleted file mode 100644 index 0479999c14..0000000000 --- a/templates/app/react-native/src/store/reducers/index.js +++ /dev/null @@ -1,12 +0,0 @@ -import { combineReducers } from '@reduxjs/toolkit'; -import AppReducer from './AppReducer'; -import LoadingReducer from './LoadingReducer'; -import PersistentStorageReducer from './PersistentStorageReducer'; - -const rootReducer = combineReducers({ - loading: LoadingReducer, - app: AppReducer, - persistentStorage: PersistentStorageReducer, -}); - -export default rootReducer; diff --git a/templates/app/react-native/src/store/sagas/AppSaga.js b/templates/app/react-native/src/store/sagas/AppSaga.js deleted file mode 100644 index 8eb795a971..0000000000 --- a/templates/app/react-native/src/store/sagas/AppSaga.js +++ /dev/null @@ -1,50 +0,0 @@ -import { all, call, put, takeLatest } from 'redux-saga/effects'; -import { Logout } from '../../api/AccountAPI'; -import { getApplicationConfiguration } from '../../api/ApplicationConfigurationAPI'; -import AppActions from '../actions/AppActions'; -import LoadingActions from '../actions/LoadingActions'; -import PersistentStorageActions from '../actions/PersistentStorageActions'; - -function* fetchAppConfig({ payload: { showLoading, callback } }) { - if (showLoading) { - yield put(LoadingActions.start({ key: 'appConfig', opacity: 1 })); - } - - const data = yield call(getApplicationConfiguration); - yield put(AppActions.setAppConfig(data)); - yield put( - PersistentStorageActions.setLanguage( - data.localization.currentCulture.cultureName, - ), - ); - if (showLoading) yield put(LoadingActions.stop({ key: 'appConfig' })); - callback(); -} - -function* setLanguage(action) { - yield put(PersistentStorageActions.setLanguage(action.payload)); - yield put(AppActions.fetchAppConfigAsync()); -} - -function* logout({ payload: { client_id, token, refresh_token } }) { - const data = { client_id, token }; - - yield call(Logout, data); - - if (!!refresh_token) { - data.token = refresh_token; - data.token_type_hint = 'refresh_token'; - yield call(Logout, data); - } - - yield put(PersistentStorageActions.setToken({})); - yield put(AppActions.fetchAppConfigAsync()); -} - -export default function* () { - yield all([ - takeLatest(AppActions.setLanguageAsync.type, setLanguage), - takeLatest(AppActions.fetchAppConfigAsync.type, fetchAppConfig), - takeLatest(AppActions.logoutAsync.type, logout), - ]); -} diff --git a/templates/app/react-native/src/store/sagas/index.js b/templates/app/react-native/src/store/sagas/index.js deleted file mode 100644 index 1b56c35c2c..0000000000 --- a/templates/app/react-native/src/store/sagas/index.js +++ /dev/null @@ -1,6 +0,0 @@ -import { all, fork } from 'redux-saga/effects'; -import AppSaga from './AppSaga'; - -export function* rootSaga() { - yield all([fork(AppSaga)]); -} diff --git a/templates/app/react-native/src/store/selectors/AppSelectors.js b/templates/app/react-native/src/store/selectors/AppSelectors.js deleted file mode 100644 index 82e0000b71..0000000000 --- a/templates/app/react-native/src/store/selectors/AppSelectors.js +++ /dev/null @@ -1,19 +0,0 @@ -import { createSelector } from 'reselect'; - -const getApp = state => state.app; - -export function createAppConfigSelector() { - return createSelector([getApp], state => state.appConfig); -} - -export function createLanguageSelector() { - return createSelector([getApp], state => state?.appConfig?.localization?.currentCulture); -} - -export function createLanguagesSelector() { - return createSelector([getApp], state => state?.appConfig?.localization?.languages); -} - -export function createGrantedPolicySelector(key) { - return createSelector([getApp], state => state?.appConfig?.auth?.grantedPolicies[key] ?? false); -} diff --git a/templates/app/react-native/src/store/selectors/LoadingSelectors.js b/templates/app/react-native/src/store/selectors/LoadingSelectors.js deleted file mode 100644 index 47a798fe2c..0000000000 --- a/templates/app/react-native/src/store/selectors/LoadingSelectors.js +++ /dev/null @@ -1,11 +0,0 @@ -import { createSelector } from 'reselect'; - -const getLoading = state => state.loading; - -export function createLoadingSelector() { - return createSelector([getLoading], loading => loading.loading); -} - -export function createOpacitySelector() { - return createSelector([getLoading], loading => loading.opacity); -} diff --git a/templates/app/react-native/src/store/selectors/PersistentStorageSelectors.js b/templates/app/react-native/src/store/selectors/PersistentStorageSelectors.js deleted file mode 100644 index d5d86f8b7d..0000000000 --- a/templates/app/react-native/src/store/selectors/PersistentStorageSelectors.js +++ /dev/null @@ -1,11 +0,0 @@ -import { createSelector } from 'reselect'; - -const getPersistentStorage = state => state.persistentStorage; - -export function createTokenSelector() { - return createSelector([getPersistentStorage], persistentStorage => persistentStorage.token); -} - -export function createTenantSelector() { - return createSelector([getPersistentStorage], persistentStorage => persistentStorage.tenant); -} diff --git a/templates/app/react-native/src/utils/DateExtensions.js b/templates/app/react-native/src/utils/DateExtensions.js deleted file mode 100644 index 890a6fdfc5..0000000000 --- a/templates/app/react-native/src/utils/DateExtensions.js +++ /dev/null @@ -1,5 +0,0 @@ -export function toLocalISOString(date) { - const timezoneOffset = date.getTimezoneOffset(); - - return new Date(date.getTime() - timezoneOffset * 60000).toISOString(); -} diff --git a/templates/app/react-native/src/utils/Debounce.js b/templates/app/react-native/src/utils/Debounce.js deleted file mode 100644 index 34588c63d4..0000000000 --- a/templates/app/react-native/src/utils/Debounce.js +++ /dev/null @@ -1,14 +0,0 @@ -export function debounce(func, wait, immediate) { - let timeout; - return (...args) => { - const context = this; - const later = () => { - timeout = null; - if (!immediate) func.apply(context, args); - }; - const callNow = immediate && !timeout; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - if (callNow) func.apply(context, args); - }; -} diff --git a/templates/app/react-native/src/utils/RandomColor.js b/templates/app/react-native/src/utils/RandomColor.js deleted file mode 100644 index a925d77763..0000000000 --- a/templates/app/react-native/src/utils/RandomColor.js +++ /dev/null @@ -1,12 +0,0 @@ -export function getRandomColors(count) { - const colors = []; - - for (let i = 0; i < count; i++) { - const r = ((i + 5) * (i + 5) * 474) % 255; - const g = ((i + 5) * (i + 5) * 1600) % 255; - const b = ((i + 5) * (i + 5) * 84065) % 255; - colors.push(`rgba(${r}, ${g}, ${b})`); - } - - return colors; -} diff --git a/templates/app/react-native/src/utils/ReduxConnect.js b/templates/app/react-native/src/utils/ReduxConnect.js deleted file mode 100644 index be68b186af..0000000000 --- a/templates/app/react-native/src/utils/ReduxConnect.js +++ /dev/null @@ -1,9 +0,0 @@ -import { connect } from 'react-redux'; - -export function connectToRedux({ component, stateProps = () => ({}), dispatchProps = () => ({}) }) { - const mapStateToProps = () => stateProps; - - const mapDispatchToProps = dispatchProps; - - return connect(mapStateToProps, mapDispatchToProps)(component); -} diff --git a/templates/app/react-native/src/utils/TokenUtils.js b/templates/app/react-native/src/utils/TokenUtils.js deleted file mode 100644 index ab1925061b..0000000000 --- a/templates/app/react-native/src/utils/TokenUtils.js +++ /dev/null @@ -1,7 +0,0 @@ -export function isTokenValid(token) { - if (!token || typeof token !== 'object' || !token.expire_time) return false; - - const now = new Date().valueOf(); - - return now < token.expire_time; -}