diff --git a/templates/app/react-native/.prettierrc b/templates/app/react-native/.prettierrc index a42642bf4f..471fac2824 100644 --- a/templates/app/react-native/.prettierrc +++ b/templates/app/react-native/.prettierrc @@ -2,7 +2,7 @@ "trailingComma": "all", "singleQuote": true, "jsxSingleQuote": false, - "printWidth": 100, + "printWidth": 80, "semi": true, "jsxBracketSameLine": true, "arrowParens": "avoid" diff --git a/templates/app/react-native/Environment.js b/templates/app/react-native/Environment.js index 412ea7b2d2..a4a23995f7 100644 --- a/templates/app/react-native/Environment.js +++ b/templates/app/react-native/Environment.js @@ -1,6 +1,5 @@ - -const yourIP = 'Your Local IP Address etc 192.168.1.64'; // See the docs https://docs.abp.io/en/abp/latest/Getting-Started-React-Native?Tiered=No -const port = 44305; +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: { diff --git a/templates/app/react-native/package.json b/templates/app/react-native/package.json index 92ac4a714e..d4c5bbc268 100644 --- a/templates/app/react-native/package.json +++ b/templates/app/react-native/package.json @@ -24,7 +24,7 @@ "prop-types": "^15.8.1", "react": "18.1.0", "react-dom": "18.1.0", - "react-native": "0.70.5", + "react-native": "0.70.8", "react-native-chart-kit": "^6.11.0", "react-native-gesture-handler": "~2.8.0", "react-native-reanimated": "~2.12.0", diff --git a/templates/app/react-native/src/api/AccountAPI.js b/templates/app/react-native/src/api/AccountAPI.js index 76a4b7f914..ddf49579d3 100644 --- a/templates/app/react-native/src/api/AccountAPI.js +++ b/templates/app/react-native/src/api/AccountAPI.js @@ -4,13 +4,12 @@ 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 + client_id: oAuthConfig.clientId, }; if (oAuthConfig.clientSecret) @@ -19,7 +18,7 @@ getLoginData = (username, password) => { return Object.entries(formData) .map(([key, value]) => `${key}=${encodeURIComponent(value)}`) .join('&'); -} +}; export const login = ({ username, password }) => api({ @@ -27,15 +26,28 @@ export const login = ({ username, password }) => url: '/connect/token', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, data: getLoginData(username, password), - baseURL: oAuthConfig.issuer + 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'; + } -export const Logout = () => - api({ - method: 'GET', - url: '/api/account/logout', + 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({ diff --git a/templates/app/react-native/src/screens/Settings/SettingsScreen.js b/templates/app/react-native/src/screens/Settings/SettingsScreen.js index cc8fb4aa9f..58ede9cbb5 100644 --- a/templates/app/react-native/src/screens/Settings/SettingsScreen.js +++ b/templates/app/react-native/src/screens/Settings/SettingsScreen.js @@ -1,17 +1,31 @@ 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 { + 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 + 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, @@ -22,6 +36,7 @@ function SettingsScreen({ tenant = {}, }) { const [user, setUser] = useState({}); + const [token, setToken] = useState(store.getState().persistentStorage.token); const fetchUser = () => { getProfileDetail().then(data => { @@ -35,6 +50,17 @@ function SettingsScreen({ }, []), ); + const logout = () => { + const { clientId } = env.oAuthConfig; + const { access_token, refresh_token } = token; + + logoutAsync({ + client_id: clientId, + token: access_token, + refresh_token: refresh_token, + }); + }; + return ( @@ -100,7 +126,7 @@ function SettingsScreen({ bg="danger.500" style={{ borderRadius: 0 }} onPress={() => { - logoutAsync(); + logout(); }}> {i18n.t('AbpAccount::Logout')} diff --git a/templates/app/react-native/src/store/actions/AppActions.js b/templates/app/react-native/src/store/actions/AppActions.js index caefb017ea..413d923129 100644 --- a/templates/app/react-native/src/store/actions/AppActions.js +++ b/templates/app/react-native/src/store/actions/AppActions.js @@ -2,14 +2,21 @@ import { createAction } from '@reduxjs/toolkit'; const fetchAppConfigAsync = createAction( 'app/fetchAppConfigAsync', - ({ callback = () => {}, showLoading = true } = {}) => ({ payload: { callback, showLoading } }), + ({ callback = () => {}, showLoading = true } = {}) => ({ + payload: { callback, showLoading }, + }), ); const setAppConfig = createAction('app/setAppConfig'); const setLanguageAsync = createAction('app/setLanguageAsync'); -const logoutAsync = createAction('app/logoutAsync'); +const logoutAsync = createAction( + 'app/logoutAsync', + ({ client_id = '', token = '', refresh_token = '' } = {}) => ({ + payload: { client_id, token, refresh_token }, + }), +); export default { fetchAppConfigAsync, diff --git a/templates/app/react-native/src/store/sagas/AppSaga.js b/templates/app/react-native/src/store/sagas/AppSaga.js index 8fc0145309..8eb795a971 100644 --- a/templates/app/react-native/src/store/sagas/AppSaga.js +++ b/templates/app/react-native/src/store/sagas/AppSaga.js @@ -6,10 +6,17 @@ 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 })); + 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)); + yield put( + PersistentStorageActions.setLanguage( + data.localization.currentCulture.cultureName, + ), + ); if (showLoading) yield put(LoadingActions.stop({ key: 'appConfig' })); callback(); } @@ -19,13 +26,22 @@ function* setLanguage(action) { yield put(AppActions.fetchAppConfigAsync()); } -function* logout() { - yield call(Logout); +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*() { +export default function* () { yield all([ takeLatest(AppActions.setLanguageAsync.type, setLanguage), takeLatest(AppActions.fetchAppConfigAsync.type, fetchAppConfig),