Skip to content

Commit

Permalink
feat(all): ✨ optimize generation of text and audio
Browse files Browse the repository at this point in the history
Signed-off-by: Yunus Andréasson <yunus@edenmind.com>
  • Loading branch information
YunusAndreasson committed Oct 21, 2023
1 parent b5d24b0 commit 9a3eab9
Show file tree
Hide file tree
Showing 17 changed files with 202 additions and 52 deletions.
Binary file modified api/.yarn/install-state.gz
Binary file not shown.
Empty file added api/.yarn/versions/62f3d57c.yml
Empty file.
2 changes: 1 addition & 1 deletion api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"license": "MIT",
"homepage": "https://openarabic.io",
"repository": "https://github.com/edenmind/OpenArabic",
"version": "1444.12.244",
"version": "1444.12.245",
"authors": [
"Yunus Andreasson <yunus@edenmind.com> (https://github.com/YunusAndreasson)"
],
Expand Down
Binary file modified mobile/.yarn/install-state.gz
Binary file not shown.
Empty file.
2 changes: 0 additions & 2 deletions mobile/components/modal-scroll-view.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ const ModalScrollView = ({ content, visible, hideModal }) => {
alignSelf: 'flex-start'
},
containerStyle: {
alignItems: 'center',
alignSelf: 'flex-start',
backgroundColor: theme.colors.background,
height: Dimensions.get('window').height,
padding: 10,
Expand Down
1 change: 0 additions & 1 deletion mobile/hooks/use-audio-player.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ export const useAudioPlayer = () => {
setIsSoundLoaded(true)

if (audioSound._loaded && !audioSound._playing && !shouldStop) {
console.log('Playing sound:', shouldPlay)
await audioSound.playAsync()
}
} catch (error) {
Expand Down
44 changes: 42 additions & 2 deletions mobile/hooks/use-practice-logic.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@ function useTextPracticeLogic() {

const audioURL = HOST.audio + wordFilename
if (shouldPlayPracticeWord) {
console.log('shouldPlayPracticeWord:', shouldPlayPracticeWord)

playSound(audioURL)
}
}, [currentWord, shouldPlayPracticeWord])
Expand Down Expand Up @@ -110,6 +108,47 @@ function useTextPracticeLogic() {
setCurrentWord(0)
}

// Add all words from the current sentence to the practice list
const addAllWordsFromCurrentSentence = () => {
const currentSentenceData = sentencesInText[currentSentence]

if (!currentSentenceData) return

dispatch({
type: 'RESET_WORDS'
})

for (const wordData of currentSentenceData.englishWords) {
const currentEnglishWord = wordData.english

// Find the corresponding Arabic word and its filename
const correspondingArabicData = currentSentenceData.arabicWords.find((aw) => aw.id === wordData.id)
const correspondingArabicWord = correspondingArabicData ? correspondingArabicData.arabic : undefined
const wordFilename = correspondingArabicData
? currentSentenceData.wordFilename[correspondingArabicData.id]
: undefined

// Get alternative English words
const shuffledAlternatives = currentSentenceData.englishWords
.filter((altWord) => altWord.english !== currentEnglishWord)
.sort(() => Math.random() - 0.5)
const alternatives = shuffledAlternatives.slice(0, 2).map((alt) => alt.english)

const wordInDesiredFormat = {
alternative1: alternatives[0],
alternative2: alternatives[1],
arabic: correspondingArabicWord,
english: currentEnglishWord,
filename: wordFilename
}

dispatch({
payload: wordInDesiredFormat,
type: 'ADD_WORD'
})
}
}

// When a word is pressed in the sentence, check if it is the correct word
const handlePress = useCallback(
async (id) => {
Expand Down Expand Up @@ -198,6 +237,7 @@ function useTextPracticeLogic() {
}, [text])

return {
addAllWordsFromCurrentSentence,
celebrationSnackBarVisibility,
currentArabicWord,
currentSentence,
Expand Down
2 changes: 1 addition & 1 deletion mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"license": "MIT",
"homepage": "https://openarabic.io",
"repository": "https://github.com/edenmind/OpenArabic",
"version": "1445.2.387",
"version": "1445.2.388",
"authors": [
"Yunus Andreasson <yunus@edenmind.com> (https://github.com/YunusAndreasson)"
],
Expand Down
85 changes: 85 additions & 0 deletions mobile/screens/text-practice-vocabulary.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/* eslint-disable react-hooks/exhaustive-deps */
/* eslint-disable react-native/no-inline-styles */
/* eslint-disable react-native/no-color-literals */
import PropTypes from 'prop-types'
import React, { useState, useMemo, useEffect } from 'react'
import { View, FlatList, Pressable, StyleSheet } from 'react-native'
import { Surface, Text, useTheme } from 'react-native-paper'

import { ButtonAnswer } from '../components/button-answer.js'
import Spinner from '../components/spinner.js'
import { useWordsLogic } from '../hooks/use-words-logic.js'
import { calculateFontSize } from '../services/ui-services.js'
import { useSharedStyles } from '../styles/common.js'

const styles = StyleSheet.create({
bottomList: {
bottom: 25,
left: 0,
position: 'absolute',
right: 0
}
})

export const PracticeVocabulary = (props) => {
const theme = useTheme()
const sharedStyle = useSharedStyles(theme)

const [currentWord, setCurrentWord] = useState(0)
const [currentWordIndex, setCurrentWordIndex] = useState(0)

const { arabic, buttonPositions, handleCorrectAnswer, handlePressOnWord, localWords } = useWordsLogic(
currentWord,
setCurrentWord,
setCurrentWordIndex
)

const fontSize = useMemo(() => calculateFontSize(arabic), [arabic])

const generateAnswerButton = (text, onPress, isCorrect = false) => (
<ButtonAnswer text={text} onPress={onPress} correct={isCorrect} incorrect={!isCorrect} />
)

useEffect(() => {
if (currentWordIndex === localWords.length) {
props.handleContinue()
}
}, [currentWordIndex])

const buttons = useMemo(() => {
const mainButton = generateAnswerButton(localWords[currentWord]?.english, handleCorrectAnswer, true)
const altButton1 = generateAnswerButton(localWords[currentWord]?.alternative1)
const altButton2 = generateAnswerButton(localWords[currentWord]?.alternative2)

return [
{ button: mainButton, position: buttonPositions[0] },
{ button: altButton1, position: buttonPositions[1] },
{ button: altButton2, position: buttonPositions[2] }
].sort((a, b) => a.position - b.position)
}, [localWords, currentWord, handleCorrectAnswer, buttonPositions])

const renderItem = ({ item }) => <>{item.button}</>

return (
<>
<Surface style={sharedStyle.wordSurface}>
<View style={sharedStyle.wordCenteredView}>
<Pressable onPress={handlePressOnWord}>
<Text style={[sharedStyle.wordText, { color: theme.colors.secondary, fontSize }]}>{arabic}</Text>
</Pressable>
</View>
</Surface>
<FlatList
style={[sharedStyle.wordContainer, styles.bottomList]}
data={buttons}
renderItem={renderItem}
ListEmptyComponent={<Spinner />}
keyExtractor={(item) => item.position.toString()}
/>
</>
)
}

PracticeVocabulary.propTypes = {
handleContinue: PropTypes.func.isRequired
}
71 changes: 44 additions & 27 deletions mobile/screens/text-practice.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
/* eslint-disable react-hooks/exhaustive-deps */
import React, { useState, useEffect } from 'react'
import { View } from 'react-native'
import { useTheme, ProgressBar } from 'react-native-paper'
import { useDispatch } from 'react-redux'

import { PracticeListening } from './text-practice-listening.js'
import { PracticeReading } from './text-practice-reading.js'
import { PracticeVocabulary } from './text-practice-vocabulary.js'
import Spinner from '../components/spinner.js'
import useTextPracticeLogic from '../hooks/use-practice-logic.js'
import { useSharedStyles } from '../styles/common.js'
Expand All @@ -19,6 +21,7 @@ const TextPractice = () => {
const dispatch = useDispatch()

const {
addAllWordsFromCurrentSentence,
isLastSentence,
currentArabicWord,
currentSentence,
Expand All @@ -36,22 +39,36 @@ const TextPractice = () => {

useEffect(() => {
if (sentenceIsComplete) {
setExerciseType()
handleStartVocabularyPractice()
}
}, [sentenceIsComplete])

useEffect(() => {
if (isLastSentence) {
handlePracticeComplete()
}
}, [isLastSentence])

const handlePracticeComplete = () => {
setExerciseType()
handleContinue()
}

const handleStartReadingPractice = () => {
setExerciseType('reading')
addAllWordsFromCurrentSentence()
}

const handleStartVocabularyPractice = () => {
setExerciseType('vocabulary')
}

const handleStartListeningPractice = () => {
setExerciseType('listening')
}

const onWordPressed = (wordId, wordArabic) => {
handlePress(wordId, wordArabic)
if (sentenceIsComplete) handlePracticeComplete()
}

return textLoading ? (
Expand All @@ -62,34 +79,34 @@ const TextPractice = () => {
style={sharedStyle.progressBar}
/>

{exerciseType === 'reading' ? (
<PracticeReading
{...{
currentArabicWord,
currentSentence,
currentWord,
currentWordsInSentence,
onWordPressed,
sentencesInText
}}
/>
) : (
{exerciseType === 'listening' && (
<PracticeListening
{...{
currentSentence,
dispatch,
handleContinue: handleStartReadingPractice,
handleReset,
isLastSentence,
isPlaying,
setIsPlaying,
setSentenceIsComplete,
setShowRepeat,
showRepeat,
text
}}
currentSentence={currentSentence}
dispatch={dispatch}
handleContinue={handleStartReadingPractice}
handleReset={handleReset}
isLastSentence={isLastSentence}
isPlaying={isPlaying}
setIsPlaying={setIsPlaying}
setSentenceIsComplete={setSentenceIsComplete}
setShowRepeat={setShowRepeat}
showRepeat={showRepeat}
text={text}
/>
)}

{exerciseType === 'reading' && (
<PracticeReading
currentArabicWord={currentArabicWord}
currentSentence={currentSentence}
currentWord={currentWord}
currentWordsInSentence={currentWordsInSentence}
onWordPressed={onWordPressed}
sentencesInText={sentencesInText}
/>
)}

{exerciseType === 'vocabulary' && <PracticeVocabulary handleContinue={handleStartListeningPractice} />}
</View>
) : (
<Spinner />
Expand Down
23 changes: 23 additions & 0 deletions mobile/screens/words-completed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import PropTypes from 'prop-types'
import React from 'react'
import { View, Image } from 'react-native'
import { Text } from 'react-native-paper'

import icon from '../assets/logo.png'
import { pluralize } from '../services/ui-services.js'

export const CompletedView = (props) => (
<View style={{ alignItems: 'center', flex: 1, justifyContent: 'center' }}>
<Image source={icon} style={{ height: 100, width: 100 }} />
<Text variant="titleLarge" style={{ paddingTop: 10, textAlign: 'center' }}>
Well Done
</Text>
<Text variant="bodyMedium" style={{ textAlign: 'center', width: 250 }}>
You have finished the {pluralize(props.localWords.length, 'word')} that you had to review, mashaAllah!
</Text>
</View>
)

CompletedView.propTypes = {
localWords: PropTypes.array.isRequired
}
20 changes: 4 additions & 16 deletions mobile/screens/words.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
/* eslint-disable react-native/no-inline-styles */
/* eslint-disable react-native/no-color-literals */
import React, { useState, useMemo } from 'react'
import { View, FlatList, Pressable, StyleSheet, Image } from 'react-native'
import { View, FlatList, Pressable, StyleSheet } from 'react-native'
import { Surface, Text, useTheme, ProgressBar } from 'react-native-paper'

import icon from '../assets/logo.png'
import { CompletedView } from './words-completed.js'
import { ButtonAnswer } from '../components/button-answer.js'
import Spinner from '../components/spinner.js'
import { useWordsLogic } from '../hooks/use-words-logic.js'
import { calculateFontSize, pluralize } from '../services/ui-services.js'
import { calculateFontSize } from '../services/ui-services.js'
import { useSharedStyles } from '../styles/common.js'

const styles = StyleSheet.create({
Expand Down Expand Up @@ -53,20 +53,8 @@ const Words = () => {

const renderItem = ({ item }) => <>{item.button}</>

const CompletedView = () => (
<View style={{ alignItems: 'center', flex: 1, justifyContent: 'center' }}>
<Image source={icon} style={{ height: 100, width: 100 }} />
<Text variant="titleLarge" style={{ paddingTop: 10, textAlign: 'center' }}>
Well Done
</Text>
<Text variant="bodyMedium" style={{ textAlign: 'center', width: 250 }}>
You have finished the {pluralize(localWords.length, 'word')} that you had to review, mashaAllah!
</Text>
</View>
)

if (currentWordIndex === localWords.length) {
return <CompletedView />
return <CompletedView localWords={localWords} />
}

return (
Expand Down
2 changes: 1 addition & 1 deletion package/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: open-arabic-helm
description: A Helm Chart for OpenArabic
version: 1444.0.299
version: 1444.0.300
apiVersion: v2
type: application
home: https://openarabic.io
Expand Down
Binary file modified web/.yarn/install-state.gz
Binary file not shown.
Empty file added web/.yarn/versions/dbebf77c.yml
Empty file.
2 changes: 1 addition & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"license": "MIT",
"homepage": "https://openarabic.io",
"repository": "https://github.com/edenmind/OpenArabic",
"version": "1444.12.227",
"version": "1444.12.228",
"authors": [
"Yunus Andreasson <yunus@edenmind.com> (https://github.com/YunusAndreasson)"
],
Expand Down

0 comments on commit 9a3eab9

Please sign in to comment.