@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
@ -1,7 +1,13 @@
|
||||
import React from 'react';
|
||||
import Navigation from "./navigation/Navigation";
|
||||
// App.tsx
|
||||
|
||||
import React from 'react';
|
||||
import Navigation from "./navigation/Navigation";
|
||||
import store from "./redux/store";
|
||||
import { Provider } from "react-redux";
|
||||
|
||||
export default function App() {
|
||||
return <Navigation/>;
|
||||
// TODO Send to homescreen instead, and include a bottom bar to navigate to Moves, Pokemongs, Trainers
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<Navigation/>
|
||||
</Provider>);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 79 KiB |
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 46 KiB |
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;
|
@ -0,0 +1,2 @@
|
||||
// config.ts
|
||||
export const API_BASE_URL = 'http://localhost:8080';
|
@ -1,22 +1,67 @@
|
||||
import React from 'react';
|
||||
import {NavigationContainer} from '@react-navigation/native';
|
||||
import {createStackNavigator} from '@react-navigation/stack';
|
||||
import MoveListScreen from '../screens/MoveListScreen';
|
||||
import MoveDetailScreen from '../screens/MoveDetailScreen';
|
||||
import {RootStackParamList} from "./navigationTypes";
|
||||
// navigation/Navigation.tsx
|
||||
|
||||
import React from 'react';
|
||||
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
||||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import MoveListScreen from '../screens/moves/MoveListScreen';
|
||||
import MoveDetailScreen from '../screens/moves/MoveDetailScreen';
|
||||
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 Tab = createBottomTabNavigator<RootTabParamList>();
|
||||
|
||||
const MoveStack = () => {
|
||||
return (
|
||||
<Stack.Navigator initialRouteName="MoveList">
|
||||
<Stack.Screen name="MoveList" component={MoveListScreen} options={{ title: 'Moves' }}/>
|
||||
<Stack.Screen
|
||||
name="MoveDetail"
|
||||
component={MoveDetailScreen}
|
||||
options={({ route }) => ({ title: route.params.move.name })}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
const Navigation = () => {
|
||||
// TODO replace 'Move Detail' by the move name itself
|
||||
return (
|
||||
<NavigationContainer>
|
||||
<Stack.Navigator initialRouteName="MoveList">
|
||||
<Stack.Screen name="MoveList" component={MoveListScreen} options={{title: 'Moves'}}/>
|
||||
<Stack.Screen name="MoveDetail" component={MoveDetailScreen} options={{title: 'Move Detail'}}/>
|
||||
</Stack.Navigator>
|
||||
<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>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
icon: {
|
||||
width: 24,
|
||||
height: 24,
|
||||
padding: 8,
|
||||
},
|
||||
});
|
||||
|
||||
export default Navigation;
|
||||
|
@ -1,6 +1,15 @@
|
||||
import {Move} from "../entities/Move";
|
||||
// navigation/navigationTypes.ts
|
||||
|
||||
import { Move } from "../entities/Move";
|
||||
|
||||
export type RootStackParamList = {
|
||||
MoveList: undefined;
|
||||
MoveDetail: { move: Move };
|
||||
};
|
||||
|
||||
export type RootTabParamList = {
|
||||
Home: undefined;
|
||||
Pokemongs: undefined;
|
||||
Moves: undefined;
|
||||
Trainers: undefined;
|
||||
};
|
||||
|
@ -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;
|