diff --git a/JokesApp/.idea/.gitignore b/JokesApp/.idea/.gitignore index b58b603..7abb13d 100644 --- a/JokesApp/.idea/.gitignore +++ b/JokesApp/.idea/.gitignore @@ -3,3 +3,5 @@ /workspace.xml # Editor-based HTTP Client requests /httpRequests/ +# GitHub Copilot persisted chat sessions +/copilot/chatSessions diff --git a/JokesApp/Theme.ts b/JokesApp/Theme.ts index 752b5c1..f6f5792 100644 --- a/JokesApp/Theme.ts +++ b/JokesApp/Theme.ts @@ -1,5 +1,75 @@ +import {DarkTheme, Theme, useTheme} from "@react-navigation/native"; +import React, {useEffect, useState} from "react"; +import DefaultTheme from "@react-navigation/native/src/theming/DefaultTheme"; +import {useColorScheme} from "react-native"; + export const indigo = "rgba(14, 14, 44, 1)"; export const purpleColor = "rgba(74, 74, 104, 1)"; export const darksalmonColor = "rgba(233, 150, 122, 1)"; export const greyColor = "rgba(140, 140, 161, 1)"; -export const whiteColor = "rgba(239, 239, 253, 1)"; \ No newline at end of file +export const whiteColor = "rgba(239, 239, 253, 1)"; + +export const blackColor = "rgba(0, 0, 0, 1)"; + + +export default function usePersonalTheme() { + + + useTheme() + const theme = React.useContext(ThemeContext); + const [isDark, setIsDark] = useState(false); + + if (isDark) { + theme.dark = true; + theme.colors = MyDarkTheme.colors; + } + else { + theme.dark = false; + theme.colors = MyLightTheme.colors; + } + + + + + + + + return theme; +} + + + +//const isDark = false; + + +const MyDarkTheme: Theme = { + dark: true, + colors: { + primary: indigo, + background: purpleColor, + card: darksalmonColor, + text: whiteColor, + border: whiteColor, + notification: whiteColor, + }, +}; + +const MyLightTheme: Theme = { + dark: false, + colors: { + primary: whiteColor, + background: greyColor, + card: '', + text: blackColor, + border: blackColor, + notification: blackColor, + }, +}; + + +const ThemeContext = React.createContext(MyDarkTheme); + + + + + diff --git a/JokesApp/assets/darkmode_icon.png b/JokesApp/assets/darkmode_icon.png new file mode 100644 index 0000000..3803edc Binary files /dev/null and b/JokesApp/assets/darkmode_icon.png differ diff --git a/JokesApp/assets/delete-icon.png b/JokesApp/assets/delete-icon.png new file mode 100644 index 0000000..2993cff Binary files /dev/null and b/JokesApp/assets/delete-icon.png differ diff --git a/JokesApp/navigation/Navigation.tsx b/JokesApp/navigation/Navigation.tsx index 96e3cb8..831a0b7 100644 --- a/JokesApp/navigation/Navigation.tsx +++ b/JokesApp/navigation/Navigation.tsx @@ -1,9 +1,9 @@ -import React from "react"; -import {NavigationContainer} from "@react-navigation/native"; +import React, {useContext, useEffect, useState} from "react"; +import {DarkTheme, DefaultTheme, NavigationContainer, Theme, useTheme} from "@react-navigation/native"; import {createBottomTabNavigator} from "@react-navigation/bottom-tabs"; import {ListJokeScreen} from "../screens/ListJokeScreen"; -import {Image, StyleSheet, View} from "react-native"; -import {darksalmonColor, greyColor, indigo, purpleColor} from "../Theme"; +import {Image, StyleSheet, useColorScheme, View} from "react-native"; +import usePersonalTheme, {darksalmonColor, greyColor, indigo, purpleColor} from "../Theme"; import {AccueilScreen} from "../screens/AccueilScreen"; import {AddJokeScreen} from "../screens/AddJokeScreen"; import {SettingsScreen} from "../screens/SettingsScreen"; @@ -14,24 +14,46 @@ const listIcon = require("../assets/list_icon.png"); const addIcon = require("../assets/add_icon.png"); const favIcon = require("../assets/favorite_icon.png"); const setIcon = require("../assets/settings_icon.png"); +import store, {getTheme, storeTheme} from "../redux/store"; export function Navigation(){ + + const BottomTabNavigator = createBottomTabNavigator(); + + const [themes, setThemes] = useState(null); + + useEffect(() => { + const fetchTheme = async () => { + const theme = await getTheme(); + setThemes(theme); + }; + + fetchTheme(); + }); + + if (themes == null) { + return null; + } + + console.log("ici le theme", themes); + + return ( - - + + }}> ( @@ -108,3 +130,30 @@ const styles = StyleSheet.create({ }); + + +const stylesDark = StyleSheet.create({ + + title: { + fontSize: 24, + color: 'darksalmon', + textAlign: 'center', + fontWeight: 'bold', + marginVertical: 20, + }, + top: { + backgroundColor : indigo + }, + addJoke: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: greyColor, + width: '70%', + height: '100%', + borderRadius: 4, + marginTop: 4, + }, + + +}); diff --git a/JokesApp/package-lock.json b/JokesApp/package-lock.json index d912438..3b4e5ff 100644 --- a/JokesApp/package-lock.json +++ b/JokesApp/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "dependencies": { "@expo/ngrok": "^2.5.0", + "@react-native-async-storage/async-storage": "^1.23.1", "@react-navigation/bottom-tabs": "^6.5.11", "@react-navigation/native": "^6.1.10", "@react-navigation/stack": "^6.3.21", @@ -3883,6 +3884,17 @@ "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": { "version": "12.3.0", "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-12.3.0.tgz", @@ -9112,6 +9124,14 @@ "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": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -10329,6 +10349,17 @@ "resolved": "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz", "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": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", diff --git a/JokesApp/package.json b/JokesApp/package.json index c8dc118..6e9c2a0 100644 --- a/JokesApp/package.json +++ b/JokesApp/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@expo/ngrok": "^2.5.0", + "@react-native-async-storage/async-storage": "^1.23.1", "@react-navigation/bottom-tabs": "^6.5.11", "@react-navigation/native": "^6.1.10", "@react-navigation/stack": "^6.3.21", diff --git a/JokesApp/redux/actions/customAction.ts b/JokesApp/redux/actions/customAction.ts index be5a602..afc68b4 100644 --- a/JokesApp/redux/actions/customAction.ts +++ b/JokesApp/redux/actions/customAction.ts @@ -5,6 +5,7 @@ export enum CustomActionType { POST_CUSTOM_JOKE = 'POST_CUSTOM_JOKE', FETCH_CUSTOMS_JOKE = 'FETCH_CUSTOMS_JOKE', FETCH_CUSTOMS_JOKE_BY_ID = 'FETCH_CUSTOMS_JOKE_BY_ID', + DELETE_CUSTOM_JOKE = 'DELETE_CUSTOM_JOKE', } export interface CustomAction { @@ -47,6 +48,13 @@ export const setCustomsJokeById = (completCustomJoke: CustomJoke): postCustomAct } +export const setDeleteCustomJoke = (deleteCustomJoke: CustomJoke): postCustomAction => { + return { + type: CustomActionType.DELETE_CUSTOM_JOKE, + payload: deleteCustomJoke + } +} + export const postJoke = (type : string, setup : string, punchline : string) => { @@ -101,3 +109,21 @@ export const getJokesCustomsById = (id : number) => { } } } + +export const deleteJoke = (id : number) => { + return async dispatch => { + try { + const custom = await fetch('https://iut-weather-api.azurewebsites.net/jokes/' + id, { + method: 'DELETE', + headers: { + Accept: "application/json", + "Content-Type": "application/json", + } + }); + console.log('Suppression'); + dispatch(setDeleteCustomJoke({} as CustomJoke)); + } catch (error) { + console.log('Error---------', error); + } + } +} diff --git a/JokesApp/redux/reducers/customJokeReducer.ts b/JokesApp/redux/reducers/customJokeReducer.ts index d7d3bae..1d09c84 100644 --- a/JokesApp/redux/reducers/customJokeReducer.ts +++ b/JokesApp/redux/reducers/customJokeReducer.ts @@ -7,6 +7,7 @@ interface state { postJoke: CustomJoke; customJokes: CustomJoke[]; completCustomJoke: CustomJoke; + deleteCustomJoke: CustomJoke; } // initial state for sampleJokes @@ -14,6 +15,7 @@ const initialState: state = { postJoke: {} as CustomJoke, customJokes: [], completCustomJoke: {} as CustomJoke, + deleteCustomJoke: {} as CustomJoke, } // app reducer for sampleJokes @@ -35,6 +37,11 @@ export default appReducer = (state = initialState, action: Action) => { ...state, completCustomJoke: action.payload, } + case CustomActionType.DELETE_CUSTOM_JOKE: + return { + ...state, + deleteCustomJoke: action.payload, + } default: return state; diff --git a/JokesApp/redux/store.ts b/JokesApp/redux/store.ts index 5d7f017..ca1d53a 100644 --- a/JokesApp/redux/store.ts +++ b/JokesApp/redux/store.ts @@ -2,6 +2,8 @@ import { configureStore } from '@reduxjs/toolkit'; import categorieReducer from './reducers/categoryReducer'; import sampleReducer from './reducers/sampleJokeReducer'; import customReducer from "./reducers/customJokeReducer"; +import AsyncStorage from '@react-native-async-storage/async-storage'; +import {Theme} from "@react-navigation/native"; const reducer = { categorieReducer: categorieReducer, @@ -20,4 +22,23 @@ const store = configureStore({ }) }); + +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); + } +} export default store; diff --git a/JokesApp/screens/JokeDetailScreen.tsx b/JokesApp/screens/JokeDetailScreen.tsx index 812c391..60b3d46 100644 --- a/JokesApp/screens/JokeDetailScreen.tsx +++ b/JokesApp/screens/JokeDetailScreen.tsx @@ -1,4 +1,4 @@ -import {View, Text, StyleSheet} from "react-native"; +import {View, Text, StyleSheet, TouchableOpacity, Image} from "react-native"; import {indigo, purpleColor, whiteColor} from "../Theme"; import React, {useEffect} from "react"; import {CustomJoke} from "../model/CustomJoke"; @@ -6,19 +6,20 @@ import {DetailJoke} from "../components/DetailJoke"; import {Joke} from "../model/Joke"; import {useDispatch, useSelector} from "react-redux"; import {getCompletJokes, setCompletJokes, setSample} from "../redux/actions/sampleAction"; -import {validatePathConfig} from "@react-navigation/native"; -import {getJokesCustomsById} from "../redux/actions/customAction"; -//svjh +import {useIsFocused, useNavigation, validatePathConfig} from "@react-navigation/native"; +import {deleteJoke, getJokesCustomsById} from "../redux/actions/customAction"; +import {Navigation} from "../navigation/Navigation"; export default function JokeDetailScreen({route}) { + const navigation = useNavigation(); + const isFocused = useIsFocused(); + const dispatch = useDispatch(); const jokeId = route.params.joke; const state = route.params.state; console.log(state); - // Déterminer quelle donnée utiliser en fonction de l'état de `state` const DataGen = state ? useSelector((state: any) => state.customReducer.completCustomJoke) : useSelector((state: any) => state.sampleReducer.completJoke); // const DataGen = useSelector((state: any) => state.sampleReducer.completJoke); - const dispatch = useDispatch(); useEffect(() => { const getDetails = async () => { // @ts-ignore @@ -27,10 +28,19 @@ export default function JokeDetailScreen({route}) { }; getDetails(); }, [dispatch]); + + async function deleteJokes() { + // @ts-ignore + await dispatch(deleteJoke(jokeId)); + navigation.goBack(); + } + return ( - + {state ? + + : null} ); } @@ -48,5 +58,11 @@ const styles = StyleSheet.create({ textAlign: 'center', fontWeight: 'bold', marginVertical: 20, + }, + img: { + width: 50, + height: 50, + margin: 20, + alignSelf: 'center' } }); \ No newline at end of file diff --git a/JokesApp/screens/ListJokeScreen.tsx b/JokesApp/screens/ListJokeScreen.tsx index fb6d87b..cc4e855 100644 --- a/JokesApp/screens/ListJokeScreen.tsx +++ b/JokesApp/screens/ListJokeScreen.tsx @@ -1,33 +1,52 @@ import {FlatList, Image, SafeAreaView, StyleSheet, Text, TouchableHighlight, TouchableOpacity, View} from "react-native"; -import React, {useEffect, useState} from "react"; +import React, {useContext, useEffect, useState} from "react"; import {JokeListItems} from "../components/ListeJokeComponent"; -import {darksalmonColor, indigo, purpleColor, whiteColor} from "../Theme"; +import {darksalmonColor, greyColor, indigo, purpleColor, whiteColor} from "../Theme"; import {CustomJoke} from "../model/CustomJoke"; import {useDispatch, useSelector} from 'react-redux'; import {getSampleJoke, setSample} from "../redux/actions/sampleAction"; import {getJokesCustoms} from "../redux/actions/customAction"; +import {useIsFocused} from "@react-navigation/native"; export function ListJokeScreen({route, navigation}) { const [isActivated2, setIsActivated2] = useState(false); + const isFocused = useIsFocused(); + + + const [styles, setStyles] = useState(stylesLight); + + function toggleActivation2() { setIsActivated2(!isActivated2); - getCustoms(); + + } const DataGen = useSelector((state: any) => state.sampleReducer.sampleJoke); const DataCustomsJoke = useSelector((state : any) => state.customReducer.customJokes) const dispatch = useDispatch(); - useEffect(() => { - const getJokes = async () => { - // @ts-ignore - await dispatch(getSampleJoke()); - }; - getJokes(); - }, [dispatch]); + const getCustoms = async () => { // @ts-ignore await dispatch(getJokesCustoms()); } + + const getSamples = async () => { + // @ts-ignore + await dispatch(getSampleJoke()); + + } + + useEffect(() => { + if (isFocused) { + if (isActivated2) { + getCustoms(); + } else { + getSamples(); + } + } + }, [isFocused, isActivated2]); + return ( @@ -48,8 +67,7 @@ export function ListJokeScreen({route, navigation}) { ); } - -const styles = StyleSheet.create({ +const stylesDark = StyleSheet.create({ title: { fontSize: 24, @@ -103,4 +121,60 @@ const styles = StyleSheet.create({ }, +}); + +const stylesLight = StyleSheet.create({ + + title: { + fontSize: 24, + color: darksalmonColor, + textAlign: 'center', + fontWeight: 'bold', + marginVertical: 20, + }, + titleResume: { + fontSize: 15, + fontWeight: 'bold', + marginBottom: 20, + }, + container: { + flex: 1, + backgroundColor: greyColor, + + }, + top: { + backgroundColor : indigo, + }, + header: { + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + margin: 9, + }, + headerText: { + fontSize: 18, + color: whiteColor, + textAlign: 'center', + fontWeight: 'bold', + }, + img2: { + tintColor: whiteColor, + justifyContent: "center", + alignItems: "center", + }, + button2:{ + flexDirection: "row", + justifyContent: "center", + marginRight: 10, + borderRadius: 10, + alignItems: "center", + height: 30, + width: 70, + + borderColor: darksalmonColor, + borderWidth: 1, + backgroundColor: darksalmonColor, + }, + + }); \ No newline at end of file diff --git a/JokesApp/screens/SettingsScreen.tsx b/JokesApp/screens/SettingsScreen.tsx index d2dd723..27590ea 100644 --- a/JokesApp/screens/SettingsScreen.tsx +++ b/JokesApp/screens/SettingsScreen.tsx @@ -1,33 +1,125 @@ -import React from "react"; +import React, {useContext, useEffect, useState} from "react"; + +import { + Appearance, + Button, + FlatList, + Image, + SafeAreaView, + ScrollView, + SectionListComponent, + StyleSheet, Switch, SwitchComponent, + Text, useColorScheme, + View +} from "react-native"; +import usePersonalTheme, { + blackColor, + darksalmonColor, + greyColor, + indigo, + purpleColor, + whiteColor +} from "../Theme"; +import {isEnabled} from "react-native/Libraries/Performance/Systrace"; +import {DarkTheme, DefaultTheme, useTheme} from "@react-navigation/native"; +import {storeTheme} from "../redux/store"; + -import {FlatList, Image, SafeAreaView, ScrollView, SectionListComponent, StyleSheet, Text, View} from "react-native"; -import {indigo, purpleColor} from "../Theme"; export function SettingsScreen() { + + const [isEnabled, setIsEnabled] = useState(false); + + const toggleSwitch = () => { + setIsEnabled(previousState => { + const newIsEnabled = !previousState; + const newTheme = newIsEnabled ? DarkTheme : DefaultTheme; + console.log("newTheme", newTheme); + storeTheme(newTheme); + return newIsEnabled; + }); + }; + + const styles = themeSettings(); + return ( - - Settings Page - In Work - + + Réglages + + + + + Dark Mode + + + + + ); -} +}; + + +export function themeSettings() { + const theme = useTheme(); + const colors = theme.colors; + console.log("themeSettings", colors) + const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: colors.background, + }, + header: { + padding: 16, + alignItems: 'center', + }, + headerText: { + color: whiteColor, + fontSize: 18, + }, + settingsContainer: { + backgroundColor: indigo, + margin: 16, + }, + title: { + color: whiteColor, + fontSize: 20, + fontWeight: 'bold', + marginTop: 7, + marginLeft: 15, + marginBottom: 8, + }, + settingRow: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + paddingVertical: 12, -const styles = StyleSheet.create({ - font: { - backgroundColor: indigo, - width: '100%', - height: '100%', - }, - text: { - fontSize: 24, - color: 'darksalmon', - textAlign: 'center', - fontWeight: 'bold', - marginVertical: 20, - } + }, + settingText: { + color: whiteColor, + marginLeft: 10, + }, + img: { + marginLeft: 10, + width: 30, + height: 30, + tintColor: darksalmonColor, + }, + switch: { + marginRight: 10, + } + }); + + return styles; + +} -}); \ No newline at end of file