Merge pull request ' Add first part of theme' (#9) from part7 into master

Reviewed-on: #9
master
Antoine PEREDERII 1 year ago
commit 6bd58278a0

@ -1,5 +1,32 @@
import {Theme} from "@react-navigation/native";
export const indigoColor = "rgba(14, 14, 44, 1)"; export const indigoColor = "rgba(14, 14, 44, 1)";
export const purpleColor = "rgba(74, 74, 104, 1)"; export const purpleColor = "rgba(74, 74, 104, 1)";
export const darksalmonColor = "rgba(233, 150, 122, 1)"; export const darksalmonColor = "rgba(233, 150, 122, 1)";
export const greyColor = "rgba(140, 140, 161, 1)"; export const greyColor = "rgba(140, 140, 161, 1)";
export const whiteColor = "rgba(239, 239, 253, 1)"; export const whiteColor = "rgba(239, 239, 253, 1)";
export const DefaultTheme: Theme = {
dark: false,
colors: {
primary: 'rgb(0, 122, 255)',
background: "rgba(239, 239, 253, 1)",
card: 'rgb(255, 255, 255)',
text: 'rgb(28, 28, 30)',
border: 'rgb(216, 216, 216)',
notification: 'rgb(255, 59, 48)',
},
};
export const DarkTheme: Theme = {
dark: true,
colors: {
primary: 'rgb(10, 132, 255)',
background: "rgba(140, 140, 161, 1)",
card: 'rgb(18, 18, 18)',
text: 'rgb(229, 229, 231)',
border: 'rgb(39, 39, 41)',
notification: 'rgb(255, 69, 58)',
},
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 B

@ -1,16 +1,29 @@
import {StyleSheet, Text, View, Image, Button, TouchableOpacity} from 'react-native'; import {StyleSheet, Text, View, Image, Button, TouchableOpacity} from 'react-native';
import {SampleJoke} from "../model/SampleJoke"; import {SampleJoke} from "../model/SampleJoke";
import {darksalmonColor, whiteColor, greyColor, indigoColor, purpleColor} from "../assets/Theme"; import {darksalmonColor, whiteColor, greyColor, indigoColor, purpleColor} from "../assets/Theme";
import React, {useState} from "react"; import React, {useEffect, useState} from "react";
import {CustomJoke} from "../model/CustomJoke"; import {CustomJoke} from "../model/CustomJoke";
import {useDispatch, useSelector} from "react-redux";
import {getCustomJokeById, getJokeById} from "../redux/thunk/GetByThunk";
import {getCustomJokesList} from "../redux/thunk/GetThunk";
import {deleteCustomJoke} from "../redux/thunk/DeleteThunk";
import {useNavigation} from "@react-navigation/native";
import {AppDispatch} from "../redux/store";
type JokeItemProps = { type JokeItemProps = {
joke: SampleJoke | CustomJoke; joke: SampleJoke | CustomJoke;
}; };
const eye = require("../assets/eye_icon.png")
const hideEye = require("../assets/eye_off_icon.png")
const heart = require("../assets/favorite_icon.png")
const bin = require("../assets/bin.png")
const heartPlain = require("../assets/plain_favorite_icon.png")
export default function JokeDetail(props: JokeItemProps) { export default function JokeDetail(props: JokeItemProps) {
const [showDescription, setShowDescription] = useState(false); // état local pour contrôler la visibilité de la description const [showDescription, setShowDescription] = useState(false);
const [showFavorite, setShowFavorite] = useState(false); // état local pour contrôler la visibilité du favori const [showFavorite, setShowFavorite] = useState(false);
const isCustom = props.joke instanceof CustomJoke;
const dispatch = useDispatch<AppDispatch>();
const navigation = useNavigation();
const toggleDescription = () => { const toggleDescription = () => {
setShowDescription(!showDescription); setShowDescription(!showDescription);
@ -20,10 +33,11 @@ export default function JokeDetail(props: JokeItemProps) {
setShowFavorite(!showFavorite); setShowFavorite(!showFavorite);
}; };
const eye = require("../assets/eye_icon.png") const deleteCustomJokes = async () => {
const hideEye = require("../assets/eye_off_icon.png") await dispatch(deleteCustomJoke(props.joke.id));
const heart = require("../assets/favorite_icon.png") await dispatch(getCustomJokesList());
const heartPlain = require("../assets/plain_favorite_icon.png") navigation.goBack();
};
return( return(
<View style={styles.container}> <View style={styles.container}>
@ -33,6 +47,15 @@ export default function JokeDetail(props: JokeItemProps) {
</View> </View>
<Text style={styles.text}>Résumé de la blague</Text> <Text style={styles.text}>Résumé de la blague</Text>
<View style={styles.row}> <View style={styles.row}>
{isCustom && (<TouchableOpacity style={styles.favContainer} onPress={ deleteCustomJokes }>
<View>
<Image
source={bin}
style={styles.imageButton}
/>
</View>
</TouchableOpacity>)
}
<TouchableOpacity style={styles.favContainer} onPress={toggleFavorite}> <TouchableOpacity style={styles.favContainer} onPress={toggleFavorite}>
<View> <View>
<Image <Image
@ -149,7 +172,7 @@ const styles = StyleSheet.create({
marginLeft: 19, marginLeft: 19,
marginTop: 20, marginTop: 20,
borderRadius: 20, borderRadius: 20,
width : 120, width : 150,
alignItems : 'center' alignItems : 'center'
} }
}) })

@ -1,4 +1,4 @@
import { NavigationContainer } from '@react-navigation/native'; import {DarkTheme, DefaultTheme, NavigationContainer, Theme} from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import {Image, View} from 'react-native'; import {Image, View} from 'react-native';
@ -9,6 +9,8 @@ import Settings from "../screens/Settings";
import {darksalmonColor, greyColor, indigoColor} from "../assets/Theme"; import {darksalmonColor, greyColor, indigoColor} from "../assets/Theme";
import StackNavigation from "./StackNavigation"; import StackNavigation from "./StackNavigation";
import {useEffect, useState} from "react";
import {getTheme} from "../redux/thunk/ThemeThunk";
export default function NavigationBar() { export default function NavigationBar() {
@ -20,8 +22,23 @@ export default function NavigationBar() {
const favoriteIcon = require("../assets/favorite_icon.png"); const favoriteIcon = require("../assets/favorite_icon.png");
const settingsIcon = require("../assets/settings_icon.png"); const settingsIcon = require("../assets/settings_icon.png");
const [themes, setTheme] = useState<Theme>(DefaultTheme);
useEffect(() => {
const fetchTheme = async () => {
const theme = await getTheme();
setTheme(theme);
};
fetchTheme();
});
if (themes == null) {
return null;
}
return ( return (
<NavigationContainer> <NavigationContainer theme={ themes.dark === false ? DefaultTheme : DarkTheme}>
<BottomTabNavigator.Navigator initialRouteName="Accueil" <BottomTabNavigator.Navigator initialRouteName="Accueil"
screenOptions={{ screenOptions={{
headerStyle: { headerStyle: {

@ -8,6 +8,7 @@
"name": "tp-react-native", "name": "tp-react-native",
"version": "1.0.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"@react-native-async-storage/async-storage": "^1.23.1",
"@react-navigation/bottom-tabs": "^6.5.11", "@react-navigation/bottom-tabs": "^6.5.11",
"@react-navigation/native": "^6.1.10", "@react-navigation/native": "^6.1.10",
"@react-navigation/stack": "^6.3.21", "@react-navigation/stack": "^6.3.21",
@ -3683,6 +3684,17 @@
"url": "https://github.com/sponsors/isaacs" "url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/@react-native-async-storage/async-storage": {
"version": "1.23.1",
"resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.23.1.tgz",
"integrity": "sha512-Qd2kQ3yi6Y3+AcUlrHxSLlnBvpdCEMVGFlVBneVOjaFaPU61g1huc38g339ysXspwY1QZA2aNhrk/KlHGO+ewA==",
"dependencies": {
"merge-options": "^3.0.4"
},
"peerDependencies": {
"react-native": "^0.0.0-0 || >=0.60 <1.0"
}
},
"node_modules/@react-native-community/cli": { "node_modules/@react-native-community/cli": {
"version": "12.3.0", "version": "12.3.0",
"resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-12.3.0.tgz", "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-12.3.0.tgz",
@ -8667,6 +8679,14 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/is-plain-obj": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
"integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
"engines": {
"node": ">=8"
}
},
"node_modules/is-plain-object": { "node_modules/is-plain-object": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
@ -9860,6 +9880,17 @@
"resolved": "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz", "resolved": "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz",
"integrity": "sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA==" "integrity": "sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA=="
}, },
"node_modules/merge-options": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz",
"integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==",
"dependencies": {
"is-plain-obj": "^2.1.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/merge-stream": { "node_modules/merge-stream": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",

@ -10,6 +10,7 @@
"web": "expo start --web" "web": "expo start --web"
}, },
"dependencies": { "dependencies": {
"@react-native-async-storage/async-storage": "^1.23.1",
"@react-navigation/bottom-tabs": "^6.5.11", "@react-navigation/bottom-tabs": "^6.5.11",
"@react-navigation/native": "^6.1.10", "@react-navigation/native": "^6.1.10",
"@react-navigation/stack": "^6.3.21", "@react-navigation/stack": "^6.3.21",

@ -4,7 +4,8 @@ import {SampleJoke} from "../../model/SampleJoke";
export enum ActionType { export enum ActionType {
FETCH_CUSTOM_JOKES = 'FETCH_CUSTOM_JOKES', FETCH_CUSTOM_JOKES = 'FETCH_CUSTOM_JOKES',
POST_CUSTOM_JOKE = "POST_CUSTOM_JOKE", POST_CUSTOM_JOKE = "POST_CUSTOM_JOKE",
FETCH_JOKES_BY_ID = "FETCH_JOKES_BY_ID" FETCH_JOKES_BY_ID = "FETCH_JOKES_BY_ID",
DELETE_CUSTOM_JOKE = "DELETE_CUSTOM_JOKE"
} }
type actionPostFetch = { type actionPostFetch = {
@ -19,7 +20,12 @@ type actionGetByFetch = {
type: ActionType.FETCH_JOKES_BY_ID; type: ActionType.FETCH_JOKES_BY_ID;
payload: CustomJoke; payload: CustomJoke;
} }
export type Action = actionPostFetch | actionGetFetch | actionGetByFetch; type actionDeleteFetch = {
type: ActionType.DELETE_CUSTOM_JOKE;
payload: string;
}
export type Action = actionPostFetch | actionGetFetch | actionGetByFetch | actionDeleteFetch;
export const setPostJoke = (customJoke: CustomJoke) => { export const setPostJoke = (customJoke: CustomJoke) => {
return { return {
@ -27,7 +33,6 @@ export const setPostJoke = (customJoke: CustomJoke) => {
payload: customJoke payload: customJoke
} }
} }
export const setCustomJokesList = (customJokesList: CustomJoke[]) => { export const setCustomJokesList = (customJokesList: CustomJoke[]) => {
return { return {
type: ActionType.FETCH_CUSTOM_JOKES, type: ActionType.FETCH_CUSTOM_JOKES,
@ -40,3 +45,9 @@ export const setCustomJokeById = (customJoke: CustomJoke) => {
payload: customJoke, payload: customJoke,
}; };
} }
export const setDeleteJoke = (jokeId: string) => {
return {
type: ActionType.DELETE_CUSTOM_JOKE,
payload: jokeId
}
}

@ -0,0 +1,18 @@
export enum ActionType {
SET_THEME = 'SET_THEME',
}
type actionFetch = {
type: ActionType.SET_THEME;
payload: String;
}
export type Action = actionFetch;
export const setTheme = (theme) => {
return {
type: ActionType.SET_THEME,
payload: theme,
};
}

@ -0,0 +1,28 @@
import {CustomJoke} from "../../model/CustomJoke";
import {SampleJoke} from "../../model/SampleJoke";
export enum ActionType {
ADD_FAVORITE = 'ADD_CUSTOM_FAVORITE',
REMOVE_FAVORITE = "REMOVE_CUSTOM_FAVORITE"
}
type actionAddFetch = {
type: ActionType.ADD_FAVORITE;
payload: string;
}
type actionRemoveFetch = {
type: ActionType.REMOVE_FAVORITE;
payload: string;
}
export type Action = actionAddFetch | actionRemoveFetch;
export const addFavorite = (joke: CustomJoke | SampleJoke) => ({
type: ActionType.ADD_FAVORITE,
payload: joke,
});
export const removeFavorite = (joke: CustomJoke | SampleJoke) => ({
type: ActionType.REMOVE_FAVORITE,
payload: joke,
});

@ -1,29 +1,28 @@
import {CustomJoke} from "../../model/CustomJoke"; import {CustomJoke} from "../../model/CustomJoke";
import {SampleJoke} from "../../model/SampleJoke";
import {Action, ActionType} from "../actions/CustomJoke"; import {Action, ActionType} from "../actions/CustomJoke";
import {Category} from "../../model/Category";
interface State { interface State {
customJokes: CustomJoke[]; customJokes: CustomJoke[];
customJoke: CustomJoke; customJoke?: CustomJoke;
jokeId: string;
} }
const initialState = { const initialState = {
customJokes: [], customJokes: [],
customJoke: new CustomJoke('', '', '', '') customJoke: null,
jokeId: ''
} }
const customReducer = (state: State = initialState, action: Action) => { const customReducer = (state: State = initialState, action: Action) => {
switch (action.type) { switch (action.type) {
case ActionType.POST_CUSTOM_JOKE: case ActionType.POST_CUSTOM_JOKE:
// @ts-ignore
return {...state, customJoke: action.payload}; return {...state, customJoke: action.payload};
case ActionType.FETCH_CUSTOM_JOKES: case ActionType.FETCH_CUSTOM_JOKES:
// @ts-ignore
return {...state, customJokes: action.payload}; return {...state, customJokes: action.payload};
case ActionType.FETCH_JOKES_BY_ID: case ActionType.FETCH_JOKES_BY_ID:
// @ts-ignore
return {...state, customJoke: action.payload}; return {...state, customJoke: action.payload};
case ActionType.DELETE_CUSTOM_JOKE:
return {...state, jokeId: action.payload};
default: default:
return state; return state;
} }

@ -0,0 +1,21 @@
import {Action, ActionType} from "../actions/DarkModeAction";
interface State {
theme: String
}
const initialState = {
theme: 'dark'
}
const themeReducer = (state: State = initialState, action: Action) => {
switch (action.type) {
case ActionType.SET_THEME:
// @ts-ignore
return {...state, theme: action.payload};
default:
return state;
}
}
export default themeReducer;

@ -0,0 +1,26 @@
import {Action, ActionType} from "../actions/FavoriteAction";
import {CustomJoke} from "../../model/CustomJoke";
import {SampleJoke} from "../../model/SampleJoke";
interface State {
joke: CustomJoke | SampleJoke
}
const initialState = {
joke: new CustomJoke('', '', '', '', '')
}
const favoriteReducer = (state: State = initialState, action: Action) => {
switch (action.type) {
case ActionType.ADD_FAVORITE:
// @ts-ignore
return {...state, joke: action.payload};
case ActionType.REMOVE_FAVORITE:
// @ts-ignore
return {...state, joke: action.payload};
default:
return state;
}
}
export default favoriteReducer;

@ -6,13 +6,13 @@ import {Category} from "../../model/Category";
interface State { interface State {
jokes: SampleJoke[]; jokes: SampleJoke[];
favoriteJokes: SampleJoke[]; favoriteJokes: SampleJoke[];
joke : SampleJoke; joke? : SampleJoke;
} }
const initialState = { const initialState = {
jokes: [], jokes: [],
favoriteJokes: [], favoriteJokes: [],
joke: new SampleJoke(1, "", "", "", ""), joke:null,
} }
const sampleReducer = (state: State = initialState, action: Action) => { const sampleReducer = (state: State = initialState, action: Action) => {

@ -2,12 +2,16 @@ import {configureStore} from '@reduxjs/toolkit'
import sampleReducer from "./reducers/SampleReducer"; import sampleReducer from "./reducers/SampleReducer";
import categoryReducer from "./reducers/CategoryReducer"; import categoryReducer from "./reducers/CategoryReducer";
import customReducer from "./reducers/CustomReducer"; import customReducer from "./reducers/CustomReducer";
import themeReducer from "./reducers/DarkModeReducer"
import favoriteReducer from "./reducers/FavoriteReducer";
// Reference here all your application reducers // Reference here all your application reducers
const reducer = { const reducer = {
sampleReducer: sampleReducer, sampleReducer: sampleReducer,
categoryReducer: categoryReducer, categoryReducer: categoryReducer,
customReducer: customReducer, customReducer: customReducer,
theme: themeReducer,
favorite: favoriteReducer
} }
// @ts-ignore // @ts-ignore
@ -18,5 +22,6 @@ const store = configureStore({
serializableCheck: false serializableCheck: false
}) })
},); },);
export type AppState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
export default store; export default store;

@ -0,0 +1,28 @@
import { setDeleteJoke } from "../actions/CustomJoke";
export const deleteItem = (id: string) => {
return async dispatch => {
try {
const response = await fetch(`https://iut-weather-api.azurewebsites.net/jokes/${id}`, {
method: 'DELETE',
headers: {
Accept: "application/json",
"Content-Type": 'application/json',
}
});
if (response.ok) {
dispatch(setDeleteJoke(id)); // Supprimer la blague dans le store Redux
console.log('Suppression de la blague réussie');
} else {
console.log('Erreur lors de la requête DELETE');
}
} catch (error) {
console.log('Erreur :', error);
}
};
};
export const deleteCustomJoke = (jokeId) => {
return deleteItem(jokeId)
}

@ -0,0 +1,47 @@
// Fonction pour ajouter une blague aux favoris
import AsyncStorage from "@react-native-async-storage/async-storage";
import {SampleJoke} from "../../model/SampleJoke";
import {CustomJoke} from "../../model/CustomJoke";
const addFavorite = async (joke: SampleJoke | CustomJoke) => {
try {
let favorites: { sampleJokes: SampleJoke[], customJokes: CustomJoke[] } = await AsyncStorage.getItem('@favorites');
if (!favorites) {
favorites = { sampleJokes: [], customJokes: [] };
}
if (joke instanceof SampleJoke) {
favorites.sampleJokes.push(joke);
} else if (joke instanceof CustomJoke) {
favorites.customJokes.push(joke);
}
await AsyncStorage.setItem('@favorites', JSON.stringify(favorites));
} catch (error) {
console.error('Error adding favorite:', error);
}
};
const removeFavorite = async (jokeId: string | number) => {
try {
var favorites: { sampleJokes: SampleJoke[], customJokes: CustomJoke[] } = await AsyncStorage.getItem('@favorites');
if (!favorites) {
return;
}
favorites.sampleJokes = favorites.sampleJokes.filter(joke => joke.id !== jokeId);
favorites.customJokes = favorites.customJokes.filter(joke => joke.id !== jokeId);
await AsyncStorage.setItem('@favorites', JSON.stringify(favorites));
} catch (error) {
console.error('Error removing favorite:', error);
}
};
const getFavorites = async () => {
try {
const favoritesString = await AsyncStorage.getItem('@favorites');
if (favoritesString !== null) {
return JSON.parse(favoritesString);
}
} catch (error) {
console.error('Error getting favorites:', error);
}
return { sampleJokes: [], customJokes: [] };
};

@ -6,19 +6,21 @@ import {setCustomJokeById} from "../actions/CustomJoke";
export const getItem = <TItem>(uri:string, constructor : (json:any) => TItem, setItem: (item: TItem) => any) => { export const getItem = <TItem>(uri:string, constructor : (json:any) => TItem, setItem: (item: TItem) => any) => {
return async dispatch => { return async dispatch => {
try { try {
console.log(";;;;;;;;;;;;;;", uri, ";;;;;;;;;;;;;;;;;;;")
const promise = await fetch(uri); const promise = await fetch(uri);
const Json = await promise.json(); const Json = await promise.json();
const Item: TItem = constructor(Json); const Item: TItem = constructor(Json);
console.log("===========", Item , "===================");
dispatch(setItem(Item)); dispatch(setItem(Item));
} catch (error) { } catch (error) {
console.log('Error---------', error); console.log('Error---------', error);
} }
} }
} }
export const getJokeById = (id) => { export const getJokeById = (id : number) => {
return getItem<SampleJoke>('https://iut-weather-api.azurewebsites.net/jokes/samples/' + id , (elt) => new SampleJoke(elt["id"], elt["type"], elt["setup"], elt["image"]), (item) => setJokeById(item)) return getItem<SampleJoke>('https://iut-weather-api.azurewebsites.net/jokes/samples/' + id.toString() , (elt) => new SampleJoke(elt["id"], elt["type"], elt["setup"], elt["image"]), (item) => setJokeById(item))
} }
export const getCustomJokeById = (id) => { export const getCustomJokeById = (id: string) => {
return getItem<CustomJoke>('https://iut-weather-api.azurewebsites.net/jokes/' + id , (elt) => new CustomJoke(elt["id"], elt["type"], elt["setup"], elt["image"]), (item) => setCustomJokeById(item)) return getItem<CustomJoke>('https://iut-weather-api.azurewebsites.net/jokes/' + id , (elt) => new CustomJoke(elt["id"], elt["type"], elt["setup"], elt["image"]), (item) => setCustomJokeById(item))
} }

@ -13,7 +13,7 @@ export const getList = <TList>(uri:string, constructor : (json:any) => TList, se
const List: TList[] = listJson.map(elt => constructor(elt)); const List: TList[] = listJson.map(elt => constructor(elt));
dispatch(setList(List)); dispatch(setList(List));
} catch (error) { } catch (error) {
console.log('Error---------', error); console.log(`Error In getList ${uri} ---------`, error);
} }
} }
} }

@ -1,7 +1,4 @@
import {setPostJoke} from "../actions/CustomJoke"; import {setPostJoke} from "../actions/CustomJoke";
import {SampleJoke} from "../../model/SampleJoke";
import {setJokesList} from "../actions/SampleAction";
import {getList} from "./GetThunk";
export const setItem = <TItem>( export const setItem = <TItem>(
uri: string, uri: string,

@ -0,0 +1,21 @@
import AsyncStorage from "@react-native-async-storage/async-storage";
import {Theme} from "@react-navigation/native";
export const storeTheme = async (theme) => {
try {
const jsonValue = JSON.stringify(theme)
await AsyncStorage.setItem('@theme', jsonValue)
console.log("theme stored")
} catch (e) {
console.log(e);
}
}
export const getTheme = async () => {
try {
const jsonValue = await AsyncStorage.getItem('@theme')
return jsonValue != null ? JSON.parse(jsonValue) as Theme : null;
} catch(e) {
console.log(e);
}
}

@ -5,7 +5,7 @@ import '../types/extension';
import {useDispatch, useSelector} from "react-redux"; import {useDispatch, useSelector} from "react-redux";
import {Image, StyleSheet, Text, TextInput, TouchableOpacity, View} from "react-native"; import {Image, StyleSheet, Text, TextInput, TouchableOpacity, View} from "react-native";
import {darksalmonColor, greyColor, indigoColor, purpleColor, whiteColor} from "../assets/Theme"; import {darksalmonColor, greyColor, indigoColor, purpleColor, whiteColor} from "../assets/Theme";
import {getSampleJokesList} from "../redux/thunk/GetThunk"; import {getCustomJokesList, getSampleJokesList} from "../redux/thunk/GetThunk";
import React, {useEffect} from "react"; import React, {useEffect} from "react";
import {postCustomJoke} from "../redux/thunk/PostThunk"; import {postCustomJoke} from "../redux/thunk/PostThunk";
@ -25,6 +25,9 @@ export default function AddScreen() {
const handleCreate = () => { const handleCreate = () => {
// @ts-ignore // @ts-ignore
dispatch(postCustomJoke(joke, downgrade, category)); dispatch(postCustomJoke(joke, downgrade, category));
// @ts-ignore
dispatch(getCustomJokesList());
clearFields(); clearFields();
}; };

@ -5,34 +5,39 @@ import {darksalmonColor, purpleColor, whiteColor} from "../assets/Theme";
import {useDispatch, useSelector} from "react-redux"; import {useDispatch, useSelector} from "react-redux";
import React, {useEffect, useState} from "react"; import React, {useEffect, useState} from "react";
import {getCustomJokesList, getSampleJokesList} from "../redux/thunk/GetThunk"; import {getCustomJokesList, getSampleJokesList} from "../redux/thunk/GetThunk";
const eye = require("../assets/eye_icon.png")
const hideEye = require("../assets/eye_off_icon.png")
export default function Catalogue() { export default function Catalogue() {
// @ts-ignore // @ts-ignore
const sampleJokes = useSelector(state => state.sampleReducer.jokes); const sampleJokes = useSelector(state => state.sampleReducer.jokes);
// @ts-ignore // @ts-ignore
const customJokes = useSelector(state => state.customReducer.customJokes); const customJokes = useSelector(state => state.customReducer.customJokes);
const [joke, setJoke] = useState([])
const dispatch = useDispatch(); const dispatch = useDispatch();
const eye = require("../assets/eye_icon.png")
const hideEye = require("../assets/eye_off_icon.png") const [showCustomJoke, setCustomJoke] = useState(false);
const toggleDescription = () => {
setCustomJoke(!showCustomJoke);
};
useEffect(() => { useEffect(() => {
if(!showCustomJoke) {
const loadSamplesJokes = async () => { const loadSamplesJokes = async () => {
// @ts-ignore // @ts-ignore
await dispatch(getSampleJokesList()); await dispatch(getSampleJokesList());
}; };
loadSamplesJokes(); loadSamplesJokes();
}, [dispatch]); setJoke(sampleJokes)
} else {
const loadCustomJokes = async () => { const loadCustomJokes = async () => {
// @ts-ignore // @ts-ignore
await dispatch(getCustomJokesList()); await dispatch(getCustomJokesList());
}; };
const [showCustomJoke, setCustomJoke] = useState(false); // état local pour contrôler la visibilité de la description
const toggleDescription = () => {
setCustomJoke(!showCustomJoke);
loadCustomJokes(); loadCustomJokes();
}; setJoke(customJokes)
}
}, [dispatch, customJokes, sampleJokes]);
return ( return (
<SafeAreaView style={styles.container}> <SafeAreaView style={styles.container}>
@ -48,7 +53,7 @@ export default function Catalogue() {
</TouchableOpacity> </TouchableOpacity>
</View> </View>
<View style={styles.container}> <View style={styles.container}>
<JokeItems jokes={showCustomJoke ? customJokes : sampleJokes}/> <JokeItems jokes={joke}/>
</View> </View>
</SafeAreaView> </SafeAreaView>
) )

@ -1,21 +1,106 @@
import { customJokeStub } from '../data/stub/CustomJokeStub';
import { sampleJokeStub } from '../data/stub/SampleJokeStub';
import JokeItems from "../components/JokeItems"; import JokeItems from "../components/JokeItems";
import '../types/extension'; import '../types/extension';
import {StyleSheet, View} from "react-native"; import {Image, SafeAreaView, StyleSheet, Text, TouchableOpacity, View} from "react-native";
import {purpleColor} from "../assets/Theme"; import {darksalmonColor, purpleColor, whiteColor} from "../assets/Theme";
import {useDispatch, useSelector} from "react-redux";
import React, {useEffect, useState} from "react";
import {getCustomJokesList, getSampleJokesList} from "../redux/thunk/GetThunk";
export default function Favorites() {
// @ts-ignore
const sampleJokes = useSelector(state => state.sampleReducer.jokes);
// @ts-ignore
const customJokes = useSelector(state => state.customReducer.customJokes);
const [joke, setJoke] = useState([])
const dispatch = useDispatch();
const eye = require("../assets/eye_icon.png")
const hideEye = require("../assets/eye_off_icon.png")
const [showCustomJoke, setCustomJoke] = useState(false); // état local pour contrôler la visibilité de la description
const toggleDescription = () => {
setCustomJoke(!showCustomJoke);
};
useEffect(() => {
if(!showCustomJoke) {
const loadSamplesJokes = async () => {
// @ts-ignore
await dispatch(getSampleJokesList());
};
loadSamplesJokes();
setJoke(sampleJokes)
} else {
const loadCustomJokes = async () => {
// @ts-ignore
await dispatch(getCustomJokesList());
};
loadCustomJokes();
setJoke(customJokes)
}
}, [dispatch, customJokes, sampleJokes]);
export default function Catalogue() {
const allJokes = [...customJokeStub, ...sampleJokeStub];
return ( return (
<SafeAreaView style={styles.container}>
<View style={styles.columnContainer}>
<Text style={styles.TextButton}>Afficher les exemples</Text>
<TouchableOpacity style={styles.Button} onPress={toggleDescription}>
<View style={styles.jokeTypeContainer}>
<Image
source={showCustomJoke ? hideEye : eye}
style={styles.imageButton}
/>
</View>
</TouchableOpacity>
</View>
<View style={styles.container}> <View style={styles.container}>
<JokeItems jokes={allJokes}/> <JokeItems jokes={joke}/>
</View> </View>
</SafeAreaView>
) )
}; };
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
backgroundColor: purpleColor backgroundColor: purpleColor,
flex:1,
},
Button:{
borderRadius : 5,
backgroundColor : darksalmonColor,
height:40,
width : 60,
flexDirection : "row"
},
jokeTypeContainer :{
display : "flex",
flex : 1,
flexDirection: "row",
alignItems : "center"
},
imageButton : {
margin : 10,
width: 40,
height:30,
top : 5,
alignSelf : "center",
backgroundColor: "none",
tintColor : whiteColor,
justifyContent: "center",
alignItems: "center",
},
TextButton : {
fontSize: 18,
color: whiteColor,
textAlign: 'center',
fontWeight: 'bold',
margin: 10,
},
columnContainer: {
marginLeft: 20,
marginRight: 20,
width: '90%',
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
} }
}); });

@ -6,8 +6,6 @@ import Categs from "../components/Categs";
import {useDispatch, useSelector} from "react-redux"; import {useDispatch, useSelector} from "react-redux";
import {useEffect} from "react"; import {useEffect} from "react";
import {getCategoriesList, getLastSampleJokesList} from "../redux/thunk/GetThunk"; import {getCategoriesList, getLastSampleJokesList} from "../redux/thunk/GetThunk";
import SampleReducer from "../redux/reducers/SampleReducer";
import CategoryReducer from "../redux/reducers/CategoryReducer";
export default function Catalogue() { export default function Catalogue() {
// @ts-ignore // @ts-ignore

@ -5,35 +5,37 @@ import {useDispatch, useSelector} from "react-redux";
import React, {useEffect, useState} from "react"; import React, {useEffect, useState} from "react";
import {getCustomJokeById, getJokeById} from "../redux/thunk/GetByThunk"; import {getCustomJokeById, getJokeById} from "../redux/thunk/GetByThunk";
import JokeDetail from "../components/JokeDetail"; import JokeDetail from "../components/JokeDetail";
import {AppDispatch, AppState} from "../redux/store";
import {CustomJoke} from "../model/CustomJoke";
import {SampleJoke} from "../model/SampleJoke";
export default function JokeDetailsScreen({route}) { export default function JokeDetailsScreen({route}) {
const idJokeDetail = route.params.idJoke; const idJokeDetail = route.params.idJoke;
// @ts-ignore const joke = useSelector<AppState>(state => state.sampleReducer.joke);
const joke = useSelector(state => state.sampleReducer.jokes); const customJoke = useSelector<AppState>(state => state.customReducer.customJoke);
// @ts-ignore const [isCustomJoke, setCustomJoke] = useState(false);
const customJoke = useSelector(state => state.customReducer.customJoke); const dispatch = useDispatch<AppDispatch>();
const [isCustomJoke, setCustomJoke] = useState(false); // état local pour contrôler la visibilité de la description
// @ts-ignore
const dispatch = useDispatch();
useEffect(() => { useEffect(() => {
if(idJokeDetail instanceof Number) { if(typeof idJokeDetail == 'number') {
const loadJoke = async () => { const loadJoke = async () => {
// @ts-ignore
await dispatch(getJokeById(idJokeDetail)); await dispatch(getJokeById(idJokeDetail));
}; };
loadJoke(); loadJoke();
setCustomJoke(!isCustomJoke) setCustomJoke(!isCustomJoke)
} else { } else {
const loadCustomJoke = async () => { const loadCustomJoke = async () => {
// @ts-ignore
await dispatch(getCustomJokeById(idJokeDetail)); await dispatch(getCustomJokeById(idJokeDetail));
}; };
loadCustomJoke(); loadCustomJoke();
} }
}, [dispatch]); }, [dispatch]);
if(!joke && !customJoke) return null;
return ( return (
<View style={styles.container}> <View style={styles.container}>
{/*@ts-ignore}*/}
<JokeDetail joke={isCustomJoke ? joke : customJoke}/> <JokeDetail joke={isCustomJoke ? joke : customJoke}/>
</View> </View>
) )

@ -1,21 +1,99 @@
import { customJokeStub } from '../data/stub/CustomJokeStub';
import { sampleJokeStub } from '../data/stub/SampleJokeStub';
import JokeItems from "../components/JokeItems";
import '../types/extension'; import '../types/extension';
import {StyleSheet, View} from "react-native"; import {Image, StyleSheet, Switch, Text, View} from "react-native";
import {purpleColor} from "../assets/Theme"; import {darksalmonColor, DefaultTheme, DarkTheme, greyColor, indigoColor, purpleColor, whiteColor} from "../assets/Theme";
import React, {useEffect, useState} from "react";
import {useTheme} from "@react-navigation/native";
import {storeTheme} from "../redux/thunk/ThemeThunk";
export default function Catalogue() { export default function Catalogue() {
const allJokes = [...customJokeStub, ...sampleJokeStub]; const light_mode = require("../assets/light_mode.png")
const dark_mode = require("../assets/dark_mode.png")
const [isDark, setDark] = React.useState(false)
const toggleTheme = () => {
setDark(previousState => {
const theme = !previousState;
const newTheme = theme ? DarkTheme : DefaultTheme;
storeTheme(newTheme);
return theme;
});
};
const styles = themeSettings();
return ( return (
<View style={styles.container}> <View style={styles.container}>
<JokeItems jokes={allJokes}/> <View style={styles.topText}>
<Text style={styles.title}>Réglages</Text>
<View style={styles.switchMode}>
<View style={styles.textContainer}>
<Image
source={isDark? dark_mode: light_mode}
style={styles.imageButton} />
<Text style={styles.darkModeText}>Dark Mode</Text>
</View>
<Switch
value={isDark}
onValueChange={toggleTheme}
style={styles.switchMode}
trackColor={{false: darksalmonColor, true: darksalmonColor}}
thumbColor={whiteColor} />
</View> </View>
) </View>
</View>
);
}; };
export function themeSettings() {
const theme = useTheme();
const colors = theme.colors;
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
backgroundColor: purpleColor paddingTop: 10,
paddingBottom: 10,
paddingLeft: 10,
paddingRight: 10,
flex: 1,
justifyContent: 'center',
backgroundColor: colors.background,
flexDirection: 'column',
},
topText: {
flex: 1,
},
title: {
padding: 10,
fontSize: 20,
color: whiteColor,
fontWeight: 'bold'
},
imageButton : {
width: 30,
height:30,
alignSelf : "center",
tintColor: darksalmonColor,
},
switchMode: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
backgroundColor: indigoColor,
padding: 20,
margin: 10,
},
darkModeText: {
color: whiteColor,
fontSize: 20,
marginLeft: 10,
paddingTop: 5,
},
textContainer: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
} }
}); });
return styles;
}

@ -1707,6 +1707,13 @@
mkdirp "^1.0.4" mkdirp "^1.0.4"
rimraf "^3.0.2" rimraf "^3.0.2"
"@react-native-async-storage/async-storage@^1.23.1":
version "1.23.1"
resolved "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.23.1.tgz"
integrity sha512-Qd2kQ3yi6Y3+AcUlrHxSLlnBvpdCEMVGFlVBneVOjaFaPU61g1huc38g339ysXspwY1QZA2aNhrk/KlHGO+ewA==
dependencies:
merge-options "^3.0.4"
"@react-native-community/cli-clean@12.3.0": "@react-native-community/cli-clean@12.3.0":
version "12.3.0" version "12.3.0"
resolved "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-12.3.0.tgz" resolved "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-12.3.0.tgz"
@ -3982,6 +3989,11 @@ is-path-inside@^3.0.2:
resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz"
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
is-plain-obj@^2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz"
integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==
is-plain-object@^2.0.4: is-plain-object@^2.0.4:
version "2.0.4" version "2.0.4"
resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz"
@ -4430,6 +4442,13 @@ memory-cache@~0.2.0:
resolved "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz" resolved "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz"
integrity sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA== integrity sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA==
merge-options@^3.0.4:
version "3.0.4"
resolved "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz"
integrity sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==
dependencies:
is-plain-obj "^2.1.0"
merge-stream@^2.0.0: merge-stream@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz"
@ -5368,7 +5387,7 @@ react-native-safe-area-context@^4.9.0, "react-native-safe-area-context@>= 3.0.0"
react-freeze "^1.0.0" react-freeze "^1.0.0"
warn-once "^0.1.0" warn-once "^0.1.0"
react-native@*, react-native@>=0.69, react-native@0.73.2: react-native@*, "react-native@^0.0.0-0 || >=0.60 <1.0", react-native@>=0.69, react-native@0.73.2:
version "0.73.2" version "0.73.2"
resolved "https://registry.npmjs.org/react-native/-/react-native-0.73.2.tgz" resolved "https://registry.npmjs.org/react-native/-/react-native-0.73.2.tgz"
integrity sha512-7zj9tcUYpJUBdOdXY6cM8RcXYWkyql4kMyGZflW99E5EuFPoC7Ti+ZQSl7LP9ZPzGD0vMfslwyDW0I4tPWUCFw== integrity sha512-7zj9tcUYpJUBdOdXY6cM8RcXYWkyql4kMyGZflW99E5EuFPoC7Ti+ZQSl7LP9ZPzGD0vMfslwyDW0I4tPWUCFw==

Loading…
Cancel
Save