Compare commits

..

8 Commits

@ -1,13 +1,21 @@
import { StatusBar } from 'expo-status-bar'; import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View, Button } from 'react-native'; import { StyleSheet, Text, View, Button } from 'react-native';
import React, { useState } from "react"; import React, { useState, useEffect } from "react";
import Navigation from './navigation/Navigation'; import Navigation from './navigation/Navigation';
import StackNavigation from './navigation/StackNavigation'; import StackNavigation from './navigation/StackNavigation';
import { NavigationContainer, DefaultTheme } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack'; import { createStackNavigator } from '@react-navigation/stack';
import {useDispatch, useSelector} from 'react-redux';
import store from './redux/store'; import store from './redux/store';
//import { DefaultTheme, Provider as PaperProvider } from 'react-native-paper';
// Import your application store // Import your application store
//import store from "./redux/store"; //import store from "./redux/store";
@ -20,8 +28,11 @@ import {
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
export default function App() { export default function App() {
//safearea
//mettre le navigateur ayant le princ // const [isDarkTheme, setIsDarkTheme] = React.useState(false);
// const theme = isDarkTheme ? darkTheme : defaultTheme;
return ( return (
<Provider store={store}> <Provider store={store}>
<SafeAreaProvider> <SafeAreaProvider>
@ -32,18 +43,20 @@ export default function App() {
} }
////<PaperProvider theme={theme}></PaperProvider>
///décommenter quand API Fonctionnel/// // ///décommenter quand API Fonctionnel///
// export default function App() { // // export default function App() {
// return ( // // return (
// <> // // <>
// {/* Bind your application store to the Provider store */} // // {/* Bind your application store to the Provider store */}
// <Provider store={store}> // // <Provider store={store}>
// <SafeAreaProvider> // // <SafeAreaProvider>
// <Navigation/> // // <Navigation/>
// </SafeAreaProvider> // // </SafeAreaProvider>
// </Provider> // // </Provider>
// </> // // </>
// ); // // );
// } // // }

@ -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>
---
&nbsp; ![Android](https://img.shields.io/badge/Android-3DDC84?style=for-the-badge&logo=android&logoColor=white)
&nbsp; ![Java](https://img.shields.io/badge/JavaScript-F7DF1E?style=for-the-badge&logo=javascript&logoColor=black)
---
</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** : ![Sketchs](./assets/Sketchs.PNG)
## ```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;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

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',
}
});

@ -1,37 +1,51 @@
import {isValidNumber} from "react-native-gesture-handler/lib/typescript/web_hammer/utils";
import {CardSet} from "./CardSet";
import {Type} from "./Type";
import {Classe} from "./Classe";
export class Card { export class Card {
constructor(id: string,name :string,health : string, attack : string, manaCost : string, rarityId : string, flavorText : string, classId : string, multiClassIds : any, img : string, imgGold : string, cropImage : string, artistName : string, fav : boolean){ constructor(id: number,name :string, img : string, imgGold : string){//,set : CardSet,type : Type,clas : Classe,rarity : string,cost : number,attack : number, health : number, desc : string,flavor : string,artist : string,collectible : boolean,elite : boolean,race : string, cropImg :string) {
this.id=id this._id=id
this.name=name this._name=name
//this._set=set //this._set=set
// this._type=type // this._type=type
// this._class=clas // this._class=clas
// this._rarity=rarity // this._rarity=rarity
this.manaCost=manaCost // this._cost=cost
this.attack = attack // this._attack = attack
this.health = health // this._health = health
this.rarityId = rarityId
// this._desc = desc // this._desc = desc
this.flavorText = flavorText // this._flavor = flavor
// this._artist = artist
this.classId = classId
this.multiClassIds = multiClassIds
this.artistName = artistName
// this._collectible = collectible // this._collectible = collectible
this.img = img this._img = img
this.imgGold = imgGold this._imgGold = imgGold
this.cropImage = cropImage // this._cropImg = cropImg
this.fav = fav
} }
// ID // // ID //
public id: string;
private _id : number;
get id(): number {
return this._id
}
// NAME // // NAME //
public name: string private _name : string;
get name(): string {
return this._name;
}
set name(value: string) {
this._name = value;
}
// private _set : CardSet;
// get set(): CardSet{
// return this._set
// }
// set set(value : CardSet){ // set set(value : CardSet){
// this._set = value // this._set = value
@ -62,15 +76,29 @@ export class Card {
// set rarity(value: string) { // set rarity(value: string) {
// this._rarity = value ; // this._rarity = value ;
// } // }
// private _cost : number
public manaCost : string; // get cost(): number {
// return this._cost;
// }
// set cost(value: number) {
// this._cost = value ;
public attack : string; // }
public health : string;
// private _attack : number
// get attack(): number {
// return this._attack;
// }
// set attack(value: number) {
// this._attack = value ;
// }
// private _health : number
// get health(): number {
// return this._health;
// }
// set health(value: number) {
// this._health = value ;
// }
// private _desc : string // private _desc : string
// get desc(): string { // get desc(): string {
// return this._desc; // return this._desc;
@ -78,17 +106,23 @@ export class Card {
// set desc(value: string) { // set desc(value: string) {
// this._desc = value ; // this._desc = value ;
// } // }
public classId : string;
public flavorText : string
public rarityId : string
public multiClassIds : any; // private _flavor : string
// get flavor(): string {
// return this._flavor;
// }
// set flavor(value: string) {
// this._flavor = value ;
// }
public artistName : string; // private _artist : string
// get artist(): string {
// return this._artist;
// }
// set artist(value: string) {
// this._artist = value ;
// }
// private _collectible : boolean // private _collectible : boolean
// get collectible(): boolean { // get collectible(): boolean {
// return this._collectible; // return this._collectible;
@ -97,11 +131,28 @@ export class Card {
// this._collectible = value; // this._collectible = value;
// } // }
public img : string; private _img : string
public imgGold : string;
public cropImage : string; get img(): string {
//FAV// return this._img;
public fav : boolean; }
set img(value: string) {
this._img = value ;
}
private _imgGold : string
get imgGold(): string {
return this._imgGold;
}
set imgGold(value: string) {
this._imgGold = value ;
}
// private _cropImg : string
// get cropImg(): string {
// return this._cropImg;
// }
// set cropImg(value: string) {
// this._cropImg = value;
// }
} }

@ -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>
// )
// }

@ -1,20 +1,96 @@
import { StatusBar } from 'expo-status-bar';
import {useColorScheme, StyleSheet, Text, View, Button } from 'react-native';
import React, { useState } from "react"; import React, { useState } from "react";
import { NavigationContainer } from '@react-navigation/native'; import { NavigationContainer, useTheme, DefaultTheme} from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import HomeScreen from '../screens/HomeScreen';
import ListScreen from '../screens/ListScreen'; import ListScreen from '../screens/ListScreen';
import ListFav from '../screens/ListFav'; import ListFav from '../screens/ListFav';
import TabBarIcon from '../components/TabBarIcon'; import TabBarIcon from '../components/TabBarIcon';
import StackNavigation from './StackNavigation'; import StackNavigation from './StackNavigation';
import DetailStackNav from './DetailStackNav';
import { Ionicons } from '@expo/vector-icons';
import { HeaderButton, HeaderButtons, Item } from 'react-navigation-header-buttons';
import { switchMode } from '../redux/actions/action_switchMode';
import {useDispatch, useSelector} from 'react-redux';
//@ts-ignore
function fHeaderButton(props) {
//@ts-ignore
return <HeaderButton {...props} IconComponent={Ionicons} iconSize={23} color="black" />;
}
export default function Navigation() { export default function Navigation() {
const BottomTabNavigator = createBottomTabNavigator(); const BottomTabNavigator = createBottomTabNavigator();
//@ts-ignore
const mode = useSelector(state => state.appReducer.mode);
const dispatch = useDispatch();
//* themes
//const { colors } = useTheme();
//const scheme = useColorScheme();
// const defaultTheme = {
// ...DefaultTheme,
// colors: {
// ...DefaultTheme.colors,
// primary: 'blue',
// accent: 'yellow',
// background: 'white',
// text: 'black',
// },
// };
// const darkTheme = {
// ...DefaultTheme,
// dark: true,
// colors: {
// ...DefaultTheme.colors,
// primary: '#BB86FC',
// accent: '#03DAC6',
// background: '#121212',
// surface: '#121212',
// text: '#FFFFFF',
// disabled: '#9E9E9E',
// placeholder: '#9E9E9E',
// backdrop: '#00000070',
// },
// };
// const MyTheme = {
// dark: false,
// colors: {
// primary: 'rgb(255, 45, 85)',
// background: 'rgb(242, 242, 242)',
// card: 'rgb(255, 255, 255)',
// text: 'rgb(28, 28, 30)',
// border: 'rgb(199, 199, 204)',
// notification: 'rgb(255, 69, 58)',
// },
// };
return ( return (
// <NavigationContainer theme={scheme === 'dark' ? darkTheme : DefaultTheme}>
<NavigationContainer> <NavigationContainer>
<BottomTabNavigator.Navigator initialRouteName="Home"> <BottomTabNavigator.Navigator
<BottomTabNavigator.Screen name="List" component={DetailStackNav} initialRouteName="Home"
screenOptions={({ route }) => ({
tabBarActiveTintColor: 'blue',
tabBarInactiveTintColor: 'gray',
})}
>
<BottomTabNavigator.Screen name="List" component={ListScreen}
options={{ options={{
title: 'List', title: 'List',
tabBarIcon: ({color}) => <TabBarIcon name="th-large" color={color}/>, tabBarIcon: ({color}) => <TabBarIcon name="th-large" color={color}/>,
@ -23,9 +99,20 @@ export default function Navigation() {
options={{ options={{
title: 'Home', title: 'Home',
tabBarIcon: ({color}) => <TabBarIcon name="home" color={color}/>, tabBarIcon: ({color}) => <TabBarIcon name="home" color={color}/>,
headerShown: false headerRight: () => (
<HeaderButtons HeaderButtonComponent={fHeaderButton}>
<Item
title="Paramètres"
iconName="md-settings"
onPress={() =>
dispatch(switchMode(mode === 'light' ? 'dark' : 'light'))}
/>
</HeaderButtons>
),
}} }}
/> />
<BottomTabNavigator.Screen name="ListFav" component={ListFav} <BottomTabNavigator.Screen name="ListFav" component={ListFav}
options={{ options={{
title: 'Liste de Favoris ♥', title: 'Liste de Favoris ♥',

@ -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;

@ -11,10 +11,16 @@ import ListFav from '../screens/ListFav';
export default function StackNavigation() { export default function StackNavigation() {
const Stack = createStackNavigator(); const Stack = createStackNavigator();
return ( return (
//<NavigationContainer>
<Stack.Navigator initialRouteName="Home"> <Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen}/> <Stack.Screen
name="Home"
component={HomeScreen}
options={{ headerShown: false }} // enlever l'header pour cet écran
/>
<Stack.Screen name="ListScreen" component={ListScreen}/> <Stack.Screen name="ListScreen" component={ListScreen}/>
<Stack.Screen name="ListFav" component={ListFav}/> <Stack.Screen name="ListFav" component={ListFav}/>
</Stack.Navigator> </Stack.Navigator>
//</NavigationContainer>
) )
} }

26908
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -6,61 +6,31 @@
"start": "expo start", "start": "expo start",
"android": "expo start --android", "android": "expo start --android",
"ios": "expo start --ios", "ios": "expo start --ios",
"web": "expo start --web", "web": "expo start --web"
"test": "jest"
},
"jest": {
"setupFiles": [
"./test/setup/jestSetupFile.js"
],
"preset": "jest-expo",
"verbose": true,
"transformIgnorePatterns": [
"node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg)"
],
"testMatch": [
"**.test.js"
],
"testEnvironment": "node",
"testEnvironmentOptions": {
"browsers": [
"chrome",
"firefox",
"safari"
]
}
}, },
"dependencies": { "dependencies": {
"@jest/globals": "^29.5.0",
"@react-native-async-storage/async-storage": "1.17.11",
"@react-navigation/bottom-tabs": "^6.5.5", "@react-navigation/bottom-tabs": "^6.5.5",
"@react-navigation/native": "^6.1.4", "@react-navigation/native": "^6.1.4",
"@react-navigation/native-stack": "^6.9.10", "@react-navigation/native-stack": "^6.9.10",
"@react-navigation/stack": "^6.3.14", "@react-navigation/stack": "^6.3.14",
"@reduxjs/toolkit": "^1.9.3", "@reduxjs/toolkit": "^1.9.3",
"@testing-library/jest-native": "^5.4.2",
"@testing-library/react-native": "^12.0.1",
"expo": "^48.0.0", "expo": "^48.0.0",
"expo-status-bar": "~1.4.4", "expo-status-bar": "~1.4.4",
"jest": "^29.2.1",
"jest-expo": "^48.0.2",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"react-native": "0.71.3", "react-native": "0.71.3",
"react-native-gesture-handler": "~2.9.0", "react-native-gesture-handler": "~2.9.0",
"react-native-paper": "^5.4.1",
"react-native-safe-area-context": "4.5.0", "react-native-safe-area-context": "4.5.0",
"react-native-table-component": "^1.2.2",
"react-native-web": "~0.18.11", "react-native-web": "~0.18.11",
"react-navigation-header-buttons": "^10.0.0",
"react-redux": "^8.0.5", "react-redux": "^8.0.5",
"redux": "^4.2.1" "redux": "^4.2.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.20.0", "@babel/core": "^7.20.0",
"@react-native-async-storage/async-storage": "^1.18.1",
"@types/jest": "^29.5.0",
"@types/react": "~18.0.27", "@types/react": "~18.0.27",
"@types/react-native": "~0.70.6", "@types/react-native": "~0.70.6",
"@types/react-native-table-component": "^1.2.4",
"typescript": "^4.9.4" "typescript": "^4.9.4"
}, },
"private": true "private": true

@ -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,5 +1,8 @@
import { Card } from '../../models/Card'; import {ADD_FAVORITE_DATA, FETCH_DATA} from '../constants';
import {FETCH_DATA} from '../constants';
//? Changer cette importe quand la classe sera definit dans un fichier correctement.
import {Card} from '../../models/Card'
export const setCardsList = (List: Card[]) => { export const setCardsList = (List: Card[]) => {
return { return {
type: FETCH_DATA, type: FETCH_DATA,

@ -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,49 +1,24 @@
import { CardProps } from '../../props/favProps' import {THEME_CHANGE, FETCH_DATA, ADD_FAVORITE_DATA} from '../constants'
import {FETCH_DATA, ADD_FAVORITE_DATA, SET_FAVS} from '../constants'
import StorageHeart from '../../service/AsyncStorage'
const initialState = { const initialState = {
mode: 'light',
cards: [], cards: [],
favoriteCards: [] favoriteCards: []
} }
// @ts-ignore // @ts-ignore
export default appReducer = (state = initialState, action) => { export default appReducer = (state = initialState, action) => {
switch (action.type) { switch (action.type) {
case ADD_FAVORITE_DATA: //Ajout d'une carte aux favoris case ADD_FAVORITE_DATA:
const a : CardProps = action.payload
if(a.route.bool ==false){
if(state.favoriteCards == undefined){
const tab = [a.route.card]
StorageHeart.setItem("favoriteList",tab)
return {...state, favoriteCards : tab};
}
// @ts-ignore // @ts-ignore
if( Array.from(state.favoriteCards).every((elem) => elem.id != a.route.card.id)){ return {...state, favoriteCards: state.favoriteCards.push(action.payload)};
case FETCH_DATA:
// @ts-ignore // @ts-ignore
const tab = state.favoriteCards.concat([a.route.card])
StorageHeart.setItem("favoriteList",tab)
return {...state, favoriteCards : tab};
}
return {...state}
}
else{
const tab = state.favoriteCards.filter((item) => item!= a.route.card)
StorageHeart.setItem("favoriteList",tab)
return {...state, favoriteCards : tab };
}
case FETCH_DATA: //Récupération des données des cartes depuis l'API
return {...state, cards: action.payload}; return {...state, cards: action.payload};
case THEME_CHANGE:
return {...state, mode: action.payload};
case SET_FAVS: //Récupération des favoris depuis l'async storage
//@ts-ignore
return {...state, favoriteCards: action.payload}
default: default:
return state; return state;
} }

@ -1,6 +1,7 @@
import {configureStore} from '@reduxjs/toolkit' import {configureStore} from '@reduxjs/toolkit'
import appReducer from './reducers/appReducer'; import appReducer from './reducers/appReducer';
// Reference here all your application reducers
const reducer = { const reducer = {
appReducer: appReducer, appReducer: appReducer,
} }
@ -9,6 +10,7 @@ const reducer = {
const store = configureStore({ const store = configureStore({
// @ts-ignore // @ts-ignore
reducer, reducer,
// @ts-ignore // @ts-ignore
middleware: (getDefaultMiddleware) => getDefaultMiddleware({serializableCheck : false}), //desactive le check de la serialization (primitif) middleware: (getDefaultMiddleware) => getDefaultMiddleware({serializableCheck : false}), //desactive le check de la serialization (primitif)
},); },);

@ -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,52 +1,91 @@
import { StyleSheet, Text, View, Button, FlatList } from 'react-native';
import { StyleSheet, View, TouchableHighlight, TextInput } from 'react-native'; import { StatusBar } from 'expo-status-bar';
import React, { useState } from "react"; import React, { useState } from "react";
import { FlatList } from 'react-native-gesture-handler';
import {useSelector} from 'react-redux';
import { Card } from '../models/Card'; import {setFavList } from '../redux/actions/action_setFavList';
import Item from '../components/ListeFavComponent'; //redux
import {useDispatch, useSelector} from 'react-redux';
import {useEffect} from 'react';
export const Cardslist = [
{
id: '1',
title: "premier élément",
},
{
id: '2',
title: "second élément",
},
{
id: '3',
title: "élément",
},
{
id: '4',
title: "barman douteux",
},
{
id: '10',
title: "dernier élément",
}
];
//@ts-ignore
export default function ListScreen({navigation}){
//@ts-ignore //@ts-ignore
var nList : Card[] = useSelector(state => state.appReducer.favoriteCards); const Item = ({title}) => (
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
const [searchValue, setSearchValue] = useState('');
const filteredList = nList.filter(item => item.name.toLowerCase().includes(searchValue.toLowerCase()));
//TODO
// export const getFavList = () => {
// //In order to use await your callback must be asynchronous using async keyword.
// return async dispatch => {
// //Then perform your asynchronous operations.
// try {
// //Have it first fetch data from our starwars url.
// //const nounoursPromise = await fetch('https://iut-weather-api.azurewebsites.net/nounours');
// //Then use the json method to get json data from api/
// //const nounoursListJson = await nounoursPromise.json();
// //const nounoursList: Nounours[] = nounoursListJson.map(elt => new Nounours(elt["name"], elt["age"], elt["nbPoils"], elt["image"]));
// dispatch(setFavList(Array{id,title}));
// } catch (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))
// }
// }
// }
return (
<View style={styles.container}>
<TextInput
style={styles.textInput}
value={searchValue}
onChangeText={text => setSearchValue(text)}
placeholder="Rechercher une carte...."
/>
<FlatList
numColumns={2}
data={filteredList}
renderItem={({item}) =>
<TouchableHighlight onPress={() => navigation.navigate("DetailCard", {card :item, other : 'anything'})}>
<Item route={{
card: item,
bool: true
}} />
</TouchableHighlight>
}
keyExtractor={(item: Card) => item.id}
/>
</View>
export default function Main(){
const [count, setCount] = useState(0);
//@ts-ignore
const mode = useSelector(state => state.appReducer.mode);
return (
<View style={mode === 'light' ? styles.container_light : styles.container_dark}>
<Text>Maman, J4AI UNE LISTE DE FAVORIS ! !</Text>
<StatusBar style="auto" />
<Text>{count}</Text>
<Button onPress={()=> setCount(count+1)} title="+1"/>
<Button onPress={()=> setCount(count+2)} title="+2"/>
<Button onPress={()=> setCount(count+10)} title="+10"/>
<FlatList data={Cardslist}
renderItem={({item}) => <Item title={item.title} />}
keyExtractor={item => item.id}/>
</View>
); );
} }
@ -55,18 +94,29 @@ export default function ListScreen({navigation}){
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
backgroundColor: '#ac9585', backgroundColor: '#fff',
alignItems: 'center', alignItems: 'center',
justifyContent: 'space-evenly', justifyContent: 'center',
}, },
textInput: { item: {
padding: 15,
margin: 5,
width:200,
backgroundColor: '#ffffff',
borderRadius : 15, borderRadius : 15,
shadowColor: 'grey', backgroundColor: '#efefef',
textAlign:'center' padding: 20,
margin : 10,
},
title: {
fontStyle: "italic",
},
container_light: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
container_dark: {
flex: 1,
backgroundColor: '#121212',
alignItems: 'center',
justifyContent: 'center',
} }
}); });

@ -1,72 +1,91 @@
import { StyleSheet,View,TouchableHighlight, TextInput} from 'react-native'; import { StyleSheet, Text, View, Button, TouchableHighlight, Pressable } from 'react-native';
import React, { useState} from "react"; import { StatusBar } from 'expo-status-bar';
import React, { useState, useEffect } from "react";
import { FlatList } from 'react-native-gesture-handler'; import { FlatList } from 'react-native-gesture-handler';
import {useSelector} from 'react-redux'; import {useDispatch, useSelector} from 'react-redux';
import { ThunkAction } from 'redux-thunk';
//? possiblement à supprimer
import { getAllCards } from "../redux/actions/actionSelection"
import { Card } from '../models/Card'; import { Card } from '../models/Card';
import { FontAwesome } from '@expo/vector-icons'; import { Image } from 'react-native';
import { useDispatch } from "react-redux"; import { ImageURISource } from 'react-native';
import {CardProps} from "../props/favProps";
import { setFavList } from "../redux/actions/action_setFavList";
//* Components import { switchMode } from '../redux/actions/action_switchMode';
import { ListItemComponent } from '../components/ListItemComponent';
import Item from '../components/ListeFavComponent'; //@ts-ignore
const Item = ({url}) => (
// <View style={styles.item}>
// <Text style={styles.title}>{title}</Text>
// </View>
<View>
<Image
source={{uri:url}}
style={{flex:1, minHeight:250, minWidth:180}}/>
</View>
);
//@ts-ignore //@ts-ignore
export default function ListScreen({navigation}){ export default function ListScreen({navigation}){
const [count, setCount] = useState(0);
//@ts-ignore
const theme = useSelector(state => state.theme);
//@ts-ignore //@ts-ignore
var nList = useSelector(state => state.appReducer.cards); const nList = useSelector(state => state.appReducer.cards);
//@ts-ignore
const mode = useSelector(state => state.appReducer.mode);
// Create a const that will hold the react-redux events dispatcher
const dispatch = useDispatch();
//* Search : // Let's define a hook that will be used to update the rendered state after the return will be called
const [searchValue, setSearchValue] = useState(''); // You cannot perform side-effects outside of a useEffect hook
useEffect(() => {
const loadCards = async () => {
//@ts-ignore //@ts-ignore
const filteredList = nList.filter(item => item.name.toLowerCase().includes(searchValue.toLowerCase())); await dispatch(getAllCards());
};
loadCards();
}, [dispatch]);
// Handle changing the theme mode
const dispatch = useDispatch() //@ts-ignore
const HandleAddFav = (props : CardProps) => { const handleThemeChange = (mode) => {
dispatch(setFavList(props)); dispatch(switchMode(mode === 'light' ? 'dark' : 'light'));
} }
return (
<View style={styles.container}>
<TextInput
style={styles.textInput}
value={searchValue}
onChangeText={text => setSearchValue(text)}
placeholder="Rechercher une carte..."
/>
return (
<View style={mode === 'light' ? styles.container_light : styles.container_dark}>
<Pressable style={mode === 'light' ? styles.button_white : styles.button_black} onPress={() => handleThemeChange(mode)}>
<Text>Switch Mode</Text>
</Pressable>
<FlatList <FlatList
numColumns={2} numColumns={2}
data={filteredList} data={nList}
renderItem={({item}) => renderItem={({item}) =>
<View>
<TouchableHighlight style={styles.imageItem} onPress={() => navigation.navigate("DetailCard", {card :item, other : 'anything'})}> //<TouchableHighlight onPress={() => navigation.navigate("CardsDetails", {"card": item})}> //* mettre la page de detail ici, renvoi a home pour l'instant
<Item route={{ <TouchableHighlight onPress={() => navigation.navigate("ListFav")}>
card: item, <Item url={item.img}/>
bool: false
}} ></Item>
</TouchableHighlight> </TouchableHighlight>
</View>
}
keyExtractor={(item: Card) => item.id}
/>
</View>
} keyExtractor={(item: Card) => item.id.toString()}/>
</View>
); );
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
imageItem:{
zIndex : 0
},
container: { container: {
flex: 1, flex: 1,
backgroundColor: '#ac9585', backgroundColor: '#ac9585',
@ -74,32 +93,63 @@ const styles = StyleSheet.create({
justifyContent: 'space-evenly', justifyContent: 'space-evenly',
}, },
textInput: { border: {
padding: 15, flex: 1,
margin: 5, backgroundColor: '#ff0000',
width:200, maxHeight : 100,
backgroundColor: '#ffffff', borderWidth : 15,
borderRadius : 15, borderRadius : 15,
shadowColor: 'grey', borderColor : '#00ffaa',
textAlign:'center' alignItems: 'center',
justifyContent: 'center',
}, },
favoriteButtonNonFav: { item: {
position: 'absolute', borderRadius : 15,
top: 10, backgroundColor: '#efefef',
right: 10, padding: 20,
backgroundColor: 'red', margin : 10,
borderRadius: 50, },
padding: 10, title: {
zIndex : 1, fontStyle: "italic",
}, },
favoriteButtonFav: {
position: 'absolute', ///themes
top: 10, container_light: {
right: 10, flex: 1,
backgroundColor: 'red', backgroundColor: '#fff',
borderRadius: 50, alignItems: 'center',
padding: 10, justifyContent: 'center',
zIndex : 1, },
container_dark: {
flex: 1,
backgroundColor: '#3e3e42',
alignItems: 'center',
justifyContent: 'center',
},
text_light: {
marginBottom: 20,
color: '#000'
},
text_dark: {
marginBottom: 20,
color: "#fff"
},
button_black: {
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 12,
paddingHorizontal: 32,
borderRadius: 4,
elevation: 3,
backgroundColor: 'black',
},
button_white: {
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 12,
paddingHorizontal: 32,
borderRadius: 4,
elevation: 3,
backgroundColor: 'white',
} }
}); });

@ -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);
}

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save