From 6cc28ef873e5529dbc1315ba29a5f270c87afa63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diogo=20Silv=C3=A9rio?= Date: Sat, 27 Jan 2018 21:01:57 -0200 Subject: [PATCH] FEATURE: Deleting cards Users may delete cards by accessing a deck and its cards list. --- actions/index.js | 9 ++ components/flashcards/CardList.js | 51 ++++++++++ components/flashcards/DeckDetails.js | 8 +- components/navigators/FlashStackNavigator.js | 4 + components/ui/CardItem.js | 98 ++++++++++++++++++++ reducers/index.js | 13 ++- services/index.js | 7 ++ 7 files changed, 185 insertions(+), 5 deletions(-) create mode 100644 components/flashcards/CardList.js create mode 100644 components/ui/CardItem.js diff --git a/actions/index.js b/actions/index.js index 676d4b2..0b767c3 100644 --- a/actions/index.js +++ b/actions/index.js @@ -3,6 +3,7 @@ export const NEW_DECK = "NEW_DECK"; export const DELETE_DECK = "DELETE_DECK"; export const ADD_CARD = "ADD_CARD"; +export const DELETE_CARD = "DELETE_CARD"; export function loadDecks(decks){ return { @@ -31,4 +32,12 @@ export function addCard(deckKey, card){ deckKey, card } +} + +export function deleteCard(deckKey, cardName){ + return { + type: DELETE_CARD, + deckKey, + cardName + } } \ No newline at end of file diff --git a/components/flashcards/CardList.js b/components/flashcards/CardList.js new file mode 100644 index 0000000..1c1a695 --- /dev/null +++ b/components/flashcards/CardList.js @@ -0,0 +1,51 @@ +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import { ScrollView, StyleSheet, Text, View, TouchableOpacity, Alert } from 'react-native'; +import CardItem from '../ui/CardItem'; +import Entypo from '@expo/vector-icons/Entypo'; + +class CardList extends Component { + + render() { + const { cards } = this.props; + const { deckKey, refresher } = this.props.navigation.state.params; + + if (cards.length === 0) { + + return ( + + + You have no cards for this deck. + + ); + } else { + + return ( + + {cards.map(card => ( + + ))} + + + ); + } + } + +} + +const styles = StyleSheet.create({ + container: { + flex: 1 + } +}); + +function mapStateToProps({ decks }, props) { + const { deckKey } = props.navigation.state.params; + const cards = decks[deckKey].cards; + + return { + cards + } +} + +export default connect(mapStateToProps)(CardList); \ No newline at end of file diff --git a/components/flashcards/DeckDetails.js b/components/flashcards/DeckDetails.js index e8a1eaa..f83bb6d 100644 --- a/components/flashcards/DeckDetails.js +++ b/components/flashcards/DeckDetails.js @@ -54,11 +54,11 @@ class DeckDetails extends Component { ]); } - shouldComponentUpdate(nextProps, nextState){ - if(nextProps.deck === null){ + shouldComponentUpdate(nextProps, nextState) { + if (nextProps.deck === null) { return false; } - + return true; } @@ -92,7 +92,7 @@ class DeckDetails extends Component { this.props.navigation.navigate('AddCard', { deckKey: deck.name, refresher: this.refresher.bind(this) })}> Add Card - + this.props.navigation.navigate('Cards', { deckKey: deck.name, refresher: this.refresher.bind(this) })}> List Cards diff --git a/components/navigators/FlashStackNavigator.js b/components/navigators/FlashStackNavigator.js index d46197a..9c97a97 100644 --- a/components/navigators/FlashStackNavigator.js +++ b/components/navigators/FlashStackNavigator.js @@ -8,6 +8,7 @@ import AddCard from '../flashcards/AddCard'; import DeckDetails from '../flashcards/DeckDetails'; import { COLOR_A_1, COLOR_B_5 } from '../../utils/colors'; +import CardList from '../flashcards/CardList'; export default class FlashStackNavigator extends Component { @@ -21,6 +22,9 @@ export default class FlashStackNavigator extends Component { }, DeckDetails: { screen: DeckDetails + }, + Cards: { + screen: CardList } }, { navigationOptions: { diff --git a/components/ui/CardItem.js b/components/ui/CardItem.js new file mode 100644 index 0000000..7270989 --- /dev/null +++ b/components/ui/CardItem.js @@ -0,0 +1,98 @@ +import React, { Component } from 'react'; +import { View, TouchableOpacity, StyleSheet, Text, Alert } from 'react-native'; +import { connect } from 'react-redux'; + +import { MaterialCommunityIcons } from '@expo/vector-icons'; + +import { deleteCardFromDeck } from '../../services'; +import { deleteCard } from '../../actions'; +import { COLOR_B_1, COLOR_WHITE, COLOR_FAILURE } from '../../utils/colors'; + +class CardItem extends Component { + + async deleteCard(cardName) { + const { deck, refresher } = this.props; + + Alert.alert( + 'Delete card?', + `Do you confirm the deletion of the card '${cardName}'?`, + [ + { + text: 'Yes', onPress: async () => { + try { + await deleteCardFromDeck(deck, cardName); + this.props.dispatch(deleteCard(deck, cardName)); + refresher(); + } catch (e) { + Alert.alert( + 'Error', + 'Error deleting card.', + [ + { text: 'Ok', onPress: () => { } } + ]); + console.error(e); + } + } + }, + { text: 'No', onPress: () => { } } + ] + ); + } + + render() { + const { card } = this.props; + + return ( + + + + {card.question} + {card.answer} + + + + + + + + + ); + } +} + +const styles = StyleSheet.create({ + mainContainer: { + flex: 8, + flexDirection: 'row', + alignItems: 'center', + padding: 5, + borderBottomWidth: 1, + borderBottomColor: COLOR_B_1 + }, + cardContainer: { + flex: 1 + }, + question: { + fontSize: 20, + fontWeight: 'bold' + }, + answer: { + fontSize: 15, + color: 'gray', + fontStyle: 'italic', + flexWrap: 'wrap' + }, + deleteContainer: { + flex: 2, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: COLOR_FAILURE + }, + deleteBtn: { + flex: 1, + alignItems: 'center', + justifyContent: 'center' + } +}); + +export default connect()(CardItem); \ No newline at end of file diff --git a/reducers/index.js b/reducers/index.js index 55f9e7f..e97128c 100644 --- a/reducers/index.js +++ b/reducers/index.js @@ -2,7 +2,8 @@ import { LOAD_DECKS, NEW_DECK, ADD_CARD, - DELETE_DECK + DELETE_DECK, + DELETE_CARD } from '../actions'; function decks(state = { decks: {} }, action) { @@ -52,6 +53,16 @@ function decks(state = { decks: {} }, action) { } } + case DELETE_CARD: { + const { deckKey, cardName } = action; + const freshDecks = decks; + + freshDecks[deckKey].cards = freshDecks[deckKey].cards.filter(card => card.question !== cardName); + + return { + decks: freshDecks + } + } default: { return state; } diff --git a/services/index.js b/services/index.js index d9c8bf7..98d28b6 100644 --- a/services/index.js +++ b/services/index.js @@ -35,5 +35,12 @@ export async function addCardToDeck(deckKey, card){ } deck.cards.push(card) + await persistDeck(deck); +} + +export async function deleteCardFromDeck(deckKey, cardName){ + const deck = await getDeck(deckKey); + deck.cards = deck.cards.filter(card => card.question !== cardName); + await persistDeck(deck); } \ No newline at end of file