Skip to content

Commit

Permalink
FEATURE: Adding cards to deck
Browse files Browse the repository at this point in the history
Adding cards to deck and updating deck list and details.
  • Loading branch information
diogosilverio committed Jan 27, 2018
1 parent 35cc9a5 commit 5b12116
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 7 deletions.
10 changes: 10 additions & 0 deletions actions/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export const LOAD_DECKS = "LOAD_DECKS";
export const NEW_DECK = "NEW_DECK";

export const ADD_CARD = "ADD_CARD";

export function loadDecks(decks){
return {
type: LOAD_DECKS,
Expand All @@ -13,4 +15,12 @@ export function newDeck(deck){
type: NEW_DECK,
deck
}
}

export function addCard(deckKey, card){
return {
type: ADD_CARD,
deckKey,
card
}
}
123 changes: 118 additions & 5 deletions components/flashcards/AddCard.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,128 @@
import React, { Component } from 'react';
import { Text, View } from 'react-native';
import { StyleSheet, Text, View, TextInput, TouchableOpacity, Alert } from 'react-native';
import { connect } from 'react-redux';
import { NavigationActions } from 'react-navigation';
import MaterialIcons from '@expo/vector-icons/MaterialIcons';

export default class AddCard extends Component {
import { addCard } from '../../actions';
import { addCardToDeck, getDeck } from '../../services';

import { COLOR_WHITE, COLOR_B_5 } from '../../utils/colors';

class AddCard extends Component {

state = {
card: {
question: '',
answer: ''
}
}

reset() {
this.setState({
deck: {
question: '',
answer: ''
}
});
}

async addCard() {
const { deckKey, refresher } = this.props.navigation.state.params;

try {
if (this.state.card.question.trim() === '' || this.state.card.answer.trim() === '') {
Alert.alert(
'Required',
"Type a valid question/answer.",
[{ text: 'Ok', onPress: () => { } }],
{ cancelable: false });
return;
}


await addCardToDeck(deckKey, this.state.card);
this.props.dispatch(addCard(deckKey, this.state.card));
refresher();
this.props.navigation.goBack();

} catch (e) {
Alert.alert(
'Error',
`Error adding a new card to deck '${deckKey}'.`,
[
{ text: 'Ok', onPress: () => { } }
]);
console.error(e);
}

this.reset();
}

render() {
const { deckKey } = this.props.navigation.state.params;
return (
<View>
<Text>AddCard</Text>
<View style={styles.container}>
<Text style={styles.text}>Question</Text>
<TextInput style={{ borderWidth: 0, width: '90%' }} maxLength={30} placeholder="What is a Java Annotation?"
value={this.state.card.question}
onChangeText={(question) => {
this.setState((prev) => {
const prevCard = prev.card;
return {
card: {
...prevCard,
question
}
}
})
}} />

<Text style={styles.text}>Answer</Text>
<TextInput style={{ borderWidth: 0, width: '90%' }} maxLength={100} multiline placeholder="Annotations is a form of metadata..."
value={this.state.card.answer}
onChangeText={(answer) => {
this.setState((prev) => {
const prevCard = prev.card;
return {
card: {
...prevCard,
answer
}
}
})
}} />

<View style={styles.rowContainer}>
<TouchableOpacity style={styles.saveBtn} onPress={this.addCard.bind(this)}>
<MaterialIcons name="done" size={40} color={COLOR_WHITE} />
</TouchableOpacity>
</View>
</View>
);
}


}
}

const styles = StyleSheet.create({
container: {
alignItems: 'center',
flex: 1
},
rowContainer: {
flexDirection: 'row',
justifyContent: 'space-between'
},
text: {
fontSize: 20
},
saveBtn: {
backgroundColor: COLOR_B_5,
borderRadius: 2,
margin: 5,
padding: 10
}
})

export default connect()(AddCard);
10 changes: 9 additions & 1 deletion components/flashcards/DeckDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ class DeckDetails extends Component {
}
}

/**
* Child navigate.goBack() does not re-render state.
* https://github.com/react-navigation/react-navigation/issues/922
*/
refresher(){
this.setState({});
}

render() {
const { deck } = this.props;
if (typeof deck === 'undefined') {
Expand All @@ -42,7 +50,7 @@ class DeckDetails extends Component {
<Text style={styles.cardText}>{deck.cards.length} Card(s)</Text>
</View>
<View style={styles.subContainerBtn}>
<TouchableOpacity style={styles.btn}>
<TouchableOpacity style={styles.btn} onPress={() => this.props.navigation.navigate('AddCard', { deckKey: deck.name, refresher: this.refresher.bind(this) })}>
<Text style={styles.btnText}>Add Card</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.btn}>
Expand Down
18 changes: 17 additions & 1 deletion reducers/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
LOAD_DECKS,
NEW_DECK
NEW_DECK,
ADD_CARD
} from '../actions';

function decks(state = { decks: {} }, action) {
Expand All @@ -26,6 +27,21 @@ function decks(state = { decks: {} }, action) {
decks
}
}
case ADD_CARD: {
const { card, deckKey } = action;
const freshDeck = decks[deckKey];

freshDeck.cards.push(card);

const freshDecks = {
...decks,
[deckKey]: freshDeck
}
return {
decks: freshDecks
}

}
default: {
return state;
}
Expand Down
18 changes: 18 additions & 0 deletions services/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,22 @@ export async function persistDeck(deck){
export async function loadDecks() {
const decks = JSON.parse(await AsyncStorage.getItem(FLASHCARDS_STORAGE_KEY));
return decks !== null ? decks : [];
}

export async function getDeck(deckKey){
const decks = await loadDecks();
const deck = decks[deckKey];
return deck;
}

export async function addCardToDeck(deckKey, card){
const deck = await getDeck(deckKey);

if(typeof deck === 'undefined'){
console.warn(`Trying to fetch invalid deck: ${deckKey}`);
return;
}

deck.cards.push(card)
await persistDeck(deck);
}

0 comments on commit 5b12116

Please sign in to comment.