Compare commits
8 Commits
master
...
DarkLightT
Author | SHA1 | Date |
---|---|---|
![]() |
f6b8ce6f08 | 2 years ago |
![]() |
c2484109a7 | 2 years ago |
![]() |
6c1d9e7d43 | 2 years ago |
![]() |
20e4362ef3 | 2 years ago |
![]() |
6d0c984e65 | 2 years ago |
![]() |
6039b9462c | 2 years ago |
![]() |
b1863bd1d3 | 2 years ago |
![]() |
1aff6817f8 | 2 years ago |
@ -0,0 +1,32 @@
|
|||||||
|
# React-Native BUT2 Notation
|
||||||
|
|
||||||
|
Duration : 10 * 2h
|
||||||
|
|
||||||
|
## Documentation (4 pts)
|
||||||
|
|
||||||
|
* Application sketches
|
||||||
|
|
||||||
|
## Basics (10 pts)
|
||||||
|
|
||||||
|
* Navigation (2 pts)
|
||||||
|
* Tab bottom navigation + at least one button
|
||||||
|
* Store (2 pts)
|
||||||
|
* Read data from redux store
|
||||||
|
* Actions (1 pts)
|
||||||
|
* Update data to redux store
|
||||||
|
* Display list of items (2 pts)
|
||||||
|
* FlatList, VirtualizedList or SectionList
|
||||||
|
* Display image (1 pts)
|
||||||
|
* Child props (1 pts)
|
||||||
|
* TextInput (1 pts)
|
||||||
|
|
||||||
|
## Application features (6 pts)
|
||||||
|
|
||||||
|
* Retrieve data using the Web API (2 pts)
|
||||||
|
* Store favorite data into phone storage (2 pts)
|
||||||
|
* Write Tests (2 pts)
|
||||||
|
|
||||||
|
## Bonus (only taken into account if the basics are all mastered)
|
||||||
|
|
||||||
|
* Dark/Light mode switch (2pts)
|
||||||
|
* Sexy UI (2 pts)
|
@ -1,79 +1 @@
|
|||||||
<div align = center>
|
[Slides](https://iutsa01.blob.core.windows.net/react-native/ReactNative.pdf)
|
||||||
|
|
||||||
<img src="assets/banner.png" width="1050" height="">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div align = center>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div align = center>
|
|
||||||
|
|
||||||
# **DesckStone**
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# :bookmark: Présentation
|
|
||||||
|
|
||||||
DeckStone est une application master detail créé grâce au framework React Native en TypeScript et en JavaScript.
|
|
||||||
Elle vous permet la gestion des cartes hearstone que vous posséder avec la consultation des informations des cartes et l'utilisation de favoris persistants.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Notation :white_check_mark:
|
|
||||||
|
|
||||||
|
|
||||||
## ``` Documentation ```
|
|
||||||
|
|
||||||
:white_check_mark: **ReadMe**
|
|
||||||
:white_check_mark: **Sketches** : 
|
|
||||||
|
|
||||||
## ```Bases```
|
|
||||||
|
|
||||||
:white_check_mark: **Navigation** : Nous avons une barre de navigation fonctionnel nous permettant de naviguer entre les trois écrans.
|
|
||||||
:white_check_mark: **Store** : Nous utilisons le store pour charger nos données et les stocker mais aussi pour gérer les favoris.
|
|
||||||
:white_check_mark: **Actions** : Nous utilisons les actions pour charger les données depuis le redux mais aussi pour ajouter et supprimer les favoris.
|
|
||||||
:white_check_mark: **Display List of Items** : Nous affichons la liste des cartes et celles des favoris dans 2 écrans différents.
|
|
||||||
:white_check_mark: **Display image** : Chaque cartes sont affiché par un component qui affiche l'image de la carte.
|
|
||||||
:white_check_mark: **Child props** : Nous utilisons un props pour passer la carte au component qui l'affiche.
|
|
||||||
:white_check_mark: **TextInput** : Une recherche par nom peut être effectué par dans les 2 listes.
|
|
||||||
|
|
||||||
## ```Application features```
|
|
||||||
|
|
||||||
:construction: **API** : Nous utilisons l'API officielle du jeu pour récupérer nos données, cependant nous n'avons pas pu gérer le changement de token, nous le changeons donc à la main.
|
|
||||||
***Source*** : https://develop.battle.net/documentation/hearthstone/game-data-apis
|
|
||||||
:white_check_mark: **Store favorite data into phone storage** : Nous utilison l'AsyncStorage pour sauvegarder la liste des favoris dans le téléphone.
|
|
||||||
:construction: **Write Tests** : Nous testons les actions, le reducer et les composants UI que nous avons implémenter, cependant les éléments utilisant indirectement l'AsyncStorage ne passent pas les tests à cause d'un problème d'utilisation du mockAsyncStorage.
|
|
||||||
|
|
||||||
## ``Bonus``
|
|
||||||
|
|
||||||
* :construction: **Dark/Light mode switch** : Nous n'avons mis en place cette gestion seulement dans la branche dédié par souci de visuel
|
|
||||||
* :construction: **Sexy UI** : A vous de juger !
|
|
||||||
|
|
||||||
|
|
||||||
# :construction: Développeurs
|
|
||||||
|
|
||||||
- Corentin RICHARD : corentin.richard@etu.uca.fr
|
|
||||||
- Pierre FERREIRA : pierre.ferreira@etu.uca.fr
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
<a href = "https://codefirst.iut.uca.fr/git/corentin.richard">
|
|
||||||
<img src="https://codefirst.iut.uca.fr/git/avatars/4372364870f18ab9104f13222fa84d2e?size=870" width="50" >
|
|
||||||
</a>
|
|
||||||
<a href = "https://codefirst.iut.uca.fr/git/pierre.ferreira">
|
|
||||||
<img src="https://codefirst.iut.uca.fr/git/avatars/edbacace5f621ae77077f206ebdcee27?size=870" width="50" >
|
|
||||||
</a>
|
|
||||||
|
|
||||||
© IUT - Auvergne
|
|
||||||
</div>
|
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
// somewhere in your configuration files
|
|
||||||
import AsyncStorageMock from '@react-native-async-storage/async-storage/jest/async-storage-mock';
|
|
||||||
|
|
||||||
AsyncStorageMock.multiGet = jest.fn(([keys], callback) => {
|
|
||||||
// do something here to retrieve data
|
|
||||||
callback([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default AsyncStorageMock;
|
|
Before Width: | Height: | Size: 144 KiB |
Before Width: | Height: | Size: 286 KiB |
Before Width: | Height: | Size: 281 KiB |
@ -1,18 +0,0 @@
|
|||||||
import { View } from 'react-native';
|
|
||||||
import { Image } from 'react-native';
|
|
||||||
|
|
||||||
|
|
||||||
type ItemProps = {
|
|
||||||
url : string //Image URL
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ListItemComponent(props : ItemProps){
|
|
||||||
return (
|
|
||||||
<View>
|
|
||||||
<Image
|
|
||||||
source={{uri: props.url}}
|
|
||||||
style={{flex:1, minHeight:250, minWidth:180}}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
import { useDispatch } from "react-redux";
|
|
||||||
import { Card } from "../models/Card";
|
|
||||||
import { setFavList } from "../redux/actions/action_setFavList";
|
|
||||||
import { Image, TouchableHighlight, View } from "react-native";
|
|
||||||
import React, { useEffect } from "react";
|
|
||||||
import {CardProps} from "../props/favProps"
|
|
||||||
import { FontAwesome } from '@expo/vector-icons';
|
|
||||||
import { StyleSheet} from 'react-native';
|
|
||||||
|
|
||||||
|
|
||||||
export default function Item(props: CardProps){ // a mettre dans components et definir une props pour passer le param
|
|
||||||
|
|
||||||
const {route} = props;
|
|
||||||
const item: Card = route.card;
|
|
||||||
const bool: boolean = route.bool;
|
|
||||||
const dispatch = useDispatch()
|
|
||||||
|
|
||||||
const HandleAddFav = (props : CardProps) => {
|
|
||||||
dispatch(setFavList(props));
|
|
||||||
}
|
|
||||||
return(
|
|
||||||
|
|
||||||
/* dispatch , */
|
|
||||||
<View style={styles.item}>
|
|
||||||
<TouchableHighlight testID="button" style={item.fav?styles.favoriteButtonFav:styles.favoriteButtonNonFav} onPress={() => HandleAddFav(props)} >
|
|
||||||
<FontAwesome name="heart-o" size={50} color="#fff" />
|
|
||||||
</TouchableHighlight>
|
|
||||||
<Image
|
|
||||||
source={{uri:item.img}}
|
|
||||||
style={{flex:1, minHeight:250, minWidth:180}}/>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
item: {
|
|
||||||
zIndex:0
|
|
||||||
},
|
|
||||||
favoriteButtonNonFav: {
|
|
||||||
position: 'absolute',
|
|
||||||
top: 10,
|
|
||||||
right: 10,
|
|
||||||
backgroundColor: 'red',
|
|
||||||
borderRadius: 50,
|
|
||||||
padding: 10,
|
|
||||||
zIndex:1
|
|
||||||
},
|
|
||||||
favoriteButtonFav: {
|
|
||||||
position: 'absolute',
|
|
||||||
top: 10,
|
|
||||||
right: 10,
|
|
||||||
backgroundColor: 'red',
|
|
||||||
borderRadius: 50,
|
|
||||||
padding: 10,
|
|
||||||
zIndex:1
|
|
||||||
},});
|
|
@ -0,0 +1,41 @@
|
|||||||
|
import { StatusBar } from 'expo-status-bar';
|
||||||
|
import { StyleSheet, Text, View, Button } from 'react-native';
|
||||||
|
import React, { useState } from "react";
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
export default function Main(props : mainProps){
|
||||||
|
const [count, setCount] = useState(0);
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<View style={styles.border}>
|
||||||
|
<Text>Maman, prend la caméra ! !</Text>
|
||||||
|
<StatusBar style="auto" />
|
||||||
|
<Text>{count}</Text>
|
||||||
|
<Button onPress={()=> setCount(count+1)} title="+1"/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: '#ffffff',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
borderWidth: 5,
|
||||||
|
borderColor : "#ff00ff",
|
||||||
|
},
|
||||||
|
border: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: '#ff0000',
|
||||||
|
maxHeight : 100,
|
||||||
|
borderWidth : 15,
|
||||||
|
borderRadius : 15,
|
||||||
|
borderColor : '#00ffaa',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
export class CardSet {
|
||||||
|
|
||||||
|
constructor(id: number,name : string, type : string ) {
|
||||||
|
this._id = id
|
||||||
|
this._name =name
|
||||||
|
this._type = type
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private _id : number
|
||||||
|
|
||||||
|
get id(): number {
|
||||||
|
return this._id
|
||||||
|
}
|
||||||
|
|
||||||
|
// NAME //
|
||||||
|
private _name : string;
|
||||||
|
get name(): string {
|
||||||
|
return this._name;
|
||||||
|
}
|
||||||
|
set name(value: string) {
|
||||||
|
this._name = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _type : string;
|
||||||
|
get type(): string {
|
||||||
|
return this._type;
|
||||||
|
}
|
||||||
|
set type(value: string) {
|
||||||
|
this._type = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
export class Classe {
|
||||||
|
constructor(private id : number, private name : string) {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
export class Type{
|
||||||
|
|
||||||
|
constructor(private id : number, private name : string) {
|
||||||
|
}
|
||||||
|
}
|
@ -1,17 +0,0 @@
|
|||||||
|
|
||||||
import { NavigationContainer } from '@react-navigation/native';
|
|
||||||
import { createStackNavigator } from '@react-navigation/stack';
|
|
||||||
import {createNativeStackNavigator} from '@react-navigation/native-stack';
|
|
||||||
|
|
||||||
import ListScreen from '../screens/ListScreen';
|
|
||||||
import DetailCard from '../screens/DetailCard';
|
|
||||||
|
|
||||||
export default function DetailStackNav() {
|
|
||||||
const Stack = createStackNavigator();
|
|
||||||
return (
|
|
||||||
<Stack.Navigator initialRouteName="CardList">
|
|
||||||
<Stack.Screen name="CardList" component={ListScreen}/>
|
|
||||||
<Stack.Screen name="DetailCard" component={DetailCard}/>
|
|
||||||
</Stack.Navigator>
|
|
||||||
)
|
|
||||||
}
|
|
@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
|
||||||
|
// import { NavigationContainer } from '@react-navigation/native';
|
||||||
|
// import { createStackNavigator } from '@react-navigation/stack';
|
||||||
|
|
||||||
|
// import HomeScreen from '../screens/HomeScreen';
|
||||||
|
// import ListScreen from '../screens/ListScreen';
|
||||||
|
// import ListFav from '../screens/ListFav';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// export default function ListNavigator() {
|
||||||
|
// const Stack = createStackNavigator();
|
||||||
|
// return (
|
||||||
|
// <Stack.Navigator initialRouteName="List">
|
||||||
|
// <Stack.Screen name="List" component={ListScreen}/>
|
||||||
|
// <Stack.Screen name="ListFav" component={ListFav}/>
|
||||||
|
// </Stack.Navigator>
|
||||||
|
// )
|
||||||
|
// }
|
@ -0,0 +1,23 @@
|
|||||||
|
// import React from 'react';
|
||||||
|
// import { SafeAreaView, StatusBar } from 'react-native';
|
||||||
|
// import ThemeContext from '../styles/themeContext';
|
||||||
|
// import theme from '../styles/theme';
|
||||||
|
// import { useSelector } from 'react-redux';
|
||||||
|
// import { lightTheme, darkTheme } from '../styles/theme';
|
||||||
|
// //@ts-ignore
|
||||||
|
// const NavigationWrapper = ({ children }) => {
|
||||||
|
|
||||||
|
// const mode = useSelector(state => state.appReducer.mode);
|
||||||
|
// const theme = mode === 'light' ? lightTheme : darkTheme;
|
||||||
|
|
||||||
|
// return (
|
||||||
|
// <SafeAreaView style={{ flex: 1 }}>
|
||||||
|
// <StatusBar />
|
||||||
|
// <ThemeContext.Provider value={theme}>
|
||||||
|
// {children}
|
||||||
|
// </ThemeContext.Provider>
|
||||||
|
// </SafeAreaView>
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
|
||||||
|
// export default NavigationWrapper;
|
File diff suppressed because it is too large
Load Diff
@ -1,8 +0,0 @@
|
|||||||
import { Card } from "../models/Card";
|
|
||||||
|
|
||||||
export interface CardProps{
|
|
||||||
route : {
|
|
||||||
card: Card;
|
|
||||||
bool: boolean;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,53 +1,91 @@
|
|||||||
import { setCardsList } from "./action_setCardsList";
|
import { setCardsList } from "./action_setCardsList";
|
||||||
|
|
||||||
import { Card } from "../../models/Card";
|
import { Card } from "../../models/Card";
|
||||||
|
|
||||||
//Fonction chargé de l'appel à l'API
|
//! se fichier devra possiblement changer de dossier !!!
|
||||||
export const getAllCards = () => {
|
|
||||||
|
//! classe pour tester
|
||||||
|
// export class Card {
|
||||||
|
// cardId : String
|
||||||
|
// name : String
|
||||||
|
// manaCost : number
|
||||||
|
// attack : number
|
||||||
|
// health : number
|
||||||
|
// desc : String
|
||||||
|
|
||||||
|
// // constructor() {
|
||||||
|
// // this.cardId = "cardId";
|
||||||
|
// // this.name = "name";
|
||||||
|
// // this.manaCost = 0;
|
||||||
|
// // this.attack = 0;
|
||||||
|
// // this.health = 0;
|
||||||
|
// // this.desc = "desc";
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// constructor(cardId : String, name : String, manaCost : number, attack : number, health : number, desc : String){
|
||||||
|
// this.cardId = cardId;
|
||||||
|
// this.name = name;
|
||||||
|
// this.manaCost = manaCost;
|
||||||
|
// this.attack = attack;
|
||||||
|
// this.health = health;
|
||||||
|
// this.desc = desc;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
//Define your action creators that will be responsible for asynchronous operations
|
||||||
|
export const getAllCards = () => {
|
||||||
|
//In order to use await your callback must be asynchronous using async keyword.
|
||||||
|
console.log("getallcard")
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
return async dispatch => {
|
return async dispatch => {
|
||||||
|
//Then perform your asynchronous operations.
|
||||||
try {
|
try {
|
||||||
|
//Have it first fetch data from our starwars url.
|
||||||
const options = {
|
const options = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
'content-length': '9508',
|
'content-length':'9505',
|
||||||
'content-type':'application/json; charset=utf-8',
|
'content-type':'application/json; charset=utf-8',
|
||||||
'etag': 'W/"74bb-d4gMlMNks7UGES3Jmn6wzUTXaLI"',
|
'etag':'W/"74bb-QMT8DIj6saBS1wT4u5WWcEmZAdw"'
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
//! Actualisation de l'API (19/04) :
|
|
||||||
const CardsPromise = await fetch('https://us.api.blizzard.com/hearthstone/cards?locale=en_US&access_token=EUo8Snb09AfE3zQR4CoaB71gq1q3qvSmgL', options);
|
|
||||||
|
|
||||||
//Then use the json method to get json data from api/
|
console.log("API")
|
||||||
const CardsListJson = await CardsPromise.json();
|
//const CardsPromise = await fetch('https://us.api.blizzard.com/hearthstone/cards?locale=en_US&access_token=EURTWhjBC2SRb4Ez42BT1kx8R2NcJc07kL', options);
|
||||||
|
//const CardsPromise = await fetch('https://us.api.blizzard.com/hearthstone/cards?locale=en_US&access_token=EUGIkMrnzNyG68ac33y5Tpn6MUtraN3QiV', options);
|
||||||
|
const CardsPromise = await fetch('https://us.api.blizzard.com/hearthstone/cards?locale=en_US&access_token=EUZvGOfXsMKYrjqLJp5mE7IJlhQuELMiPk', options);
|
||||||
|
//console.log("FETCH")
|
||||||
|
//console.log(CardsPromise)
|
||||||
|
|
||||||
|
|
||||||
|
//Then use the json method to get json data from api/
|
||||||
|
const CardsListJson = await CardsPromise.json(); //! ??????
|
||||||
|
console.log("jsoncardlist")
|
||||||
|
//console.log(CardsListJson['cards'])
|
||||||
|
|
||||||
|
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
const CardsList: Card[] = CardsListJson['cards'].map(elt => new Card(elt["id"] ? elt["id"] : 1,
|
const CardsList: Card[] = CardsListJson['cards'].map(elt => new Card(elt["id"] ? elt["id"] : 1,
|
||||||
elt["name"] ? elt["name"] : "",
|
elt["name"] ? elt["name"] : "",
|
||||||
elt["health"] ? elt["health"] : 0,
|
|
||||||
elt["attack"] ? elt["attack"] : 0,
|
|
||||||
elt["manaCost"] ? elt["manaCost"] : 0,
|
|
||||||
elt["rarityId"] ? elt["rarityId"] : 0,
|
|
||||||
elt["flavorText"] ? elt["flavorText"] : "",
|
|
||||||
elt["classId"] ? elt["classId"] : 0,
|
|
||||||
elt["multiClassIds"] ? elt["multiClassIds"] : "Nothing",
|
|
||||||
elt["image"] ? elt["image"] : "",
|
elt["image"] ? elt["image"] : "",
|
||||||
elt["imageGold"] ? elt["imageGold"] : "",
|
elt["imageGold"] ? elt["imageGold"] : "",
|
||||||
elt["cropImage"] ? elt["cropImage"] : "",
|
|
||||||
elt["artistName"] ? elt["artistName"] : "",
|
|
||||||
)); //, elt["cardSet"], elt["type"], elt["faction"], elt["rarity"], elt["cost"], elt["attack"], elt["health"],elt["text"], elt["flavor"], elt["artist"], elt["collectible"], elt["elite"], elt["race"], elt["img"], elt["imgGold"]
|
)); //, elt["cardSet"], elt["type"], elt["faction"], elt["rarity"], elt["cost"], elt["attack"], elt["health"],elt["text"], elt["flavor"], elt["artist"], elt["collectible"], elt["elite"], elt["race"], elt["img"], elt["imgGold"]
|
||||||
//elt["cardId"] == null ? elt["cardId"] : ""
|
//elt["cardId"] == null ? elt["cardId"] : ""
|
||||||
|
|
||||||
|
//console.log("TOTO")
|
||||||
|
|
||||||
|
console.log("CardsList")
|
||||||
//call the action
|
//call the action
|
||||||
|
|
||||||
dispatch(setCardsList(CardsList));
|
dispatch(setCardsList(CardsList));
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('Error---------', error);
|
console.log('Error---------', error);
|
||||||
|
//You can dispatch to another action if you want to display an error message in the application
|
||||||
|
//dispatch(fetchDataRejected(error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,10 +1,8 @@
|
|||||||
import { Card } from '../../models/Card';
|
import {ADD_FAVORITE_DATA, FETCH_DATA} from '../constants';
|
||||||
import { CardProps } from '../../props/favProps';
|
|
||||||
import {ADD_FAVORITE_DATA} from '../constants';
|
|
||||||
|
|
||||||
export const setFavList = (props : CardProps) => {
|
export const setFavList = (List: String[]) => {
|
||||||
return {
|
return {
|
||||||
type: ADD_FAVORITE_DATA,
|
type: ADD_FAVORITE_DATA,
|
||||||
payload: props,
|
payload: List,
|
||||||
};
|
};
|
||||||
}
|
}
|
@ -1,9 +0,0 @@
|
|||||||
import { Card } from '../../models/Card';
|
|
||||||
import {SET_FAVS} from '../constants';
|
|
||||||
|
|
||||||
export const setList = (list: []) => {
|
|
||||||
return {
|
|
||||||
type: SET_FAVS,
|
|
||||||
payload: list
|
|
||||||
};
|
|
||||||
}
|
|
@ -0,0 +1,15 @@
|
|||||||
|
import {ADD_FAVORITE_DATA, FETCH_DATA} from '../constants';
|
||||||
|
|
||||||
|
export const setList = (List: String[]) => {
|
||||||
|
return {
|
||||||
|
type: FETCH_DATA,
|
||||||
|
payload: List,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// export const setFavList = (List: String[]) => {
|
||||||
|
// return {
|
||||||
|
// type: ADD_FAVORITE_DATA,
|
||||||
|
// payload: List,
|
||||||
|
// };
|
||||||
|
// }
|
@ -0,0 +1,13 @@
|
|||||||
|
import {THEME_CHANGE} from '../constants';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// switch mode according to what is specified...
|
||||||
|
|
||||||
|
//@ts-ignore
|
||||||
|
export const switchMode = (mode) => {
|
||||||
|
return {
|
||||||
|
type: THEME_CHANGE,
|
||||||
|
payload: mode,
|
||||||
|
};
|
||||||
|
};
|
@ -1,6 +1,9 @@
|
|||||||
|
|
||||||
|
|
||||||
export const FETCH_DATA = "FETCH_DATA"
|
export const FETCH_DATA = "FETCH_DATA"
|
||||||
|
|
||||||
export const ADD_FAVORITE_DATA = "ADD_FAVORITE_DATA"
|
export const ADD_FAVORITE_DATA = "ADD_FAVORITE_DATA"
|
||||||
|
|
||||||
export const SET_FAVS = "SET_FAVS"
|
export const DISPLAY_ALL_CARD = "DISPLAY_ALL_CARD"
|
||||||
|
|
||||||
|
export const THEME_CHANGE = 'THEME_CHANGE';
|
@ -1,91 +0,0 @@
|
|||||||
import { StyleSheet, Text, View, Button, FlatList } from 'react-native';
|
|
||||||
import { StatusBar } from 'expo-status-bar';
|
|
||||||
import React, { useState } from "react";
|
|
||||||
import { Card } from '../models/Card';
|
|
||||||
|
|
||||||
import { Table, Row, Rows } from 'react-native-table-component';
|
|
||||||
|
|
||||||
//* Components
|
|
||||||
import {ListItemComponent} from '../components/ListItemComponent'
|
|
||||||
import { ScrollView } from 'react-native-gesture-handler';
|
|
||||||
|
|
||||||
//@ts-ignore
|
|
||||||
export default function DetailMain({ route }){
|
|
||||||
|
|
||||||
const { card, other } = route.params;
|
|
||||||
|
|
||||||
const tableHead = ['Stat', 'Value'];
|
|
||||||
const tableData = [
|
|
||||||
['Mana cost :', card.manaCost],
|
|
||||||
['Attack : ', card.attack],
|
|
||||||
['Health : ', card.health],
|
|
||||||
['Rarity : ', card.rarity],
|
|
||||||
['Artist : ', card.artistName],
|
|
||||||
['Class : ', card.classId],
|
|
||||||
]
|
|
||||||
|
|
||||||
let pressed : Boolean = false
|
|
||||||
const [titleText, setTitleText] = useState(card.name)
|
|
||||||
const onPressTitle = () => {
|
|
||||||
pressed ? setTitleText(titleText + '\n' + 'Id : ' + card.id) : setTitleText(card.name)
|
|
||||||
pressed ? pressed = false :pressed = true
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<View style={styles.container}>
|
|
||||||
<View style={styles.titleView}>
|
|
||||||
<Text style={styles.title} onPress={onPressTitle} >{titleText}</Text>
|
|
||||||
<Text style={styles.flavor}>{card.flavorText}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={styles.item}>
|
|
||||||
<ListItemComponent url={card.img}/>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<ScrollView>
|
|
||||||
<Table borderStyle={{borderWidth: 5, borderColor: '#c8e1ff'}}>
|
|
||||||
<Row data={tableHead} style={styles.head} textStyle={styles.text}/>
|
|
||||||
<Rows data={tableData} textStyle={styles.text}/>
|
|
||||||
</Table>
|
|
||||||
</ScrollView>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
container: {
|
|
||||||
flex: 1,
|
|
||||||
backgroundColor: '#fff',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
},
|
|
||||||
item: {
|
|
||||||
borderRadius : 15,
|
|
||||||
backgroundColor: '#efefef',
|
|
||||||
padding: 15,
|
|
||||||
maxHeight:300,
|
|
||||||
maxWidth:350,
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
fontStyle: "italic",
|
|
||||||
fontWeight: "bold",
|
|
||||||
fontSize: 20,
|
|
||||||
},
|
|
||||||
flavor: {
|
|
||||||
fontStyle: "italic",
|
|
||||||
},
|
|
||||||
titleView: {
|
|
||||||
flex: 1,
|
|
||||||
justifyContent: 'center',
|
|
||||||
paddingVertical: 0,
|
|
||||||
paddingHorizontal : 15,
|
|
||||||
backgroundColor: '#fff',
|
|
||||||
borderRadius : 10,
|
|
||||||
borderWidth: 10,
|
|
||||||
borderColor: '#efefef',
|
|
||||||
maxHeight:100,
|
|
||||||
minHeight: 100
|
|
||||||
},
|
|
||||||
head: { height: 40, backgroundColor: '#f1f8ff', minWidth: '90%'},
|
|
||||||
text: { margin: 6 }
|
|
||||||
});
|
|
@ -1,108 +1,110 @@
|
|||||||
import { StyleSheet, Text, View, TouchableNativeFeedback, Image } from 'react-native';
|
import { StyleSheet, Text, View, TouchableNativeFeedback } from 'react-native';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useNavigation } from '@react-navigation/native';
|
||||||
import { useEffect } from 'react';
|
import { NavigationContainer } from '@react-navigation/native';
|
||||||
import { getAllCards } from "../redux/actions/actionSelection"
|
import { useTheme } from 'react-native-paper';
|
||||||
import StorageHeart from '../service/AsyncStorage';
|
import StackNavigation from '../navigation/StackNavigation'
|
||||||
import { setList } from '../redux/actions/action_setFavs';
|
import { Colors } from 'react-native/Libraries/NewAppScreen';
|
||||||
import Navigation from '../navigation/Navigation';
|
import {useDispatch, useSelector} from 'react-redux';
|
||||||
|
|
||||||
|
|
||||||
// @ts-ignore //
|
|
||||||
export default function HomeScreen({navigation}) {
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
//chargement des listes
|
|
||||||
useEffect(() => {
|
|
||||||
|
|
||||||
const loadFavCards = async () => {
|
|
||||||
//@ts-ignore
|
|
||||||
const list = await StorageHeart.getItem("favoriteList")
|
|
||||||
|
|
||||||
if(list!=null){
|
//import Button from 'react-bootstrap/Button';
|
||||||
//@ts-ignore
|
|
||||||
dispatch(setList(list))
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
// @ts-ignore //(ta gueule pour l'erreur sur navigation)
|
||||||
loadFavCards();
|
export default function HomeScreen({navigation}) {
|
||||||
}, [dispatch]);
|
|
||||||
useEffect(() => {
|
|
||||||
|
|
||||||
const loadCards = async () => {
|
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
await dispatch(getAllCards());
|
const mode = useSelector(state => state.appReducer.mode);
|
||||||
};
|
|
||||||
loadCards();
|
|
||||||
}, [dispatch]);
|
|
||||||
|
|
||||||
|
const { colors } = useTheme();
|
||||||
|
|
||||||
|
|
||||||
// affichage de la homePage
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={mode === 'light' ? styles.container_light : styles.container_dark}>
|
||||||
<View style={styles.centered}>
|
<View style={styles.centered}>
|
||||||
<Text style={styles.title}>DeckStone</Text>
|
<Text style={styles.title}>Mes super Nounours !</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text style={styles.txt}>Votre gestionnaire de cartes Hearthstone </Text>
|
<Text>Mon super texte ...</Text>
|
||||||
{/* <MyCustomComponent /> */}
|
{/* <MyCustomComponent /> */}
|
||||||
<View style={styles.MidArea}>
|
<View style={styles.MidArea}>
|
||||||
<Image source={require("../assets/logo.png")} style={styles.ige} resizeMode='cover' ></Image>
|
<Text style={styles.textStyle}>Nous sommes actuellement dans l'écran d'accueil !</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.butContain}>
|
<View style={styles.MidArea}>
|
||||||
|
<View>
|
||||||
|
<Text style={styles.t3}> Vous cherchez une entités ? </Text>
|
||||||
<TouchableNativeFeedback onPress={() => navigation.navigate("ListScreen")}>
|
<TouchableNativeFeedback onPress={() => navigation.navigate("ListScreen")}>
|
||||||
<Text style={styles.ButtonStyle}>List</Text>
|
<Text style={styles.ButtonStyle}> Consulter la liste global !</Text>
|
||||||
</TouchableNativeFeedback>
|
</TouchableNativeFeedback>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View>
|
||||||
|
<Text style={styles.t3}> Vous avez des entités favorites ? </Text>
|
||||||
<TouchableNativeFeedback onPress={() => navigation.navigate("ListFav")}>
|
<TouchableNativeFeedback onPress={() => navigation.navigate("ListFav")}>
|
||||||
<Text style={styles.ButtonStyle}>Favoris</Text>
|
<Text style={styles.ButtonStyle}>Aller sur la page de favoris !</Text>
|
||||||
</TouchableNativeFeedback>
|
</TouchableNativeFeedback>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
</View>
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
backgroundColor: "#ac9585",
|
backgroundColor: "darksalmon",
|
||||||
alignItems: "center"
|
alignItems: "center"
|
||||||
},
|
},
|
||||||
centered: {
|
centered: {
|
||||||
alignItems: "center"
|
alignItems: "center"
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
fontSize: 56,
|
fontSize: 20,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
|
|
||||||
},
|
},
|
||||||
MidArea: {
|
MidArea: {
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
|
backgroundColor: "white",
|
||||||
|
paddingTop: 50,
|
||||||
|
paddingBottom: 50,
|
||||||
|
margin: 40,
|
||||||
|
padding: 2,
|
||||||
|
|
||||||
|
borderRadius: 15,
|
||||||
|
borderWidth:5,
|
||||||
|
borderColor:"#ababab"
|
||||||
},
|
},
|
||||||
txt: {
|
textStyle: {
|
||||||
|
textAlign: "center",
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
},
|
},
|
||||||
butContain: {
|
|
||||||
flexDirection: 'row'
|
|
||||||
|
|
||||||
},
|
|
||||||
ige:{
|
|
||||||
maxWidth: "110%",
|
|
||||||
maxHeight: 400,
|
|
||||||
},
|
|
||||||
|
|
||||||
ButtonStyle :{
|
ButtonStyle :{
|
||||||
backgroundColor: "#F5F5F5",
|
backgroundColor: "#2E8AE6",
|
||||||
borderRadius: 15,
|
borderRadius: 15,
|
||||||
padding: 20,
|
padding: 20,
|
||||||
color: "black",
|
color: "white",
|
||||||
fontSize : 36,
|
fontSize : 20,
|
||||||
width: "45%",
|
fontWeight: 'bold',
|
||||||
textAlign: 'center',
|
},
|
||||||
margin: 10,
|
t3 :{
|
||||||
|
fontSize : 20,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
},
|
},
|
||||||
|
container_light: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
container_dark: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: '#3e3e42',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
text_light: {
|
||||||
|
marginBottom: 20,
|
||||||
|
color: '#000'
|
||||||
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
import { Card } from "../models/Card";
|
|
||||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
||||||
|
|
||||||
|
|
||||||
export default class StorageHeart {
|
|
||||||
|
|
||||||
static async getItem(key: string): Promise<any> {
|
|
||||||
try {
|
|
||||||
const value = await AsyncStorage.getItem(key);
|
|
||||||
|
|
||||||
if (value !== null) {
|
|
||||||
return JSON.parse(value);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`AsyncStorage getItem error: ${e}`);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async setItem(key: string, value: any): Promise<void> {
|
|
||||||
try {
|
|
||||||
await AsyncStorage.setItem(key, JSON.stringify(value));
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`AsyncStorage setItem error: ${e}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
|
||||||
|
export const lightTheme = {
|
||||||
|
backgroundColor: 'white',
|
||||||
|
textColor: 'black',
|
||||||
|
headerBackgroundColor: 'lightblue',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const darkTheme = {
|
||||||
|
backgroundColor: 'black',
|
||||||
|
textColor: 'white',
|
||||||
|
headerBackgroundColor: 'darkblue',
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const theme = {
|
||||||
|
backgroundColor: '#FFFFFF',
|
||||||
|
textColor: '#000000',
|
||||||
|
headerBackgroundColor: '#FFFFFF',
|
||||||
|
dark: false,
|
||||||
|
colors: {
|
||||||
|
primary: 'blue',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default theme;
|
@ -0,0 +1,6 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import theme from './theme';
|
||||||
|
|
||||||
|
const ThemeContext = React.createContext(theme);
|
||||||
|
|
||||||
|
export default ThemeContext;
|
@ -1,36 +0,0 @@
|
|||||||
import { configureStore } from "@reduxjs/toolkit";
|
|
||||||
import testReducer from "../setup/testReducer";
|
|
||||||
import { Provider } from "react-redux";
|
|
||||||
import { render } from "react-dom";
|
|
||||||
import Item from "../../components/ListItemComponent";
|
|
||||||
|
|
||||||
jest.useFakeTimers();
|
|
||||||
|
|
||||||
const store = configureStore({
|
|
||||||
reducer: {
|
|
||||||
appReducer: testReducer,
|
|
||||||
},
|
|
||||||
middleware: (getDefaultMiddleWare) =>
|
|
||||||
getDefaultMiddleWare({
|
|
||||||
serializableCheck: false
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const Wrapper = ({children}) => (<Provider store={store}>{children}</Provider>);
|
|
||||||
|
|
||||||
describe('<Item/>', ()=> {
|
|
||||||
test('Assert displayed values', () =>{
|
|
||||||
const expectedCardInfos = store.getState().appReducer.cards[0];
|
|
||||||
|
|
||||||
render(<Wrapper>
|
|
||||||
<Item route={{
|
|
||||||
card: expectedCardInfos,
|
|
||||||
bool: false
|
|
||||||
}} ></Item>
|
|
||||||
</Wrapper>)
|
|
||||||
|
|
||||||
expect(screen.getByTestId('card-url')).toHaveProperty("source", {uri: expectedCardInfos.img})
|
|
||||||
|
|
||||||
|
|
||||||
})
|
|
||||||
})
|
|
@ -1,44 +0,0 @@
|
|||||||
import { configureStore } from "@reduxjs/toolkit";
|
|
||||||
import testReducer from "../setup/testReducer";
|
|
||||||
import { Provider } from "react-redux";
|
|
||||||
import { render } from "react-dom";
|
|
||||||
import Item from "../../components/ListItemComponent";
|
|
||||||
import { fireEvent } from "@testing-library/react-native";
|
|
||||||
import AsyncStorageMock from "../../__mocks__/@react-native-community/async-storage";
|
|
||||||
|
|
||||||
|
|
||||||
jest.useFakeTimers();
|
|
||||||
|
|
||||||
const store = configureStore({
|
|
||||||
reducer: {
|
|
||||||
appReducer: testReducer,
|
|
||||||
},
|
|
||||||
middleware: (getDefaultMiddleWare) =>
|
|
||||||
getDefaultMiddleWare({
|
|
||||||
serializableCheck: false
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const Wrapper = ({children}) => (<Provider store={store}>{children}</Provider>);
|
|
||||||
|
|
||||||
describe('<Item/>', ()=> {
|
|
||||||
test('Assert displayed values for fav list', () =>{
|
|
||||||
const expectedCardInfos = store.getState().appReducer.favoriteCards[0];
|
|
||||||
|
|
||||||
render(<Wrapper>
|
|
||||||
<Item route={{
|
|
||||||
card: expectedCardInfos,
|
|
||||||
bool: true,
|
|
||||||
}} ></Item>
|
|
||||||
</Wrapper>)
|
|
||||||
|
|
||||||
expect(screen.getByTestId('card-url')).toHaveProperty("source", {uri: expectedCardInfos.img})
|
|
||||||
|
|
||||||
let size = store.getState().appReducer.favoriteCards.length;
|
|
||||||
|
|
||||||
fireEvent.press(screen.getByTestId("button"))
|
|
||||||
|
|
||||||
expect(store.getState().appReducer.favoriteCards.length).toBe(size - 1)
|
|
||||||
|
|
||||||
})
|
|
||||||
});
|
|
@ -1,14 +0,0 @@
|
|||||||
import { Card } from "../../models/Card";
|
|
||||||
import { setCardsList } from "../../redux/actions/action_setCardsList";
|
|
||||||
|
|
||||||
describe('setCardLIst',() => {
|
|
||||||
it('should take the list', () => {
|
|
||||||
const payload = [new Card("1","test1","",""),new Card("2","test2","","",true)];
|
|
||||||
const expectation = {
|
|
||||||
type: "FETCH_DATA",
|
|
||||||
payload: payload,
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(setCardsList(payload)).toEqual(expectation);
|
|
||||||
})
|
|
||||||
})
|
|
@ -1,16 +0,0 @@
|
|||||||
import { Card } from "../../models/Card";
|
|
||||||
import { setFavList } from "../../redux/actions/action_setFavList";
|
|
||||||
import { setList } from "../../redux/actions/action_setFavs";
|
|
||||||
|
|
||||||
|
|
||||||
describe('setFavs',() => {
|
|
||||||
it('should take the list', () => {
|
|
||||||
const payload = [new Card("1","test1","",""),new Card("2","test2","","",true)];
|
|
||||||
const expectation = {
|
|
||||||
type: "SET_FAVS",
|
|
||||||
payload: payload,
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(setList(payload)).toEqual(expectation);
|
|
||||||
})
|
|
||||||
})
|
|
@ -1,25 +0,0 @@
|
|||||||
import appReducer from "../../redux/reducers/appReducer";
|
|
||||||
|
|
||||||
describe('Test Reducer', () => {
|
|
||||||
let initialState = {
|
|
||||||
cards: [],
|
|
||||||
favoriteCards: []
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should return initial state', () => {
|
|
||||||
expect(appReducer(undefined, {})).toEqual(initialState);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle FETCH_DATA', () => {
|
|
||||||
const payload = [new Card("1","test1","",""),new Card("2","test2","","",true)];
|
|
||||||
expect(
|
|
||||||
appReducer(initialState, {
|
|
||||||
type: "FETCH_DATA",
|
|
||||||
payload,
|
|
||||||
})
|
|
||||||
).toEqual({
|
|
||||||
cards: payload,
|
|
||||||
favoriteCards: [],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,3 +0,0 @@
|
|||||||
import mockAsyncStorage from '@react-native-async-storage/async-storage/jest/async-storage-mock';
|
|
||||||
|
|
||||||
jest.mock('@react-native-community/async-storage', () => mockAsyncStorage);
|
|
@ -1,13 +0,0 @@
|
|||||||
import { Card } from "../../models/Card";
|
|
||||||
import appReducer from "../../redux/reducers/appReducer"
|
|
||||||
|
|
||||||
const initialState = {
|
|
||||||
cards: [new Card("1","test1","url1","urlGold2")],
|
|
||||||
favoriteCards: [new Card("1","test1","url1","urlGold2")]
|
|
||||||
}
|
|
||||||
|
|
||||||
//@ts-ignore
|
|
||||||
export default testReducer = (state = initialState, action) => {
|
|
||||||
//@ts-ignore
|
|
||||||
return appReducer(initialState,action);
|
|
||||||
}
|
|
Loading…
Reference in new issue