🤖 Fix #14: Make this app work with Android (#21)

Co-authored-by: Alexis DRAI <alexis.drai@etu.uca.fr>
Reviewed-on: #21
main
Alexis Drai 2 years ago
parent cc86eb36c9
commit faf7d8c34b

@ -1,9 +1,9 @@
// App.tsx // 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 store from "./redux/store";
import { Provider } from "react-redux"; import { Provider } from "react-redux";
import { SafeAreaProvider } from "react-native-safe-area-context"; import { SafeAreaProvider } from "react-native-safe-area-context";
export default function App() { export default function App() {

@ -60,31 +60,47 @@ This app will contain a home page, and a "master/detail" tab for `Moves` with ba
The home screen provides a logo, and tab navigation options to other parts of the application. The home screen provides a logo, and tab navigation options to other parts of the application.
<img src="./docs/home.png" width="410" style="margin:20px"> <img src="./docs/home.png" width="410" style="margin:20px" alt="">
### Collection ### Collection
The collection screen displays a list of `Moves` fetched from the API. The collection screen displays a list of `Moves` fetched from the API.
<img src="./docs/moves.png" width="410" style="margin:20px"> <img src="./docs/moves.png" width="410" style="margin:20px" alt="">>
### Detail ### Detail
The detail screen displays detailed information about a selected `Move`. The detail screen displays detailed information about a selected `Move`.
<img src="./docs/move.png" width="410" style="margin:20px"> <img src="./docs/move.png" width="410" style="margin:20px" alt="">>
### Creating ### Creating
The creating screen provides a form for creating a new `Move`. The creating screen provides a form for creating a new `Move`.
<img src="./docs/create.png" width="410" style="margin:20px"> <img src="./docs/create.png" width="410" style="margin:20px" alt="">>
### Updating ### Updating
The updating screen provides a form for updating an existing `Move`. The updating screen provides a form for updating an existing `Move`.
<img src="./docs/update.png" width="410" style="margin:20px"> <img src="./docs/update.png" width="410" style="margin:20px" alt="">>
## Some business logic
While the back end doesn't forbid a `Move` from being both weak against and effective against the same one type,
it should. That's one of the flaws of that API.
In this app, we did implement the business logic described above in our callbacks for our `MultiSelect` elements, like
so:
```typescript
const handleSelectType = (selectedTypes: string[], setTypes: React.Dispatch<React.SetStateAction<string[]>>, otherSelectedTypes: string[]) => {
const uniqueSelectedTypes = Array.from(new Set(selectedTypes));
const withoutDuplicatesFromOtherColumn = uniqueSelectedTypes.filter(type => !otherSelectedTypes.includes(type));
setTypes(withoutDuplicatesFromOtherColumn);
};
```
## Using the app ## Using the app
@ -94,9 +110,16 @@ In order to use this app, you will need to run the dedicated backend. A `README`
with [instructions](https://github.com/draialexis/pokemong_api#user-content-prep-steps) is provided with [instructions](https://github.com/draialexis/pokemong_api#user-content-prep-steps) is provided
for that purpose. for that purpose.
### Connecting to the backend locally
First, please find the `config.ts` file at the root of this project, and replace ~~`192.168.0.15`~~
with the IPv4 address associated with your own Wi-Fi adapter.
To find that address out, you can run `ipconfig` on Windows or `ifconfig` on macOS/Linux in your terminal.
### Running this app ### Running this app
With the [Expo CLI](https://docs.expo.dev/more/expo-cli/) installed, at the root of the project, simply run Then, with the [Expo CLI](https://docs.expo.dev/more/expo-cli/) installed, at the root of the project, simply run
```bash ```bash
npx expo start npx expo start

@ -1,6 +1,6 @@
// components/AlertModal.tsx // components/AlertModal.tsx
import { Button, Modal, StyleSheet, Text, View } from 'react-native'; import { Button, Modal, StyleSheet, Text, View } from 'react-native';
import React from 'react'; import React from 'react';
type AlertModalProps = { type AlertModalProps = {
visible: boolean; visible: boolean;

@ -1,7 +1,7 @@
// components/MoveListItem.test.ts // components/MoveListItem.test.ts
import { Move } from "../entities/Move"; import { Move } from "../entities/Move";
import React from "react"; import React from "react";
import { StyleSheet, Text, TouchableOpacity, ViewStyle } from "react-native"; import { StyleSheet, Text, TouchableOpacity, ViewStyle } from "react-native";
type MoveListItemProps = { type MoveListItemProps = {

@ -1,9 +1,9 @@
// components/TypeTacticsInfoList.test.ts // 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';
import { TypeName } from "../entities/TypeName"; import { TypeName } from "../entities/TypeName";
describe('TypeTacticsInfoList component', () => { describe('TypeTacticsInfoList component', () => {
it('renders types correctly', () => { it('renders types correctly', () => {

@ -1,6 +1,6 @@
// components/TypeTacticsInfoList.ts // components/TypeTacticsInfoList.ts
import React from "react"; import React from "react";
import { ScrollView, StyleSheet, Text, View } from "react-native"; import { ScrollView, StyleSheet, Text, View } from "react-native";
type TypeListProps = { type TypeListProps = {

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

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

@ -1,15 +1,15 @@
// navigation/Navigation.tsx // navigation/Navigation.tsx
import React from 'react'; import React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { NavigationContainer } from '@react-navigation/native'; import { NavigationContainer } from '@react-navigation/native';
import MoveListScreen from '../screens/moves/MoveListScreen'; import MoveListScreen from '../screens/moves/MoveListScreen';
import MoveDetailScreen from '../screens/moves/MoveDetailScreen'; import MoveDetailScreen from '../screens/moves/MoveDetailScreen';
import HomeScreen from '../screens/HomeScreen'; import HomeScreen from '../screens/HomeScreen';
import { createStackNavigator } from '@react-navigation/stack'; import { createStackNavigator } from '@react-navigation/stack';
import { RootStackParamList, RootTabParamList } from "./navigationTypes"; import { RootStackParamList, RootTabParamList } from "./navigationTypes";
import { Image, StyleSheet } from 'react-native'; import { Image, StyleSheet } from 'react-native';
import MoveFormScreen from "../screens/moves/MoveFormScreen"; import MoveFormScreen from "../screens/moves/MoveFormScreen";
const Stack = createStackNavigator<RootStackParamList>(); const Stack = createStackNavigator<RootStackParamList>();
const Tab = createBottomTabNavigator<RootTabParamList>(); const Tab = createBottomTabNavigator<RootTabParamList>();

@ -1,10 +1,8 @@
// redux/actions/moveAction.ts // redux/actions/moveAction.ts
import { CREATE_MOVE, DELETE, DELETE_MOVE, GET, GET_MOVES, MOVE_ERROR, POST, PUT, UPDATE_MOVE } from '../constants'; import { CREATE_MOVE, DELETE, DELETE_MOVE, GET, GET_MOVES, MOVE_ERROR, POST, PUT, UPDATE_MOVE } from '../constants';
import { import { Move } from "../../entities/Move";
Move import { Dispatch } from "redux";
} from "../../entities/Move"; import { API_BASE_URL } from "../../config";
import { Dispatch } from "redux";
import { API_BASE_URL } from "../../config";
export const createMove = (move: Move) => { export const createMove = (move: Move) => {

@ -1,6 +1,6 @@
// redux/reducers/moveReducer.ts // redux/reducers/moveReducer.ts
import { CREATE_MOVE, DELETE_MOVE, GET_MOVES, MOVE_ERROR, UPDATE_MOVE } from '../constants'; import { CREATE_MOVE, DELETE_MOVE, GET_MOVES, MOVE_ERROR, UPDATE_MOVE } from '../constants';
import { Move } from "../../entities/Move"; import { Move } from "../../entities/Move";
export type MoveState = { export type MoveState = {
moves: Move[]; moves: Move[];

@ -1,6 +1,6 @@
// redux/store.ts // redux/store.ts
import { configureStore } from '@reduxjs/toolkit' import { configureStore } from '@reduxjs/toolkit'
import moveReducer, { MoveState } from './reducers/moveReducer'; import moveReducer, { MoveState } from './reducers/moveReducer';
export type AppDispatch = typeof store.dispatch; export type AppDispatch = typeof store.dispatch;

@ -1,6 +1,6 @@
// screens/HomeScreen.tsx // screens/HomeScreen.tsx
import React from 'react'; import React from 'react';
import { Image, View } from 'react-native'; import { Image, View } from 'react-native';
const HomeScreen = () => { const HomeScreen = () => {

@ -1,15 +1,15 @@
// screens/moves/MoveDetailScreen.tsx // screens/moves/MoveDetailScreen.tsx
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { Button, ScrollView, StyleSheet, Text, View } from 'react-native'; import { Button, ScrollView, StyleSheet, Text, View } from 'react-native';
import { RouteProp } from '@react-navigation/native'; import { RouteProp } from '@react-navigation/native';
import { RootStackParamList } from "../../navigation/navigationTypes"; import { RootStackParamList } from "../../navigation/navigationTypes";
import TypeTacticsInfoList from "../../components/TypeTacticsInfoList" import TypeTacticsInfoList from "../../components/TypeTacticsInfoList"
import { StackNavigationProp } from "@react-navigation/stack"; import { StackNavigationProp } from "@react-navigation/stack";
import { MOVE_DETAIL, MOVE_FORM } from '../../navigation/constants'; import { MOVE_DETAIL, MOVE_FORM } from '../../navigation/constants';
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { RootState } from "../../redux/store"; import { RootState } from "../../redux/store";
import { Move } from "../../entities/Move"; import { Move } from "../../entities/Move";
type MoveDetailScreenNavigationProp = StackNavigationProp<RootStackParamList, typeof MOVE_DETAIL>; type MoveDetailScreenNavigationProp = StackNavigationProp<RootStackParamList, typeof MOVE_DETAIL>;
@ -35,7 +35,10 @@ const MoveDetailScreen = ({ navigation, route }: Props) => {
return ( return (
<ScrollView style={styles.container}> <ScrollView style={styles.container}>
<Button title="Edit Move" onPress={() => navigation.navigate(MOVE_FORM, { move: move })}/> <Button
title="Edit Move"
color={styles.updateButton.backgroundColor}
onPress={() => navigation.navigate(MOVE_FORM, { move: move })}/>
<Text style={styles.title}>Name: {move?.name}</Text> <Text style={styles.title}>Name: {move?.name}</Text>
<Text style={styles.detail}>Category: {move?.category}</Text> <Text style={styles.detail}>Category: {move?.category}</Text>
<Text style={styles.detail}>Power: {move?.power}</Text> <Text style={styles.detail}>Power: {move?.power}</Text>
@ -50,6 +53,9 @@ const MoveDetailScreen = ({ navigation, route }: Props) => {
}; };
const styles = StyleSheet.create({ const styles = StyleSheet.create({
updateButton: {
backgroundColor: '#BA22DA',
},
container: { container: {
flex: 1, flex: 1,
backgroundColor: '#FFFFFF', backgroundColor: '#FFFFFF',

@ -1,23 +1,32 @@
// screens/moves/MoveFormScreen.tsx // screens/moves/MoveFormScreen.tsx
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { Button, StyleSheet, Text, TextInput } from 'react-native'; import {
import { StackNavigationProp } from '@react-navigation/stack'; Button,
import { RootStackParamList } from "../../navigation/navigationTypes"; KeyboardAvoidingView,
import { useDispatch, useSelector } from 'react-redux'; Modal,
import { createMove, updateMove } from '../../redux/actions/moveActions'; Platform,
import { AppDispatch, RootState } from "../../redux/store"; ScrollView,
import { Move } from "../../entities/Move"; StyleSheet,
import { RouteProp } from "@react-navigation/native"; Text,
import { MOVE_FORM } from "../../navigation/constants"; TextInput,
import { Picker } from "@react-native-community/picker"; View
import { ItemValue } from "@react-native-community/picker/typings/Picker"; } from 'react-native';
import { MoveCategoryName } from "../../entities/MoveCategoryName"; import { StackNavigationProp } from '@react-navigation/stack';
import { TypeName } from "../../entities/TypeName"; import { RootStackParamList } from "../../navigation/navigationTypes";
import MultiSelect from "react-native-multiple-select"; import { useDispatch, useSelector } from 'react-redux';
import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view"; import { createMove, updateMove } from '../../redux/actions/moveActions';
import AlertModal from "../../components/AlertModal"; import { AppDispatch, RootState } from "../../redux/store";
import { MOVE_ERROR } from "../../redux/constants"; import { Move } from "../../entities/Move";
import { RouteProp } from "@react-navigation/native";
import { MOVE_FORM } from "../../navigation/constants";
import { Picker } from "@react-native-community/picker";
import { ItemValue } from "@react-native-community/picker/typings/Picker";
import { MoveCategoryName } from "../../entities/MoveCategoryName";
import { TypeName } from "../../entities/TypeName";
import MultiSelect from "react-native-multiple-select";
import AlertModal from "../../components/AlertModal";
import { MOVE_ERROR } from "../../redux/constants";
type MoveFormScreenNavigationProp = StackNavigationProp<RootStackParamList, typeof MOVE_FORM>; type MoveFormScreenNavigationProp = StackNavigationProp<RootStackParamList, typeof MOVE_FORM>;
type MoveFormScreenRouteProp = RouteProp<RootStackParamList, typeof MOVE_FORM>; type MoveFormScreenRouteProp = RouteProp<RootStackParamList, typeof MOVE_FORM>;
@ -50,6 +59,14 @@ const MoveFormScreen = ({ navigation, route }: Props) => {
}); });
}, [navigation, route.params?.move]); }, [navigation, route.params?.move]);
const [isModalVisible, setModalVisible] = useState(false);
const [currentMultiSelect, setCurrentMultiSelect] = useState<'weakAgainst' | 'effectiveAgainst'>('weakAgainst');
const handleOpenModal = (multiSelect: 'weakAgainst' | 'effectiveAgainst') => {
setCurrentMultiSelect(multiSelect);
setModalVisible(true);
};
const [selectedWeakAgainst, setSelectedWeakAgainst] = useState<string[]>(move.type.weakAgainst); const [selectedWeakAgainst, setSelectedWeakAgainst] = useState<string[]>(move.type.weakAgainst);
const [selectedEffectiveAgainst, setSelectedEffectiveAgainst] = useState<string[]>(move.type.effectiveAgainst); const [selectedEffectiveAgainst, setSelectedEffectiveAgainst] = useState<string[]>(move.type.effectiveAgainst);
@ -76,89 +93,113 @@ const MoveFormScreen = ({ navigation, route }: Props) => {
}; };
return ( return (
<KeyboardAwareScrollView style={styles.container}> <KeyboardAvoidingView
<AlertModal style={styles.container}
visible={!!error} behavior={Platform.OS === "ios" ? "padding" : "height"}
message={error || ''} >
onClose={() => dispatch({ type: MOVE_ERROR, payload: null })} <ScrollView contentContainerStyle={{ paddingBottom: 20 }}>
/> <AlertModal
<Text style={styles.label}>Name: </Text> visible={!!error}
<TextInput message={error || ''}
value={move.name} onClose={() => dispatch({ type: MOVE_ERROR, payload: null })}
onChangeText={(text) => setMove({ ...move, name: text })} />
style={styles.input} <View style={styles.row}>
/> <Text style={styles.label}>Name: </Text>
<Text style={styles.label}>Category: </Text> <TextInput
<Picker value={move.name}
selectedValue={move.category} onChangeText={(text) => setMove({ ...move, name: text })}
style={styles.input} style={styles.input}
onValueChange={(itemValue: ItemValue) => />
setMove({ ...move, category: itemValue as MoveCategoryName }) </View>
}> <View style={styles.row}>
{Object.values(MoveCategoryName).map((value) => <Text style={styles.label}>Category: </Text>
<Picker.Item key={value} label={value} value={value}/> <Picker
)} selectedValue={move.category}
</Picker> style={styles.input}
<Text style={styles.label}>Power: </Text> onValueChange={(itemValue: ItemValue) =>
<TextInput setMove({ ...move, category: itemValue as MoveCategoryName })
value={move.power.toString()} }>
onChangeText={(text) => { {Object.values(MoveCategoryName).map((value) =>
if (!isNaN(Number(text))) { <Picker.Item key={value} label={value} value={value}/>
setMove({ ...move, power: Number(text) }); )}
} </Picker>
}} </View>
style={styles.input} <View style={styles.row}>
keyboardType="numeric" <Text style={styles.label}>Power: </Text>
/> <TextInput
<Text style={styles.label}>Accuracy: </Text> value={move.power.toString()}
<TextInput onChangeText={(text) => {
value={move.accuracy.toString()} if (!isNaN(Number(text))) {
onChangeText={(text) => { setMove({ ...move, power: Number(text) });
if (!isNaN(Number(text))) { }
setMove({ ...move, accuracy: Number(text) }); }}
} style={styles.input}
}} keyboardType="numeric"
style={styles.input} />
keyboardType="numeric" </View>
/> <View style={styles.row}>
<Text style={styles.label}>Type: </Text> <Text style={styles.label}>Accuracy: </Text>
<Picker <TextInput
selectedValue={move.type.name} value={move.accuracy.toString()}
style={styles.input} onChangeText={(text) => {
onValueChange={(itemValue: ItemValue) => if (!isNaN(Number(text))) {
setMove({ ...move, type: { ...move.type, name: itemValue as TypeName } }) setMove({ ...move, accuracy: Number(text) });
}> }
{Object.values(TypeName).map((value) => }}
<Picker.Item key={value} label={value} value={value}/> style={styles.input}
)} keyboardType="numeric"
</Picker> />
<Text style={styles.label}>Weak Against: </Text> </View>
<MultiSelect <View style={styles.row}>
items={ <Text style={styles.label}>Type: </Text>
Object.values(TypeName).map((value) => ({ id: value, name: value })) <Picker
} selectedValue={move.type.name}
uniqueKey="id" style={styles.input}
onSelectedItemsChange={ onValueChange={(itemValue: ItemValue) =>
(selectedItems) => handleSelectType(selectedItems, setSelectedWeakAgainst, selectedEffectiveAgainst) setMove({ ...move, type: { ...move.type, name: itemValue as TypeName } })
} }>
selectedItems={selectedWeakAgainst} {Object.values(TypeName).map((value) =>
displayKey="name" <Picker.Item key={value} label={value} value={value}/>
/> )}
<Text style={styles.label}>Effective Against: </Text> </Picker>
<MultiSelect </View>
items={ <Text>Weak Against: {selectedWeakAgainst.join(', ')}</Text>
Object.values(TypeName).map((value) => ({ id: value, name: value })) <View style={styles.buttonContainer}>
} <Button title="Select Weak Against" onPress={() => handleOpenModal('weakAgainst')}/>
uniqueKey="id" </View>
onSelectedItemsChange={ <Text>Effective Against: {selectedEffectiveAgainst.join(', ')}</Text>
(selectedItems) => handleSelectType(selectedItems, setSelectedEffectiveAgainst, selectedWeakAgainst) <View style={styles.buttonContainer}>
} <Button title="Select Effective Against" onPress={() => handleOpenModal('effectiveAgainst')}/>
selectedItems={selectedEffectiveAgainst} </View>
displayKey="name" <View style={styles.buttonContainer}>
/> <Button title="Save" onPress={handleSave} color={styles.saveButton.backgroundColor}/>
<Button title="Save" onPress={handleSave}/> </View>
</ScrollView>
</KeyboardAwareScrollView> <Modal
animationType="slide"
transparent={true}
visible={isModalVisible}
onRequestClose={() => {
setModalVisible(!isModalVisible);
}}>
<View style={styles.centeredView}>
<View style={styles.modalView}>
<MultiSelect
items={
Object.values(TypeName).map((value) => ({ id: value, name: value }))
}
uniqueKey="id"
onSelectedItemsChange={
(selectedItems) => handleSelectType(selectedItems, currentMultiSelect === 'weakAgainst' ? setSelectedWeakAgainst : setSelectedEffectiveAgainst, currentMultiSelect === 'weakAgainst' ? selectedEffectiveAgainst : selectedWeakAgainst)
}
selectedItems={currentMultiSelect === 'weakAgainst' ? selectedWeakAgainst : selectedEffectiveAgainst}
displayKey="name"
/>
<Button title="Close" onPress={() => setModalVisible(false)}/>
</View>
</View>
</Modal>
</KeyboardAvoidingView>
); );
}; };
@ -167,14 +208,49 @@ const styles = StyleSheet.create({
flex: 1, flex: 1,
padding: 16, padding: 16,
}, },
label: {
marginRight: 8,
},
input: { input: {
backgroundColor: '#CEC', backgroundColor: '#CEC',
borderRadius: 8, borderRadius: 8,
height: 32, minHeight: 32,
flex: 1,
}, },
label: { row: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'flex-start',
margin: 8, margin: 8,
} flexWrap: 'wrap',
},
centeredView: {
flex: 1,
justifyContent: "center",
alignItems: "center",
marginTop: 22
},
modalView: {
margin: 20,
backgroundColor: "white",
borderRadius: 20,
padding: 35,
alignItems: "center",
shadowColor: "#000",
shadowOffset: {
width: 0,
height: 2
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5
},
buttonContainer: {
margin: 8,
},
saveButton: {
backgroundColor: '#BADA55',
},
}); });
export default MoveFormScreen; export default MoveFormScreen;

@ -1,18 +1,18 @@
// screens/moves/MoveListScreen.tsx // screens/moves/MoveListScreen.tsx
import React from 'react'; import React from 'react';
import { Button, FlatList, StyleSheet, View } from 'react-native'; import { Button, FlatList, StyleSheet, View } from 'react-native';
import { StackNavigationProp } from '@react-navigation/stack'; import { StackNavigationProp } from '@react-navigation/stack';
import { RootStackParamList } from "../../navigation/navigationTypes"; import { RootStackParamList } from "../../navigation/navigationTypes";
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { deleteMove, getMoves } from '../../redux/actions/moveActions'; import { deleteMove, getMoves } from '../../redux/actions/moveActions';
import { MoveState } from "../../redux/reducers/moveReducer"; import { MoveState } from "../../redux/reducers/moveReducer";
import { AppDispatch } from "../../redux/store"; import { AppDispatch } from "../../redux/store";
import MoveListItem from "../../components/MoveListItem"; import MoveListItem from "../../components/MoveListItem";
import { MOVE_DETAIL, MOVE_FORM, MOVE_LIST } from "../../navigation/constants"; import { MOVE_DETAIL, MOVE_FORM, MOVE_LIST } from "../../navigation/constants";
import { RouteProp, useFocusEffect } from "@react-navigation/native"; import { RouteProp, useFocusEffect } from "@react-navigation/native";
import AlertModal from "../../components/AlertModal"; import AlertModal from "../../components/AlertModal";
import { MOVE_ERROR } from "../../redux/constants"; import { MOVE_ERROR } from "../../redux/constants";
type MoveListScreenNavigationProp = StackNavigationProp<RootStackParamList, typeof MOVE_LIST>; type MoveListScreenNavigationProp = StackNavigationProp<RootStackParamList, typeof MOVE_LIST>;
type MoveListScreenRouteProp = RouteProp<RootStackParamList, typeof MOVE_LIST>; type MoveListScreenRouteProp = RouteProp<RootStackParamList, typeof MOVE_LIST>;
@ -49,7 +49,10 @@ const MoveListScreen = ({ navigation }: Props) => {
<FlatList <FlatList
data={moves} data={moves}
ListHeaderComponent={ ListHeaderComponent={
<Button title="Add Move" onPress={() => navigation.navigate(MOVE_FORM, { move: undefined })}/> <Button
title="Add Move"
color={styles.addButton.backgroundColor}
onPress={() => navigation.navigate(MOVE_FORM, { move: undefined })}/>
} }
renderItem={({ item }) => ( renderItem={({ item }) => (
<View style={styles.listItemContainer}> <View style={styles.listItemContainer}>
@ -77,6 +80,9 @@ const styles = StyleSheet.create({
deleteButton: { deleteButton: {
backgroundColor: '#FF6961', backgroundColor: '#FF6961',
}, },
addButton: {
backgroundColor: '#BADA55',
},
listItemContainer: { listItemContainer: {
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'space-between', justifyContent: 'space-between',

Loading…
Cancel
Save