diff --git a/JokesApp/App.tsx b/JokesApp/App.tsx
index 2e66510..6cfeeaa 100644
--- a/JokesApp/App.tsx
+++ b/JokesApp/App.tsx
@@ -3,14 +3,18 @@ import React from "react";
import {ListJokeScreen} from "./screens/ListJokeScreen";
import {Navigation} from "./navigation/Navigation";
import {darksalmonColor, indigo, purpleColor} from "./Theme";
+import {Provider} from "react-redux";
+import store from "./redux/store";
export default function App() {
return (
+
+
);
}
diff --git a/JokesApp/api/fetch.ts b/JokesApp/api/fetch.ts
new file mode 100644
index 0000000..e69de29
diff --git a/JokesApp/components/ListAllCategories.tsx b/JokesApp/components/ListAllCategories.tsx
index c081443..cb56ec0 100644
--- a/JokesApp/components/ListAllCategories.tsx
+++ b/JokesApp/components/ListAllCategories.tsx
@@ -1,20 +1,21 @@
import {Joke} from "../model/Joke";
import {Image, StyleSheet, Text, View} from "react-native";
import React from "react";
-import {indigo} from "../Theme";
+import {greyColor, indigo} from "../Theme";
+import {Categorie} from "../model/Categorie";
-type JokeListItemProps = {
- item: String;
+type ListAllCategories = {
+ item: Categorie;
}
-export function ListAllCategories(props: JokeListItemProps) {
+export function ListAllCategories(props: ListAllCategories) {
return (
- {props.item}
+ {props.item.name}
);
@@ -30,7 +31,7 @@ const styles = StyleSheet.create({
},
chip: {
borderRadius: 16,
- backgroundColor: 'rgba(140, 140, 161, 1)',
+ backgroundColor: greyColor,
padding: 5,
marginTop: 5,
alignSelf: 'flex-start',
diff --git a/JokesApp/model/Categorie.ts b/JokesApp/model/Categorie.ts
new file mode 100644
index 0000000..a960a92
--- /dev/null
+++ b/JokesApp/model/Categorie.ts
@@ -0,0 +1,24 @@
+import {Joke} from "./Joke";
+
+export class Categorie{
+
+ private _name : string;
+
+ private _number : number;
+
+
+ get name(): string {
+ return this._name;
+ }
+
+ get number(): number {
+ return this._number;
+ }
+
+ public constructor(name :string, number :number) {
+ this._name = name;
+ this._number = number;
+ }
+
+
+}
\ No newline at end of file
diff --git a/JokesApp/model/CategorieFactory.ts b/JokesApp/model/CategorieFactory.ts
new file mode 100644
index 0000000..1b6ec86
--- /dev/null
+++ b/JokesApp/model/CategorieFactory.ts
@@ -0,0 +1,12 @@
+import {Categorie} from "./Categorie";
+
+export class CategorieFactory {
+ public static createCategories(jsonArray: string): Categorie[] {
+ let array = [];
+ let json = JSON.parse(jsonArray);
+ json.forEach(function (categorie) {
+ array.push(new Categorie(categorie.name, categorie.number));
+ });
+ return array;
+ }
+}
\ No newline at end of file
diff --git a/JokesApp/model/JokeFactory.ts b/JokesApp/model/JokeFactory.ts
index 57597a3..1e4b563 100644
--- a/JokesApp/model/JokeFactory.ts
+++ b/JokesApp/model/JokeFactory.ts
@@ -1,5 +1,6 @@
import { CustomJoke } from "./CustomJoke";
import { SampleJoke } from "./SampleJoke";
+import {Categorie} from "./Categorie";
export class JokeFactory {
@@ -25,6 +26,4 @@ export class JokeFactory {
return array;
}
-
-
}
\ No newline at end of file
diff --git a/JokesApp/package-lock.json b/JokesApp/package-lock.json
index 0ef945d..be1dc40 100644
--- a/JokesApp/package-lock.json
+++ b/JokesApp/package-lock.json
@@ -12,12 +12,15 @@
"@react-navigation/bottom-tabs": "^6.5.11",
"@react-navigation/native": "^6.1.9",
"@react-navigation/stack": "^6.3.20",
+ "@reduxjs/toolkit": "^2.2.1",
"@types/react": "~18.2.45",
"expo": "~50.0.3",
"expo-status-bar": "~1.11.1",
"react": "18.2.0",
"react-native": "0.73.2",
"react-native-gesture-handler": "^2.15.0",
+ "react-redux": "^9.1.0",
+ "redux": "^5.0.1",
"typescript": "^5.3.0"
},
"devDependencies": {
@@ -6244,6 +6247,29 @@
"react-native-screens": ">= 3.0.0"
}
},
+ "node_modules/@reduxjs/toolkit": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.1.tgz",
+ "integrity": "sha512-8CREoqJovQW/5I4yvvijm/emUiCCmcs4Ev4XPWd4mizSO+dD3g5G6w34QK5AGeNrSH7qM8Fl66j4vuV7dpOdkw==",
+ "dependencies": {
+ "immer": "^10.0.3",
+ "redux": "^5.0.1",
+ "redux-thunk": "^3.1.0",
+ "reselect": "^5.0.1"
+ },
+ "peerDependencies": {
+ "react": "^16.9.0 || ^17.0.0 || ^18",
+ "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "react": {
+ "optional": true
+ },
+ "react-redux": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@segment/loosely-validate-event": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@segment/loosely-validate-event/-/loosely-validate-event-2.0.0.tgz",
@@ -6411,6 +6437,11 @@
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
"integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="
},
+ "node_modules/@types/use-sync-external-store": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
+ "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
+ },
"node_modules/@types/yargs": {
"version": "17.0.32",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
@@ -8839,6 +8870,15 @@
"node": ">=16.x"
}
},
+ "node_modules/immer": {
+ "version": "10.0.3",
+ "resolved": "https://registry.npmjs.org/immer/-/immer-10.0.3.tgz",
+ "integrity": "sha512-pwupu3eWfouuaowscykeckFmVTpqbzW+rXFCX8rQLkZzM9ftBmU/++Ra+o+L27mz03zJTlyV4UUr+fdKNffo4A==",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/immer"
+ }
+ },
"node_modules/import-fresh": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
@@ -12062,6 +12102,32 @@
"async-limiter": "~1.0.0"
}
},
+ "node_modules/react-redux": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.0.tgz",
+ "integrity": "sha512-6qoDzIO+gbrza8h3hjMA9aq4nwVFCKFtY2iLxCtVT38Swyy2C/dJCGBXHeHLtx6qlg/8qzc2MrhOeduf5K32wQ==",
+ "dependencies": {
+ "@types/use-sync-external-store": "^0.0.3",
+ "use-sync-external-store": "^1.0.0"
+ },
+ "peerDependencies": {
+ "@types/react": "^18.2.25",
+ "react": "^18.0",
+ "react-native": ">=0.69",
+ "redux": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "react-native": {
+ "optional": true
+ },
+ "redux": {
+ "optional": true
+ }
+ }
+ },
"node_modules/react-refresh": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
@@ -12123,6 +12189,19 @@
"node": ">=0.10.0"
}
},
+ "node_modules/redux": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
+ "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w=="
+ },
+ "node_modules/redux-thunk": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
+ "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
+ "peerDependencies": {
+ "redux": "^5.0.0"
+ }
+ },
"node_modules/regenerate": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
@@ -12234,6 +12313,11 @@
"path-parse": "^1.0.5"
}
},
+ "node_modules/reselect": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.0.tgz",
+ "integrity": "sha512-aw7jcGLDpSgNDyWBQLv2cedml85qd95/iszJjN988zX1t7AVRJi19d9kto5+W7oCfQ94gyo40dVbT6g2k4/kXg=="
+ },
"node_modules/resolve": {
"version": "1.22.8",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
@@ -13361,6 +13445,14 @@
"react": ">=16.8"
}
},
+ "node_modules/use-sync-external-store": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
+ "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
diff --git a/JokesApp/package.json b/JokesApp/package.json
index 5b6e36d..e1c4c48 100644
--- a/JokesApp/package.json
+++ b/JokesApp/package.json
@@ -13,12 +13,15 @@
"@react-navigation/bottom-tabs": "^6.5.11",
"@react-navigation/native": "^6.1.9",
"@react-navigation/stack": "^6.3.20",
+ "@reduxjs/toolkit": "^2.2.1",
"@types/react": "~18.2.45",
"expo": "~50.0.3",
"expo-status-bar": "~1.11.1",
"react": "18.2.0",
"react-native": "0.73.2",
"react-native-gesture-handler": "^2.15.0",
+ "react-redux": "^9.1.0",
+ "redux": "^5.0.1",
"typescript": "^5.3.0"
},
"devDependencies": {
diff --git a/JokesApp/redux/actions/categoriesAction.ts b/JokesApp/redux/actions/categoriesAction.ts
new file mode 100644
index 0000000..178d976
--- /dev/null
+++ b/JokesApp/redux/actions/categoriesAction.ts
@@ -0,0 +1,33 @@
+import {Categorie} from "../../model/Categorie";
+import {CategorieFactory} from "../../model/CategorieFactory";
+
+
+export enum CategoriesActionType {
+ FETCH_CATEGORIES = 'FETCH_CATEGORIES',
+}
+
+export interface CategoriesAction {
+ type: CategoriesActionType;
+ payload: Categorie[];
+}
+
+export type Action = CategoriesAction;
+
+export const setCategories = (categories: Categorie[]): CategoriesAction => {
+ return {
+ type: CategoriesActionType.FETCH_CATEGORIES,
+ payload: categories
+ }
+}
+
+export const getCategorie = async() : Promise => {
+ try {
+ const categories = await fetch('https://iut-weather-api.azurewebsites.net/jokes/categories/top');
+ const categorieJson = await categories.text();
+ return CategorieFactory.createCategories(categorieJson);
+
+ }
+ catch (error) {
+ console.log('Error---------', error);
+ }
+}
\ No newline at end of file
diff --git a/JokesApp/redux/actions/sampleAction.ts b/JokesApp/redux/actions/sampleAction.ts
new file mode 100644
index 0000000..8e81c1b
--- /dev/null
+++ b/JokesApp/redux/actions/sampleAction.ts
@@ -0,0 +1,52 @@
+import {SampleJoke} from "../../model/SampleJoke";
+import {JokeFactory} from "../../model/JokeFactory";
+
+export enum SampleActionType {
+ FETCH_SAMPLE = 'FETCH_SAMPLE',
+ FECTH_LAST_JOKES = 'FECTH_LAST_JOKES'
+}
+
+export interface SampleAction {
+ type: SampleActionType;
+ payload: SampleJoke[];
+}
+
+export type Action = SampleAction;
+
+
+export const setSample = (sample: SampleJoke[]): SampleAction => {
+ return {
+ type: SampleActionType.FETCH_SAMPLE,
+ payload: sample
+ }
+}
+
+export const setRecentJokes = (recentJokes: SampleJoke[]): SampleAction => {
+ return {
+ type: SampleActionType.FECTH_LAST_JOKES,
+ payload: recentJokes
+ }
+}
+
+export const getSampleJoke = async() : Promise => {
+ try {
+ const sample = await fetch('https://iut-weather-api.azurewebsites.net/jokes/samples');
+ const sampleJson = await sample.text();
+ return JokeFactory.createSampleJokes(sampleJson);
+ }
+ catch (error) {
+ console.log('Error---------', error);
+ }
+}
+
+export const getLatestJokes = async() : Promise => {
+ try {
+ const sample = await fetch('https://iut-weather-api.azurewebsites.net/jokes/lasts');
+ const sampleJson = await sample.text();
+ return JokeFactory.createSampleJokes(sampleJson);
+ }
+ catch (error) {
+ console.log('Error---------', error);
+ }
+
+}
diff --git a/JokesApp/redux/constants.ts b/JokesApp/redux/constants.ts
new file mode 100644
index 0000000..e69de29
diff --git a/JokesApp/redux/reducers/categoryReducer.ts b/JokesApp/redux/reducers/categoryReducer.ts
new file mode 100644
index 0000000..04ffb17
--- /dev/null
+++ b/JokesApp/redux/reducers/categoryReducer.ts
@@ -0,0 +1,22 @@
+import {Action, CategoriesActionType} from "../actions/categoriesAction";
+import {Categorie} from "../../model/Categorie";
+
+interface State {
+ categories: Categorie[];
+}
+
+const initialState: State = {
+ categories: [],
+}
+// @ts-ignore
+export default appReducer = (state = initialState, action: Action) => {
+ switch (action.type) {
+ case CategoriesActionType.FETCH_CATEGORIES:
+ return {
+ ...state,
+ categories: action.payload
+ }
+ default:
+ return state;
+ }
+}
\ No newline at end of file
diff --git a/JokesApp/redux/reducers/sampleJokeReducer.ts b/JokesApp/redux/reducers/sampleJokeReducer.ts
new file mode 100644
index 0000000..af2bf1a
--- /dev/null
+++ b/JokesApp/redux/reducers/sampleJokeReducer.ts
@@ -0,0 +1,32 @@
+import {SampleJoke} from "../../model/SampleJoke";
+import {Action, SampleActionType} from "../actions/sampleAction";
+
+
+interface state {
+ sampleJoke: SampleJoke[];
+ recentJokes: SampleJoke[];
+}
+
+const initialState: state = {
+ sampleJoke: [],
+ recentJokes: [],
+}
+
+// @ts-ignore
+export default appReducer = (state = initialState, action: Action) => {
+ switch (action.type) {
+ case SampleActionType.FETCH_SAMPLE:
+ return {
+ ...state,
+ sampleJoke: action.payload
+ }
+ case SampleActionType.FECTH_LAST_JOKES:
+ return {
+ ...state,
+ recentJokes: action.payload
+ }
+
+ default:
+ return state;
+ }
+}
\ No newline at end of file
diff --git a/JokesApp/redux/store.ts b/JokesApp/redux/store.ts
new file mode 100644
index 0000000..28eabdb
--- /dev/null
+++ b/JokesApp/redux/store.ts
@@ -0,0 +1,19 @@
+import { configureStore } from '@reduxjs/toolkit';
+import categorieReducer from './reducers/categoryReducer';
+import sampleReducer from './reducers/sampleJokeReducer';
+
+const reducer = {
+ categorieReducer: categorieReducer,
+ sampleReducer: sampleReducer
+};
+
+// @ts-ignore
+const store = configureStore({
+ reducer: reducer,
+ middleware: (getDefaultMiddleware) =>
+ getDefaultMiddleware({
+ serializableCheck: false
+ })
+});
+
+export default store;
diff --git a/JokesApp/screens/AccueilScreen.tsx b/JokesApp/screens/AccueilScreen.tsx
index a05eb2b..339e8b3 100644
--- a/JokesApp/screens/AccueilScreen.tsx
+++ b/JokesApp/screens/AccueilScreen.tsx
@@ -1,28 +1,35 @@
import {FlatList, Image, SafeAreaView, ScrollView, SectionListComponent, StyleSheet, Text, View} from "react-native";
import {indigo, purpleColor} from "../Theme";
import {Joke} from "../model/Joke";
-import {DataGen, ListJokeScreen} from "./ListJokeScreen";
-import React from "react";
+import React, {useEffect} from "react";
import {HorizontalListJokeComponent} from "../components/HorizontalListJokeComponent";
import {ListAllCategories} from "../components/ListAllCategories";
import {CustomJoke} from "../model/CustomJoke";
+import {JokeFactory} from "../model/JokeFactory";
+import {JokeStub} from "../model/JokeStub";
+import {useDispatch, useSelector} from "react-redux";
+import {getLatestJokes, getSampleJoke, setRecentJokes, setSample} from "../redux/actions/sampleAction";
+import {getCategorie, setCategories} from "../redux/actions/categoriesAction";
+import {Categorie} from "../model/Categorie";
-let taille = DataGen.length;
-let LastJokes = DataGen.slice(taille - 20, taille);
-function filterCategory(jokes: Joke[]): String[] {
- let categories: String[] = [];
- jokes.forEach(joke => {
- if (!categories.includes(joke.type())) {
- categories.push(joke.type());
- }
- });
- return categories;
-}
export function AccueilScreen() {
- // Permet de filtrer les types des blagues pour les afficher dans la liste des categories
- const FiltereData = filterCategory(LastJokes);
+ const DataGen = useSelector((state: any) => state.sampleReducer.recentJokes);
+ const DataCate = useSelector((state: any) => state.categorieReducer.categories);
+ const dispatch = useDispatch();
+ useEffect(() => {
+
+ const getJokes = async () => {
+ dispatch(setRecentJokes(await getLatestJokes()));
+ };
+ getJokes();
+
+ const getTopCategories = async () => {
+ dispatch(setCategories(await getCategorie()));
+ };
+ getTopCategories();
+ }, [dispatch]);
return (
@@ -33,7 +40,7 @@ export function AccueilScreen() {
Dernieres Blagues
item.id.toString()}
/>
@@ -42,10 +49,9 @@ export function AccueilScreen() {
item.toString()}
- />
+ keyExtractor={(item : Categorie) => item.name}/>
);
}
@@ -88,4 +94,4 @@ categories: {
}
-});
+});
\ No newline at end of file
diff --git a/JokesApp/screens/ListJokeScreen.tsx b/JokesApp/screens/ListJokeScreen.tsx
index 4caac33..b12bbc4 100644
--- a/JokesApp/screens/ListJokeScreen.tsx
+++ b/JokesApp/screens/ListJokeScreen.tsx
@@ -1,20 +1,22 @@
import {FlatList, SafeAreaView, StyleSheet, Text, View} from "react-native";
-import React from "react";
+import React, {useEffect} from "react";
import {JokeListItems} from "../components/ListeJokeComponent";
-import {Joke} from "../model/Joke";
-import {JokeFactory} from "../model/JokeFactory";
-import {JokeStub} from "../model/JokeStub";
import {indigo, purpleColor} from "../Theme";
import {CustomJoke} from "../model/CustomJoke";
+import {useDispatch, useSelector} from 'react-redux';
+import {getLatestJokes, getSampleJoke, setSample} from "../redux/actions/sampleAction";
-const DATACUSTOM = JokeFactory.createCustomJokes(JokeStub.customJokes)
-const DATASAMPLE = JokeFactory.createSampleJokes(JokeStub.sampleJokes)
-
-//@ts-ignore
-export let DataGen: Joke[] = DATACUSTOM.concat(DATASAMPLE);
export function ListJokeScreen() {
+ const DataGen = useSelector((state: any) => state.sampleReducer.sampleJoke);
+ const dispatch = useDispatch();
+ useEffect(() => {
+ const getJokes = async () => {
+ dispatch(setSample(await getSampleJoke()));
+ };
+ getJokes();
+ }, [dispatch]);
return (