@moustafahelmi/react-native-quran-app 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.bundle/config +2 -0
- package/.eslintrc.js +4 -0
- package/.prettierrc.js +7 -0
- package/.watchmanconfig +1 -0
- package/App.tsx +23 -0
- package/Gemfile +9 -0
- package/Gemfile.lock +105 -0
- package/MIGRATION.md +163 -0
- package/README.md +210 -0
- package/ReactotronConfig.js +7 -0
- package/__tests__/App.test.tsx +17 -0
- package/android/app/build.gradle +118 -0
- package/android/app/debug.keystore +0 -0
- package/android/app/proguard-rules.pro +10 -0
- package/android/app/src/debug/AndroidManifest.xml +9 -0
- package/android/app/src/main/AndroidManifest.xml +25 -0
- package/android/app/src/main/assets/fonts/Cairo.ttf +0 -0
- package/android/app/src/main/assets/fonts/QCF_BSML.ttf +0 -0
- package/android/app/src/main/assets/fonts/QCF_P001.ttf +0 -0
- package/android/app/src/main/java/com/quranapp/MainActivity.kt +22 -0
- package/android/app/src/main/java/com/quranapp/MainApplication.kt +43 -0
- package/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
- package/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/android/app/src/main/res/values/strings.xml +3 -0
- package/android/app/src/main/res/values/styles.xml +9 -0
- package/android/build.gradle +21 -0
- package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/android/gradle.properties +41 -0
- package/android/gradlew +249 -0
- package/android/gradlew.bat +92 -0
- package/android/link-assets-manifest.json +17 -0
- package/android/settings.gradle +4 -0
- package/app.json +4 -0
- package/babel.config.js +3 -0
- package/index.js +11 -0
- package/ios/.xcode.env +11 -0
- package/ios/Podfile +40 -0
- package/ios/Podfile.lock +1460 -0
- package/ios/QuranApp/AppDelegate.h +6 -0
- package/ios/QuranApp/AppDelegate.mm +31 -0
- package/ios/QuranApp/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
- package/ios/QuranApp/Images.xcassets/Contents.json +6 -0
- package/ios/QuranApp/Info.plist +57 -0
- package/ios/QuranApp/LaunchScreen.storyboard +47 -0
- package/ios/QuranApp/PrivacyInfo.xcprivacy +38 -0
- package/ios/QuranApp/main.m +10 -0
- package/ios/QuranApp.xcodeproj/project.pbxproj +729 -0
- package/ios/QuranApp.xcodeproj/xcshareddata/xcschemes/QuranApp.xcscheme +88 -0
- package/ios/QuranApp.xcworkspace/contents.xcworkspacedata +10 -0
- package/ios/QuranAppTests/Info.plist +24 -0
- package/ios/QuranAppTests/QuranAppTests.m +66 -0
- package/ios/link-assets-manifest.json +17 -0
- package/jest.config.js +3 -0
- package/metro.config.js +11 -0
- package/package.json +54 -0
- package/react-native.config.js +7 -0
- package/screenshots/1.png +0 -0
- package/screenshots/2.png +0 -0
- package/screenshots/3.png +0 -0
- package/screenshots/4.png +0 -0
- package/screenshots/5.png +0 -0
- package/src/assets/fonts/Cairo.ttf +0 -0
- package/src/assets/fonts/QCF_BSML.ttf +0 -0
- package/src/assets/fonts/QCF_P001.ttf +0 -0
- package/src/assets/images/bookmark.png +0 -0
- package/src/assets/images/close.png +0 -0
- package/src/assets/images/copy.png +0 -0
- package/src/assets/images/down-chevron.png +0 -0
- package/src/assets/images/mushafFrame.png +0 -0
- package/src/assets/images/pause.png +0 -0
- package/src/assets/images/play-button.png +0 -0
- package/src/assets/images/play.svg +16 -0
- package/src/assets/images/playNext.png +0 -0
- package/src/assets/images/surahNameFrame.png +0 -0
- package/src/common/chapters.ts +1346 -0
- package/src/common/constants.ts +27 -0
- package/src/common/images.ts +13 -0
- package/src/common/index.ts +8 -0
- package/src/common/juzs.ts +411 -0
- package/src/common/priorityPages.ts +46 -0
- package/src/common/themes.ts +7 -0
- package/src/components/index.ts +3 -0
- package/src/components/lists/index.ts +3 -0
- package/src/components/lists/pageVersesList.tsx +220 -0
- package/src/components/lists/verseLinesWordsList.tsx +90 -0
- package/src/components/modals/index.ts +3 -0
- package/src/components/modals/optionsModal.tsx +126 -0
- package/src/components/modals/recitersModal.tsx +118 -0
- package/src/components/sections/audioPlayer.tsx +185 -0
- package/src/components/sections/audioPlayerControls.tsx +100 -0
- package/src/components/sections/index.ts +4 -0
- package/src/components/sections/loader.tsx +33 -0
- package/src/helpers/index.ts +1 -0
- package/src/helpers/quranHelpers.tsx +17 -0
- package/src/hooks/apis/index.ts +10 -0
- package/src/hooks/apis/useGetChapterAudio.ts +111 -0
- package/src/hooks/apis/useGetChapterByPage.ts +166 -0
- package/src/hooks/apis/useGetChapterLookup.ts +31 -0
- package/src/hooks/apis/useGetReciters.ts +44 -0
- package/src/hooks/controllers/index.ts +25 -0
- package/src/hooks/controllers/useAudioPlayerController.tsx +63 -0
- package/src/hooks/controllers/useOptionsModalController.ts +99 -0
- package/src/hooks/controllers/usePageFontFileController.ts +255 -0
- package/src/hooks/controllers/usePageLineController.ts +108 -0
- package/src/hooks/helpers/index.ts +6 -0
- package/src/hooks/helpers/useQuranFontPreloader.ts +225 -0
- package/src/hooks/index.ts +3 -0
- package/src/index.ts +5 -0
- package/src/layouts/bismillahText.tsx +18 -0
- package/src/layouts/index.ts +4 -0
- package/src/layouts/quranChapterHeader.tsx +49 -0
- package/src/layouts/quranPageLayout.tsx +178 -0
- package/src/types/global.d.ts +7 -0
- package/src/types/index.ts +212 -0
- package/src/utils/axiosInstance.ts +7 -0
- package/src/utils/fileHandlers.ts +96 -0
- package/src/utils/handleBeforeAndAfterCurrentVerse.ts +41 -0
- package/src/utils/index.ts +6 -0
- package/src/utils/matrics.ts +37 -0
- package/tsconfig.json +3 -0
- package/videos/1.gif +0 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import {forwardRef, useEffect, useImperativeHandle, useState} from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Dimensions,
|
|
4
|
+
Image,
|
|
5
|
+
StyleSheet,
|
|
6
|
+
Text,
|
|
7
|
+
TouchableOpacity,
|
|
8
|
+
View,
|
|
9
|
+
} from 'react-native';
|
|
10
|
+
import TrackPlayer, {useProgress} from 'react-native-track-player';
|
|
11
|
+
import {
|
|
12
|
+
IReciter,
|
|
13
|
+
ISurahVerse,
|
|
14
|
+
IVersesBeforeAndAfterCurrentVerse,
|
|
15
|
+
} from '../../types';
|
|
16
|
+
import {COLORS, IMAGES} from '../../common';
|
|
17
|
+
import {useAudioPlayerController} from '../../hooks';
|
|
18
|
+
import {RecitersModal} from '../modals';
|
|
19
|
+
import AudioPlayerControls from './audioPlayerControls';
|
|
20
|
+
import Slider from '@react-native-community/slider';
|
|
21
|
+
interface IProps {
|
|
22
|
+
allReciter: IReciter[];
|
|
23
|
+
setSelectedVerse: (verse: ISurahVerse) => void;
|
|
24
|
+
chapterId: number;
|
|
25
|
+
selectedVerse: ISurahVerse;
|
|
26
|
+
versesBeforeAndAfterCurrentVerse: IVersesBeforeAndAfterCurrentVerse;
|
|
27
|
+
setVersesBeforeAndAfterCurrentVerse: (
|
|
28
|
+
value: IVersesBeforeAndAfterCurrentVerse,
|
|
29
|
+
) => void;
|
|
30
|
+
originalVerse: ISurahVerse[];
|
|
31
|
+
showSlider?: boolean;
|
|
32
|
+
autoCompleteAudioAfterPlayingVerse?: boolean;
|
|
33
|
+
}
|
|
34
|
+
const {height} = Dimensions.get('screen');
|
|
35
|
+
const AudioPlayer = (props: IProps, ref: any) => {
|
|
36
|
+
const {
|
|
37
|
+
allReciter,
|
|
38
|
+
setSelectedVerse,
|
|
39
|
+
chapterId,
|
|
40
|
+
selectedVerse,
|
|
41
|
+
versesBeforeAndAfterCurrentVerse,
|
|
42
|
+
setVersesBeforeAndAfterCurrentVerse,
|
|
43
|
+
originalVerse,
|
|
44
|
+
showSlider,
|
|
45
|
+
autoCompleteAudioAfterPlayingVerse,
|
|
46
|
+
} = props;
|
|
47
|
+
const [selectedReciter, setSelectedReciter] = useState<IReciter>();
|
|
48
|
+
const {duration, position} = useProgress();
|
|
49
|
+
const {
|
|
50
|
+
changeHandler,
|
|
51
|
+
renderplayPauseBtn,
|
|
52
|
+
onPlayPause,
|
|
53
|
+
openReciterModal,
|
|
54
|
+
recitersModalRef,
|
|
55
|
+
formatTime,
|
|
56
|
+
} = useAudioPlayerController();
|
|
57
|
+
const [showPlayer, setShowPlayer] = useState(false);
|
|
58
|
+
useImperativeHandle(ref, () => ({
|
|
59
|
+
async setShowPlayerHandler(value: boolean) {
|
|
60
|
+
// if (value === false) await TrackPlayer?.reset();
|
|
61
|
+
setShowPlayer(value);
|
|
62
|
+
},
|
|
63
|
+
isPlayerShown() {
|
|
64
|
+
return showPlayer;
|
|
65
|
+
},
|
|
66
|
+
_renderSelelctedReciter() {
|
|
67
|
+
return selectedReciter;
|
|
68
|
+
},
|
|
69
|
+
}));
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
if (allReciter?.length > 0) setSelectedReciter(allReciter[0]);
|
|
72
|
+
}, [allReciter]);
|
|
73
|
+
const closeAudioPlayer = async () => {
|
|
74
|
+
setShowPlayer(false);
|
|
75
|
+
await TrackPlayer.pause();
|
|
76
|
+
setSelectedVerse({} as ISurahVerse);
|
|
77
|
+
};
|
|
78
|
+
return (
|
|
79
|
+
showPlayer && (
|
|
80
|
+
<View style={styles.container}>
|
|
81
|
+
<View style={styles.closeContainer}>
|
|
82
|
+
<TouchableOpacity
|
|
83
|
+
onPress={closeAudioPlayer}
|
|
84
|
+
style={[{backgroundColor: COLORS.white, borderRadius: 10}]}>
|
|
85
|
+
<Image source={IMAGES.close} style={styles.closeIcon} />
|
|
86
|
+
</TouchableOpacity>
|
|
87
|
+
</View>
|
|
88
|
+
{showSlider && (
|
|
89
|
+
<Slider
|
|
90
|
+
minimumValue={0}
|
|
91
|
+
maximumValue={duration}
|
|
92
|
+
value={position}
|
|
93
|
+
minimumTrackTintColor={COLORS.darkBlack}
|
|
94
|
+
maximumTrackTintColor={COLORS.light}
|
|
95
|
+
thumbTintColor={COLORS.lighBlack}
|
|
96
|
+
onSlidingComplete={changeHandler}
|
|
97
|
+
/>
|
|
98
|
+
)}
|
|
99
|
+
<View style={styles.row}>
|
|
100
|
+
<View style={{flex: 1}}>
|
|
101
|
+
<Text>
|
|
102
|
+
{formatTime(position)} / {formatTime(duration)}
|
|
103
|
+
</Text>
|
|
104
|
+
</View>
|
|
105
|
+
<View style={styles.controlersContainer}>
|
|
106
|
+
<AudioPlayerControls
|
|
107
|
+
onPlayPause={onPlayPause}
|
|
108
|
+
renderplayPauseBtn={renderplayPauseBtn}
|
|
109
|
+
versesBeforeAndAfterCurrentVerse={
|
|
110
|
+
versesBeforeAndAfterCurrentVerse
|
|
111
|
+
}
|
|
112
|
+
setVersesBeforeAndAfterCurrentVerse={
|
|
113
|
+
setVersesBeforeAndAfterCurrentVerse
|
|
114
|
+
}
|
|
115
|
+
selectedReciter={selectedReciter as IReciter}
|
|
116
|
+
setSelectedVerse={setSelectedVerse}
|
|
117
|
+
selectedVerse={selectedVerse}
|
|
118
|
+
originalVerse={originalVerse}
|
|
119
|
+
autoCompleteAudioAfterPlayingVerse={
|
|
120
|
+
autoCompleteAudioAfterPlayingVerse
|
|
121
|
+
}
|
|
122
|
+
/>
|
|
123
|
+
</View>
|
|
124
|
+
|
|
125
|
+
<TouchableOpacity
|
|
126
|
+
onPress={openReciterModal}
|
|
127
|
+
style={styles.recitersBtn}>
|
|
128
|
+
<Text numberOfLines={1}>{selectedReciter?.name}</Text>
|
|
129
|
+
<Image source={IMAGES.arrowDown} style={styles.closeIcon} />
|
|
130
|
+
</TouchableOpacity>
|
|
131
|
+
</View>
|
|
132
|
+
<RecitersModal
|
|
133
|
+
ref={recitersModalRef}
|
|
134
|
+
allReciter={allReciter}
|
|
135
|
+
selectedReciter={selectedReciter}
|
|
136
|
+
setSelectedReciter={setSelectedReciter}
|
|
137
|
+
chapterId={chapterId}
|
|
138
|
+
selectedVerse={selectedVerse}
|
|
139
|
+
autoCompleteAudioAfterPlayingVerse={
|
|
140
|
+
autoCompleteAudioAfterPlayingVerse
|
|
141
|
+
}
|
|
142
|
+
/>
|
|
143
|
+
</View>
|
|
144
|
+
)
|
|
145
|
+
);
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
export default forwardRef(AudioPlayer);
|
|
149
|
+
|
|
150
|
+
const styles = StyleSheet.create({
|
|
151
|
+
container: {
|
|
152
|
+
width: '90%',
|
|
153
|
+
backgroundColor: 'white',
|
|
154
|
+
position: 'absolute',
|
|
155
|
+
bottom: 10,
|
|
156
|
+
borderRadius: 10,
|
|
157
|
+
padding: 10,
|
|
158
|
+
},
|
|
159
|
+
row: {
|
|
160
|
+
flexDirection: 'row',
|
|
161
|
+
justifyContent: 'space-between',
|
|
162
|
+
alignItems: 'center',
|
|
163
|
+
width: '90%',
|
|
164
|
+
alignSelf: 'center',
|
|
165
|
+
},
|
|
166
|
+
controlersContainer: {
|
|
167
|
+
flex: 2,
|
|
168
|
+
alignItems: 'center',
|
|
169
|
+
justifyContent: 'center',
|
|
170
|
+
},
|
|
171
|
+
closeContainer: {
|
|
172
|
+
width: '100%',
|
|
173
|
+
alignItems: 'flex-start',
|
|
174
|
+
position: 'absolute',
|
|
175
|
+
top: -20,
|
|
176
|
+
left: 0,
|
|
177
|
+
},
|
|
178
|
+
closeIcon: {width: 25, height: 25},
|
|
179
|
+
recitersBtn: {
|
|
180
|
+
flex: 1,
|
|
181
|
+
paddingHorizontal: 5,
|
|
182
|
+
flexDirection: 'row',
|
|
183
|
+
alignItems: 'center',
|
|
184
|
+
},
|
|
185
|
+
});
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {I18nManager, Image, TouchableOpacity, View} from 'react-native';
|
|
3
|
+
import {IMAGES} from '../../common';
|
|
4
|
+
import {
|
|
5
|
+
IReciter,
|
|
6
|
+
ISurahVerse,
|
|
7
|
+
IVersesBeforeAndAfterCurrentVerse,
|
|
8
|
+
} from '../../types';
|
|
9
|
+
import {useOptionsModalController} from '../../hooks';
|
|
10
|
+
import handleVersesBeforeAndAfterCurrentVerse from '../../utils/handleBeforeAndAfterCurrentVerse';
|
|
11
|
+
interface IProps {
|
|
12
|
+
onPlayPause: () => void;
|
|
13
|
+
renderplayPauseBtn: () => React.ReactNode;
|
|
14
|
+
versesBeforeAndAfterCurrentVerse: IVersesBeforeAndAfterCurrentVerse;
|
|
15
|
+
selectedReciter: IReciter;
|
|
16
|
+
setSelectedVerse: (value: ISurahVerse) => void;
|
|
17
|
+
selectedVerse: ISurahVerse;
|
|
18
|
+
setVersesBeforeAndAfterCurrentVerse: (
|
|
19
|
+
value: IVersesBeforeAndAfterCurrentVerse,
|
|
20
|
+
) => void;
|
|
21
|
+
originalVerse: ISurahVerse[];
|
|
22
|
+
autoCompleteAudioAfterPlayingVerse?: boolean;
|
|
23
|
+
}
|
|
24
|
+
const AudioPlayerControls = ({
|
|
25
|
+
onPlayPause,
|
|
26
|
+
renderplayPauseBtn,
|
|
27
|
+
versesBeforeAndAfterCurrentVerse,
|
|
28
|
+
selectedReciter,
|
|
29
|
+
selectedVerse,
|
|
30
|
+
setSelectedVerse,
|
|
31
|
+
setVersesBeforeAndAfterCurrentVerse,
|
|
32
|
+
autoCompleteAudioAfterPlayingVerse,
|
|
33
|
+
originalVerse,
|
|
34
|
+
}: IProps) => {
|
|
35
|
+
const {onPlayerPress} = useOptionsModalController({} as any);
|
|
36
|
+
return (
|
|
37
|
+
<View
|
|
38
|
+
style={{
|
|
39
|
+
flexDirection: 'row',
|
|
40
|
+
alignItems: 'center',
|
|
41
|
+
}}>
|
|
42
|
+
<TouchableOpacity
|
|
43
|
+
style={{
|
|
44
|
+
marginHorizontal: 12,
|
|
45
|
+
transform: [{rotate: I18nManager.isRTL ? '0deg' : '180deg'}],
|
|
46
|
+
}}
|
|
47
|
+
onPress={() => {
|
|
48
|
+
if (!versesBeforeAndAfterCurrentVerse?.beforeCurrentVerse) return;
|
|
49
|
+
if (versesBeforeAndAfterCurrentVerse?.beforeCurrentVerse)
|
|
50
|
+
setSelectedVerse(
|
|
51
|
+
versesBeforeAndAfterCurrentVerse?.beforeCurrentVerse,
|
|
52
|
+
);
|
|
53
|
+
handleVersesBeforeAndAfterCurrentVerse({
|
|
54
|
+
selectedVerse: versesBeforeAndAfterCurrentVerse?.beforeCurrentVerse,
|
|
55
|
+
setVersesBeforeAndAfterCurrentVerse,
|
|
56
|
+
originalVerse,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
onPlayerPress({
|
|
60
|
+
reciterId: selectedReciter?.id,
|
|
61
|
+
verse_key:
|
|
62
|
+
versesBeforeAndAfterCurrentVerse?.beforeCurrentVerse?.verse_key,
|
|
63
|
+
chapterId: selectedVerse?.chapter_id,
|
|
64
|
+
autoCompleteAudioAfterPlayingVerse,
|
|
65
|
+
});
|
|
66
|
+
}}>
|
|
67
|
+
<Image source={IMAGES.playNext} style={{width: 20, height: 20}} />
|
|
68
|
+
</TouchableOpacity>
|
|
69
|
+
|
|
70
|
+
<TouchableOpacity style={{marginHorizontal: 12}} onPress={onPlayPause}>
|
|
71
|
+
{renderplayPauseBtn()}
|
|
72
|
+
</TouchableOpacity>
|
|
73
|
+
|
|
74
|
+
<TouchableOpacity
|
|
75
|
+
style={{
|
|
76
|
+
marginHorizontal: 12,
|
|
77
|
+
transform: [{rotate: I18nManager.isRTL ? '180deg' : '0deg'}],
|
|
78
|
+
}}
|
|
79
|
+
onPress={() => {
|
|
80
|
+
if (!versesBeforeAndAfterCurrentVerse?.afterCurrentVerse) return;
|
|
81
|
+
onPlayerPress({
|
|
82
|
+
reciterId: selectedReciter?.id,
|
|
83
|
+
verse_key:
|
|
84
|
+
versesBeforeAndAfterCurrentVerse?.afterCurrentVerse?.verse_key,
|
|
85
|
+
chapterId: selectedVerse?.chapter_id,
|
|
86
|
+
autoCompleteAudioAfterPlayingVerse,
|
|
87
|
+
});
|
|
88
|
+
setSelectedVerse(versesBeforeAndAfterCurrentVerse?.afterCurrentVerse);
|
|
89
|
+
handleVersesBeforeAndAfterCurrentVerse({
|
|
90
|
+
selectedVerse: versesBeforeAndAfterCurrentVerse?.afterCurrentVerse,
|
|
91
|
+
setVersesBeforeAndAfterCurrentVerse,
|
|
92
|
+
originalVerse,
|
|
93
|
+
});
|
|
94
|
+
}}>
|
|
95
|
+
<Image source={IMAGES.playNext} style={{width: 20, height: 20}} />
|
|
96
|
+
</TouchableOpacity>
|
|
97
|
+
</View>
|
|
98
|
+
);
|
|
99
|
+
};
|
|
100
|
+
export default AudioPlayerControls;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {ActivityIndicator, I18nManager, Text, View} from 'react-native';
|
|
2
|
+
import {COLORS} from '../../common';
|
|
3
|
+
|
|
4
|
+
const Loader = ({
|
|
5
|
+
chapterProgress,
|
|
6
|
+
showTxt,
|
|
7
|
+
}: {
|
|
8
|
+
chapterProgress?: number;
|
|
9
|
+
showTxt?: boolean;
|
|
10
|
+
}) => {
|
|
11
|
+
const txt =
|
|
12
|
+
chapterProgress && chapterProgress < 100
|
|
13
|
+
? I18nManager.isRTL
|
|
14
|
+
? 'جاري تحميل ملقات السورة'
|
|
15
|
+
: 'Downloading the chapter files'
|
|
16
|
+
: I18nManager.isRTL
|
|
17
|
+
? 'جاري تحميل خطوط السورة'
|
|
18
|
+
: 'Downloading the chapter fonts';
|
|
19
|
+
const chapterProgressCondition = chapterProgress
|
|
20
|
+
? chapterProgress > 0 && chapterProgress != 100
|
|
21
|
+
: false;
|
|
22
|
+
return (
|
|
23
|
+
<View style={{alignItems: 'center', justifyContent: 'center'}}>
|
|
24
|
+
<ActivityIndicator color={COLORS.lightBackground} size={25} />
|
|
25
|
+
<Text style={{marginTop: 5}}>
|
|
26
|
+
{showTxt && txt}
|
|
27
|
+
{chapterProgressCondition && `(${chapterProgress}%)`}
|
|
28
|
+
</Text>
|
|
29
|
+
</View>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default Loader;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './quranHelpers';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {IQuranChapters} from '../types';
|
|
2
|
+
import {QuranChapters, SURAH_WORD_AR} from '../common';
|
|
3
|
+
|
|
4
|
+
export const getChapterCodeV1 = (chapterId: number): number =>
|
|
5
|
+
QuranChapters.find((item: IQuranChapters) => item?.id === chapterId)
|
|
6
|
+
?.code_v1 as number;
|
|
7
|
+
|
|
8
|
+
export const _renderChapterName = (chapterId: number) => {
|
|
9
|
+
const surahName = `${String.fromCharCode(
|
|
10
|
+
getChapterCodeV1(chapterId),
|
|
11
|
+
)} ${SURAH_WORD_AR}`;
|
|
12
|
+
return surahName;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const _renderChapterAyahs = (chapterId: number) => {
|
|
16
|
+
return QuranChapters?.find(chapter => chapter?.id == chapterId)?.versesCount;
|
|
17
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import useGetChapterByPage from './useGetChapterByPage';
|
|
2
|
+
import useGetChapterLookup from './useGetChapterLookup';
|
|
3
|
+
import useGetReciters from './useGetReciters';
|
|
4
|
+
import useGetChapterAudio from './useGetChapterAudio';
|
|
5
|
+
export {
|
|
6
|
+
useGetChapterByPage,
|
|
7
|
+
useGetChapterLookup,
|
|
8
|
+
useGetReciters,
|
|
9
|
+
useGetChapterAudio,
|
|
10
|
+
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import TrackPlayer from 'react-native-track-player';
|
|
2
|
+
import {QURAN_API} from '../../common';
|
|
3
|
+
import {axiosInstance} from '../../utils';
|
|
4
|
+
import {useState} from 'react';
|
|
5
|
+
|
|
6
|
+
let timeoutAudio: any;
|
|
7
|
+
const useGetChapterAudio = () => {
|
|
8
|
+
const [isVersePositionLoading, setIsVersePositionLoading] = useState(false);
|
|
9
|
+
const getVerseAudio = async (
|
|
10
|
+
reciterId: number,
|
|
11
|
+
verseKey: string,
|
|
12
|
+
callback?: () => void,
|
|
13
|
+
autoCompleteAudioAfterPlayingVerse?: boolean,
|
|
14
|
+
) => {
|
|
15
|
+
// clear timeout created before
|
|
16
|
+
if (timeoutAudio) clearTimeout(timeoutAudio);
|
|
17
|
+
try {
|
|
18
|
+
setIsVersePositionLoading(true);
|
|
19
|
+
const url = `${QURAN_API}/audio/reciters/${reciterId}/timestamp?verse_key=${verseKey}`;
|
|
20
|
+
const response = await axiosInstance.get(url);
|
|
21
|
+
const result = response.data?.result;
|
|
22
|
+
setIsVersePositionLoading(false);
|
|
23
|
+
const start = result.timestamp_from / 1000;
|
|
24
|
+
await TrackPlayer.play();
|
|
25
|
+
await TrackPlayer.seekTo(start);
|
|
26
|
+
if (callback) callback();
|
|
27
|
+
const stop = result.timestamp_to / 1000 - result.timestamp_from / 1000;
|
|
28
|
+
if (!autoCompleteAudioAfterPlayingVerse)
|
|
29
|
+
timeoutAudio = setTimeout(() => {
|
|
30
|
+
TrackPlayer.pause();
|
|
31
|
+
clearTimeout(timeoutAudio);
|
|
32
|
+
}, stop * 1000);
|
|
33
|
+
} catch (e) {
|
|
34
|
+
setIsVersePositionLoading(false);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const getChapterAudionUrl = async ({
|
|
39
|
+
reciterId,
|
|
40
|
+
chapterId,
|
|
41
|
+
verse_key,
|
|
42
|
+
autoCompleteAudioAfterPlayingVerse,
|
|
43
|
+
callback,
|
|
44
|
+
}: {
|
|
45
|
+
reciterId: number;
|
|
46
|
+
chapterId: number;
|
|
47
|
+
verse_key?: string;
|
|
48
|
+
autoCompleteAudioAfterPlayingVerse?: boolean;
|
|
49
|
+
callback?: () => void;
|
|
50
|
+
}) => {
|
|
51
|
+
const queryParams = {
|
|
52
|
+
chapter: chapterId,
|
|
53
|
+
segments: false,
|
|
54
|
+
};
|
|
55
|
+
const queryString = Object.entries(queryParams)
|
|
56
|
+
.map(
|
|
57
|
+
([key, value]) =>
|
|
58
|
+
`${encodeURIComponent(key)}=${encodeURIComponent(value)}`,
|
|
59
|
+
)
|
|
60
|
+
.join('&');
|
|
61
|
+
try {
|
|
62
|
+
const url = `${QURAN_API}/audio/reciters/${reciterId}/audio_files?${queryString}`;
|
|
63
|
+
const response = await axiosInstance.get(url);
|
|
64
|
+
const chapterAudioFiles = response.data.audio_files;
|
|
65
|
+
setUpChapterAudio(
|
|
66
|
+
chapterAudioFiles,
|
|
67
|
+
verse_key,
|
|
68
|
+
reciterId,
|
|
69
|
+
chapterId,
|
|
70
|
+
autoCompleteAudioAfterPlayingVerse,
|
|
71
|
+
callback,
|
|
72
|
+
);
|
|
73
|
+
return response?.data;
|
|
74
|
+
} catch (error) {}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const resetTrackPlayer = async () => {
|
|
78
|
+
await TrackPlayer.reset();
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const setUpChapterAudio = async (
|
|
82
|
+
chapterAudioFiles: [{audio_url: string}],
|
|
83
|
+
verse_key?: string,
|
|
84
|
+
reciterId?: number,
|
|
85
|
+
chapterId?: number,
|
|
86
|
+
autoCompleteAudioAfterPlayingVerse?: boolean,
|
|
87
|
+
callback?: () => void,
|
|
88
|
+
) => {
|
|
89
|
+
const audioFileUrl = chapterAudioFiles[0].audio_url;
|
|
90
|
+
resetTrackPlayer();
|
|
91
|
+
await TrackPlayer.add([
|
|
92
|
+
{
|
|
93
|
+
id: '1',
|
|
94
|
+
url: audioFileUrl,
|
|
95
|
+
title: 'Al Quran',
|
|
96
|
+
duration: 60,
|
|
97
|
+
chapter_id: chapterId,
|
|
98
|
+
},
|
|
99
|
+
]);
|
|
100
|
+
if (verse_key)
|
|
101
|
+
getVerseAudio(
|
|
102
|
+
reciterId as number,
|
|
103
|
+
verse_key,
|
|
104
|
+
callback,
|
|
105
|
+
autoCompleteAudioAfterPlayingVerse,
|
|
106
|
+
);
|
|
107
|
+
};
|
|
108
|
+
return {getChapterAudionUrl, getVerseAudio, isVersePositionLoading};
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export default useGetChapterAudio;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import {useEffect, useState} from 'react';
|
|
2
|
+
import {
|
|
3
|
+
IChapterLookUp,
|
|
4
|
+
IChapterVerses,
|
|
5
|
+
IQuranJuzs,
|
|
6
|
+
ISurahVerse,
|
|
7
|
+
QuranTypesEnums,
|
|
8
|
+
} from '../../types';
|
|
9
|
+
import {
|
|
10
|
+
DEFAULT_VERSES_PARAMS,
|
|
11
|
+
QURAN_CHAPTERS_DIRECTORY,
|
|
12
|
+
QURAN_JUZS_DIRECTORY,
|
|
13
|
+
QuranJuzs,
|
|
14
|
+
} from '../../common';
|
|
15
|
+
import {
|
|
16
|
+
axiosInstance,
|
|
17
|
+
handleQuranChaptersDirectory,
|
|
18
|
+
handleQuranJuzsDirectory,
|
|
19
|
+
isFileExists,
|
|
20
|
+
readFromLocalStorageFile,
|
|
21
|
+
saveChapterAsJsonFile,
|
|
22
|
+
} from '../../utils';
|
|
23
|
+
import {usePageFontFileController, usePageLineController} from '../controllers';
|
|
24
|
+
interface IProps {
|
|
25
|
+
chapterLookUp: IChapterLookUp[] | undefined;
|
|
26
|
+
chapterId: number;
|
|
27
|
+
type: QuranTypesEnums;
|
|
28
|
+
QURAN_FONTS_API: string;
|
|
29
|
+
}
|
|
30
|
+
const useGetChapterByPage = ({
|
|
31
|
+
chapterLookUp,
|
|
32
|
+
chapterId,
|
|
33
|
+
type,
|
|
34
|
+
QURAN_FONTS_API,
|
|
35
|
+
}: IProps) => {
|
|
36
|
+
const [chapterVerses, setChapterVerse] = useState<IChapterVerses[]>([]);
|
|
37
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
38
|
+
const [chapterProgress, setChapterProgress] = useState(0);
|
|
39
|
+
|
|
40
|
+
const {downoladThePageFont} = usePageFontFileController();
|
|
41
|
+
const {_renderVersesNewForm} = usePageLineController();
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
checkIfTheChapterFileExistsInLocalStorage();
|
|
45
|
+
}, []);
|
|
46
|
+
const setChapetersHandler = (chapters: IChapterVerses[]) => {
|
|
47
|
+
setChapterVerse([...chapters]);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const checkIfTheChapterFileExistsInLocalStorage = async () => {
|
|
51
|
+
const chapterFileName = `${chapterId}.json`;
|
|
52
|
+
const chapterPath = `${
|
|
53
|
+
type === QuranTypesEnums?.chapter
|
|
54
|
+
? QURAN_CHAPTERS_DIRECTORY
|
|
55
|
+
: QURAN_JUZS_DIRECTORY
|
|
56
|
+
}/${chapterFileName}`;
|
|
57
|
+
const isFileExistsLocaly = await isFileExists(chapterPath);
|
|
58
|
+
if (type === QuranTypesEnums.chapter) await handleQuranChaptersDirectory();
|
|
59
|
+
else await handleQuranJuzsDirectory();
|
|
60
|
+
|
|
61
|
+
if (!isFileExistsLocaly) {
|
|
62
|
+
const res: IChapterVerses[] | undefined | any =
|
|
63
|
+
await getTargetChapterPage();
|
|
64
|
+
await saveChapterAsJsonFile(chapterFileName, JSON.stringify(res), type);
|
|
65
|
+
await handleFontLoad(res);
|
|
66
|
+
if (res) setChapetersHandler([...res]);
|
|
67
|
+
} else {
|
|
68
|
+
const storedChapterFile = await readFromLocalStorageFile(
|
|
69
|
+
chapterFileName,
|
|
70
|
+
type,
|
|
71
|
+
);
|
|
72
|
+
await handleFontLoad(JSON.parse(storedChapterFile as any));
|
|
73
|
+
if (storedChapterFile)
|
|
74
|
+
setChapetersHandler([...JSON.parse(storedChapterFile)]);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const handleFontLoad = async (verses: ISurahVerse[]) => {
|
|
78
|
+
const pagesNumbers = getSurahOrJuzPagesCount(verses);
|
|
79
|
+
const promises = pagesNumbers?.map((item: number, index) => {
|
|
80
|
+
return downoladThePageFont(Number(item), () => {}, QURAN_FONTS_API);
|
|
81
|
+
});
|
|
82
|
+
try {
|
|
83
|
+
const promiseRes = await Promise.all(promises);
|
|
84
|
+
setTimeout(() => {
|
|
85
|
+
setIsLoading(false);
|
|
86
|
+
}, 500);
|
|
87
|
+
} catch (e) {}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const getTargetChapterPage = async () => {
|
|
91
|
+
const params = _rednerQueryParams();
|
|
92
|
+
try {
|
|
93
|
+
const response = await axiosInstance.get(
|
|
94
|
+
`/verses/by_${type}/${chapterId}?${params}`,
|
|
95
|
+
{
|
|
96
|
+
onDownloadProgress: progressEvent => {
|
|
97
|
+
if (progressEvent && progressEvent?.total) {
|
|
98
|
+
setChapterProgress(
|
|
99
|
+
Math.round((progressEvent.loaded / progressEvent?.total) * 100),
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
);
|
|
105
|
+
const allChapterVerses: ISurahVerse[] = response?.data?.verses;
|
|
106
|
+
return await handleAllChapterPagesFormat(allChapterVerses);
|
|
107
|
+
} catch (e) {}
|
|
108
|
+
};
|
|
109
|
+
const getSurahOrJuzPagesCount = (verses: ISurahVerse[]) => {
|
|
110
|
+
const pagesNumbers: number[] = [];
|
|
111
|
+
verses?.map((verse: ISurahVerse) => {
|
|
112
|
+
if (!pagesNumbers.includes(verse?.page_number))
|
|
113
|
+
pagesNumbers.push(verse?.page_number);
|
|
114
|
+
});
|
|
115
|
+
return pagesNumbers;
|
|
116
|
+
};
|
|
117
|
+
const handleAllChapterPagesFormat = async (
|
|
118
|
+
allChapterVerses: ISurahVerse[],
|
|
119
|
+
) => {
|
|
120
|
+
const chapterPagesWithVersesToSave = [];
|
|
121
|
+
const pagesArr = getSurahOrJuzPagesCount(allChapterVerses);
|
|
122
|
+
const pagesCount = pagesArr?.length;
|
|
123
|
+
for (let i = 0; i < pagesCount; i++) {
|
|
124
|
+
const currentPage: any = pagesArr[i];
|
|
125
|
+
const currentPageVerses = allChapterVerses?.filter(
|
|
126
|
+
item => item?.page_number == currentPage,
|
|
127
|
+
);
|
|
128
|
+
const currentPageJuzNumber = currentPageVerses[0]?.juz_number;
|
|
129
|
+
const chapter_id = currentPageVerses[0]?.chapter_id;
|
|
130
|
+
const isFirstChapterPage = currentPageVerses[0]?.verse_number == 1;
|
|
131
|
+
chapterPagesWithVersesToSave.push({
|
|
132
|
+
verses: _renderVersesNewForm({
|
|
133
|
+
pageVerses: currentPageVerses,
|
|
134
|
+
}),
|
|
135
|
+
page_number: currentPage,
|
|
136
|
+
juz_number: currentPageJuzNumber,
|
|
137
|
+
originalVerses: currentPageVerses,
|
|
138
|
+
chapter_id,
|
|
139
|
+
isFirstChapterPage,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return chapterPagesWithVersesToSave;
|
|
144
|
+
};
|
|
145
|
+
const _rednerQueryParams = () => {
|
|
146
|
+
const juzVersesCount = QuranTypesEnums?.juz
|
|
147
|
+
? QuranJuzs.find((item: IQuranJuzs) => item?.juz_number == chapterId)
|
|
148
|
+
?.verses_count
|
|
149
|
+
: 0;
|
|
150
|
+
const per_page = type === QuranTypesEnums.chapter ? 'all' : juzVersesCount;
|
|
151
|
+
const queryParams = {
|
|
152
|
+
...DEFAULT_VERSES_PARAMS,
|
|
153
|
+
per_page,
|
|
154
|
+
};
|
|
155
|
+
const queryString = Object.entries(queryParams)
|
|
156
|
+
.map(
|
|
157
|
+
([key, value]) =>
|
|
158
|
+
`${encodeURIComponent(key)}=${encodeURIComponent(value)}`,
|
|
159
|
+
)
|
|
160
|
+
.join('&');
|
|
161
|
+
return queryString;
|
|
162
|
+
};
|
|
163
|
+
return {chapterVerses, isLoading, chapterProgress};
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export default useGetChapterByPage;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {useEffect, useState} from 'react';
|
|
2
|
+
import {IChapterLookUp, QuranTypesEnums} from '../../types';
|
|
3
|
+
import {axiosInstance} from '../../utils';
|
|
4
|
+
|
|
5
|
+
interface IProps {
|
|
6
|
+
chapterId: number;
|
|
7
|
+
type: QuranTypesEnums;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const useGetChapterLookup = ({chapterId, type}: IProps) => {
|
|
11
|
+
const [chapterLookUp, setChapterLookUp] = useState<IChapterLookUp[]>();
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
fetchChapterLookUp();
|
|
14
|
+
}, []);
|
|
15
|
+
const fetchChapterLookUp = async () => {
|
|
16
|
+
try {
|
|
17
|
+
let response = await axiosInstance.get(
|
|
18
|
+
`https://api.quran.com/api/qdc/pages/lookup?${type}_number=${chapterId}`,
|
|
19
|
+
);
|
|
20
|
+
let pageslookup = Object.entries(response?.data?.pages);
|
|
21
|
+
const pageslookupEdited = pageslookup?.map(([key, value]) => ({
|
|
22
|
+
page_number: key,
|
|
23
|
+
page_range: value,
|
|
24
|
+
}));
|
|
25
|
+
setChapterLookUp(pageslookupEdited as IChapterLookUp[]);
|
|
26
|
+
} catch (e) {}
|
|
27
|
+
};
|
|
28
|
+
return {chapterLookUp};
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default useGetChapterLookup;
|