| { | |||||
| "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true, | |||||
| "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true | |||||
| } |
| node_modules/ | |||||
| .expo/ | |||||
| npm-debug.* | |||||
| *.jks | |||||
| *.p8 | |||||
| *.p12 | |||||
| *.key | |||||
| *.mobileprovision | |||||
| *.orig.* | |||||
| web-build/ | |||||
| # macOS | |||||
| .DS_Store |
| import 'react-native-gesture-handler'; | |||||
| import React from 'react'; | |||||
| import { StyleSheet, Text, View } from 'react-native'; | |||||
| import { NavigationContainer } from '@react-navigation/native'; | |||||
| import { createBottomTabNavigator } from "@react-navigation/bottom-tabs"; | |||||
| import { Provider } from 'react-redux'; | |||||
| import { SafeAreaProvider } from 'react-native-safe-area-context'; | |||||
| import store from "./store"; | |||||
| // import { Counter } from './screens/Counter' | |||||
| import { LogIn } from './screens/LogIn'; | |||||
| import { createStackNavigator } from '@react-navigation/stack'; | |||||
| const A = () => <View><Text>Home</Text></View> | |||||
| const B = () => <View><Text>Settings</Text></View> | |||||
| const C = () => <View><Text>C</Text></View> | |||||
| const Tab = createBottomTabNavigator(); | |||||
| const Stack = createStackNavigator(); | |||||
| const Home = () => { | |||||
| return ( | |||||
| <Tab.Navigator> | |||||
| <Tab.Screen name="A" component={A} /> | |||||
| <Tab.Screen name="B" component={B} /> | |||||
| </Tab.Navigator> | |||||
| ) | |||||
| } | |||||
| function App() { | |||||
| return ( | |||||
| <NavigationContainer> | |||||
| <Stack.Navigator> | |||||
| <Stack.Screen name="Home" component={Home} /> | |||||
| <Stack.Screen name="Profile" component={C} /> | |||||
| </Stack.Navigator> | |||||
| </NavigationContainer> | |||||
| ); | |||||
| } | |||||
| const AppWrapper = () => { | |||||
| return ( | |||||
| <Provider store={store}> | |||||
| <SafeAreaProvider> | |||||
| <App /> | |||||
| </SafeAreaProvider> | |||||
| </Provider> | |||||
| ); | |||||
| }; | |||||
| const styles = StyleSheet.create({ | |||||
| container: { | |||||
| flex: 1, | |||||
| backgroundColor: '#fff', | |||||
| alignItems: 'center', | |||||
| justifyContent: 'center', | |||||
| }, | |||||
| }); | |||||
| export default AppWrapper; |
| { | |||||
| "expo": { | |||||
| "name": "native", | |||||
| "slug": "native", | |||||
| "version": "1.0.0", | |||||
| "orientation": "portrait", | |||||
| "icon": "./assets/icon.png", | |||||
| "splash": { | |||||
| "image": "./assets/splash.png", | |||||
| "resizeMode": "contain", | |||||
| "backgroundColor": "#ffffff" | |||||
| }, | |||||
| "updates": { | |||||
| "fallbackToCacheTimeout": 0 | |||||
| }, | |||||
| "assetBundlePatterns": [ | |||||
| "**/*" | |||||
| ], | |||||
| "ios": { | |||||
| "supportsTablet": true | |||||
| }, | |||||
| "android": { | |||||
| "adaptiveIcon": { | |||||
| "foregroundImage": "./assets/adaptive-icon.png", | |||||
| "backgroundColor": "#FFFFFF" | |||||
| } | |||||
| }, | |||||
| "web": { | |||||
| "favicon": "./assets/favicon.png" | |||||
| } | |||||
| } | |||||
| } |
| module.exports = function(api) { | |||||
| api.cache(true); | |||||
| return { | |||||
| presets: ['babel-preset-expo'], | |||||
| }; | |||||
| }; |
| import React from 'react' | |||||
| import { StyleSheet, Text, TouchableOpacity, View } from "react-native"; | |||||
| import { heightPercentageToDP as hp, widthPercentageToDP as wp } from "react-native-responsive-screen"; | |||||
| export default Button = ({ onPress, title, style }) => { | |||||
| return ( | |||||
| <TouchableOpacity | |||||
| onPress={onPress} | |||||
| style={[{ paddingHorizontal: hp("2.6%"), justifyContent: "space-between" }, styles.widgetBox, style]} | |||||
| > | |||||
| <View style={[styles.alignCenter, styles.row]}> | |||||
| <Text trunk={true} numberOfLines={2} style={{ color: "#4C5A81", fontSize: hp("2.3%"), textAlign: 'center' }}>{title}</Text> | |||||
| </View> | |||||
| </TouchableOpacity> | |||||
| ); | |||||
| } | |||||
| const styles = StyleSheet.create({ | |||||
| alignCenter: { | |||||
| alignItems: "center", | |||||
| }, | |||||
| row: { | |||||
| flexDirection: "row", | |||||
| }, | |||||
| text: { | |||||
| color: "#4C5A81", | |||||
| fontSize: hp("2.3%"), | |||||
| paddingLeft: wp("5%"), | |||||
| width: wp("60%"), | |||||
| }, | |||||
| widgetBox: { | |||||
| backgroundColor: "#fff", | |||||
| borderRadius: wp("3%"), | |||||
| marginVertical: hp("1.3%"), | |||||
| paddingVertical: hp("2%"), | |||||
| position: "relative", | |||||
| flexDirection: "row", | |||||
| alignItems: "center", | |||||
| }, | |||||
| }); |
| const { getDefaultConfig } = require("expo/metro-config"); | |||||
| module.exports = (async () => { | |||||
| const { | |||||
| resolver: { sourceExts, assetExts } | |||||
| } = await getDefaultConfig(__dirname); | |||||
| return { | |||||
| transformer: { | |||||
| babelTransformerPath: require.resolve("react-native-svg-transformer") | |||||
| }, | |||||
| resolver: { | |||||
| assetExts: assetExts.filter(ext => ext !== "svg"), | |||||
| sourceExts: [...sourceExts, "svg"] | |||||
| } | |||||
| }; | |||||
| })(); |
| { | |||||
| "main": "node_modules/expo/AppEntry.js", | |||||
| "scripts": { | |||||
| "start": "expo start", | |||||
| "android": "expo start --android", | |||||
| "ios": "expo start --ios", | |||||
| "web": "expo start --web", | |||||
| "eject": "expo eject" | |||||
| }, | |||||
| "dependencies": { | |||||
| "@react-native-community/masked-view": "0.1.10", | |||||
| "@react-navigation/bottom-tabs": "^5.11.11", | |||||
| "@react-navigation/native": "^5.9.4", | |||||
| "@reduxjs/toolkit": "^1.5.1", | |||||
| "axios": "^0.21.1", | |||||
| "expo": "~41.0.1", | |||||
| "expo-status-bar": "~1.0.4", | |||||
| "react": "16.13.1", | |||||
| "react-dom": "16.13.1", | |||||
| "react-native": "https://github.com/expo/react-native/archive/sdk-41.0.0.tar.gz", | |||||
| "react-native-gesture-handler": "~1.10.2", | |||||
| "react-native-reanimated": "~2.1.0", | |||||
| "react-native-responsive-screen": "^1.4.2", | |||||
| "react-native-safe-area-context": "3.2.0", | |||||
| "react-native-screens": "~3.0.0", | |||||
| "react-native-svg": "12.1.0", | |||||
| "react-native-svg-transformer": "^0.14.3", | |||||
| "react-native-web": "~0.13.12", | |||||
| "react-navigation-stack": "^2.10.4", | |||||
| "react-redux": "^7.2.4", | |||||
| "redux-thunk": "^2.3.0" | |||||
| }, | |||||
| "devDependencies": { | |||||
| "@babel/core": "^7.9.0" | |||||
| }, | |||||
| "private": true | |||||
| } |
| const npmCheck = require('npm-check') | |||||
| const compareVersions = require('compare-versions'); | |||||
| const exec = require('child_process').exec; | |||||
| npmCheck() | |||||
| .then(currentState => { | |||||
| packages = currentState.get('packages') | |||||
| packages.forEach(p => { | |||||
| const { moduleName, latest, installed } = p | |||||
| if (moduleName === 'expo' && compareVersions(latest, installed) >= 1) { | |||||
| console.log('\x1b[31m', `Latest version of expo is ${latest}, but you are running ${installed}. Please run: expo upgrade`, '\x1b[0m') | |||||
| } | |||||
| }) | |||||
| }) | |||||
| .then(() => new Promise((resolve) => { | |||||
| exec('node -v', (err, nodeVersion, stdErr) => { | |||||
| nodeVersion = nodeVersion.trim().substring(1) | |||||
| const supportedNode = '14.17.0' | |||||
| if (compareVersions(supportedNode, nodeVersion) >= 1) { | |||||
| console.log('\x1b[31m', `You should have node version ${supportedNode} or later. Please download the latest version`, '\x1b[0m') | |||||
| process.exit(1) | |||||
| } | |||||
| resolve() | |||||
| }) | |||||
| })) | |||||
| .then(() => new Promise((resolve) => { | |||||
| exec('npm -v', (err, npmVersion, stdErr) => { | |||||
| npmVersion = npmVersion.trim() | |||||
| const supportedNpm = '7.16.0' | |||||
| if (compareVersions(supportedNpm, npmVersion) >= 1) { | |||||
| console.log('\x1b[31m', `You should have npm version ${supportedNpm} or later. Please run: npm install -g npm@latest`, '\x1b[0m') | |||||
| process.exit(1) | |||||
| } | |||||
| resolve() | |||||
| }) | |||||
| })) | |||||
| .then(() => new Promise((resolve) => { | |||||
| exec('expo-cli --version', (err, expoCliVersion, stdErr) => { | |||||
| const supportedExpoCli = '4.5.2' | |||||
| expoCliVersion = expoCliVersion.trim() | |||||
| if (err || compareVersions(supportedExpoCli, expoCliVersion) >= 1) { | |||||
| console.log('\x1b[31m', `You should have globally installed expo-cli version ${supportedExpoCli} or later. Please run: npm i -g expo-cli@latest`, '\x1b[0m') | |||||
| process.exit(1) | |||||
| } | |||||
| resolve() | |||||
| }) | |||||
| })) |
| import React from "react"; | |||||
| import { StyleSheet, Text, TouchableOpacity, View } from "react-native"; | |||||
| import { useDispatch, useSelector } from "react-redux"; | |||||
| import { decrement, increment } from "../store/actions"; | |||||
| export const Counter = () => { | |||||
| const dispatch = useDispatch() | |||||
| const value = useSelector(s => s.test.value) | |||||
| return ( | |||||
| <View style={[{ width: '75%' }]}> | |||||
| <Text style={[styles.textAlignCenter]}>{value}</Text> | |||||
| <View style={[styles.row, styles.justifySpaceBetween]}> | |||||
| <TouchableOpacity style={[styles.button, styles.justifyCenter]} onPress={() => dispatch(increment())}> | |||||
| <Text style={[styles.textAlignCenter]}>Increment</Text> | |||||
| </TouchableOpacity> | |||||
| <TouchableOpacity style={[styles.button, styles.justifyCenter]} onPress={() => dispatch(decrement())}> | |||||
| <Text style={[styles.textAlignCenter]}>Decrement</Text> | |||||
| </TouchableOpacity> | |||||
| </View> | |||||
| </View> | |||||
| ) | |||||
| } | |||||
| const styles = StyleSheet.create({ | |||||
| row: { | |||||
| flexDirection: "row", | |||||
| }, | |||||
| flex: { | |||||
| flex: 1 | |||||
| }, | |||||
| justifySpaceBetween: { | |||||
| justifyContent: 'space-between' | |||||
| }, | |||||
| button: { | |||||
| backgroundColor: "#D9F1EC", | |||||
| padding: '3%', | |||||
| marginTop: '10%' | |||||
| }, | |||||
| textAlignCenter: { | |||||
| textAlign: 'center' | |||||
| }, | |||||
| justifyCenter: { | |||||
| justifyContent: 'center' | |||||
| }, | |||||
| alignCenter: { | |||||
| alignItems: 'center' | |||||
| } | |||||
| }) |
| import React from "react"; | |||||
| import { useState } from "react"; | |||||
| import { View, TextInput } from "react-native"; | |||||
| import Button from "../components/Buttons/Button"; | |||||
| import { login } from "../thunks/user.thunk"; | |||||
| export const LogIn = () => { | |||||
| const [username, setUsername] = useState() | |||||
| const [password, setPassword] = useState() | |||||
| const [error, setError] = useState() | |||||
| return ( | |||||
| <View> | |||||
| <TextInput focus={true} | |||||
| style={{ marginTop: 20 }} | |||||
| placeholder='Username' /> | |||||
| <TextInput focus={true} | |||||
| style={{ marginTop: 20 }} | |||||
| placeholder='Password' | |||||
| secureTextEntry /> | |||||
| <Button onPress={() => dispatch( | |||||
| login(username, password, function (result) { | |||||
| if (!result.OK) { | |||||
| setUsername(""); | |||||
| setPassword(""); | |||||
| setError(result.data.Message ? result.data.Message : "Error occurred during Login,please try again!"); | |||||
| } | |||||
| }) | |||||
| )} title='Log In' style={{ backgroundColor: 'red' }} /> | |||||
| </View> | |||||
| ) | |||||
| } |
| export const API_ENDPOINT = __DEV__ ? "https://mystratadevapi.kalla.co/" : "https://my.kalla.co/"; |
| import axios from "axios"; | |||||
| import { setConnectionError } from "../store/actions"; | |||||
| import store from "../store/store"; | |||||
| import { API_ENDPOINT } from './endpointDef'; | |||||
| import { refreshTokens } from "./tokenService/tokenApiClient"; | |||||
| const axiosApiInstance = axios.create(); | |||||
| const globalLog = false; | |||||
| const defaultOptions = { log: false } | |||||
| export const Get = (url, options) => { | |||||
| return request("GET", url, { ...defaultOptions, ...options }); | |||||
| } | |||||
| export const Post = (url, data, options) => { | |||||
| return request("POST", url, { ...defaultOptions, ...options, data }); | |||||
| } | |||||
| export const Put = (url, data, options) => { | |||||
| return request("PUT", url, { ...defaultOptions, ...options, data }); | |||||
| } | |||||
| export const Delete = (url, options) => { | |||||
| return request("DELETE", url, { ...defaultOptions, ...options }); | |||||
| } | |||||
| const isLogging = (log) => { | |||||
| return (globalLog || log); | |||||
| } | |||||
| const request = (method, url, options) => { | |||||
| const { data, log } = options; | |||||
| if (isLogging(log)) console.log("REQUEST URL : ", url); | |||||
| const requestObject = { | |||||
| method, | |||||
| url: API_ENDPOINT + url, | |||||
| timeout: 60000 | |||||
| } | |||||
| if (data) { | |||||
| if (isLogging(log)) console.log(`DATA FOR : ${url}`, data); | |||||
| requestObject.data = data; | |||||
| } | |||||
| else { | |||||
| requestObject.data = undefined; | |||||
| } | |||||
| // console.log("url", url) | |||||
| return axiosApiInstance(requestObject).then((response) => { | |||||
| if (isLogging(log)) console.log(`RESPONSE for ${url} : `, response); | |||||
| return { ...response, OK: true }; | |||||
| }) | |||||
| .catch((error) => { | |||||
| if (isLogging(log)) console.log(`RESPONSE for catch ${url} : `, (error.response ? error.response : error)); | |||||
| //if we get a request timeout error | |||||
| if (!error.response) { | |||||
| store.dispatch(setConnectionError(error)); | |||||
| return { error, OK: false }; | |||||
| } | |||||
| //if we get a response other than OK | |||||
| if (error.response) { | |||||
| return { ...error.response, OK: false }; | |||||
| } | |||||
| return { OK: false }; | |||||
| }) | |||||
| } | |||||
| axiosApiInstance.interceptors.request.use( | |||||
| (config) => { | |||||
| // console.log('sending request') | |||||
| const accessToken = store.getState().user.token; | |||||
| config.headers = { | |||||
| "Authorization": `Bearer ${accessToken}`, | |||||
| "Accept": "application/json", | |||||
| "Content-Type": "application/json", | |||||
| "Referer": config.url | |||||
| }; | |||||
| return config; | |||||
| }, | |||||
| (error) => { | |||||
| Promise.reject(error); | |||||
| } | |||||
| ); | |||||
| axiosApiInstance.interceptors.response.use( | |||||
| (response) => { | |||||
| return response; | |||||
| }, | |||||
| async (error) => { | |||||
| // console.log('********************************************************************', error) | |||||
| const originalRequest = error.config | |||||
| if (originalRequest._retried) { | |||||
| console.log("Request second attempt failed.") | |||||
| } | |||||
| if (!originalRequest._retried && error.response && error.response.status === 401) { | |||||
| try { | |||||
| await refreshTokens() | |||||
| } | |||||
| catch (e) { | |||||
| throw error; | |||||
| } | |||||
| return await axiosApiInstance({ ...originalRequest, _retried: true }) | |||||
| } | |||||
| throw error; | |||||
| } | |||||
| ); | |||||
| export default axiosApiInstance; |
| import store from '../../store/store'; | |||||
| import { refreshTokens } from './tokenApiClient' | |||||
| import { validateAccessToken } from './validator' | |||||
| let lock; | |||||
| export async function getAccessToken() { | |||||
| if (lock) { | |||||
| await lock; | |||||
| } | |||||
| else { | |||||
| lock = new Promise((resolve, reject) => { | |||||
| }) | |||||
| } | |||||
| if (!validateAccessToken()) { | |||||
| console.log("REFRESHING TOKEN") | |||||
| await refreshTokens(); | |||||
| } | |||||
| return store.getState().user.token | |||||
| } |
| import axios from "axios"; | |||||
| import { batchActions } from 'redux-batched-actions'; | |||||
| import { logOut, setRefreshToken, setToken } from "../../store/actions"; | |||||
| import store from "../../store/store"; | |||||
| import { API_ENDPOINT } from '../endpointDef'; | |||||
| let refreshingPromise = null | |||||
| export async function refreshTokens() { | |||||
| // console.log("RefreshTokens") | |||||
| if (!refreshingPromise) { | |||||
| refreshingPromise = new Promise(async (resolve, reject) => { | |||||
| // console.log("Sending the requst for new tokens") | |||||
| const refreshToken = store.getState().user.refreshToken | |||||
| const username = store.getState().user.username | |||||
| try { | |||||
| const { data } = await axios.post(`${API_ENDPOINT}api/token/refresh`, { Token: refreshToken, Username: username, }); | |||||
| // console.log("Dispatch from refresh", data.Data.AccessToken, data.Data.RefreshToken) | |||||
| store.dispatch(batchActions([setToken(data.Data.AccessToken), setRefreshToken(data.Data.RefreshToken)])) | |||||
| resolve() | |||||
| } | |||||
| catch (e) { | |||||
| // console.log("Could not refresh token.") | |||||
| store.dispatch(logOut()) | |||||
| reject(e) | |||||
| } | |||||
| finally { | |||||
| refreshingPromise = null | |||||
| } | |||||
| }) | |||||
| } | |||||
| // else { | |||||
| // console.log("Someone is refreshing...") | |||||
| // } | |||||
| await refreshingPromise | |||||
| } |
| import store from "../../store/store"; | |||||
| import jwt_decode from "jwt-decode"; | |||||
| export function validateAccessToken() { | |||||
| const accessToken = store.getState().user.token | |||||
| if (!accessToken) { | |||||
| return false; | |||||
| } | |||||
| const decoded = jwt_decode(accessToken); | |||||
| // console.log("DECODED", decoded) | |||||
| const exp = decoded.exp * 1000 | |||||
| const isExpired = exp - (Date.now() + 60 * 1000) | |||||
| // console.log("isExpired", isExpired) | |||||
| if (isExpired <= 0) | |||||
| return false | |||||
| return true | |||||
| } |
| import { Post } from './index'; | |||||
| export const loginAPI = async (username, password) => { | |||||
| const body = JSON.stringify({ Username: username, Password: password }); | |||||
| return Post(`api/login`, body); | |||||
| }; |
| import { createAction } from "@reduxjs/toolkit"; | |||||
| const createTestAction = (name) => createAction(`TEST_${name}`) | |||||
| export const increment = createTestAction('INCREMENT') | |||||
| export const decrement = createTestAction('DECREMENT') | |||||
| const createUserAction = (name) => createAction(`USER_${name}`) | |||||
| export const setToken = createUserAction('TOKEN') | |||||
| export const setRefreshToken = createUserAction('REFRESHTOKEN') | |||||
| export const authLoaded = createUserAction('AUTH_LOADED') | |||||
| export const logOut = createUserAction('LOGOUT') | |||||
| export const setUsername = createUserAction('USERNAME') | |||||
| export const logInSuccess = createUserAction('LOGIN_SUCCESS') | |||||
| export const setConnectionError = createUserAction('SET_CONNECTION_ERROR') |
| import AsyncStorage from '@react-native-async-storage/async-storage'; | |||||
| import { batchActions } from 'redux-batched-actions'; | |||||
| import { authLoaded, setRefreshToken, setToken, setUsername } from './actions' | |||||
| // AsyncStorage.getAllKeys() | |||||
| // .then((keys) => AsyncStorage.multiGet(keys) | |||||
| // .then((data) => console.log(data))); | |||||
| async function safeSetStorage(key, value) { | |||||
| if (value) { | |||||
| await AsyncStorage.setItem(key, value) | |||||
| } | |||||
| else { | |||||
| await AsyncStorage.removeItem(key) | |||||
| } | |||||
| } | |||||
| const accessTokenKey = 'userToken' | |||||
| const refreshTokenKey = 'refreshToken' | |||||
| const userNameKey = 'userName' | |||||
| function subscibeOnAuthChanges(store) { | |||||
| let lastAccessToken = store.getState().user.token; | |||||
| let lastRefreshToken = store.getState().user.refreshToken; | |||||
| let lastUsername = store.getState().user.username; | |||||
| // console.log("In subscribe function", lastAccessToken, lastRefreshToken, lastUsername) | |||||
| store.subscribe(async () => { | |||||
| const state = store.getState(); | |||||
| // console.log("In subscribe...") | |||||
| const newAccessToken = state.user.token; | |||||
| const newRefreshToken = state.user.refreshToken; | |||||
| const newUsername = state.user.username; | |||||
| // console.log("In subscribe", newAccessToken, newRefreshToken, newUsername) | |||||
| if (lastAccessToken !== newAccessToken) { | |||||
| lastAccessToken = newAccessToken | |||||
| await safeSetStorage(accessTokenKey, newAccessToken) | |||||
| } | |||||
| if (lastRefreshToken !== newRefreshToken) { | |||||
| lastRefreshToken = newRefreshToken | |||||
| await safeSetStorage(refreshTokenKey, newRefreshToken) | |||||
| } | |||||
| if (lastUsername !== newUsername) { | |||||
| lastUsername = newUsername | |||||
| await safeSetStorage(userNameKey, newUsername) | |||||
| } | |||||
| }) | |||||
| } | |||||
| async function loadFromStorage(store) { | |||||
| try { | |||||
| // leave comments this for testing | |||||
| // console.log("getting Token from storage"); | |||||
| // await AsyncStorage.setItem(accessTokenKey, "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI4MmVlNjNkMi0yZDRjLTRkZjQtYTQ0MS1hZWRkZjhiNGEwMmYiLCJzdWIiOiJpbGlqYSt1Y2lAcHJvZml0b3B0aWNzLmNvbSIsInVuaXF1ZV9uYW1lIjoiaWxpamErdWNpQHByb2ZpdG9wdGljcy5jb20iLCJVdGNPZmZzZXRNaW51dGVzIjoiLTI0MCIsIkFncmVlbWVudEFjY2VwdGVkIjoiVHJ1ZSIsIk9yZ2FuaXphdGlvbklkIjoiMTkiLCJPcmdhbml6YXRpb25Ib3N0bmFtZSI6Im15c3RyYXRhZGV2Mi5wcm9maXRvcHRpY3MuY29tIiwibmJmIjoxNjE2Njk0NTI2LCJleHAiOjE2MTY2OTYzMjYsImlzcyI6InN0cmF0YWNsZWFyLmNvbSIsImF1ZCI6IkNsaWVudCJ9.kk0TQENidNJfQpkLy6uVUZULHp1r46gA7IyhAMHkqsI"); | |||||
| userToken = await AsyncStorage.getItem(accessTokenKey); | |||||
| // leave this for testing | |||||
| // await AsyncStorage.setItem(refreshTokenKey, "IBFRYNvviwI0+C3CVy1sViQlvFDTXsTUZWa8fgzv6oLQtYTng3A+8xc4gMqpX+y1lR8WIhN4uDlEV19uu+prWEVpi8qkjp999Li0t+eiEoe7OdeK+vQwecowvvJZRt1WK/Qb3biMC76IhElZta/riy2yZ9jQRBhiEfPs+yK+GJI="); | |||||
| // await AsyncStorage.setItem(refreshTokenKey, "TV+VsT+Qwb9hh1oi8mG4CBX0gnN9y9aNfXT/QUPZsPwCJ3n1svOO/KwT14Z5UZxhU+8h5ocuvipj4dIGDJa7wJHkV7j5/mBKfSgVqMvX43VdFXkp19Dg0h4Tg9Elh6GpR0N6FSCDJl3igv83W3OpC4UWXylQKW+0gr+tjOPmT78="); | |||||
| refreshToken = await AsyncStorage.getItem(refreshTokenKey); | |||||
| // leave this for testing | |||||
| // await AsyncStorage.setItem(userNameKey, "ilija+uci@profitoptics.com"); | |||||
| userName = await AsyncStorage.getItem(userNameKey); | |||||
| // console.log('Loaded from storage', userToken) | |||||
| store.dispatch(batchActions([setToken(userToken), setRefreshToken(refreshToken), setUsername(userName), authLoaded()])) | |||||
| } catch (eror) { | |||||
| console.error("There was an error getting token from storage", eror); | |||||
| } | |||||
| } | |||||
| function init(store) { | |||||
| loadFromStorage(store) | |||||
| .then(() => subscibeOnAuthChanges(store)) | |||||
| } | |||||
| export { init }; |
| import { createStore, applyMiddleware } from "redux"; | |||||
| import { enableBatching } from 'redux-batched-actions'; | |||||
| import thunk from "redux-thunk"; | |||||
| import reducers from "./reducers"; | |||||
| import { init } from './authPersistor' | |||||
| const store = createStore(enableBatching(reducers), applyMiddleware(thunk)); | |||||
| init(store) | |||||
| export default store; |
| import { combineReducers } from "redux"; | |||||
| // import { persistReducer } from "redux-persist"; | |||||
| // import immutableTransform from "redux-persist-transform-immutable"; | |||||
| import test from "./test"; | |||||
| import user from "./user"; | |||||
| const rootReducer = combineReducers({ | |||||
| test, | |||||
| user | |||||
| }); | |||||
| export default rootReducer; |
| import { createReducer } from '@reduxjs/toolkit' | |||||
| import { increment, decrement } from '../actions/index' | |||||
| export default createReducer({ | |||||
| value: 0 | |||||
| }, builder => { | |||||
| builder | |||||
| .addCase(increment, (state) => { | |||||
| state.value += 1 | |||||
| }) | |||||
| .addCase(decrement, (state) => { | |||||
| state.value -= 1 | |||||
| }) | |||||
| }) |
| import { setToken, setRefreshToken, authLoaded, logOut, setUsername, logInSuccess, setConnectionError } from "../actions/index"; | |||||
| export default createReducer({ | |||||
| token: null, | |||||
| refreshToken: null, | |||||
| username: "", | |||||
| logInSuccess: "", | |||||
| connectionError: "", | |||||
| isAuthLoadedFromStorage: false | |||||
| }, builder => { | |||||
| builder | |||||
| .addCase(setToken, (state, { payload }) => { | |||||
| state.token = payload | |||||
| }) | |||||
| .addCase(setRefreshToken, (state, { payload }) => { | |||||
| state.refreshToken = payload | |||||
| }) | |||||
| .addCase(authLoaded, (state) => { | |||||
| state.isAuthLoadedFromStorage = true | |||||
| }) | |||||
| .addCase(logOut, (state) => { | |||||
| state.username = null; | |||||
| state.token = null; | |||||
| state.refreshToken = null; | |||||
| }) | |||||
| .addCase(setUsername, (state, { payload }) => { | |||||
| state.username = payload | |||||
| }) | |||||
| .addCase(logInSuccess, (state, { payload }) => { | |||||
| state.logInSuccess = payload | |||||
| }) | |||||
| .addCase(setConnectionError, (state) => { | |||||
| state.connectionError = payload | |||||
| }) | |||||
| }) |
| import { loginAPI, } from "../service/user"; | |||||
| export const login = (username, password, callback) => { | |||||
| return (dispatch) => | |||||
| loginAPI(username, password) | |||||
| .then((responseJson) => { | |||||
| if (responseJson.OK && responseJson.data.Data.AccessToken != null) { | |||||
| dispatch(batchActions([ | |||||
| setToken(responseJson.data.Data.AccessToken), | |||||
| setRefreshToken(responseJson.data.Data.RefreshToken), | |||||
| setUsername(username)])) | |||||
| } else callback(responseJson); | |||||
| }) | |||||
| .catch((error) => { | |||||
| console.log("error", error); | |||||
| }); | |||||
| }; |