From f519052b7b3912a322f1b12dd485dd0adaa5c46d Mon Sep 17 00:00:00 2001 From: Nurmuhammet Allanov Date: Mon, 1 Sep 2025 13:11:31 +0500 Subject: [PATCH] Enhance app functionality and localization - Added new dependencies: expo-file-system and expo-sharing. - Updated package versions for @babel/core and react-dom. - Introduced a new index screen for displaying prayer times with city selection. - Refactored ServicesGrid to accept a dynamic services array and improved layout. - Updated localization for new phrases and service titles in Turkmen. - Enhanced LostKeyModal with a close button and additional text. - Improved PhrasebookModal to allow expandable phrases for better user interaction. --- Umra.code-workspace | 8 ++ app/(tabs)/_layout.tsx | 13 ++- app/(tabs)/home.tsx | 11 +- app/(tabs)/index.tsx | 178 +++++++++++++++++++++++++++- app/(tabs)/services.tsx | 12 -- components/LostKeyModal.tsx | 26 ++++- components/PhrasebookModal.tsx | 24 ++-- components/ServicesGrid.tsx | 52 +++++---- locales/tk.json | 7 +- package-lock.json | 205 ++++++++++++++++++++++++--------- package.json | 6 +- utils/pdf.ts | 16 +++ 12 files changed, 452 insertions(+), 106 deletions(-) create mode 100644 Umra.code-workspace create mode 100644 utils/pdf.ts diff --git a/Umra.code-workspace b/Umra.code-workspace new file mode 100644 index 0000000..876a149 --- /dev/null +++ b/Umra.code-workspace @@ -0,0 +1,8 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": {} +} \ No newline at end of file diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx index f810bb7..663aed0 100644 --- a/app/(tabs)/_layout.tsx +++ b/app/(tabs)/_layout.tsx @@ -26,7 +26,7 @@ export default function TabLayout() { backgroundColor: Colors[colorScheme].secondary, borderTopColor: Colors[colorScheme].secondary, }, - headerShown: false, // Hide header globally for tabs + headerShown: false, // hide header globally for tabs }}> , }} /> + , + }} + /> , + title: i18n.t('Programs'), + tabBarIcon: ({ color }) => , }} /> diff --git a/app/(tabs)/home.tsx b/app/(tabs)/home.tsx index 152333a..92a83de 100644 --- a/app/(tabs)/home.tsx +++ b/app/(tabs)/home.tsx @@ -31,14 +31,22 @@ export default function HomeScreen() { const currentLanguage = languages.find(l => l.value === i18n.locale.substring(0, 2))?.label; + const services = [ + { name: 'quran', icon: 'book-open-variant' }, + { name: 'hadith', icon: 'book-open-page-variant' }, + { name: 'dua', icon: 'human-greeting' }, + ]; + return ( + {i18n.t('home')} setModalVisible(true)} style={pickerSelectStyles.inputIOS}> {currentLanguage} + - + @@ -85,6 +93,7 @@ const styles = StyleSheet.create({ paddingHorizontal: 15, }, header: { + display: 'none', // hide for now, will show later flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index 1963b3d..0f601e1 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -1,5 +1,179 @@ -import { Redirect } from 'expo-router'; +import { Pressable, SafeAreaView, ScrollView, StyleSheet, Text, View } from 'react-native'; +import { useCallback, useEffect, useState } from 'react'; + +import { getPrayerTimes, cities } from '../../utils/prayerTimeCalculator'; +import i18n from '../../i18n'; +import { useColorScheme } from 'react-native'; +import Colors from '../../constants/Colors'; + +type Prayer = { + name: string; + time: string; +}; + +type City = keyof typeof cities; export default function TabIndex() { - return ; + const colorScheme = useColorScheme(); + const theme = Colors[colorScheme ?? 'light']; + const [selectedCity, setSelectedCity] = useState('Makkah'); + const [prayerTimes, setPrayerTimes] = useState([]); + const [nextPrayerName, setNextPrayerName] = useState(null); + + const prayerNameMapping: { [key: string]: string } = { + fajr: i18n.t('fajr'), + dhuhr: i18n.t('dhuhr'), + asr: i18n.t('asr'), + maghrib: i18n.t('maghrib'), + isha: i18n.t('isha'), + }; + + const updatePrayerTimes = useCallback(() => { + const times = getPrayerTimes(selectedCity); + const prayers: Prayer[] = Object.keys(prayerNameMapping).map((key) => ({ + name: prayerNameMapping[key], + time: times[key] || '-----', + })); + setPrayerTimes(prayers); + + const now = new Date(); + let nextPrayer: Prayer | null = null; + + for (const prayer of prayers) { + if (prayer.time === '-----') continue; + const [hours, minutes] = prayer.time.split(':').map(Number); + const prayerDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), hours, minutes); + if (prayerDate > now) { + nextPrayer = prayer; + break; + } + } + + if (!nextPrayer && prayers.length > 0) { + const firstPrayer = prayers.find((p) => p.time !== '-----'); + if (firstPrayer) { + nextPrayer = firstPrayer; + } + } + + setNextPrayerName(nextPrayer ? nextPrayer.name : null); + }, [selectedCity]); + + useEffect(() => { + updatePrayerTimes(); + const interval = setInterval(updatePrayerTimes, 60000); // Update every minute + return () => clearInterval(interval); + }, [updatePrayerTimes]); + + const renderPrayerTime = (prayer: Prayer) => { + const isNextPrayer = prayer.name === nextPrayerName; + return ( + + + + {prayer.name} + + + + {new Date(`1970-01-01T${prayer.time}:00`).toLocaleTimeString('en-US', { + hour: 'numeric', + minute: 'numeric', + hour12: false, + })} + + + ); + }; + + return ( + + + {(Object.keys(cities) as City[]).map((city) => ( + setSelectedCity(city)} + style={[styles.cityButton, { backgroundColor: theme.secondary }, selectedCity === city && [styles.activeCityButton, { backgroundColor: theme.tint }]]}> + + {i18n.t(city)} + + + ))} + + + {prayerTimes.map(renderPrayerTime)} + + + ); } + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + citySelector: { + flexDirection: 'row', + justifyContent: 'center', + paddingVertical: 20, + paddingHorizontal: 10, + }, + cityButton: { + paddingVertical: 10, + paddingHorizontal: 25, + borderRadius: 20, + marginHorizontal: 5, + }, + activeCityButton: {}, + cityButtonText: { + fontSize: 18, + fontWeight: '500', + }, + activeCityButtonText: { + color: '#fff', + }, + listContainer: { + paddingHorizontal: 20, + }, + prayerRow: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + paddingVertical: 25, + paddingHorizontal: 20, + borderRadius: 15, + marginBottom: 10, + shadowColor: '#000', + shadowOffset: { + width: 0, + height: 1, + }, + shadowOpacity: 0.1, + shadowRadius: 3, + elevation: 2, + }, + nextPrayerRow: {}, + prayerName: { + fontSize: 28, + fontWeight: '600', + }, + inCityText: { + fontSize: 18, + }, + prayerTime: { + fontSize: 28, + fontWeight: 'bold', + }, + nextPrayerText: { + color: '#fff', + }, + nextPrayerTimeText: { + color: '#fff', + fontSize: 32, + }, +}); diff --git a/app/(tabs)/services.tsx b/app/(tabs)/services.tsx index 487a248..6c770d0 100644 --- a/app/(tabs)/services.tsx +++ b/app/(tabs)/services.tsx @@ -7,14 +7,12 @@ import React, { useState } from 'react'; import CurrencyConverterModal from '@/components/CurrencyConverterModal'; import HotelBusinessCardModal from '@/components/HotelBusinessCardModal'; import LostKeyModal from '@/components/LostKeyModal'; -import TranslatorModal from '@/components/TranslatorModal'; import PhrasebookModal from '@/components/PhrasebookModal'; export default function ServicesScreen() { const [currencyModalVisible, setCurrencyModalVisible] = useState(false); const [hotelModalVisible, setHotelModalVisible] = useState(false); const [lostKeyModalVisible, setLostKeyModalVisible] = useState(false); - const [translatorModalVisible, setTranslatorModalVisible] = useState(false); const [phrasebookModalVisible, setPhrasebookModalVisible] = useState(false); const services = [ @@ -36,12 +34,6 @@ export default function ServicesScreen() { icon: , onPress: () => setLostKeyModalVisible(true), }, - { - title: i18n.t('Translator'), - name: 'translator', - icon: , - onPress: () => setTranslatorModalVisible(true), - }, { title: i18n.t('Phrasebook'), name: 'phrasebook', @@ -73,10 +65,6 @@ export default function ServicesScreen() { visible={lostKeyModalVisible} onClose={() => setLostKeyModalVisible(false)} /> - setTranslatorModalVisible(false)} - /> setPhrasebookModalVisible(false)} diff --git a/components/LostKeyModal.tsx b/components/LostKeyModal.tsx index 869b725..b0e455d 100644 --- a/components/LostKeyModal.tsx +++ b/components/LostKeyModal.tsx @@ -1,3 +1,4 @@ +import { FontAwesome } from '@expo/vector-icons'; import React from 'react'; import { Modal, View, Text, StyleSheet, TouchableOpacity, Dimensions } from 'react-native'; @@ -18,7 +19,16 @@ const LostKeyModal: React.FC = ({ visible, onClose }) => { > - Mastercard + + + + + Aşak resepşyna şul aşakdaky haty görkeziň + Mastercard ماستركارد @@ -34,7 +44,7 @@ const styles = StyleSheet.create({ backgroundColor: 'rgba(0, 0, 0, 0.5)', }, modalContainer: { - width: width * 0.6, + width: width * 0.9, padding: 20, backgroundColor: 'white', borderRadius: 10, @@ -48,10 +58,22 @@ const styles = StyleSheet.create({ shadowRadius: 3.84, elevation: 5, }, + closeButton: { + position: 'absolute', + top: 15, + right: 15, + zIndex: 1, + }, text: { fontSize: 40, marginBottom: 10, }, + smallText: { + marginTop: 15, + fontSize: 20, + color: '#666', + textAlign: 'center', + }, arabicText: { fontFamily: 'System', writingDirection: 'rtl', diff --git a/components/PhrasebookModal.tsx b/components/PhrasebookModal.tsx index dd8e351..b1f0fae 100644 --- a/components/PhrasebookModal.tsx +++ b/components/PhrasebookModal.tsx @@ -10,6 +10,8 @@ type PhrasebookModalProps = { }; const PHRASES = [ + { tk: 'Bu näçe?', ar: 'بكم هذا؟ (Bikam hadha?)' }, + { tk: 'Arzanladyň', ar: 'تَخْفيض Takfidun' }, { tk: 'Salam', ar: 'مرحبا (Marhaban)' }, { tk: 'Hawa', ar: 'نعم (Na\'am)' }, { tk: 'Ýok', ar: 'لا (La)' }, @@ -18,19 +20,26 @@ const PHRASES = [ { tk: 'Haýyş edýärin', ar: 'من فضلك (Min fadlik)' }, { tk: 'Bagyşlaň', ar: 'آسف (Asif)' }, { tk: 'Men size nähili kömek edip bilerin?', ar: 'كيف يمكنني مساعدتك؟ (Kayfa yumkinuni musa\'adatuk?)' }, - { tk: 'Bu näçe?', ar: 'بكم هذا؟ (Bikam hadha?)' }, { tk: 'Hajathana nirede?', ar: 'أين الحمام؟ (Ayna al-hammam?)' }, { tk: 'Men ýolumy ýitirdim', ar: 'لقد ضللت طريقي (Laqad dalalt tariqi)' }, { tk: 'Lukman çagyryň', ar: 'اتصل بطبيب (Ittasil bi-tabib)' }, ]; const PhrasebookModal = ({ visible, onClose }: PhrasebookModalProps) => { - const renderItem = ({ item }: { item: { tk: string; ar: string } }) => ( - - {item.tk} - {item.ar} - - ); + const [expandedIndex, setExpandedIndex] = React.useState(null); + + const renderItem = ({ item, index }: { item: { tk: string; ar: string }; index: number }) => { + const isExpanded = index === expandedIndex; + + return ( + setExpandedIndex(isExpanded ? null : index)}> + + {item.tk} + {isExpanded && {item.ar}} + + + ); + }; return ( { renderItem={renderItem} keyExtractor={(item, index) => index.toString()} ItemSeparatorComponent={() => } + extraData={expandedIndex} /> diff --git a/components/ServicesGrid.tsx b/components/ServicesGrid.tsx index b4d1e47..922d8c8 100644 --- a/components/ServicesGrid.tsx +++ b/components/ServicesGrid.tsx @@ -1,34 +1,42 @@ import React from 'react'; -import { View, Text, StyleSheet } from 'react-native'; +import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'; import Colors from '@/constants/Colors'; import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; +import { router } from 'expo-router'; import i18n from '@/i18n'; -const services = [ - { name: 'quran', icon: 'book-open-variant' }, - { name: 'hadith', icon: 'book-open-page-variant' }, - { name: 'dua', icon: 'human-greeting' }, -]; - -export default function ServicesGrid() { - const colorScheme = 'dark'; +type ServicesGridProps = { + services: { + name: string; + icon: any; + }[]; +}; +const ServicesGrid = ({ services }: ServicesGridProps) => { + const handlePress = (name: string) => { + if (name === 'quran') { + + } + }; return ( {i18n.t('servicesToEnrich')} - {services.map((service, index) => ( - - - + {services.map((service) => ( + handlePress(service.name)}> + + {i18n.t(service.name)} - + ))} ); -} +}; const styles = StyleSheet.create({ container: { @@ -37,8 +45,8 @@ const styles = StyleSheet.create({ title: { fontSize: 18, fontWeight: 'bold', - color: Colors.dark.text, - marginBottom: 15, + color: 'white', + marginBottom: 10, }, grid: { flexDirection: 'row', @@ -50,13 +58,15 @@ const styles = StyleSheet.create({ iconContainer: { width: 60, height: 60, - borderRadius: 15, + borderRadius: 30, justifyContent: 'center', alignItems: 'center', - marginBottom: 10, + marginBottom: 5, }, serviceName: { - color: Colors.dark.text, - fontSize: 14, + color: 'white', + textAlign: 'center', }, }); + +export default ServicesGrid; diff --git a/locales/tk.json b/locales/tk.json index d59315f..55aee99 100644 --- a/locales/tk.json +++ b/locales/tk.json @@ -1,5 +1,5 @@ { - "home": "Baş sahypa", + "home": "Öý", "services": "Hyzmatlar", "supplications": "Dogalar", "yourJourneyToHajj": "Haj syýahatyňyz", @@ -10,6 +10,7 @@ "newExperience": "Täze tejribe", "prayerTimes": "Namaz wagtlary", "programs": "Programmalar", + "Programs": "Respisaniýa", "leftOnPrayer": "{{prayerName}} namazyna çenli galdy", "servicesToEnrich": "Ruhy tejribäňizi baýlaşdyrmak üçin hyzmatlar", "quran": "Kuran", @@ -45,5 +46,7 @@ "Translator": "Perewod", "Phrasebook": "Sözlük", "Enter text in Turkmen": "Türkmençe sözleri gir", - "Translate": "Terjime et" + "Translate": "Terjime et", + "Salah": "Namaz", + "menuSalah": "Namaz" } diff --git a/package-lock.json b/package-lock.json index 4fe112a..0245949 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,10 +13,12 @@ "@react-navigation/native": "^7.1.6", "adhan": "^4.4.3", "expo": "~53.0.20", + "expo-file-system": "~18.1.11", "expo-font": "~13.3.2", "expo-linking": "~7.1.7", "expo-localization": "^16.1.6", "expo-router": "~5.1.4", + "expo-sharing": "~13.1.5", "expo-splash-screen": "~0.30.10", "expo-status-bar": "~2.2.3", "expo-system-ui": "~5.0.10", @@ -24,17 +26,15 @@ "expo-web-browser": "~14.2.0", "i18n-js": "^4.5.1", "react": "19.0.0", - "react-dom": "19.0.0", "react-native": "0.79.5", "react-native-picker-select": "^9.3.1", "react-native-reanimated": "~3.17.4", "react-native-safe-area-context": "5.4.0", "react-native-screens": "~4.11.1", - "react-native-vector-icons": "^10.3.0", - "react-native-web": "~0.20.0" + "react-native-vector-icons": "^10.3.0" }, "devDependencies": { - "@babel/core": "^7.25.2", + "@babel/core": "^7.20.0", "@types/react": "~19.0.10", "@types/react-native-vector-icons": "^6.4.18", "jest": "^29.2.1", @@ -4349,6 +4349,8 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", + "optional": true, + "peer": true, "dependencies": { "node-fetch": "^2.7.0" } @@ -4378,6 +4380,8 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz", "integrity": "sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==", + "optional": true, + "peer": true, "dependencies": { "hyphenate-style-name": "^1.0.3" } @@ -5179,6 +5183,14 @@ "node": ">=10" } }, + "node_modules/expo-sharing": { + "version": "13.1.5", + "resolved": "https://registry.npmjs.org/expo-sharing/-/expo-sharing-13.1.5.tgz", + "integrity": "sha512-X/5sAEiWXL2kdoGE3NO5KmbfcmaCWuWVZXHu8OQef7Yig4ZgHFkGD11HKJ5KqDrDg+SRZe4ISd6MxE7vGUgm4w==", + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-splash-screen": { "version": "0.30.10", "resolved": "https://registry.npmjs.org/expo-splash-screen/-/expo-splash-screen-0.30.10.tgz", @@ -5291,6 +5303,13 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, + "node_modules/fast-loops": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-loops/-/fast-loops-1.1.4.tgz", + "integrity": "sha512-8dbd3XWoKCTms18ize6JmQF1SFnnfj5s0B7rRry22EofgMu7B6LKHVh+XfFqFGsqnbH54xgeO83PzpKI+ODhlg==", + "optional": true, + "peer": true + }, "node_modules/fast-uri": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", @@ -5318,6 +5337,8 @@ "version": "3.0.5", "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.5.tgz", "integrity": "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==", + "optional": true, + "peer": true, "dependencies": { "cross-fetch": "^3.1.5", "fbjs-css-vars": "^1.0.0", @@ -5331,12 +5352,16 @@ "node_modules/fbjs-css-vars": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", - "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==" + "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==", + "optional": true, + "peer": true }, "node_modules/fbjs/node_modules/promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "optional": true, + "peer": true, "dependencies": { "asap": "~2.0.3" } @@ -5767,7 +5792,9 @@ "node_modules/hyphenate-style-name": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz", - "integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==" + "integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==", + "optional": true, + "peer": true }, "node_modules/i18n-js": { "version": "4.5.1", @@ -5900,11 +5927,14 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "node_modules/inline-style-prefixer": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-7.0.1.tgz", - "integrity": "sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-6.0.4.tgz", + "integrity": "sha512-FwXmZC2zbeeS7NzGjJ6pAiqRhXR0ugUShSNb6GApMl6da0/XGc4MOJsoWAywia52EEWbXNSy0pzkwz/+Y+swSg==", + "optional": true, + "peer": true, "dependencies": { - "css-in-js-utils": "^3.1.0" + "css-in-js-utils": "^3.1.0", + "fast-loops": "^1.1.3" } }, "node_modules/invariant": { @@ -6423,6 +6453,55 @@ "react-native": "*" } }, + "node_modules/jest-expo/node_modules/react": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", + "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-expo/node_modules/react-dom": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz", + "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==", + "dev": true, + "peer": true, + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.1" + } + }, + "node_modules/jest-expo/node_modules/react-server-dom-webpack": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-server-dom-webpack/-/react-server-dom-webpack-19.0.0.tgz", + "integrity": "sha512-hLug9KEXLc8vnU9lDNe2b2rKKDaqrp5gNiES4uyu2Up3FZfZJZmdwLFXlWzdA9gTB/6/cWduSB2K1Lfag2pSvw==", + "dev": true, + "dependencies": { + "acorn-loose": "^8.3.0", + "neo-async": "^2.6.1", + "webpack-sources": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "react": "^19.0.0", + "react-dom": "^19.0.0", + "webpack": "^5.59.0" + } + }, + "node_modules/jest-expo/node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "dev": true, + "peer": true + }, "node_modules/jest-get-type": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", @@ -8058,6 +8137,8 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "optional": true, + "peer": true, "dependencies": { "whatwg-url": "^5.0.0" }, @@ -8076,17 +8157,23 @@ "node_modules/node-fetch/node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "optional": true, + "peer": true }, "node_modules/node-fetch/node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "optional": true, + "peer": true }, "node_modules/node-fetch/node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "optional": true, + "peer": true, "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -8619,7 +8706,9 @@ "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "optional": true, + "peer": true }, "node_modules/pretty-bytes": { "version": "5.6.0", @@ -8865,14 +8954,27 @@ } }, "node_modules/react-dom": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", - "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "optional": true, + "peer": true, "dependencies": { - "scheduler": "^0.25.0" + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" }, "peerDependencies": { - "react": "^19.0.0" + "react": "^18.3.1" + } + }, + "node_modules/react-dom/node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "optional": true, + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" } }, "node_modules/react-fast-compare": { @@ -9121,33 +9223,39 @@ } }, "node_modules/react-native-web": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.20.0.tgz", - "integrity": "sha512-OOSgrw+aON6R3hRosCau/xVxdLzbjEcsLysYedka0ZON4ZZe6n9xgeN9ZkoejhARM36oTlUgHIQqxGutEJ9Wxg==", + "version": "0.19.13", + "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.19.13.tgz", + "integrity": "sha512-etv3bN8rJglrRCp/uL4p7l8QvUNUC++QwDbdZ8CB7BvZiMvsxfFIRM1j04vxNldG3uo2puRd6OSWR3ibtmc29A==", + "optional": true, + "peer": true, "dependencies": { "@babel/runtime": "^7.18.6", "@react-native/normalize-colors": "^0.74.1", "fbjs": "^3.0.4", - "inline-style-prefixer": "^7.0.1", + "inline-style-prefixer": "^6.0.1", "memoize-one": "^6.0.0", "nullthrows": "^1.1.1", "postcss-value-parser": "^4.2.0", "styleq": "^0.1.3" }, "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, "node_modules/react-native-web/node_modules/@react-native/normalize-colors": { "version": "0.74.89", "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.89.tgz", - "integrity": "sha512-qoMMXddVKVhZ8PA1AbUCk83trpd6N+1nF2A6k1i6LsQObyS92fELuk8kU/lQs6M7BsMHwqyLCpQJ1uFgNvIQXg==" + "integrity": "sha512-qoMMXddVKVhZ8PA1AbUCk83trpd6N+1nF2A6k1i6LsQObyS92fELuk8kU/lQs6M7BsMHwqyLCpQJ1uFgNvIQXg==", + "optional": true, + "peer": true }, "node_modules/react-native-web/node_modules/memoize-one": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", - "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", + "optional": true, + "peer": true }, "node_modules/react-native/node_modules/@react-native/virtualized-lists": { "version": "0.79.5", @@ -9246,25 +9354,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-server-dom-webpack": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/react-server-dom-webpack/-/react-server-dom-webpack-19.0.0.tgz", - "integrity": "sha512-hLug9KEXLc8vnU9lDNe2b2rKKDaqrp5gNiES4uyu2Up3FZfZJZmdwLFXlWzdA9gTB/6/cWduSB2K1Lfag2pSvw==", - "dev": true, - "dependencies": { - "acorn-loose": "^8.3.0", - "neo-async": "^2.6.1", - "webpack-sources": "^3.2.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "peerDependencies": { - "react": "^19.0.0", - "react-dom": "^19.0.0", - "webpack": "^5.59.0" - } - }, "node_modules/react-test-renderer": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-19.0.0.tgz", @@ -9780,7 +9869,9 @@ "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "optional": true, + "peer": true }, "node_modules/setprototypeof": { "version": "1.2.0", @@ -10178,7 +10269,9 @@ "node_modules/styleq": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/styleq/-/styleq-0.1.3.tgz", - "integrity": "sha512-3ZUifmCDCQanjeej1f6kyl/BeP/Vae5EYkQ9iJfUm/QwZvlgnZzyflqAsAWYURdtea8Vkvswu2GrC57h3qffcA==" + "integrity": "sha512-3ZUifmCDCQanjeej1f6kyl/BeP/Vae5EYkQ9iJfUm/QwZvlgnZzyflqAsAWYURdtea8Vkvswu2GrC57h3qffcA==", + "optional": true, + "peer": true }, "node_modules/sucrase": { "version": "3.35.0", @@ -10250,13 +10343,17 @@ "dev": true }, "node_modules/tapable": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", - "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz", + "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==", "dev": true, "peer": true, "engines": { "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/tar": { @@ -10591,6 +10688,8 @@ "url": "https://github.com/sponsors/faisalman" } ], + "optional": true, + "peer": true, "bin": { "ua-parser-js": "script/cli.js" }, @@ -10838,9 +10937,9 @@ } }, "node_modules/webpack": { - "version": "5.101.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.101.2.tgz", - "integrity": "sha512-4JLXU0tD6OZNVqlwzm3HGEhAHufSiyv+skb7q0d2367VDMzrU1Q/ZeepvkcHH0rZie6uqEtTQQe0OEOOluH3Mg==", + "version": "5.101.3", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.101.3.tgz", + "integrity": "sha512-7b0dTKR3Ed//AD/6kkx/o7duS8H3f1a4w3BYpIriX4BzIhjkn4teo05cptsxvLesHFKK5KObnadmCHBwGc+51A==", "dev": true, "peer": true, "dependencies": { diff --git a/package.json b/package.json index ea262a9..ee588c7 100644 --- a/package.json +++ b/package.json @@ -18,10 +18,12 @@ "@react-navigation/native": "^7.1.6", "adhan": "^4.4.3", "expo": "~53.0.20", + "expo-file-system": "~18.1.11", "expo-font": "~13.3.2", "expo-linking": "~7.1.7", "expo-localization": "^16.1.6", "expo-router": "~5.1.4", + "expo-sharing": "~13.1.5", "expo-splash-screen": "~0.30.10", "expo-status-bar": "~2.2.3", "expo-system-ui": "~5.0.10", @@ -29,14 +31,12 @@ "expo-web-browser": "~14.2.0", "i18n-js": "^4.5.1", "react": "19.0.0", - "react-dom": "19.0.0", "react-native": "0.79.5", "react-native-picker-select": "^9.3.1", "react-native-reanimated": "~3.17.4", "react-native-safe-area-context": "5.4.0", "react-native-screens": "~4.11.1", - "react-native-vector-icons": "^10.3.0", - "react-native-web": "~0.19.6" + "react-native-vector-icons": "^10.3.0" }, "devDependencies": { "@babel/core": "^7.20.0", diff --git a/utils/pdf.ts b/utils/pdf.ts new file mode 100644 index 0000000..4d7ccf3 --- /dev/null +++ b/utils/pdf.ts @@ -0,0 +1,16 @@ +import * as FileSystem from 'expo-file-system'; +import * as Linking from 'expo-linking'; +import { Asset } from 'expo-asset'; + +export const openPdf = async () => { + // const asset = Asset.fromModule(require('../assets/pdf/quran_in_turkmen.pdf')); + // const localUri = `${FileSystem.documentDirectory}${asset.name}`; + + // const fileInfo = await FileSystem.getInfoAsync(localUri); + + // if (!fileInfo.exists) { + // await FileSystem.downloadAsync(asset.uri, localUri); + // } + + // await Linking.openURL(localUri); +};