mirror of
https://github.com/Telecominfraproject/wlan-sdk-mobile-app.git
synced 2025-10-29 01:42:24 +00:00
Converted all components to use functional definition rather than Classes. Removed 'zustand' and replaced with 'react-redux' for state. Some minor adjustments to the project organization.
This commit is contained in:
6
index.js
6
index.js
@@ -2,8 +2,8 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {AppRegistry} from 'react-native';
|
||||
import App from './App';
|
||||
import {name as appName} from './app.json';
|
||||
import { AppRegistry } from 'react-native';
|
||||
import App from './src/App';
|
||||
import { name as appName } from './app.json';
|
||||
|
||||
AppRegistry.registerComponent(appName, () => App);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"@react-navigation/bottom-tabs": "^6.0.9",
|
||||
"@react-navigation/native": "^6.0.6",
|
||||
"@react-navigation/native-stack": "^6.2.5",
|
||||
"@reduxjs/toolkit": "^1.6.2",
|
||||
"axios": "^0.23.0",
|
||||
"react": "17.0.2",
|
||||
"react-native": "0.66.1",
|
||||
@@ -24,7 +25,8 @@
|
||||
"react-native-safe-area-context": "^3.3.2",
|
||||
"react-native-screens": "^3.8.0",
|
||||
"react-native-url-polyfill": "^1.3.0",
|
||||
"zustand": "^3.6.0"
|
||||
"react-redux": "^7.2.6",
|
||||
"redux": "^4.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.9",
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import React from 'react';
|
||||
import store from './store/Store';
|
||||
import { Provider } from 'react-redux';
|
||||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
||||
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
||||
import { Image } from 'react-native';
|
||||
|
||||
import BrandSelector from './src/screens/BrandSelector';
|
||||
import SignIn from './src/screens/SignIn';
|
||||
import ForgotPassword from './src/screens/ForgotPassword';
|
||||
import ResetPassword from './src/screens/ResetPassword';
|
||||
import DeviceList from './src/screens/DeviceList';
|
||||
import DeviceDetails from './src/screens/DeviceDetails';
|
||||
import Profile from './src/screens/Profile';
|
||||
import BrandSelector from './screens/BrandSelector';
|
||||
import SignIn from './screens/SignIn';
|
||||
import ForgotPassword from './screens/ForgotPassword';
|
||||
import ResetPassword from './screens/ResetPassword';
|
||||
import DeviceList from './screens/DeviceList';
|
||||
import DeviceDetails from './screens/DeviceDetails';
|
||||
import Profile from './screens/Profile';
|
||||
|
||||
const Stack = createNativeStackNavigator();
|
||||
const Tab = createBottomTabNavigator();
|
||||
@@ -18,15 +20,17 @@ const DeviceStack = createNativeStackNavigator();
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<NavigationContainer>
|
||||
<Stack.Navigator>
|
||||
<Stack.Screen name="BrandSelector" component={BrandSelector} options={{ title: 'Select Brand' }} />
|
||||
<Stack.Screen name="SignIn" component={SignIn} options={{ title: 'Sign In' }} />
|
||||
<Stack.Screen name="ForgotPassword" component={ForgotPassword} options={{ title: 'Forgot Password' }} />
|
||||
<Stack.Screen name="ResetPassword" component={ResetPassword} options={{ title: 'Password Reset' }} />
|
||||
<Stack.Screen name="Main" component={TabScreens} options={{ headerShown: false }} />
|
||||
</Stack.Navigator>
|
||||
</NavigationContainer>
|
||||
<Provider store={store}>
|
||||
<NavigationContainer>
|
||||
<Stack.Navigator>
|
||||
<Stack.Screen name="BrandSelector" component={BrandSelector} options={{ title: 'Select Brand' }} />
|
||||
<Stack.Screen name="SignIn" component={SignIn} options={{ title: 'Sign In' }} />
|
||||
<Stack.Screen name="ForgotPassword" component={ForgotPassword} options={{ title: 'Forgot Password' }} />
|
||||
<Stack.Screen name="ResetPassword" component={ResetPassword} options={{ title: 'Password Reset' }} />
|
||||
<Stack.Screen name="Main" component={TabScreens} options={{ headerShown: false }} />
|
||||
</Stack.Navigator>
|
||||
</NavigationContainer>
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -40,7 +44,7 @@ function TabScreens() {
|
||||
headerShown: false,
|
||||
tabBarIcon: ({ tintColor }) => (
|
||||
<Image
|
||||
source={require('./src/assets/server-solid.png')}
|
||||
source={require('./assets/server-solid.png')}
|
||||
style={{ width: 26, height: 26, tintColor: tintColor }}
|
||||
/>
|
||||
),
|
||||
@@ -53,7 +57,7 @@ function TabScreens() {
|
||||
title: 'Profile',
|
||||
tabBarIcon: ({ tintColor }) => (
|
||||
<Image
|
||||
source={require('./src/assets/user-solid.png')}
|
||||
source={require('./assets/user-solid.png')}
|
||||
style={{ width: 26, height: 26, tintColor: tintColor }}
|
||||
/>
|
||||
),
|
||||
@@ -1,21 +1,21 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { useStore } from './Store';
|
||||
import store from './store/Store';
|
||||
|
||||
export function primaryColor() {
|
||||
let brandInfo = useStore.getState().brandInfo;
|
||||
export var primaryColor = '#2194f3';
|
||||
export var primaryColorStyle = StyleSheet.create({});
|
||||
|
||||
function updatePrimaryColorInfo() {
|
||||
const state = store.getState();
|
||||
let brandInfo = state.brandInfo.value;
|
||||
|
||||
if (brandInfo && brandInfo.primaryColor) {
|
||||
return brandInfo.primaryColor;
|
||||
primaryColor = brandInfo.primaryColor;
|
||||
return StyleSheet.create({
|
||||
color: brandInfo.primaryColor,
|
||||
});
|
||||
}
|
||||
|
||||
return '#2194f3';
|
||||
}
|
||||
|
||||
export function primaryColorStyle() {
|
||||
return StyleSheet.create({
|
||||
color: primaryColor(),
|
||||
});
|
||||
}
|
||||
store.subscribe(updatePrimaryColorInfo);
|
||||
|
||||
export const pageStyle = StyleSheet.create({
|
||||
container: {
|
||||
|
||||
24
src/Store.js
24
src/Store.js
@@ -1,24 +0,0 @@
|
||||
import create from 'zustand';
|
||||
|
||||
export const useStore = create(set => ({
|
||||
// Session information
|
||||
session: null,
|
||||
setSession: state => {
|
||||
set({ session: state });
|
||||
},
|
||||
clearSession: () => set({ session: null }),
|
||||
|
||||
// Brand Info
|
||||
brandInfo: null,
|
||||
setBrandInfo: state => {
|
||||
set({ brandInfo: state });
|
||||
},
|
||||
clearBrandInfo: () => set({ brandInfo: null }),
|
||||
|
||||
// System Info
|
||||
systemInfo: null,
|
||||
setSystemInfo: state => {
|
||||
set({ systemInfo: state });
|
||||
},
|
||||
clearSystemInfo: () => set({ systemInfo: null }),
|
||||
}));
|
||||
@@ -1,19 +1,21 @@
|
||||
// Used the following for a basis for generating react-native from OpenAPI
|
||||
// https://majidlotfinia.medium.com/openapi-generator-for-react-native-by-swagger-58847cadd9e8
|
||||
import 'react-native-url-polyfill/auto';
|
||||
import {strings} from '../localization/LocalizationStrings';
|
||||
import { strings } from '../localization/LocalizationStrings';
|
||||
import axios from 'axios';
|
||||
import {useStore} from '../Store';
|
||||
import {showGeneralError} from '../Utils';
|
||||
import {AuthenticationApiFactory, Configuration as SecurityConfiguration} from './generated/owSecurityApi';
|
||||
import {DevicesApiFactory, Configuration as GatewayConfiguration} from './generated/owGatewayApi';
|
||||
import store from '../store/Store';
|
||||
import { setSystemInfo } from '../store/SystemInfoSlice';
|
||||
import { showGeneralError } from '../Utils';
|
||||
import { AuthenticationApiFactory, Configuration as SecurityConfiguration } from './generated/owSecurityApi';
|
||||
import { DevicesApiFactory, Configuration as GatewayConfiguration } from './generated/owGatewayApi';
|
||||
|
||||
const axiosInstance = axios.create({});
|
||||
axiosInstance.interceptors.request.use(
|
||||
config => {
|
||||
const session = useStore.getState().session;
|
||||
const state = store.getState();
|
||||
const session = state.session.value;
|
||||
if (session) {
|
||||
config.headers.Authorization = 'Bearer ' + useStore.getState().session.access_token;
|
||||
config.headers.Authorization = 'Bearer ' + session.access_token;
|
||||
}
|
||||
|
||||
return config;
|
||||
@@ -35,19 +37,19 @@ const authenticationApi = new AuthenticationApiFactory(
|
||||
const gatewayConfig = new GatewayConfiguration();
|
||||
var devicesApi = null;
|
||||
|
||||
function getDevicesApi() {
|
||||
if (devicesApi === null) {
|
||||
let url = getBaseUrlForApi('owgw');
|
||||
devicesApi = url ? new DevicesApiFactory(gatewayConfig, url, axiosInstance) : null;
|
||||
}
|
||||
store.subscribe(generateDevicesApi);
|
||||
generateDevicesApi();
|
||||
|
||||
return devicesApi;
|
||||
function generateDevicesApi() {
|
||||
let url = getBaseUrlForApi('owgw');
|
||||
devicesApi = url ? new DevicesApiFactory(gatewayConfig, url, axiosInstance) : null;
|
||||
}
|
||||
|
||||
// Get the base URL from the System Info. This is returned in a call to SystemInfo and it
|
||||
// is needed in order to provide the proper base URIs for the other API systems.
|
||||
function getBaseUrlForApi(type) {
|
||||
const systemInfo = useStore.getState().systemInfo;
|
||||
const state = store.getState();
|
||||
const systemInfo = state.systemInfo.value;
|
||||
|
||||
if (systemInfo && systemInfo.endpoints) {
|
||||
const endpoints = systemInfo.endpoints;
|
||||
@@ -67,7 +69,7 @@ function getBaseUrlForApi(type) {
|
||||
|
||||
function setApiSystemInfo(systemInfo) {
|
||||
// Set the state, then we can use the getBaseUrlForApi to verify it has the proper information
|
||||
useStore.getState().setSystemInfo(systemInfo);
|
||||
store.dispatch(setSystemInfo(systemInfo));
|
||||
|
||||
let valid = true;
|
||||
const typesToValidate = ['owgw']; // Include all API types that might be used
|
||||
@@ -88,6 +90,8 @@ function setApiSystemInfo(systemInfo) {
|
||||
}
|
||||
|
||||
function handleApiError(title, error) {
|
||||
const state = store.getState();
|
||||
const session = state.session.value;
|
||||
let message = strings.errors.unknown;
|
||||
|
||||
if (error.response) {
|
||||
@@ -99,7 +103,7 @@ function handleApiError(title, error) {
|
||||
|
||||
case 403:
|
||||
console.error(error);
|
||||
if (useStore.getState().session === null) {
|
||||
if (session === null) {
|
||||
// If not currently signed in then return a credentials error
|
||||
message = strings.errors.credentials;
|
||||
} else {
|
||||
@@ -128,4 +132,4 @@ function handleApiError(title, error) {
|
||||
showGeneralError(title, message);
|
||||
}
|
||||
|
||||
export {authenticationApi, getDevicesApi, handleApiError, setApiSystemInfo};
|
||||
export { authenticationApi, devicesApi, handleApiError, setApiSystemInfo };
|
||||
|
||||
@@ -1,26 +1,24 @@
|
||||
import React, { Component } from 'react';
|
||||
import React from 'react';
|
||||
import { StyleSheet, TouchableOpacity, View, Text, Image } from 'react-native';
|
||||
|
||||
export class BrandItem extends Component {
|
||||
render() {
|
||||
return (
|
||||
<TouchableOpacity onPress={this.props.onPress}>
|
||||
<View style={brandItemStyle.container}>
|
||||
<Image style={brandItemStyle.icon} source={{ uri: this.getCompanyIconUri() }} />
|
||||
<Text style={brandItemStyle.text}>{this.getCompanyName()}</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
const BrandItem = props => {
|
||||
const getCompanyIconUri = () => {
|
||||
return props.brand.iconUri;
|
||||
};
|
||||
|
||||
getCompanyIconUri() {
|
||||
return this.props.brand.iconUri;
|
||||
}
|
||||
const getCompanyName = () => {
|
||||
return props.brand.name;
|
||||
};
|
||||
|
||||
getCompanyName() {
|
||||
return this.props.brand.name;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<TouchableOpacity onPress={props.onPress}>
|
||||
<View style={brandItemStyle.container}>
|
||||
<Image style={brandItemStyle.icon} source={{ uri: getCompanyIconUri() }} />
|
||||
<Text style={brandItemStyle.text}>{getCompanyName()}</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
const brandItemStyle = StyleSheet.create({
|
||||
container: {
|
||||
@@ -41,3 +39,5 @@ const brandItemStyle = StyleSheet.create({
|
||||
fontSize: 14,
|
||||
},
|
||||
});
|
||||
|
||||
export default BrandItem;
|
||||
|
||||
@@ -1,42 +1,40 @@
|
||||
import React, { Component } from 'react';
|
||||
import React from 'react';
|
||||
import { StyleSheet, TouchableOpacity, View, Text, Image } from 'react-native';
|
||||
|
||||
export class DeviceItem extends Component {
|
||||
render() {
|
||||
return (
|
||||
<TouchableOpacity onPress={this.props.onPress}>
|
||||
<View style={deviceItemStyle.container}>
|
||||
<Image style={deviceItemStyle.icon} source={this.getDeviceIcon()} />
|
||||
|
||||
<View style={deviceItemStyle.textContainer}>
|
||||
<Text>{this.getDeviceName()}</Text>
|
||||
<Text>{this.getDeviceType()}</Text>
|
||||
</View>
|
||||
|
||||
<Image style={deviceItemStyle.icon} source={this.getDeviceStatusIcon()} />
|
||||
|
||||
<Text>></Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
getDeviceIcon() {
|
||||
const DeviceItem = props => {
|
||||
const getDeviceIcon = () => {
|
||||
return require('../assets/server-solid.png');
|
||||
}
|
||||
};
|
||||
|
||||
getDeviceName() {
|
||||
return this.props.device.compatible;
|
||||
}
|
||||
const getDeviceName = () => {
|
||||
return props.device.compatible;
|
||||
};
|
||||
|
||||
getDeviceType() {
|
||||
return this.props.device.manufacturer;
|
||||
}
|
||||
const getDeviceType = () => {
|
||||
return props.device.manufacturer;
|
||||
};
|
||||
|
||||
getDeviceStatusIcon() {
|
||||
const getDeviceStatusIcon = () => {
|
||||
return require('../assets/wifi-solid.png');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<TouchableOpacity onPress={props.onPress}>
|
||||
<View style={deviceItemStyle.container}>
|
||||
<Image style={deviceItemStyle.icon} source={getDeviceIcon()} />
|
||||
|
||||
<View style={deviceItemStyle.textContainer}>
|
||||
<Text>{getDeviceName()}</Text>
|
||||
<Text>{getDeviceType()}</Text>
|
||||
</View>
|
||||
|
||||
<Image style={deviceItemStyle.icon} source={getDeviceStatusIcon()} />
|
||||
|
||||
<Text>></Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
const deviceItemStyle = StyleSheet.create({
|
||||
container: {
|
||||
@@ -63,3 +61,5 @@ const deviceItemStyle = StyleSheet.create({
|
||||
marginLeft: 10,
|
||||
},
|
||||
});
|
||||
|
||||
export default DeviceItem;
|
||||
|
||||
@@ -1,92 +1,93 @@
|
||||
import React, { Component } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { selectBrandInfo, setBrandInfo } from '../store/BrandInfoSlice';
|
||||
import { strings } from '../localization/LocalizationStrings';
|
||||
import { useStore } from '../Store';
|
||||
import { pageStyle, pageItemStyle, primaryColor } from '../AppStyle';
|
||||
import { StyleSheet, View, Text, FlatList, TextInput, ActivityIndicator } from 'react-native';
|
||||
import { BrandItem } from '../components/BrandItem';
|
||||
import BrandItem from '../components/BrandItem';
|
||||
|
||||
export default class BrandSelector extends Component {
|
||||
state = {
|
||||
loading: false,
|
||||
brands: [
|
||||
{
|
||||
id: 'openwifi',
|
||||
name: 'OpenWifi',
|
||||
iconUri: 'https://14oranges-ui.arilia.com/assets/14Oranges_Logo.png',
|
||||
primaryColor: '#19255f',
|
||||
},
|
||||
{
|
||||
id: 'openwifigreen',
|
||||
name: 'OpenWifi (Green)',
|
||||
iconUri: 'https://14oranges-ui.arilia.com/assets/14Oranges_Logo.png',
|
||||
primaryColor: '#1a3e1b',
|
||||
},
|
||||
],
|
||||
filtered: false,
|
||||
filteredBrands: [],
|
||||
};
|
||||
const BrandSelector = props => {
|
||||
const dispatch = useDispatch();
|
||||
const brandInfo = useSelector(selectBrandInfo);
|
||||
// Currently this following state does not change, but the expectation is that this information
|
||||
// will come from an API so it is being left as is for this development
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [brands, setBrands] = useState([
|
||||
{
|
||||
id: 'openwifi',
|
||||
name: 'OpenWifi',
|
||||
iconUri: 'https://14oranges-ui.arilia.com/assets/14Oranges_Logo.png',
|
||||
primaryColor: '#19255f',
|
||||
},
|
||||
{
|
||||
id: 'openwifigreen',
|
||||
name: 'OpenWifi (Green)',
|
||||
iconUri: 'https://14oranges-ui.arilia.com/assets/14Oranges_Logo.png',
|
||||
primaryColor: '#1a3e1b',
|
||||
},
|
||||
]);
|
||||
const [filtered, setFiltered] = useState(false);
|
||||
const [filteredBrands, setFilteredBrands] = useState();
|
||||
|
||||
componentDidMount() {
|
||||
if (useStore.getState().brandInfo !== null) {
|
||||
this.props.navigation.navigate('SignIn');
|
||||
useEffect(() => {
|
||||
if (brandInfo !== null) {
|
||||
props.navigation.navigate('SignIn');
|
||||
}
|
||||
}
|
||||
// No dependencies as this is only to run once on mount. There are plenty of
|
||||
// hacks around this eslint warning, but disabling it makes the most sense.
|
||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={pageStyle.container}>
|
||||
<View style={pageItemStyle.container}>
|
||||
<Text style={pageItemStyle.title}>{strings.brandSelector.title}</Text>
|
||||
</View>
|
||||
<View style={pageItemStyle.container}>
|
||||
<Text style={pageItemStyle.description}>{strings.brandSelector.description}</Text>
|
||||
</View>
|
||||
{this.state.loading ? (
|
||||
<View style={pageItemStyle.container}>
|
||||
<ActivityIndicator size="large" color={primaryColor()} animating={this.state.loading} />
|
||||
</View>
|
||||
) : (
|
||||
<View style={pageItemStyle.containerBrands}>
|
||||
<View style={[pageItemStyle.container, brandingSelectorStyle.containerSearch]}>
|
||||
<TextInput
|
||||
style={pageItemStyle.inputText}
|
||||
placeholder="Search"
|
||||
onChangeText={search => this.filterBrands(search)}
|
||||
/>
|
||||
</View>
|
||||
<View style={pageItemStyle.container}>
|
||||
<FlatList
|
||||
style={brandingSelectorStyle.containerList}
|
||||
data={this.state.filtered ? this.state.filteredBrands : this.state.brands}
|
||||
renderItem={({ item }) => <BrandItem brand={item} onPress={this.onCompanySelect.bind(this, item)} />}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
filterBrands = searchText => {
|
||||
const filterBrands = searchText => {
|
||||
if (searchText) {
|
||||
let searchTextLowerCase = searchText.toLowerCase();
|
||||
this.setState({ filtered: true });
|
||||
this.setState({
|
||||
filteredBrands: this.state.brands.filter(b => b.name.toLowerCase().startsWith(searchTextLowerCase)),
|
||||
});
|
||||
setFiltered(true);
|
||||
setFilteredBrands(brands.filter(b => b.name.toLowerCase().startsWith(searchTextLowerCase)));
|
||||
} else {
|
||||
this.setState({ filtered: false });
|
||||
this.setState({ filteredBrands: [] });
|
||||
setFiltered(false);
|
||||
setFilteredBrands([]);
|
||||
}
|
||||
};
|
||||
|
||||
onCompanySelect = async item => {
|
||||
useStore.getState().setBrandInfo(item);
|
||||
const onCompanySelect = async item => {
|
||||
dispatch(setBrandInfo(item));
|
||||
|
||||
// Replace to the main screen. Use replace to ensure no back button
|
||||
this.props.navigation.navigate('SignIn');
|
||||
props.navigation.navigate('SignIn');
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={pageStyle.container}>
|
||||
<View style={pageItemStyle.container}>
|
||||
<Text style={pageItemStyle.title}>{strings.brandSelector.title}</Text>
|
||||
</View>
|
||||
<View style={pageItemStyle.container}>
|
||||
<Text style={pageItemStyle.description}>{strings.brandSelector.description}</Text>
|
||||
</View>
|
||||
{loading ? (
|
||||
<View style={pageItemStyle.container}>
|
||||
<ActivityIndicator size="large" color={primaryColor} animating={loading} />
|
||||
</View>
|
||||
) : (
|
||||
<View style={pageItemStyle.containerBrands}>
|
||||
<View style={[pageItemStyle.container, brandingSelectorStyle.containerSearch]}>
|
||||
<TextInput
|
||||
style={pageItemStyle.inputText}
|
||||
placeholder="Search"
|
||||
onChangeText={search => filterBrands(search)}
|
||||
/>
|
||||
</View>
|
||||
<View style={pageItemStyle.container}>
|
||||
<FlatList
|
||||
style={brandingSelectorStyle.containerList}
|
||||
data={filtered ? filteredBrands : brands}
|
||||
renderItem={({ item }) => <BrandItem brand={item} onPress={() => onCompanySelect(item)} />}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const brandingSelectorStyle = StyleSheet.create({
|
||||
containerBrands: {
|
||||
@@ -104,3 +105,5 @@ const brandingSelectorStyle = StyleSheet.create({
|
||||
width: '100%',
|
||||
},
|
||||
});
|
||||
|
||||
export default BrandSelector;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import React, { Component } from 'react';
|
||||
import React from 'react';
|
||||
import { pageStyle, pageItemStyle } from '../AppStyle';
|
||||
import { View, Text } from 'react-native';
|
||||
|
||||
export default class DeviceDetails extends Component {
|
||||
render() {
|
||||
return (
|
||||
<View style={pageStyle.container}>
|
||||
<View style={pageItemStyle.container}>
|
||||
<Text>Device Details</Text>
|
||||
</View>
|
||||
const DeviceDetails = props => {
|
||||
return (
|
||||
<View style={pageStyle.container}>
|
||||
<View style={pageItemStyle.container}>
|
||||
<Text>Device Details</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeviceDetails;
|
||||
|
||||
@@ -1,44 +1,41 @@
|
||||
import React, { Component } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { strings } from '../localization/LocalizationStrings';
|
||||
import { pageStyle, pageItemStyle } from '../AppStyle';
|
||||
import { View, Text, FlatList } from 'react-native';
|
||||
import { getDevicesApi, handleApiError } from '../api/apiHandler';
|
||||
import { DeviceItem } from '../components/DeviceItem';
|
||||
import { devicesApi, handleApiError } from '../api/apiHandler';
|
||||
import DeviceItem from '../components/DeviceItem';
|
||||
|
||||
export default class DeviceList extends Component {
|
||||
state = { devices: [] };
|
||||
const DeviceList = props => {
|
||||
const [devices, setDevices] = useState([]);
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={pageStyle.container}>
|
||||
<View style={pageItemStyle.container}>
|
||||
<Text style={{ fontSize: 24, fontWeight: 'bold' }}>Devices</Text>
|
||||
</View>
|
||||
<View style={pageItemStyle.container}>
|
||||
<FlatList
|
||||
data={this.state.devices}
|
||||
renderItem={({ item }) => <DeviceItem device={item} onPress={this.onDevicePress} />}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
useEffect(() => {
|
||||
getDevices();
|
||||
}, []);
|
||||
|
||||
onDevicePress = async () => {
|
||||
this.props.navigation.navigate('DeviceDetails');
|
||||
const onDevicePress = async () => {
|
||||
props.navigation.navigate('DeviceDetails');
|
||||
};
|
||||
|
||||
componentDidMount = () => {
|
||||
this.getDevices();
|
||||
};
|
||||
|
||||
getDevices = async () => {
|
||||
const getDevices = async () => {
|
||||
try {
|
||||
const response = await getDevicesApi().getDeviceList();
|
||||
this.setState({ devices: response.data.devices });
|
||||
const response = await devicesApi.getDeviceList();
|
||||
setDevices(response.data.devices);
|
||||
console.log(response.data);
|
||||
} catch (error) {
|
||||
handleApiError(strings.errors.titleDeviceList, error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={pageStyle.container}>
|
||||
<View style={pageItemStyle.container}>
|
||||
<Text style={{ fontSize: 24, fontWeight: 'bold' }}>Devices</Text>
|
||||
</View>
|
||||
<View style={pageItemStyle.container}>
|
||||
<FlatList data={devices} renderItem={({ item }) => <DeviceItem device={item} onPress={onDevicePress} />} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeviceList;
|
||||
|
||||
@@ -1,79 +1,36 @@
|
||||
import React, { Component } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { clearSession } from '../store/SessionSlice';
|
||||
import { pageStyle, pageItemStyle, primaryColor } from '../AppStyle';
|
||||
import { View, Text, TextInput, Button, ActivityIndicator, Alert } from 'react-native';
|
||||
import { strings } from '../localization/LocalizationStrings';
|
||||
import { authenticationApi, handleApiError } from '../api/apiHandler';
|
||||
import { useStore } from '../Store';
|
||||
|
||||
export default class ForgotPassword extends Component {
|
||||
state = {
|
||||
email: '',
|
||||
loading: false,
|
||||
};
|
||||
const ForgotPassword = props => {
|
||||
const dispatch = useDispatch();
|
||||
const [email, setEmail] = useState();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={pageStyle.container}>
|
||||
<View style={pageItemStyle.container}>
|
||||
<Text>Forgot Password</Text>
|
||||
</View>
|
||||
<View style={pageItemStyle.container}>
|
||||
<ActivityIndicator size="large" animating={this.state.loading} />
|
||||
</View>
|
||||
<View style={pageItemStyle.container}>
|
||||
<TextInput
|
||||
style={pageItemStyle.inputText}
|
||||
placeholder={strings.placeholders.email}
|
||||
autoComplete="email"
|
||||
autoCapitalize="none"
|
||||
autoFocus={true}
|
||||
keyboardType="email-address"
|
||||
textContentType="emailAddress"
|
||||
returnKeyType="go"
|
||||
onChangeText={text => this.setState({ email: text })}
|
||||
onSubmitEditing={() => {
|
||||
this.state.email && this.onSubmit;
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
<View style={pageItemStyle.containerButton}>
|
||||
<Button
|
||||
title={strings.buttons.sendEmail}
|
||||
color={primaryColor()}
|
||||
onPress={this.onSubmit}
|
||||
disabled={this.state.loading || !this.state.email}
|
||||
/>
|
||||
</View>
|
||||
<View style={pageItemStyle.containerButton}>
|
||||
<Button
|
||||
title={strings.buttons.signIn}
|
||||
color={primaryColor()}
|
||||
onPress={this.backToSignin}
|
||||
disabled={this.state.loading}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
validateEmail = () => {
|
||||
const validateEmail = () => {
|
||||
const re = /\S+@\S+\.\S+/;
|
||||
const valid = re.test(this.state.email);
|
||||
const valid = re.test(email);
|
||||
if (!valid) {
|
||||
Alert.alert(strings.errors.titleForgotPassword, strings.errors.badEmail);
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
};
|
||||
|
||||
const onSubmit = async () => {
|
||||
if (validateEmail()) {
|
||||
setLoading(true);
|
||||
|
||||
onSubmit = async () => {
|
||||
if (this.validateEmail()) {
|
||||
this.setState({ loading: true });
|
||||
try {
|
||||
useStore.getState().clearSession();
|
||||
// Clear the session information
|
||||
dispatch(clearSession());
|
||||
|
||||
const response = await authenticationApi.getAccessToken(
|
||||
{
|
||||
userId: this.state.email,
|
||||
userId: email,
|
||||
},
|
||||
undefined,
|
||||
true,
|
||||
@@ -82,12 +39,53 @@ export default class ForgotPassword extends Component {
|
||||
Alert.alert(strings.messages.message, strings.messages.resetEmail);
|
||||
} catch (error) {
|
||||
handleApiError(strings.errors.titleForgotPassword, error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
this.setState({ loading: false });
|
||||
}
|
||||
};
|
||||
|
||||
backToSignin = () => {
|
||||
this.props.navigation.replace('SignIn');
|
||||
const backToSignin = () => {
|
||||
props.navigation.replace('SignIn');
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={pageStyle.container}>
|
||||
<View style={pageItemStyle.container}>
|
||||
<Text>Forgot Password</Text>
|
||||
</View>
|
||||
<View style={pageItemStyle.container}>
|
||||
<ActivityIndicator size="large" animating={loading} />
|
||||
</View>
|
||||
<View style={pageItemStyle.container}>
|
||||
<TextInput
|
||||
style={pageItemStyle.inputText}
|
||||
placeholder={strings.placeholders.email}
|
||||
autoComplete="email"
|
||||
autoCapitalize="none"
|
||||
autoFocus={true}
|
||||
keyboardType="email-address"
|
||||
textContentType="emailAddress"
|
||||
returnKeyType="go"
|
||||
onChangeText={text => setEmail(text)}
|
||||
onSubmitEditing={() => {
|
||||
email && onSubmit;
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
<View style={pageItemStyle.containerButton}>
|
||||
<Button
|
||||
title={strings.buttons.sendEmail}
|
||||
color={primaryColor}
|
||||
onPress={onSubmit}
|
||||
disabled={loading || !email}
|
||||
/>
|
||||
</View>
|
||||
<View style={pageItemStyle.containerButton}>
|
||||
<Button title={strings.buttons.signIn} color={primaryColor} onPress={backToSignin} disabled={loading} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default ForgotPassword;
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
import React, { Component } from 'react';
|
||||
import React from 'react';
|
||||
import { strings } from '../localization/LocalizationStrings';
|
||||
import { useStore } from '../Store';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { clearSession } from '../store/SessionSlice';
|
||||
import { pageStyle, pageItemStyle, primaryColor } from '../AppStyle';
|
||||
import { View, Button } from 'react-native';
|
||||
|
||||
export default class Profile extends Component {
|
||||
render() {
|
||||
return (
|
||||
<View style={pageStyle.container}>
|
||||
<View style={pageItemStyle.containerButton}>
|
||||
<Button title={strings.buttons.signOut} color={primaryColor()} onPress={this.onSignOutPress} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
const Profile = props => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
onSignOutPress = async () => {
|
||||
// Clear the session information and go back to the sign in pageStyle
|
||||
useStore.getState().clearSession();
|
||||
this.props.navigation.replace('BrandSelector');
|
||||
const onSignOutPress = async () => {
|
||||
// Clear the session information and go back to the start
|
||||
dispatch(clearSession());
|
||||
|
||||
props.navigation.replace('BrandSelector');
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={pageStyle.container}>
|
||||
<View style={pageItemStyle.containerButton}>
|
||||
<Button title={strings.buttons.signOut} color={primaryColor} onPress={onSignOutPress} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default Profile;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { pageStyle, pageItemStyle, primaryColor } from "../AppStyle";
|
||||
import { pageStyle, pageItemStyle, primaryColor } from '../AppStyle';
|
||||
import { View, Text, TextInput, Button, Alert, ActivityIndicator } from 'react-native';
|
||||
import { strings } from '../localization/LocalizationStrings';
|
||||
import { authenticationApi, handleApiError } from '../api/apiHandler';
|
||||
@@ -13,7 +13,7 @@ export default function ResetPassword(props) {
|
||||
|
||||
useEffect(() => {
|
||||
console.log(userId, password);
|
||||
}, []);
|
||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const onCancel = () => {
|
||||
props.navigation.replace('SignIn');
|
||||
@@ -46,7 +46,7 @@ export default function ResetPassword(props) {
|
||||
const checkPassword = () => {
|
||||
const valid = validatePassword(newPassword);
|
||||
|
||||
if(newPassword === password) {
|
||||
if (newPassword === password) {
|
||||
Alert.alert(strings.errors.titleResetPassword, strings.errors.samePassword);
|
||||
return false;
|
||||
}
|
||||
@@ -54,14 +54,14 @@ export default function ResetPassword(props) {
|
||||
Alert.alert(strings.errors.titleResetPassword, strings.errors.mismatchPassword);
|
||||
return false;
|
||||
}
|
||||
if(!valid) {
|
||||
if (!valid) {
|
||||
Alert.alert(strings.errors.titleResetPassword, strings.errors.badFormat);
|
||||
return false;
|
||||
}
|
||||
return valid && newPassword !== password && newPassword === confirmPassword;
|
||||
};
|
||||
|
||||
const validatePassword = (password) => {
|
||||
const validatePassword = password => {
|
||||
const reg = /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$/;
|
||||
return reg.test(password);
|
||||
};
|
||||
@@ -102,17 +102,13 @@ export default function ResetPassword(props) {
|
||||
<View style={pageItemStyle.containerButton}>
|
||||
<Button
|
||||
title={strings.buttons.submit}
|
||||
color={primaryColor()}
|
||||
color={primaryColor}
|
||||
onPress={onSubmit}
|
||||
disabled={loading || !newPassword || !confirmPassword}
|
||||
/>
|
||||
</View>
|
||||
<View style={pageItemStyle.containerButton}>
|
||||
<Button
|
||||
title={strings.buttons.cancel}
|
||||
color={primaryColor()}
|
||||
onPress={onCancel}
|
||||
disabled={loading} />
|
||||
<Button title={strings.buttons.cancel} color={primaryColor} onPress={onCancel} disabled={loading} />
|
||||
</View>
|
||||
<View style={pageItemStyle.container}>
|
||||
<View>
|
||||
|
||||
@@ -1,123 +1,60 @@
|
||||
import React, { Component } from 'react';
|
||||
import React, { useState, useEffect, createRef } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { clearSession, setSession } from '../store/SessionSlice';
|
||||
import { selectBrandInfo } from '../store/BrandInfoSlice';
|
||||
import { strings } from '../localization/LocalizationStrings';
|
||||
import { useStore } from '../Store';
|
||||
import { pageStyle, pageItemStyle, primaryColor, primaryColorStyle } from '../AppStyle';
|
||||
import { StyleSheet, Text, View, Image, Button, TextInput, ActivityIndicator } from 'react-native';
|
||||
import { handleApiError, authenticationApi, setApiSystemInfo } from '../api/apiHandler';
|
||||
|
||||
export default class SignIn extends Component {
|
||||
state = {
|
||||
email: '',
|
||||
password: '',
|
||||
loading: false,
|
||||
};
|
||||
const SignIn = props => {
|
||||
const dispatch = useDispatch();
|
||||
const brandInfo = useSelector(selectBrandInfo);
|
||||
const [email, setEmail] = useState();
|
||||
const [password, setPassword] = useState();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const passwordRef = createRef();
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.passwordRef = React.createRef();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
useEffect(() => {
|
||||
// If the brand is not selected, then resort back to the brand selector
|
||||
if (useStore.getState().brandInfo === null) {
|
||||
this.props.navigation.replace('BrandSelector');
|
||||
if (brandInfo === null) {
|
||||
props.navigation.replace('BrandSelector');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={pageStyle.container}>
|
||||
<View style={pageItemStyle.container}>
|
||||
<Image style={signInStyle.headerImage} source={{ uri: useStore.getState().brandInfo.iconUri }} />
|
||||
</View>
|
||||
{this.state.loading ? (
|
||||
<View style={pageItemStyle.container}>
|
||||
<ActivityIndicator size="large" color={primaryColor()} animating={this.state.loading} />
|
||||
</View>
|
||||
) : (
|
||||
<View style={signInStyle.containerForm}>
|
||||
<View style={pageItemStyle.container}>
|
||||
<Text style={pageItemStyle.description}>{strings.signIn.description}</Text>
|
||||
</View>
|
||||
<View style={pageItemStyle.container}>
|
||||
<TextInput
|
||||
style={pageItemStyle.inputText}
|
||||
placeholder={strings.placeholders.username}
|
||||
autoComplete="email"
|
||||
autoCapitalize="none"
|
||||
autoFocus={true}
|
||||
keyboardType="email-address"
|
||||
textContentType="username"
|
||||
returnKeyType="next"
|
||||
value={this.state.email}
|
||||
onChangeText={text => this.setState({ email: text })}
|
||||
onSubmitEditing={() => this.passwordRef.current.focus()}
|
||||
/>
|
||||
</View>
|
||||
<View style={pageItemStyle.container}>
|
||||
<TextInput
|
||||
style={pageItemStyle.inputText}
|
||||
ref={this.passwordRef}
|
||||
placeholder={strings.placeholders.password}
|
||||
secureTextEntry={true}
|
||||
autoCapitalize="none"
|
||||
textContentType="password"
|
||||
returnKeyType="go"
|
||||
onChangeText={text => this.setState({ password: text })}
|
||||
onSubmitEditing={() => this.onSignInPress()}
|
||||
/>
|
||||
</View>
|
||||
<View style={pageItemStyle.containerButton}>
|
||||
<Text style={[pageItemStyle.buttonText, primaryColorStyle()]} onPress={this.onForgotPasswordPress}>
|
||||
{strings.buttons.forgotPassword}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={pageItemStyle.containerButton}>
|
||||
<Button title={strings.buttons.signIn} color={primaryColor()} onPress={this.onSignInPress} />
|
||||
</View>
|
||||
{/*<View style={pageItemStyle.containerButton}>
|
||||
<Button title={"Reset"}
|
||||
onPress={() => this.props.navigation.navigate("ResetPassword", {userId: this.state.email, password: this.state.password})} />
|
||||
</View>*/}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
onSignInPress = async () => {
|
||||
const onSignInPress = async () => {
|
||||
try {
|
||||
this.setState({ loading: true });
|
||||
setLoading(true);
|
||||
|
||||
// Make sure to clear any session information, this ensures error messaging is handled properly as well
|
||||
useStore.getState().clearSession();
|
||||
dispatch(clearSession());
|
||||
|
||||
const response = await authenticationApi.getAccessToken({
|
||||
userId: this.state.email,
|
||||
password: this.state.password,
|
||||
userId: email,
|
||||
password: password,
|
||||
});
|
||||
useStore.getState().setSession(response.data);
|
||||
dispatch(setSession(response.data));
|
||||
|
||||
// must reset password
|
||||
console.log(JSON.stringify(response.data, null, '\t'));
|
||||
if (response.data.userMustChangePassword) {
|
||||
this.props.navigation.navigate('ResetPassword', {
|
||||
userId: this.state.email,
|
||||
password: this.state.password,
|
||||
props.navigation.navigate('ResetPassword', {
|
||||
userId: email,
|
||||
password: password,
|
||||
});
|
||||
} else {
|
||||
// Update the system endpoints and navigate to the main view.
|
||||
this.getSystemEndpointsNavigateToMain();
|
||||
getSystemEndpointsNavigateToMain();
|
||||
}
|
||||
} catch (error) {
|
||||
// Clear the loading state
|
||||
this.setState({ loading: false });
|
||||
setLoading(false);
|
||||
|
||||
handleApiError(strings.errors.titleSignIn, error);
|
||||
}
|
||||
};
|
||||
|
||||
getSystemEndpointsNavigateToMain = async () => {
|
||||
const getSystemEndpointsNavigateToMain = async () => {
|
||||
try {
|
||||
// The system info is necessary before moving on to the next view as it'll provide
|
||||
// the endpoints needed for communicating with the other systems
|
||||
@@ -129,19 +66,74 @@ export default class SignIn extends Component {
|
||||
setApiSystemInfo(response.data);
|
||||
|
||||
// Replace to the main screen. Use replace to ensure no back button
|
||||
this.props.navigation.replace('Main');
|
||||
props.navigation.replace('Main');
|
||||
} catch (error) {
|
||||
// Make sure the loading state is done in all cases
|
||||
this.setState({ loading: false });
|
||||
setLoading(false);
|
||||
|
||||
handleApiError(strings.errors.titleSystemSetup, error);
|
||||
}
|
||||
};
|
||||
|
||||
onForgotPasswordPress = async () => {
|
||||
this.props.navigation.navigate('ForgotPassword');
|
||||
const onForgotPasswordPress = async () => {
|
||||
props.navigation.navigate('ForgotPassword');
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={pageStyle.container}>
|
||||
<View style={pageItemStyle.container}>
|
||||
<Image style={signInStyle.headerImage} source={{ uri: brandInfo.iconUri }} />
|
||||
</View>
|
||||
{loading ? (
|
||||
<View style={pageItemStyle.container}>
|
||||
<ActivityIndicator size="large" color={primaryColor} animating={loading} />
|
||||
</View>
|
||||
) : (
|
||||
<View style={signInStyle.containerForm}>
|
||||
<View style={pageItemStyle.container}>
|
||||
<Text style={pageItemStyle.description}>{strings.signIn.description}</Text>
|
||||
</View>
|
||||
<View style={pageItemStyle.container}>
|
||||
<TextInput
|
||||
style={pageItemStyle.inputText}
|
||||
placeholder={strings.placeholders.username}
|
||||
autoComplete="email"
|
||||
autoCapitalize="none"
|
||||
autoFocus={true}
|
||||
keyboardType="email-address"
|
||||
textContentType="username"
|
||||
returnKeyType="next"
|
||||
value={email}
|
||||
onChangeText={text => setEmail(text)}
|
||||
onSubmitEditing={() => passwordRef.current.focus()}
|
||||
/>
|
||||
</View>
|
||||
<View style={pageItemStyle.container}>
|
||||
<TextInput
|
||||
style={pageItemStyle.inputText}
|
||||
ref={passwordRef}
|
||||
placeholder={strings.placeholders.password}
|
||||
secureTextEntry={true}
|
||||
autoCapitalize="none"
|
||||
textContentType="password"
|
||||
returnKeyType="go"
|
||||
onChangeText={text => setPassword(text)}
|
||||
onSubmitEditing={() => onSignInPress()}
|
||||
/>
|
||||
</View>
|
||||
<View style={pageItemStyle.containerButton}>
|
||||
<Text style={[pageItemStyle.buttonText, primaryColorStyle]} onPress={onForgotPasswordPress}>
|
||||
{strings.buttons.forgotPassword}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={pageItemStyle.containerButton}>
|
||||
<Button title={strings.buttons.signIn} color={primaryColor} onPress={onSignInPress} />
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const signInStyle = StyleSheet.create({
|
||||
containerForm: {
|
||||
@@ -160,3 +152,5 @@ const signInStyle = StyleSheet.create({
|
||||
marginBottom: 10,
|
||||
},
|
||||
});
|
||||
|
||||
export default SignIn;
|
||||
|
||||
20
src/store/BrandInfoSlice.js
Normal file
20
src/store/BrandInfoSlice.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
export const brandInfoSlice = createSlice({
|
||||
name: 'brandInfo',
|
||||
initialState: {
|
||||
value: null,
|
||||
},
|
||||
reducers: {
|
||||
setBrandInfo: (state, action) => {
|
||||
state.value = action.payload;
|
||||
},
|
||||
clearBrandInfo: state => {
|
||||
state.value -= null;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const selectBrandInfo = state => state.brandInfo.value;
|
||||
export const { setBrandInfo, clearBrandInfo } = brandInfoSlice.actions;
|
||||
export default brandInfoSlice.reducer;
|
||||
20
src/store/SessionSlice.js
Normal file
20
src/store/SessionSlice.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
export const sessionSlice = createSlice({
|
||||
name: 'session',
|
||||
initialState: {
|
||||
value: null,
|
||||
},
|
||||
reducers: {
|
||||
setSession: (state, action) => {
|
||||
state.value = action.payload;
|
||||
},
|
||||
clearSession: state => {
|
||||
state.value -= null;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const selectSession = state => state.session.value;
|
||||
export const { setSession, clearSession } = sessionSlice.actions;
|
||||
export default sessionSlice.reducer;
|
||||
16
src/store/Store.js
Normal file
16
src/store/Store.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import brandInfoReducer from './BrandInfoSlice';
|
||||
import sessionReducer from './SessionSlice';
|
||||
import systemInfoReducer from './SystemInfoSlice';
|
||||
|
||||
export default configureStore({
|
||||
reducer: {
|
||||
brandInfo: brandInfoReducer,
|
||||
session: sessionReducer,
|
||||
systemInfo: systemInfoReducer,
|
||||
},
|
||||
middleware: getDefaultMiddleware =>
|
||||
getDefaultMiddleware({
|
||||
serializableCheck: false,
|
||||
}),
|
||||
});
|
||||
20
src/store/SystemInfoSlice.js
Normal file
20
src/store/SystemInfoSlice.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
export const systemInfoSlice = createSlice({
|
||||
name: 'systemInfo',
|
||||
initialState: {
|
||||
value: null,
|
||||
},
|
||||
reducers: {
|
||||
setSystemInfo: (state, action) => {
|
||||
state.value = action.payload;
|
||||
},
|
||||
clearSystemInfo: state => {
|
||||
state.value -= null;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const selectSystemInfo = state => state.systemInfo.value;
|
||||
export const { setSystemInfo, clearSystemInfo } = systemInfoSlice.actions;
|
||||
export default systemInfoSlice.reducer;
|
||||
Reference in New Issue
Block a user