From 94746d853174115a0460da49bd3c9b85f3744b30 Mon Sep 17 00:00:00 2001 From: "h.hartz" <henri.hartz@etu.unistra.fr> Date: Thu, 16 Jan 2025 16:03:02 +0100 Subject: [PATCH 1/6] feat: add eas config to get apk --- app.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app.json b/app.json index c930458..af7c947 100644 --- a/app.json +++ b/app.json @@ -13,13 +13,15 @@ "backgroundColor": "#ffffff" }, "ios": { - "supportsTablet": true + "supportsTablet": true, + "bundleIdentifier": "com.lykomonix.vili" }, "android": { "adaptiveIcon": { "foregroundImage": "./assets/adaptive-icon.png", "backgroundColor": "#ffffff" - } + }, + "package": "com.lykomonix.vili" }, "web": { "favicon": "./assets/favicon.png" -- GitLab From 85980898ba3e272bdad54b5b73a1f07bed4572b3 Mon Sep 17 00:00:00 2001 From: "h.hartz" <henri.hartz@etu.unistra.fr> Date: Thu, 16 Jan 2025 16:11:56 +0100 Subject: [PATCH 2/6] refactor: delete answerButtonImage --- components/buttons/AnswerButtonImage.tsx | 73 ------------------------ screens/PlayingQuiz/PlayingQuizBody.tsx | 23 +++----- 2 files changed, 7 insertions(+), 89 deletions(-) delete mode 100644 components/buttons/AnswerButtonImage.tsx diff --git a/components/buttons/AnswerButtonImage.tsx b/components/buttons/AnswerButtonImage.tsx deleted file mode 100644 index 8716144..0000000 --- a/components/buttons/AnswerButtonImage.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import {TouchableOpacity, Text, StyleSheet, View, Image} from "react-native"; -import {ButtonsStyles} from "../../styles/ButtonsStyles"; -import {TextsStyles} from "../../styles/TextsStyles"; - -interface Props{ - url: string; - handleButtonPressed:() => void; - buttonStyle?: any; - indicator?: number | undefined; -} - -/** - * AnswerButtonImage : Un bouton par défaut, il possède déjà un style mais on peut le surcharger grâce à la propriété buttonStyle - * @param url - Texte du bouton - * @param handleButtonPressed - Fonction qui sera exécutée lorsque le bouton sera pressé - * @param buttonStyle - Surcharge du style du bouton - * @param buttonText - Surcharge du style du texte du bouton - */ -export default function AnswerButtonImage({url, handleButtonPressed, buttonStyle, indicator}: Props){ - return( - <View style={styles.containerButton}> - <TouchableOpacity onPress={handleButtonPressed} style={[styles.defaultButton,buttonStyle]}> - {indicator !== undefined && ( - <View style={styles.indicator}> - <Text style={styles.indicatorText}>{indicator}</Text> - </View> - )} - <Image - style={[styles.answerImage]} - source={{uri: url}} - /> - </TouchableOpacity> - </View> - ) -} - -const styles = StyleSheet.create({ - defaultButton: { - backgroundColor: "#45128C", - padding: 10, - alignItems: "center", - justifyContent: 'center', - borderRadius: 10, - }, - indicator: { - position: "absolute", - top: "-16%", - left: "5%", - width: 25, - height: 25, - borderRadius: 15, - backgroundColor: "#D3D3D3", - justifyContent: "center", - alignItems: "center", - zIndex: 1, - }, - indicatorText: { - color: "#000", - fontWeight: "bold", - fontSize: 14, - }, - answerImage: { - width: '100%', - height: '100%', - }, - containerButton: { - height: 150, - width: '100%', - display: 'flex', - alignItems: 'center', - justifyContent: 'center' - } -}) \ No newline at end of file diff --git a/screens/PlayingQuiz/PlayingQuizBody.tsx b/screens/PlayingQuiz/PlayingQuizBody.tsx index 619e3b7..532b978 100644 --- a/screens/PlayingQuiz/PlayingQuizBody.tsx +++ b/screens/PlayingQuiz/PlayingQuizBody.tsx @@ -136,23 +136,14 @@ export default function PlayingQuizBody({ quizInformations, runId, actualQuestio contentContainerStyle={styles.buttonListContentContainer} > {actualQuestion.answers.map((answer, index) => ( - actualQuestion.type === "image" ? - <AnswerButtonImage - key={index} - url={answer.text} - handleButtonPressed={() => onAnsweredButtonClicked(answer.id)} - buttonStyle={getStyleOfAnswerButton(answer.id, selectedAnswerId, correctAnswerId, quizState)} - /> - : - <AnswerButton - key={index} - text={answer.text} - handleButtonPressed={() => onAnsweredButtonClicked(answer.id)} - buttonStyle={getStyleOfAnswerButton(answer.id, selectedAnswerId, correctAnswerId, quizState)} - buttonText={getStyleOfAnswerButtonText(answer.id, selectedAnswerId, correctAnswerId, quizState)} - /> + <AnswerButton + key={index} + text={answer.text} + handleButtonPressed={() => onAnsweredButtonClicked(answer.id)} + buttonStyle={getStyleOfAnswerButton(answer.id, selectedAnswerId, correctAnswerId, quizState)} + buttonText={getStyleOfAnswerButtonText(answer.id, selectedAnswerId, correctAnswerId, quizState)} + /> ))} - {/* </View> */} </ScrollView> <View style={styles.playButtonContainer}> <BlueButton onPress={() => quizState === QuizState.ANSWERING ? onValidation() : onContinue()} text={quizState === QuizState.ANSWERING ? "CONFIRM" : "CONTINUE"} buttonStyle={{borderRadius: 50}} isDisabled={false}/> -- GitLab From ac9f6d408e46bda1987bd684b741da04379c6df1 Mon Sep 17 00:00:00 2001 From: "h.hartz" <henri.hartz@etu.unistra.fr> Date: Thu, 16 Jan 2025 17:14:00 +0100 Subject: [PATCH 3/6] feat: add package expo av --- package-lock.json | 59 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 5 +++- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 2a605a2..1fa0471 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "axios": "^1.7.9", "dotenv": "^16.4.7", "expo": "~52.0.14", + "expo-av": "~15.0.2", "expo-splash-screen": "^0.29.13", "expo-status-bar": "~2.0.0", "he": "^1.2.0", @@ -28,9 +29,11 @@ "react-i18next": "^15.1.1", "react-native": "0.76.3", "react-native-dropdown-select-list": "^2.0.5", + "react-native-fs": "^2.20.0", "react-native-progress": "^5.0.1", "react-native-safe-area-context": "^4.12.0", "react-native-screens": "~4.1.0", + "react-native-sound": "^0.11.2", "react-native-sse": "^1.2.1", "react-native-vector-icons": "^10.2.0", "react-query": "^3.39.3", @@ -4662,6 +4665,11 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, + "node_modules/base-64": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", + "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==" + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -6126,6 +6134,23 @@ "react-native": "*" } }, + "node_modules/expo-av": { + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/expo-av/-/expo-av-15.0.2.tgz", + "integrity": "sha512-AHIHXdqLgK1dfHZF0JzX3YSVySGMrWn9QtPzaVjw54FAzvXfMt4sIoq4qRL/9XWCP9+ICcCs/u3EcvmxQjrfcA==", + "license": "MIT", + "peerDependencies": { + "expo": "*", + "react": "*", + "react-native": "*", + "react-native-web": "*" + }, + "peerDependenciesMeta": { + "react-native-web": { + "optional": true + } + } + }, "node_modules/expo-constants": { "version": "17.0.3", "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-17.0.3.tgz", @@ -9740,6 +9765,25 @@ "integrity": "sha512-TepbcagQVUMB6nLuIlVU2ghRpQHAECOeZWe8K04ymW6NqbKbxuczZSDFfdCiABiiQ2dFD+8Dz65y4K7/uUEqGg==", "license": "MIT" }, + "node_modules/react-native-fs": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/react-native-fs/-/react-native-fs-2.20.0.tgz", + "integrity": "sha512-VkTBzs7fIDUiy/XajOSNk0XazFE9l+QlMAce7lGuebZcag5CnjszB+u4BdqzwaQOdcYb5wsJIsqq4kxInIRpJQ==", + "license": "MIT", + "dependencies": { + "base-64": "^0.1.0", + "utf8": "^3.0.0" + }, + "peerDependencies": { + "react-native": "*", + "react-native-windows": "*" + }, + "peerDependenciesMeta": { + "react-native-windows": { + "optional": true + } + } + }, "node_modules/react-native-progress": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-native-progress/-/react-native-progress-5.0.1.tgz", @@ -9776,6 +9820,15 @@ "react-native": "*" } }, + "node_modules/react-native-sound": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/react-native-sound/-/react-native-sound-0.11.2.tgz", + "integrity": "sha512-LmGc8lgOK3qecYMVQpyHvww/C+wgT6sWeMpVbOe4NCRGC2yKd4fo4U0KBUo9PO7AqKESO3I/2GZg1/C0+bwiiA==", + "license": "MIT", + "peerDependencies": { + "react-native": ">=0.8.0" + } + }, "node_modules/react-native-sse": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/react-native-sse/-/react-native-sse-1.2.1.tgz", @@ -11563,6 +11616,12 @@ "react": ">=16.8" } }, + "node_modules/utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==", + "license": "MIT" + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index fd3a2bf..50d19f5 100644 --- a/package.json +++ b/package.json @@ -29,14 +29,17 @@ "react-i18next": "^15.1.1", "react-native": "0.76.3", "react-native-dropdown-select-list": "^2.0.5", + "react-native-fs": "^2.20.0", "react-native-progress": "^5.0.1", "react-native-safe-area-context": "^4.12.0", "react-native-screens": "~4.1.0", + "react-native-sound": "^0.11.2", "react-native-sse": "^1.2.1", "react-native-vector-icons": "^10.2.0", "react-query": "^3.39.3", "rxjs": "^7.8.1", - "socket.io-client": "^4.8.1" + "socket.io-client": "^4.8.1", + "expo-av": "~15.0.2" }, "devDependencies": { "@babel/core": "^7.20.0", -- GitLab From 4a26dbb6da09a39a8337d1bc0adb078833baa64f Mon Sep 17 00:00:00 2001 From: "h.hartz" <henri.hartz@etu.unistra.fr> Date: Thu, 16 Jan 2025 17:15:02 +0100 Subject: [PATCH 4/6] refactor: delete package unused --- package.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/package.json b/package.json index 50d19f5..26120e5 100644 --- a/package.json +++ b/package.json @@ -29,11 +29,9 @@ "react-i18next": "^15.1.1", "react-native": "0.76.3", "react-native-dropdown-select-list": "^2.0.5", - "react-native-fs": "^2.20.0", "react-native-progress": "^5.0.1", "react-native-safe-area-context": "^4.12.0", "react-native-screens": "~4.1.0", - "react-native-sound": "^0.11.2", "react-native-sse": "^1.2.1", "react-native-vector-icons": "^10.2.0", "react-query": "^3.39.3", -- GitLab From 2d06580da50e2b680f7f717095bf249c065582c6 Mon Sep 17 00:00:00 2001 From: "h.hartz" <henri.hartz@etu.unistra.fr> Date: Thu, 16 Jan 2025 17:15:44 +0100 Subject: [PATCH 5/6] feat: add base 64 audio --- components/buttons/Base64AudioPlayer.tsx | 51 ++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 components/buttons/Base64AudioPlayer.tsx diff --git a/components/buttons/Base64AudioPlayer.tsx b/components/buttons/Base64AudioPlayer.tsx new file mode 100644 index 0000000..ba0e71b --- /dev/null +++ b/components/buttons/Base64AudioPlayer.tsx @@ -0,0 +1,51 @@ +import React, { useEffect, useState } from 'react'; +import { View, Button, Text } from 'react-native'; +import { Audio } from 'expo-av'; + +interface Props { + base64Audio: string; + forceEnd: boolean; +} + +export default function Base64AudioPlayer({ base64Audio, forceEnd }: Props) { + const [sound, setSound] = useState<Audio.Sound | null>(null); + const [isPlaying, setIsPlaying] = useState(false); + + const playAudio = async () => { + try { + const { sound } = await Audio.Sound.createAsync( + { uri: `${base64Audio}` } + ); + setSound(sound); + + await sound.playAsync(); + setIsPlaying(true); + + sound.setOnPlaybackStatusUpdate((status) => { + if (status.isLoaded && !status.isBuffering && status.didJustFinish) { + setIsPlaying(false); + } + }); + } catch (error) { + console.error('Erreur lors de la lecture de l\'audio :', error); + } + }; + + const stopAudio = async () => { + if (sound) { + await sound.stopAsync(); + setIsPlaying(false); + } + }; + + useEffect(() => { + stopAudio(); + }, [forceEnd]); + + return ( + <View style={{ padding: 20 }}> + <Text style={{ marginBottom: 10 }}>Appuyer pour jouer l'audio</Text> + <Button title={isPlaying ? 'Arrêter l\'audio' : 'Jouer l\'audio'} onPress={isPlaying ? stopAudio : playAudio} /> + </View> + ); +} \ No newline at end of file -- GitLab From 196b00c31b32bb05a7cd34a361aabd2b19d1b3f8 Mon Sep 17 00:00:00 2001 From: "h.hartz" <henri.hartz@etu.unistra.fr> Date: Thu, 16 Jan 2025 17:16:08 +0100 Subject: [PATCH 6/6] feat: add audio response --- components/buttons/AnswerButton.tsx | 9 ++++++--- screens/PlayingQuiz/PlayingQuizBody.tsx | 7 +++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/components/buttons/AnswerButton.tsx b/components/buttons/AnswerButton.tsx index 5b861da..fb4c08a 100644 --- a/components/buttons/AnswerButton.tsx +++ b/components/buttons/AnswerButton.tsx @@ -2,6 +2,8 @@ import {TouchableOpacity, Text, StyleSheet, View, Image} from "react-native"; import {ButtonsStyles} from "../../styles/ButtonsStyles"; import {TextsStyles} from "../../styles/TextsStyles"; import React from "react"; +import Base64AudioPlayer from "./Base64AudioPlayer"; + interface Props{ text: string; @@ -9,6 +11,7 @@ interface Props{ buttonStyle?: any; buttonText?: any; indicator?: number | undefined; + forceEnd: boolean; } /** @@ -18,7 +21,7 @@ interface Props{ * @param buttonStyle - Surcharge du style du bouton * @param buttonText - Surcharge du style du texte du bouton */ -export default function AnswerButton({text, handleButtonPressed, buttonStyle, buttonText, indicator}: Props){ +export default function AnswerButton({text, handleButtonPressed, buttonStyle, buttonText, indicator, forceEnd}: Props){ return( <View style={styles.containerButton}> <TouchableOpacity onPress={handleButtonPressed} style={[styles.defaultButton,buttonStyle]}> @@ -35,9 +38,9 @@ export default function AnswerButton({text, handleButtonPressed, buttonStyle, bu /> : text.includes('data:audio') ? - <Text style={[TextsStyles.defaultButtonText, buttonText]}>sound: not supported</Text> + <Base64AudioPlayer base64Audio={text} forceEnd={forceEnd}/> : - <Text style={[TextsStyles.defaultButtonText, buttonText]}>{text}</Text> + <Text style={[TextsStyles.defaultButtonText, buttonText]}>{text}</Text> } </TouchableOpacity> </View> diff --git a/screens/PlayingQuiz/PlayingQuizBody.tsx b/screens/PlayingQuiz/PlayingQuizBody.tsx index 532b978..a861318 100644 --- a/screens/PlayingQuiz/PlayingQuizBody.tsx +++ b/screens/PlayingQuiz/PlayingQuizBody.tsx @@ -11,7 +11,6 @@ import {Answer} from "../../models/Answer"; import {Quiz} from "../../models/Quiz"; import AnswerButton from "../../components/buttons/AnswerButton"; import {QuizInformations} from "../../models/QuizInformations"; -import AnswerButtonImage from "../../components/buttons/AnswerButtonImage"; interface Props { quizInformations?: QuizInformations; @@ -71,6 +70,7 @@ export default function PlayingQuizBody({ quizInformations, runId, actualQuestio const [quizState, setQuizState] = useState(QuizState.ANSWERING); const [correctAnswerId, setCorrectAnswerId] = useState<number | null>(null); const [isLastQuestion, setIsLastQuestion] = useState(false); + const [forceEnd, setForceEnd] = useState(false); const {answerQuestion} = useQuizService(); const getCorrectAnswer = (answers: Answer[]): Answer => { @@ -81,6 +81,7 @@ export default function PlayingQuizBody({ quizInformations, runId, actualQuestio const onValidation = async () => { if(selectedAnswerId === null) return; setQuizState(QuizState.LOADING); + setForceEnd(!forceEnd); const answerId = actualQuestion.answers.find((answer) => answer.id === selectedAnswerId)?.id; if(!answerId || !runId) return; const answereFetched = await answerQuestion(runId, actualQuestion.id, answerId); @@ -101,7 +102,8 @@ export default function PlayingQuizBody({ quizInformations, runId, actualQuestio } }; - const onContinue = () => { + const onContinue = async () => { + await setForceEnd(!forceEnd); if(isLastQuestion) { navigation.reset({ index: 0, @@ -142,6 +144,7 @@ export default function PlayingQuizBody({ quizInformations, runId, actualQuestio handleButtonPressed={() => onAnsweredButtonClicked(answer.id)} buttonStyle={getStyleOfAnswerButton(answer.id, selectedAnswerId, correctAnswerId, quizState)} buttonText={getStyleOfAnswerButtonText(answer.id, selectedAnswerId, correctAnswerId, quizState)} + forceEnd={forceEnd} /> ))} </ScrollView> -- GitLab