Skip to content

Commit

Permalink
feat(all): ✨ add explanation and play feature
Browse files Browse the repository at this point in the history
Fixes #2217 #2209

Signed-off-by: Yunus Andréasson <[email protected]>
  • Loading branch information
YunusAndreasson committed Aug 6, 2023
1 parent 281dc5f commit 0788924
Show file tree
Hide file tree
Showing 28 changed files with 252 additions and 70 deletions.
Binary file modified api/.yarn/install-state.gz
Binary file not shown.
Empty file added api/.yarn/versions/a865d729.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.229",
"version": "1444.12.230",
"authors": [
"Yunus Andreasson <[email protected]> (https://github.com/YunusAndreasson)"
],
Expand Down
8 changes: 6 additions & 2 deletions api/schemas/texts.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const getTextsOptions = {
handler: listTexts
}

// used for categories
const getTextsWithIdOptions = {
schema: {
response: {
Expand Down Expand Up @@ -80,6 +81,7 @@ const getTextOptions = {
slug: { type: 'string' },
image: { type: 'string' },
introduction: { type: 'string' },
explanation: { type: 'string' },
views: { type: 'string' },
timeAgo: { type: 'string' },
readingTime: { type: 'string' },
Expand Down Expand Up @@ -147,7 +149,8 @@ const updateTextOptions = {
properties: {
quiz: { type: 'boolean' },
arabic: { type: 'string', minLength: 1, maxLength: 50 },
english: { type: 'string', minLength: 1, maxLength: 50 }
english: { type: 'string', minLength: 1, maxLength: 50 },
explanation: { type: 'string' }
}
}
}
Expand Down Expand Up @@ -208,7 +211,8 @@ const postTextOptions = {
properties: {
quiz: { type: 'boolean' },
arabic: { type: 'string', maxLength: 50 },
english: { type: 'string', maxLength: 50 }
english: { type: 'string', maxLength: 50 },
explanation: { type: 'string' }
}
}
}
Expand Down
1 change: 1 addition & 0 deletions api/schemas/words.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ const getWordsOptions = {
grammar: { type: 'string' },
filename: { type: 'string' },
date: { type: 'string' },
explanation: { type: 'string' },
publishDate: { type: 'string' },
arabicSentenceFilename: { type: 'string' }
}
Expand Down
1 change: 1 addition & 0 deletions api/services/texts.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ async function getAllWordsFromTexts(textsCollection) {
arabicSentence: sentence.arabic,
englishSentence: sentence.english,
audioSentence: sentence.filename,
explanation: sentence.explanation,
author: text.author,
source: text.source,
arabicSentenceFilename: sentence.filename
Expand Down
Binary file modified mobile/.yarn/install-state.gz
Binary file not shown.
Empty file.
1 change: 1 addition & 0 deletions mobile/components/modal-scroll-view.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const ModalScrollView = ({
...sharedStyled.arabicHeading,
alignSelf: 'center',
marginHorizontal: 10,
paddingBottom: 15,
textAlign: 'center',
writingDirection: 'rtl'
},
Expand Down
11 changes: 7 additions & 4 deletions mobile/components/play-sound.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,20 @@ import PropTypes from 'prop-types'
import { StyleSheet } from 'react-native'

// This is more of a component than a server and might be better placed in the components folder
export default function PlaySound({ audioFileNames, buttonText }) {
export default function PlaySound({ audioFileNames, buttonText, mode = 'elevated', margin = 10 }) {
const [sound, setSound] = React.useState()

const styles = StyleSheet.create({
button: {
marginBottom: 10,
marginTop: 10
marginBottom: margin,
marginTop: margin
}
})

// function that loops over audioFileName that is an array and calls playSound with the should that should be played
const playAllSounds = async () => {
console.log(audioFileNames)

if (Array.isArray(audioFileNames)) {
for (const audioFileName of audioFileNames) {
await playSound(audioFileName)
Expand Down Expand Up @@ -69,13 +71,14 @@ export default function PlaySound({ audioFileNames, buttonText }) {
}, [sound])

return (
<Button onPress={playAllSounds} mode="elevated" style={styles.button} icon={'play'}>
<Button onPress={playAllSounds} mode={mode} style={styles.button} icon={'play'}>
{buttonText}
</Button>
)
}

PlaySound.propTypes = {
mode: PropTypes.string,
audioFileNames: PropTypes.any.isRequired,
buttonText: PropTypes.string.isRequired
}
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": "1444.12.329",
"version": "1444.12.330",
"authors": [
"Yunus Andreasson <[email protected]> (https://github.com/YunusAndreasson)"
],
Expand Down
2 changes: 1 addition & 1 deletion mobile/screens/text-bilingual-heading.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,6 @@ TextBilingualHeading.propTypes = {
image: PropTypes.string.isRequired,
author: PropTypes.string.isRequired,
source: PropTypes.string.isRequired,
introduction: PropTypes.string.isOptional
introduction: PropTypes.string
})
}
6 changes: 5 additions & 1 deletion mobile/screens/text-bilingual-sentences-word-pair.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import React, { Fragment } from 'react'
import PropTypes from 'prop-types'
import { StyleSheet, View } from 'react-native'
import { Text, useTheme } from 'react-native-paper'
import { Text, useTheme, Button } from 'react-native-paper'
import PlaySound from '../components/play-sound.js'
import { useSharedStyles } from '../styles/common.js'
import ModalScrollView from '../components/modal-scroll-view.js'
Expand All @@ -25,6 +25,7 @@ function TextBilingualSentencesWords({ word }) {
const sharedStyle = useSharedStyles(theme)
const [visible, setVisible] = React.useState(false)
const hideModal = () => setVisible(false)
const showModal = () => setVisible(true)

const explanation = formatGrammar(word.grammar, sharedStyle)

Expand All @@ -39,6 +40,9 @@ function TextBilingualSentencesWords({ word }) {
</View>
<View style={styles.flexOne}>
<PlaySound audioFileNames={word.filename} buttonText={UI.play} />
<Button mode="elevated" textColor={theme.colors.tertiary} onPress={showModal} icon="eye-outline">
{UI.explain}
</Button>
</View>
</View>
<ModalScrollView
Expand Down
2 changes: 1 addition & 1 deletion mobile/screens/text-bilingual-sentences.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ function TextBilingualSentences(props) {
<Button
mode="elevated"
textColor={theme.colors.tertiary}
icon="abjad-arabic"
icon="eye-outline"
onPress={() => {
getListOfWordPairs(<WordPairs words={util.filterArrayFromEmptyElements(sentence.words, filterFunction)} />)
showModal()
Expand Down
5 changes: 5 additions & 0 deletions mobile/screens/text-list-card-quote.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import PropTypes from 'prop-types'
import React, { useState, useCallback } from 'react'
import { StyleSheet, Animated, Share } from 'react-native'
import { useSharedStyles } from '../styles/common.js'
import PlaySound from '../components/play-sound.js'

export default function TextListCardQuote({ text }) {
const [scaleValue] = useState(new Animated.Value(1))
Expand Down Expand Up @@ -87,6 +88,10 @@ export default function TextListCardQuote({ text }) {
New ☀️
</Chip>
)}
<PlaySound
audioFileNames={`https://openarabic.ams3.digitaloceanspaces.com/audio/${text.sentences[0].filename}`}
buttonText={'Play'}
/>
<Button onPress={onShare}>Share</Button>
</Card.Actions>
</Card>
Expand Down
58 changes: 52 additions & 6 deletions mobile/screens/text-practice.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { View, ScrollView, Alert } from 'react-native'
import { Text, Surface, Divider, useTheme } from 'react-native-paper'
import { Text, Surface, Divider, useTheme, Button } from 'react-native-paper'
import { useSelector } from 'react-redux'
import React, { useState, useEffect, useCallback } from 'react'
import { useSharedStyles } from '../styles/common.js'
Expand All @@ -8,6 +8,9 @@ import WordsContextHighLighted from '../components/context-highlighted.js'
import TextPracticeArabicWords from './text-practice-arabic-words.js'
import { getThreeRandomWords, vibrateBetweenTwoColors } from '../services/utility-service.js'
import Spinner from '../components/spinner.js'
import ModalScrollView from '../components/modal-scroll-view.js'
import { formatGrammar } from '../services/ui-services.js'
import PlaySound from '../components/play-sound.js'

const selector = (state) => state.text
const textLoadSelector = (state) => state.textLoading
Expand All @@ -22,6 +25,9 @@ const TextPractice = () => {
const [currentArabicWordsInSentence, setCurrentArabicWordsInSentence] = useState([])
const [color, setColor] = useState(theme.colors.elevation.level2)
const [currentEnglishWord, setCurrentEnglishWord] = useState(0)
const hideModal = () => setVisible(false)
const [visible, setVisible] = React.useState(false)
const [explanation, setExplanation] = useState('')

// update the state for currentArabicWordsInSentence with the arabic words in the current sentence (sentencesInText[currentSentence].arabicWords) when the component loads
useEffect(() => {
Expand Down Expand Up @@ -64,16 +70,17 @@ const TextPractice = () => {
const wordsInSentence = sentence.words.map((word, wordIndex) => {
return {
arabicWord: { arabic: word.arabic, id: wordIndex },
englishWord: { english: word.english, id: wordIndex }
englishWord: { english: word.english, id: wordIndex },
explanation: word.explanation
}
})

const arabicWords = wordsInSentence.map((word) => word.arabicWord).sort(() => Math.random() - 0.5)
const englishWords = wordsInSentence.map((word) => word.englishWord)
const explanations = wordsInSentence.map((word) => word.explanation)
const filename = sentence.filename

const explanation = sentence.explanation

return { arabicWords, englishWords, explanation }
return { arabicWords, englishWords, explanations, filename }
})
}, [text])

Expand Down Expand Up @@ -147,9 +154,48 @@ const TextPractice = () => {
englishWord={currentEnglishWord}
/>
</View>
<View style={{ position: 'absolute', right: 10, bottom: 20, flexDirection: 'row' }}>
<Button
mode="contained-tonal"
style={{ marginHorizontal: 5 }}
icon="eye-outline"
onPress={() => {
let combinedExplanations = ''

for (const [index, word] of sentencesInText[currentSentence].englishWords.entries()) {
const currentEnglishWord = word.english.charAt(0).toUpperCase() + word.english.slice(1)
const currentArabicWord = sentencesInText[currentSentence].arabicWords[index].arabic
const currentExplanation = sentencesInText[currentSentence].explanations[index]

combinedExplanations += `⟶ ${currentArabicWord}\n↠ ${currentEnglishWord}\n${currentExplanation}\n\n`
}

setExplanation(formatGrammar(combinedExplanations, sharedStyle))
setVisible(true)
}}
>
Explain
</Button>
<PlaySound
audioFileNames={sentencesInText[currentSentence].filename}
buttonText="Play"
margin={0}
mode="contained-tonal"
/>
<Button mode="contained" style={{ marginHorizontal: 5 }}>
Next
</Button>
</View>
</Surface>

<Divider style={{ ...sharedStyle.divider, opacity: 0 }} />
<ModalScrollView
visible={visible}
titleLanguage="english"
content={<View>{explanation}</View>}
title={'Explain'}
hideModal={hideModal}
/>

<TextPracticeArabicWords
testID="textPracticeArabicWords"
currentArabicWordsInSentence={currentArabicWordsInSentence}
Expand Down
63 changes: 27 additions & 36 deletions mobile/screens/words-content.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ import * as Haptics from 'expo-haptics'
import PropTypes from 'prop-types'
import PlaySound from '../components/play-sound.js'
import { vibrateBetweenTwoColors, generateRandomPositions } from '../services/utility-service.js'
import ModalScrollView from '../components/modal-scroll-view.js'
import { formatGrammar } from '../services/ui-services.js'

const wordsSelector = (state) => state.words

const WordsContent = ({
Expand All @@ -31,8 +28,6 @@ const WordsContent = ({
const [color, setColor] = useState(theme.colors.elevation.level2)
const [buttonPositions, setButtonPositions] = useState(generateRandomPositions())
const [timeoutId, setTimeoutId] = useState()
const hideModal = () => setVisible(false)
const [visible, setVisible] = React.useState(false)
const sharedStyle = useSharedStyles(theme)
const [wrongAnswers, setWrongAnswers] = useState(0)
const [wrongAnswerAlreadyAdded, setWrongAnswerAlreadyAdded] = useState([])
Expand All @@ -51,7 +46,7 @@ const WordsContent = ({

marginBottom: 10,
marginVertical: 10,
minHeight: 350
minHeight: 320
},
text: {
color: theme.colors.primary,
Expand Down Expand Up @@ -157,14 +152,6 @@ const WordsContent = ({
{ button: button3, position: buttonPositions[2] }
].sort((a, b) => a.position - b.position)

const details = (
<View>
{words[currentWord]?.grammar
? formatGrammar(words[currentWord].grammar, sharedStyle)
: 'No explanation available'}
</View>
)

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

return (
Expand All @@ -181,35 +168,45 @@ const WordsContent = ({
style={{ height: 7, borderRadius: 10, backgroundColor: theme.colors.elevation.level2 }}
/>
<Surface style={styles.surface}>
<Text
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text
style={{
fontFamily: 'uthman',
width: '97%',
fontSize: 120,
textAlign: 'center',
color: theme.colors.secondary,
paddingBottom: 50
}}
>
{words[currentWord]?.arabic.trim()}
</Text>
</View>

{/* <Text
style={{
paddingTop: 15,
fontFamily: 'uthman',
width: '97%',
fontSize: 100,
...sharedStyle.arabicBody,
fontSize: 30,
textAlign: 'center',
color: theme.colors.secondary
paddingHorizontal: 10,
lineHeight: 45
}}
>
{words[currentWord]?.arabic.trim()}
</Text>

<Text style={{ ...sharedStyle.arabicBody, fontSize: 30, textAlign: 'center', paddingHorizontal: 30 }}>
{words[currentWord]?.arabicSentence}
</Text>
</Text> */}

<View style={{ position: 'absolute', bottom: 25, left: 10 }}>
{/* <View style={{ position: 'absolute', bottom: 15, left: 15, width: 130 }}>
<Text variant="labelSmall" style={{ color: theme.colors.outline }}>
{words[currentWord]?.source}
</Text>
</View>
</View> */}

<View style={{ position: 'absolute', bottom: 5, right: 10 }}>
<View style={{ flexDirection: 'row', position: 'absolute', bottom: 5, right: 10 }}>
<PlaySound
mode="text"
audioFileNames={[
`https://openarabic.ams3.digitaloceanspaces.com/audio/${words[currentWord].filename}`,
`https://openarabic.ams3.digitaloceanspaces.com/audio/${words[currentWord].filename}`,
`https://openarabic.ams3.digitaloceanspaces.com/audio/${words[currentWord].arabicSentenceFilename}`
`https://openarabic.ams3.digitaloceanspaces.com/audio/${words[currentWord].filename}`
]}
buttonText={'Play'}
style={{}}
Expand All @@ -222,12 +219,6 @@ const WordsContent = ({
duration={3500}
text="Session Completed Successfully!"
/>
<ModalScrollView
visible={visible}
content={details}
title={words[currentWord]?.arabic}
hideModal={hideModal}
/>
</>
}
/>
Expand Down
Loading

0 comments on commit 0788924

Please sign in to comment.