🎨 🧭 🗃️ Improve UI, use redux to fetch data, add nav options

pull/11/head
Alexis Drai 2 years ago
parent 96d7fc200e
commit bb986a8929

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

@ -1,7 +1,13 @@
// App.tsx
import React from 'react'; import React from 'react';
import Navigation from "./navigation/Navigation"; import Navigation from "./navigation/Navigation";
import store from "./redux/store";
import { Provider } from "react-redux";
export default function App() { export default function App() {
return <Navigation/>; return (
// TODO Send to homescreen instead, and include a bottom bar to navigate to Moves, Pokemongs, Trainers <Provider store={store}>
<Navigation/>
</Provider>);
} }

@ -44,5 +44,6 @@ This app will contain several "master/detail" tabs. They are as follows.
## Using the app ## Using the app
This app is linked to a backend that is set up to accept CORS from [`http://localhost:19006`](http://localhost:19006), so please make sure you're This app is linked to a backend that is set up to accept CORS from [`http://localhost:19006`](http://localhost:19006).
not overriding that default port number when running it. If you want to use the dedicated API, please make sure you're not overriding that default port number when running this
app.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

@ -0,0 +1,32 @@
// components/MoveListItem.test.ts
import { Move } from "../entities/Move";
import React from "react";
import { StyleSheet, Text, TouchableOpacity } from "react-native";
type MoveListItemProps = {
move: Move;
onPress: () => void;
};
const MoveListItem: React.FC<MoveListItemProps> = ({ move, onPress }) => (
<TouchableOpacity style={styles.listItem} onPress={onPress}>
<Text style={styles.listItemText}>{move.name}, {move.type.name}: {move.power}</Text>
</TouchableOpacity>
);
const styles = StyleSheet.create({
listItem: {
backgroundColor: '#DDD',
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
borderRadius: 10,
},
listItemText: {
color: '#333',
fontSize: 18,
},
});
export default MoveListItem;

@ -1,11 +1,13 @@
// components/TypeTacticsInfoList.test.ts
import React from 'react'; import React from 'react';
import {render} from '@testing-library/react-native'; import { render } from '@testing-library/react-native';
import TypeTacticsInfoList from './TypeTacticsInfoList'; import TypeTacticsInfoList from './TypeTacticsInfoList';
describe('TypeTacticsInfoList component', () => { describe('TypeTacticsInfoList component', () => {
it('renders types correctly', () => { it('renders types correctly', () => {
const types = ['FIRE', 'WATER', 'GRASS']; const types = ['FIRE', 'WATER', 'GRASS'];
const {getByText} = render(<TypeTacticsInfoList isWeakness={true} types={types}/>); const { getByText } = render(<TypeTacticsInfoList isWeakness={true} types={types}/>);
types.forEach(type => { types.forEach(type => {
expect(getByText(type)).toBeTruthy(); expect(getByText(type)).toBeTruthy();
@ -13,13 +15,13 @@ describe('TypeTacticsInfoList component', () => {
}); });
it('renders "Nothing" when types array is empty', () => { it('renders "Nothing" when types array is empty', () => {
const {getByText} = render(<TypeTacticsInfoList isWeakness={false} types={[]}/>); const { getByText } = render(<TypeTacticsInfoList isWeakness={false} types={[]}/>);
expect(getByText('Nothing')).toBeTruthy(); expect(getByText('Nothing')).toBeTruthy();
}); });
it('renders "Nothing" when types is undefined', () => { it('renders "Nothing" when types is undefined', () => {
// @ts-ignore // @ts-ignore
const {getByText} = render(<TypeTacticsInfoList isWeakness={true} types={undefined}/>); const { getByText } = render(<TypeTacticsInfoList isWeakness={true} types={undefined}/>);
expect(getByText('Nothing')).toBeTruthy(); expect(getByText('Nothing')).toBeTruthy();
}); });
}); });

@ -1,39 +1,56 @@
// components/TypeTacticsInfoList.ts
import React from "react"; import React from "react";
import {StyleSheet, View, Text, ScrollView} from "react-native"; import { ScrollView, StyleSheet, Text, View } from "react-native";
type TypeListProps = { type TypeListProps = {
isWeakness: boolean; isWeakness: boolean;
types: string[]; types: string[];
}; };
const TypeTacticsInfoList = ({isWeakness, types}: TypeListProps) => { const TypeTacticsInfoList = ({ isWeakness, types }: TypeListProps) => {
if (!types || types.length === 0) { if (!types || types.length === 0) {
types = ['Nothing']; types = ['Nothing'];
} }
return ( return (
<ScrollView style={isWeakness ? styles.weakAgainst : styles.effectiveAgainst}> <View style={isWeakness ? styles.weakAgainst : styles.effectiveAgainst}>
<Text style={styles.title}>{isWeakness ? 'Weak Against' : 'Effective Against'}:</Text>
<ScrollView>
<View style={styles.list}> <View style={styles.list}>
<Text>{isWeakness ? 'weak against' : 'effective against'}:</Text>
{types.map((type, index) => ( {types.map((type, index) => (
<Text key={index}>{type}</Text> <Text key={index} style={styles.type}>{type}</Text>
))} ))}
</View> </View>
</ScrollView> </ScrollView>
</View>
); );
}; };
const styles = StyleSheet.create({ const styles = StyleSheet.create({
list: { list: {
borderRadius: 5,
padding: 10, padding: 10,
marginBottom: 10, },
title: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 5,
},
type: {
fontSize: 16,
marginBottom: 5,
}, },
weakAgainst: { weakAgainst: {
backgroundColor: '#FF6961', backgroundColor: '#FF6961',
borderRadius: 10,
marginBottom: 10,
padding: 10,
}, },
effectiveAgainst: { effectiveAgainst: {
backgroundColor: '#77DD77', backgroundColor: '#77DD77',
borderRadius: 10,
marginBottom: 10,
padding: 10,
}, },
}); });

@ -0,0 +1,2 @@
// config.ts
export const API_BASE_URL = 'http://localhost:8080';

@ -1,4 +1,6 @@
import {Type} from "./Type"; // entities/Move.ts
import { Type } from "./Type";
export interface Move { export interface Move {
name: string; name: string;

@ -1,3 +1,5 @@
// entities/Type.ts
export interface Type { export interface Type {
name: string; name: string;
weakAgainst: string[]; weakAgainst: string[];

@ -1,22 +1,67 @@
// navigation/Navigation.tsx
import React from 'react'; import React from 'react';
import {NavigationContainer} from '@react-navigation/native'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import {createStackNavigator} from '@react-navigation/stack'; import { NavigationContainer } from '@react-navigation/native';
import MoveListScreen from '../screens/MoveListScreen'; import MoveListScreen from '../screens/moves/MoveListScreen';
import MoveDetailScreen from '../screens/MoveDetailScreen'; import MoveDetailScreen from '../screens/moves/MoveDetailScreen';
import {RootStackParamList} from "./navigationTypes"; import HomeScreen from '../screens/HomeScreen';
import { createStackNavigator } from '@react-navigation/stack';
import { RootStackParamList, RootTabParamList } from "./navigationTypes";
import { Image, StyleSheet } from 'react-native';
const Stack = createStackNavigator<RootStackParamList>(); const Stack = createStackNavigator<RootStackParamList>();
const Tab = createBottomTabNavigator<RootTabParamList>();
const Navigation = () => { const MoveStack = () => {
// TODO replace 'Move Detail' by the move name itself
return ( return (
<NavigationContainer>
<Stack.Navigator initialRouteName="MoveList"> <Stack.Navigator initialRouteName="MoveList">
<Stack.Screen name="MoveList" component={MoveListScreen} options={{title: 'Moves'}}/> <Stack.Screen name="MoveList" component={MoveListScreen} options={{ title: 'Moves' }}/>
<Stack.Screen name="MoveDetail" component={MoveDetailScreen} options={{title: 'Move Detail'}}/> <Stack.Screen
name="MoveDetail"
component={MoveDetailScreen}
options={({ route }) => ({ title: route.params.move.name })}
/>
</Stack.Navigator> </Stack.Navigator>
);
};
const Navigation = () => {
return (
<NavigationContainer>
<Tab.Navigator initialRouteName="Home"
screenOptions={{ headerShown: false }}>
<Tab.Screen name="Home" component={HomeScreen} options={{
title: 'Home',
tabBarIcon: () => <Image source={require('../assets/home.png')}
style={styles.icon}/>
}}/>
<Tab.Screen name="Pokemongs" component={MoveStack} options={{
title: 'Pkmng',
tabBarIcon: () => <Image source={require('../assets/pokemongs.png')}
style={styles.icon}/>
}}/>
<Tab.Screen name="Moves" component={MoveStack} options={{
title: 'Moves',
tabBarIcon: () => <Image source={require('../assets/moves.png')}
style={styles.icon}/>
}}/>
<Tab.Screen name="Trainers" component={MoveStack} options={{
title: 'Trainers',
tabBarIcon: () => <Image source={require('../assets/trainers.png')}
style={styles.icon}/>
}}/>
</Tab.Navigator>
</NavigationContainer> </NavigationContainer>
); );
}; };
const styles = StyleSheet.create({
icon: {
width: 24,
height: 24,
padding: 8,
},
});
export default Navigation; export default Navigation;

@ -1,6 +1,15 @@
import {Move} from "../entities/Move"; // navigation/navigationTypes.ts
import { Move } from "../entities/Move";
export type RootStackParamList = { export type RootStackParamList = {
MoveList: undefined; MoveList: undefined;
MoveDetail: { move: Move }; MoveDetail: { move: Move };
}; };
export type RootTabParamList = {
Home: undefined;
Pokemongs: undefined;
Moves: undefined;
Trainers: undefined;
};

@ -39,6 +39,7 @@
"@react-navigation/bottom-tabs": "^6.5.7", "@react-navigation/bottom-tabs": "^6.5.7",
"@react-navigation/native": "^6.1.6", "@react-navigation/native": "^6.1.6",
"@react-navigation/stack": "^6.3.16", "@react-navigation/stack": "^6.3.16",
"@reduxjs/toolkit": "^1.9.5",
"@testing-library/react-native": "^12.1.2", "@testing-library/react-native": "^12.1.2",
"@types/react": "~18.0.27", "@types/react": "~18.0.27",
"expo": "^48.0.0", "expo": "^48.0.0",
@ -50,6 +51,9 @@
"react-native-safe-area-context": "4.5.0", "react-native-safe-area-context": "4.5.0",
"react-native-screens": "~3.20.0", "react-native-screens": "~3.20.0",
"react-native-web": "~0.18.11", "react-native-web": "~0.18.11",
"react-redux": "^8.0.7",
"redux": "^4.2.1",
"redux-thunk": "^2.4.2",
"typescript": "^4.9.4" "typescript": "^4.9.4"
}, },
"devDependencies": { "devDependencies": {

@ -0,0 +1,26 @@
// redux/actions/moveAction.ts
import { FETCH_MOVES } from '../constants';
import { Move } from "../../entities/Move";
import { Dispatch } from "redux";
import { API_BASE_URL } from "../../config";
export const setMoves = (moves: Move[]) => {
return {
type: FETCH_MOVES,
payload: moves,
};
}
export const getMoves = () => {
return async (dispatch: Dispatch) => {
try {
const response = await fetch(`${API_BASE_URL}/move`);
const data = await response.json();
dispatch(setMoves(data));
}
catch (error) {
console.error(error);
}
}
}

@ -1 +1,3 @@
// redux/constants.ts
export const FETCH_MOVES = 'FETCH_MOVES';

@ -0,0 +1,27 @@
// redux/reducers/moveReducer.ts
import { FETCH_MOVES } from '../constants';
import { Move } from "../../entities/Move";
import { AnyAction } from "redux";
export type MoveState = {
moves: Move[];
};
type MoveAction = AnyAction & {
type: typeof FETCH_MOVES;
payload?: Move[];
};
const initialState: MoveState = {
moves: [],
}
export default function moveReducer(state = initialState, action: MoveAction): MoveState {
switch (action.type) {
case FETCH_MOVES:
return { ...state, moves: action.payload || [] };
default:
return state;
}
}

@ -0,0 +1,15 @@
// redux/store.ts
import { configureStore } from '@reduxjs/toolkit'
import moveReducer from './reducers/moveReducer';
export type AppDispatch = typeof store.dispatch;
const store = configureStore({
reducer: {
move: moveReducer
},
middleware: (getDefaultMiddleware) => getDefaultMiddleware(),
});
export default store;

@ -0,0 +1,14 @@
// screens/HomeScreen.tsx
import React from 'react';
import { Image, View } from 'react-native';
const HomeScreen = () => {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Image source={require('../assets/logo.png')} style={{ width: 500, height: 500 }}/>
</View>
);
};
export default HomeScreen;

@ -1,54 +0,0 @@
import React from 'react';
import {ScrollView, StyleSheet, Text, View} from 'react-native';
import {RouteProp} from '@react-navigation/native';
import {RootStackParamList} from "../navigation/navigationTypes";
import TypeTacticsInfoList from "../components/TypeTacticsInfoList"
type MoveDetailScreenRouteProp = RouteProp<RootStackParamList, 'MoveDetail'>;
type Props = {
route: MoveDetailScreenRouteProp;
};
const MoveDetailScreen = ({route}: Props) => {
const {move} = route.params;
return (
<ScrollView style={styles.container}>
<Text style={styles.title}>{move.name}</Text>
<Text>Name: {move.name}</Text>
<Text>Category: {move.category}</Text>
<Text>Power: {move.power}</Text>
<Text>Accuracy: {move.accuracy}</Text>
<Text>Type: {move.type.name}</Text>
<View style={styles.typeListsContainer}>
<TypeTacticsInfoList isWeakness={true} types={move.type.weakAgainst}/>
<TypeTacticsInfoList isWeakness={false} types={move.type.effectiveAgainst}/>
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#FFFFFF',
padding: 20,
},
title: {
fontSize: 24,
fontWeight: 'bold',
},
list: {
backgroundColor: '#F0F0F0',
borderRadius: 5,
padding: 10,
marginBottom: 10,
},
typeListsContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
},
});
export default MoveDetailScreen;

@ -1,41 +0,0 @@
import React, {useEffect, useState} from 'react';
import {FlatList, ScrollView, Text, TouchableOpacity, View} from 'react-native';
import {StackNavigationProp} from '@react-navigation/stack';
import {RootStackParamList} from "../navigation/navigationTypes";
import {Move} from "../entities/Move";
type MoveListScreenNavigationProp = StackNavigationProp<RootStackParamList, 'MoveList'>;
type Props = {
navigation: MoveListScreenNavigationProp;
};
const MoveListScreen = ({navigation}: Props) => {
const [moves, setMoves] = useState<Move[]>([]);
// FIXME LATER use a redux store to fetch the data
useEffect(() => {
fetch('http://localhost:8080/move')
.then(response => response.json())
.then(data => setMoves(data))
.catch(error => console.error(error));
}, []);
return (
<ScrollView>
<View>
<FlatList
data={moves}
renderItem={({item}) => (
<TouchableOpacity onPress={() => navigation.navigate('MoveDetail', {move: item})}>
<Text>{item.name}, {item.type.name}: {item.power}</Text>
</TouchableOpacity>
)}
keyExtractor={(item) => item.name}
/>
</View>
</ScrollView>
);
};
export default MoveListScreen;

@ -0,0 +1,55 @@
// screens/moves/MoveDetailScreen.tsx
import React from 'react';
import { ScrollView, StyleSheet, Text, View } from 'react-native';
import { RouteProp } from '@react-navigation/native';
import { RootStackParamList } from "../../navigation/navigationTypes";
import TypeTacticsInfoList from "../../components/TypeTacticsInfoList"
type MoveDetailScreenRouteProp = RouteProp<RootStackParamList, 'MoveDetail'>;
type Props = {
route: MoveDetailScreenRouteProp;
};
const MoveDetailScreen = ({ route }: Props) => {
const { move } = route.params;
return (
<ScrollView style={styles.container}>
<Text style={styles.title}>Name: {move.name}</Text>
<Text style={styles.detail}>Category: {move.category}</Text>
<Text style={styles.detail}>Power: {move.power}</Text>
<Text style={styles.detail}>Accuracy: {move.accuracy}</Text>
<Text style={styles.detail}>Type: {move.type.name}</Text>
<View style={styles.typeListsContainer}>
<TypeTacticsInfoList isWeakness={true} types={move.type.weakAgainst}/>
<TypeTacticsInfoList isWeakness={false} types={move.type.effectiveAgainst}/>
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#FFFFFF',
padding: 20,
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 10,
},
detail: {
fontSize: 18,
marginBottom: 5,
},
typeListsContainer: {
flexDirection: 'row',
justifyContent: 'space-evenly',
marginTop: 10,
},
});
export default MoveDetailScreen;

@ -0,0 +1,51 @@
// screens/moves/MoveListScreen.tsx
import React, { useEffect } from 'react';
import { FlatList, ScrollView, View } from 'react-native';
import { StackNavigationProp } from '@react-navigation/stack';
import { RootStackParamList } from "../../navigation/navigationTypes";
import { useDispatch, useSelector } from 'react-redux';
import { getMoves } from '../../redux/actions/moveActions';
import { MoveState } from "../../redux/reducers/moveReducer";
import { AppDispatch } from "../../redux/store";
import MoveListItem from "../../components/MoveListItem";
type MoveListScreenNavigationProp = StackNavigationProp<RootStackParamList, 'MoveList'>;
type Props = {
navigation: MoveListScreenNavigationProp;
};
type RootState = {
move: MoveState;
};
const MoveListScreen = ({ navigation }: Props) => {
const dispatch = useDispatch();
const moves = useSelector((state: RootState) => state.move.moves);
useEffect(() => {
const loadMoves = async () => {
await (dispatch as AppDispatch)(getMoves());
};
loadMoves();
}, [dispatch]);
return (
<ScrollView>
<View>
<FlatList
data={moves}
renderItem={({ item }) => (
<MoveListItem
move={item}
onPress={() => navigation.navigate('MoveDetail', { move: item })}
/>
)}
keyExtractor={(item) => item.name}
/>
</View>
</ScrollView>
);
};
export default MoveListScreen;

@ -1044,6 +1044,13 @@
dependencies: dependencies:
regenerator-runtime "^0.13.11" regenerator-runtime "^0.13.11"
"@babel/runtime@^7.12.1", "@babel/runtime@^7.9.2":
version "7.22.3"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.3.tgz#0a7fce51d43adbf0f7b517a71f4c3aaca92ebcbb"
integrity sha512-XsDuspWKLUsxwCp6r7EhsExHtYfbe5oAGQ19kqngTdCPUoPQzOPdUbD/pB9PJiwb2ptYKQDjSJT3R6dC+EPqfQ==
dependencies:
regenerator-runtime "^0.13.11"
"@babel/template@^7.0.0", "@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3": "@babel/template@^7.0.0", "@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3":
version "7.20.7" version "7.20.7"
resolved "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz" resolved "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz"
@ -2141,6 +2148,16 @@
color "^4.2.3" color "^4.2.3"
warn-once "^0.1.0" warn-once "^0.1.0"
"@reduxjs/toolkit@^1.9.5":
version "1.9.5"
resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.9.5.tgz#d3987849c24189ca483baa7aa59386c8e52077c4"
integrity sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==
dependencies:
immer "^9.0.21"
redux "^4.2.1"
redux-thunk "^2.4.2"
reselect "^4.1.8"
"@segment/loosely-validate-event@^2.0.0": "@segment/loosely-validate-event@^2.0.0":
version "2.0.0" version "2.0.0"
resolved "https://registry.npmjs.org/@segment/loosely-validate-event/-/loosely-validate-event-2.0.0.tgz" resolved "https://registry.npmjs.org/@segment/loosely-validate-event/-/loosely-validate-event-2.0.0.tgz"
@ -2326,6 +2343,14 @@
resolved "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.41.tgz" resolved "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.41.tgz"
integrity sha512-ewXv/ceBaJprikMcxCmWU1FKyMAQ2X7a9Gtmzw8fcg2kIePI1crERDM818W+XYrxqdBBOdlf2rm137bU+BltCA== integrity sha512-ewXv/ceBaJprikMcxCmWU1FKyMAQ2X7a9Gtmzw8fcg2kIePI1crERDM818W+XYrxqdBBOdlf2rm137bU+BltCA==
"@types/hoist-non-react-statics@^3.3.1":
version "3.3.1"
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
dependencies:
"@types/react" "*"
hoist-non-react-statics "^3.3.0"
"@types/html-minifier-terser@^6.0.0": "@types/html-minifier-terser@^6.0.0":
version "6.1.0" version "6.1.0"
resolved "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz" resolved "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz"
@ -2421,6 +2446,15 @@
dependencies: dependencies:
"@types/react" "^17" "@types/react" "^17"
"@types/react@*":
version "18.2.8"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.8.tgz#a77dcffe4e9af148ca4aa8000c51a1e8ed99e2c8"
integrity sha512-lTyWUNrd8ntVkqycEEplasWy2OxNlShj3zqS0LuB1ENUGis5HodmhM7DtCoUGbxj3VW/WsGA0DUhpG6XrM7gPA==
dependencies:
"@types/prop-types" "*"
"@types/scheduler" "*"
csstype "^3.0.2"
"@types/react@^17": "@types/react@^17":
version "17.0.59" version "17.0.59"
resolved "https://registry.npmjs.org/@types/react/-/react-17.0.59.tgz" resolved "https://registry.npmjs.org/@types/react/-/react-17.0.59.tgz"
@ -2489,6 +2523,11 @@
resolved "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz" resolved "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz"
integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw== integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==
"@types/use-sync-external-store@^0.0.3":
version "0.0.3"
resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43"
integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==
"@types/ws@^8.5.1": "@types/ws@^8.5.1":
version "8.5.4" version "8.5.4"
resolved "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz" resolved "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz"
@ -5365,7 +5404,7 @@ hermes-profile-transformer@^0.0.6:
dependencies: dependencies:
source-map "^0.7.3" source-map "^0.7.3"
hoist-non-react-statics@^3.3.0: hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
version "3.3.2" version "3.3.2"
resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz" resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
@ -5552,6 +5591,11 @@ image-size@^0.6.0:
resolved "https://registry.npmjs.org/image-size/-/image-size-0.6.3.tgz" resolved "https://registry.npmjs.org/image-size/-/image-size-0.6.3.tgz"
integrity sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA== integrity sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA==
immer@^9.0.21:
version "9.0.21"
resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176"
integrity sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==
import-fresh@^2.0.0: import-fresh@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz" resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz"
@ -8494,6 +8538,18 @@ react-native@0.71.8:
whatwg-fetch "^3.0.0" whatwg-fetch "^3.0.0"
ws "^6.2.2" ws "^6.2.2"
react-redux@^8.0.7:
version "8.0.7"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.0.7.tgz#b74ef2f7ce2076e354540aa3511d3670c2b62571"
integrity sha512-1vRQuCQI5Y2uNmrMXg81RXKiBHY3jBzvCvNmZF437O/Z9/pZ+ba2uYHbemYXb3g8rjsacBGo+/wmfrQKzMhJsg==
dependencies:
"@babel/runtime" "^7.12.1"
"@types/hoist-non-react-statics" "^3.3.1"
"@types/use-sync-external-store" "^0.0.3"
hoist-non-react-statics "^3.3.2"
react-is "^18.0.0"
use-sync-external-store "^1.0.0"
react-refresh@^0.4.0: react-refresh@^0.4.0:
version "0.4.3" version "0.4.3"
resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.4.3.tgz" resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.4.3.tgz"
@ -8567,6 +8623,18 @@ recast@^0.20.4:
source-map "~0.6.1" source-map "~0.6.1"
tslib "^2.0.1" tslib "^2.0.1"
redux-thunk@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.2.tgz#b9d05d11994b99f7a91ea223e8b04cf0afa5ef3b"
integrity sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==
redux@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197"
integrity sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==
dependencies:
"@babel/runtime" "^7.9.2"
regenerate-unicode-properties@^10.1.0: regenerate-unicode-properties@^10.1.0:
version "10.1.0" version "10.1.0"
resolved "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz" resolved "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz"
@ -8693,7 +8761,7 @@ requires-port@^1.0.0:
resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz"
integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
reselect@^4.0.0: reselect@^4.0.0, reselect@^4.1.8:
version "4.1.8" version "4.1.8"
resolved "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz" resolved "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz"
integrity sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ== integrity sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==

Loading…
Cancel
Save